Merge "WM: Fix Letterbox placement"
diff --git a/Android.bp b/Android.bp
index e096c9d..93c94e3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -151,6 +151,7 @@
         "core/java/android/content/pm/dex/IArtManager.aidl",
         "core/java/android/content/pm/dex/ISnapshotRuntimeProfileCallback.aidl",
         "core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl",
+        "core/java/android/content/rollback/IRollbackManager.aidl",
         "core/java/android/database/IContentObserver.aidl",
         "core/java/android/debug/IAdbManager.aidl",
         "core/java/android/debug/IAdbTransport.aidl",
@@ -207,6 +208,8 @@
         "core/java/android/hardware/usb/IUsbSerialReader.aidl",
         "core/java/android/net/ICaptivePortal.aidl",
         "core/java/android/net/IConnectivityManager.aidl",
+        "core/java/android/hardware/ISensorPrivacyListener.aidl",
+        "core/java/android/hardware/ISensorPrivacyManager.aidl",
         "core/java/android/net/IIpConnectivityMetrics.aidl",
         "core/java/android/net/IEthernetManager.aidl",
         "core/java/android/net/IEthernetServiceListener.aidl",
@@ -266,7 +269,7 @@
         "core/java/android/os/storage/IStorageEventListener.aidl",
         "core/java/android/os/storage/IStorageShutdownObserver.aidl",
         "core/java/android/os/storage/IObbActionListener.aidl",
-        "core/java/android/permission/IRuntimePermissionPresenter.aidl",
+        "core/java/android/permission/IPermissionController.aidl",
         "core/java/android/rolecontrollerservice/IRoleControllerService.aidl",
         ":keystore_aidl",
         "core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl",
@@ -355,6 +358,7 @@
         "core/java/android/view/autofill/IAutoFillManagerClient.aidl",
         "core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl",
         "core/java/android/view/autofill/IAutofillWindowPresenter.aidl",
+        "core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl",
         "core/java/android/view/contentcapture/IContentCaptureManager.aidl",
         "core/java/android/view/IApplicationToken.aidl",
         "core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl",
@@ -385,6 +389,7 @@
         "core/java/android/speech/tts/ITextToSpeechService.aidl",
         "core/java/com/android/internal/app/IAppOpsActiveCallback.aidl",
         "core/java/com/android/internal/app/IAppOpsCallback.aidl",
+        "core/java/com/android/internal/app/IAppOpsNotedCallback.aidl",
         "core/java/com/android/internal/app/IAppOpsService.aidl",
         "core/java/com/android/internal/app/IBatteryStats.aidl",
         "core/java/com/android/internal/app/ISoundTriggerService.aidl",
@@ -457,11 +462,13 @@
         "media/java/android/media/IAudioRoutesObserver.aidl",
         "media/java/android/media/IAudioService.aidl",
         "media/java/android/media/IAudioServerStateDispatcher.aidl",
+        "media/java/android/media/IMediaController2.aidl",
         "media/java/android/media/IMediaHTTPConnection.aidl",
         "media/java/android/media/IMediaHTTPService.aidl",
         "media/java/android/media/IMediaResourceMonitor.aidl",
         "media/java/android/media/IMediaRouterClient.aidl",
         "media/java/android/media/IMediaRouterService.aidl",
+        "media/java/android/media/IMediaSession2.aidl",
         "media/java/android/media/IMediaScannerListener.aidl",
         "media/java/android/media/IMediaScannerService.aidl",
         "media/java/android/media/IPlaybackConfigDispatcher.aidl",
@@ -517,6 +524,8 @@
         "telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl",
         "telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl",
         "telecomm/java/com/android/internal/telecom/IInCallService.aidl",
+        "telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.aidl",
+        "telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl",
         "telecomm/java/com/android/internal/telecom/ITelecomService.aidl",
         "telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl",
         "telephony/java/android/telephony/data/IDataService.aidl",
@@ -625,6 +634,7 @@
         "wifi/java/android/net/wifi/rtt/IRttCallback.aidl",
         "wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl",
         "wifi/java/android/net/wifi/hotspot2/IProvisioningCallback.aidl",
+        "wifi/java/android/net/wifi/IDppCallback.aidl",
         "wifi/java/android/net/wifi/IWifiScanner.aidl",
         "packages/services/PacProcessor/com/android/net/IProxyService.aidl",
         "packages/services/Proxy/com/android/net/IProxyCallback.aidl",
@@ -713,6 +723,7 @@
 
     static_libs: [
         "apex_aidl_interface-java",
+        "networkstack-aidl-interfaces-java",
         "framework-protos",
         "mediaplayer2-protos",
         "android.hidl.base-V1.0-java",
@@ -855,6 +866,17 @@
     output_extension: "srcjar",
 }
 
+// AIDL interfaces between the core system and the networking mainline module.
+aidl_interface {
+    name: "networkstack-aidl-interfaces",
+    local_include_dir: "core/java",
+    srcs: [
+        "core/java/android/net/INetworkStackConnector.aidl",
+        "core/java/android/net/dhcp/DhcpServingParamsParcel.aidl",
+    ],
+    api_dir: "aidl/networkstack",
+}
+
 // Build ext.jar
 // ============================================================
 java_library {
@@ -1190,6 +1212,7 @@
 metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " +
     "--hide-package com.android.okhttp " +
     "--hide-package com.android.org.conscrypt --hide-package com.android.server " +
+    "--error UnhiddenSystemApi " +
     "--hide RequiresPermission " +
     "--hide MissingPermission --hide BroadcastBehavior " +
     "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
@@ -1606,6 +1629,7 @@
     dex_mapping_filename: "dex-mapping.txt",
     args: metalava_framework_docs_args +
         " --hide ReferencesHidden " +
+        " --hide UnhiddenSystemApi " +
         " --show-unannotated " +
         " --show-annotation android.annotation.SystemApi " +
         " --show-annotation android.annotation.TestApi "
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 478e4fe..30c2c69 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -251,6 +251,8 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/overlay/ExperimentNavigationBarSlim)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/overlay/ExperimentNavigationBarSlim)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/ExperimentNavigationBarSlim)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/SystemUI)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio)
 # ******************************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
 # ******************************************************************
diff --git a/apct-tests/perftests/autofill/Android.mk b/apct-tests/perftests/autofill/Android.mk
index 28555a0..f4da40b 100644
--- a/apct-tests/perftests/autofill/Android.mk
+++ b/apct-tests/perftests/autofill/Android.mk
@@ -22,7 +22,7 @@
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
+    androidx.test.rules \
     androidx.annotation_annotation \
     apct-perftests-utils
 
diff --git a/apct-tests/perftests/autofill/AndroidManifest.xml b/apct-tests/perftests/autofill/AndroidManifest.xml
index e706a32..9c8abc3 100644
--- a/apct-tests/perftests/autofill/AndroidManifest.xml
+++ b/apct-tests/perftests/autofill/AndroidManifest.xml
@@ -35,6 +35,6 @@
 
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.perftests.autofill"/>
 </manifest>
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java b/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
index 86a5c10..6979f0f 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
@@ -16,34 +16,23 @@
 
 package android.view.autofill;
 
+import static org.junit.Assert.assertTrue;
+
 import android.os.Looper;
 import android.perftests.utils.PerfStatusReporter;
 import android.perftests.utils.SettingsHelper;
 import android.perftests.utils.SettingsStateKeeperRule;
-import android.perftests.utils.ShellHelper;
-import android.view.View;
 import android.perftests.utils.StubActivity;
 import android.provider.Settings;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.InstrumentationRegistry;
 
-import com.android.perftests.autofill.R;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
 
-import java.util.Locale;
-import java.util.Collection;
-import java.util.Arrays;
-
-import org.junit.Test;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Rule;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.assertTrue;
 
 /**
  * Base class for all autofill tests.
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
index 62662e4..8090826 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
@@ -16,34 +16,18 @@
 
 package android.view.autofill;
 
-import android.app.Activity;
-import android.os.Looper;
-import android.os.Bundle;
-import android.perftests.utils.PerfStatusReporter;
-import android.util.Log;
-import android.view.View;
-import android.widget.EditText;
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.StubActivity;
-import android.provider.Settings;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.InstrumentationRegistry;
-import com.android.perftests.autofill.R;
-
-import java.util.Locale;
-import java.util.Collection;
-import java.util.Arrays;
-
-import org.junit.Test;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.runner.RunWith;
-
 import static android.view.autofill.AutofillManager.AutofillCallback.EVENT_INPUT_HIDDEN;
 import static android.view.autofill.AutofillManager.AutofillCallback.EVENT_INPUT_SHOWN;
 
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.StubActivity;
+import android.view.View;
+import android.widget.EditText;
+
+import com.android.perftests.autofill.R;
+
+import org.junit.Test;
+
 public class LoginTest extends AbstractAutofillPerfTestCase {
 
     private EditText mUsername;
diff --git a/apct-tests/perftests/multiuser/Android.mk b/apct-tests/perftests/multiuser/Android.mk
index 5ff4ebc..5852044 100644
--- a/apct-tests/perftests/multiuser/Android.mk
+++ b/apct-tests/perftests/multiuser/Android.mk
@@ -20,7 +20,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
+    androidx.test.rules \
     apct-perftests-utils
 
 LOCAL_PACKAGE_NAME := MultiUserPerfTests
diff --git a/apct-tests/perftests/multiuser/AndroidManifest.xml b/apct-tests/perftests/multiuser/AndroidManifest.xml
index adb316f..e96771c 100644
--- a/apct-tests/perftests/multiuser/AndroidManifest.xml
+++ b/apct-tests/perftests/multiuser/AndroidManifest.xml
@@ -25,7 +25,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.perftests.multiuser"/>
 
 </manifest>
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java
index d3a3ce5..ba33e64 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java
@@ -18,9 +18,10 @@
 import android.app.Activity;
 import android.app.Instrumentation;
 import android.os.Bundle;
-import android.support.test.InstrumentationRegistry;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 855be08..2fdba0a 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -27,9 +27,10 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
@@ -50,7 +51,7 @@
  * adb install -r \
  *     ${ANDROID_PRODUCT_OUT}/data/app/MultiUserPerfTests/MultiUserPerfTests.apk &&
  * adb shell am instrument -e class android.multiuser.UserLifecycleTests \
- *     -w com.android.perftests.multiuser/android.support.test.runner.AndroidJUnitRunner
+ *     -w com.android.perftests.multiuser/androidx.test.runner.AndroidJUnitRunner
  *
  * or
  *
diff --git a/apct-tests/perftests/utils/Android.mk b/apct-tests/perftests/utils/Android.mk
index 604f0ad..19f83c8 100644
--- a/apct-tests/perftests/utils/Android.mk
+++ b/apct-tests/perftests/utils/Android.mk
@@ -2,7 +2,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
+    androidx.test.rules \
     androidx.annotation_annotation
 
 # Build all java files in the java subdirectory
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
index da17818..93bf541 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
@@ -20,9 +20,10 @@
 import android.app.Instrumentation;
 import android.os.Bundle;
 import android.os.Debug;
-import android.support.test.InstrumentationRegistry;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+
 import java.io.File;
 import java.util.ArrayList;
 import java.util.concurrent.TimeUnit;
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/PerfManualStatusReporter.java b/apct-tests/perftests/utils/src/android/perftests/utils/PerfManualStatusReporter.java
index 0de6f1d..8187c6f 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/PerfManualStatusReporter.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/PerfManualStatusReporter.java
@@ -16,7 +16,7 @@
 
 package android.perftests.utils;
 
-import android.support.test.InstrumentationRegistry;
+import androidx.test.InstrumentationRegistry;
 
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/PerfStatusReporter.java b/apct-tests/perftests/utils/src/android/perftests/utils/PerfStatusReporter.java
index 4b7b98d..d54a851 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/PerfStatusReporter.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/PerfStatusReporter.java
@@ -16,16 +16,17 @@
 
 package android.perftests.utils;
 
-import android.support.test.InstrumentationRegistry;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
 
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
 /**
  * Use this rule to make sure we report the status after the test success.
  *
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java b/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java
index 895547d..7b52576 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java
@@ -17,14 +17,13 @@
 
 import android.app.UiAutomation;
 import android.os.ParcelFileDescriptor;
-import android.support.test.InstrumentationRegistry;
 import android.text.TextUtils;
 import android.util.AndroidRuntimeException;
-import android.util.Log;
-
-import java.io.FileInputStream;
 
 import androidx.annotation.NonNull;
+import androidx.test.InstrumentationRegistry;
+
+import java.io.FileInputStream;
 
 /**
  * Provides Shell-based utilities such as running a command.
diff --git a/api/current.txt b/api/current.txt
index 189028b..faf7c24 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -417,7 +417,7 @@
     field public static final int clipOrientation = 16843274; // 0x101020a
     field public static final int clipToPadding = 16842987; // 0x10100eb
     field public static final int closeIcon = 16843905; // 0x1010481
-    field public static final int codes = 16843330; // 0x1010242
+    field public static final deprecated int codes = 16843330; // 0x1010242
     field public static final int collapseColumns = 16843083; // 0x101014b
     field public static final int collapseContentDescription = 16843984; // 0x10104d0
     field public static final int collapseIcon = 16844031; // 0x10104ff
@@ -718,7 +718,7 @@
     field public static final int homeAsUpIndicator = 16843531; // 0x101030b
     field public static final int homeLayout = 16843549; // 0x101031d
     field public static final int horizontalDivider = 16843053; // 0x101012d
-    field public static final int horizontalGap = 16843327; // 0x101023f
+    field public static final deprecated int horizontalGap = 16843327; // 0x101023f
     field public static final int horizontalScrollViewStyle = 16843603; // 0x1010353
     field public static final int horizontalSpacing = 16843028; // 0x1010114
     field public static final int host = 16842792; // 0x1010028
@@ -726,7 +726,7 @@
     field public static final int hotSpotY = 16844056; // 0x1010518
     field public static final int hyphenationFrequency = 16843998; // 0x10104de
     field public static final int icon = 16842754; // 0x1010002
-    field public static final int iconPreview = 16843337; // 0x1010249
+    field public static final deprecated int iconPreview = 16843337; // 0x1010249
     field public static final int iconSpaceReserved = 16844129; // 0x1010561
     field public static final int iconTint = 16844126; // 0x101055e
     field public static final int iconTintMode = 16844127; // 0x101055f
@@ -787,12 +787,12 @@
     field public static final int isGame = 16843764; // 0x10103f4
     field public static final int isIndicator = 16843079; // 0x1010147
     field public static final int isLightTheme = 16844176; // 0x1010590
-    field public static final int isModifier = 16843334; // 0x1010246
-    field public static final int isRepeatable = 16843336; // 0x1010248
+    field public static final deprecated int isModifier = 16843334; // 0x1010246
+    field public static final deprecated int isRepeatable = 16843336; // 0x1010248
     field public static final int isScrollContainer = 16843342; // 0x101024e
     field public static final int isSplitRequired = 16844177; // 0x1010591
     field public static final int isStatic = 16844122; // 0x101055a
-    field public static final int isSticky = 16843335; // 0x1010247
+    field public static final deprecated int isSticky = 16843335; // 0x1010247
     field public static final int isolatedProcess = 16843689; // 0x10103a9
     field public static final int isolatedSplits = 16844107; // 0x101054b
     field public static final int itemBackground = 16843056; // 0x1010130
@@ -802,27 +802,27 @@
     field public static final int justificationMode = 16844135; // 0x1010567
     field public static final int keepScreenOn = 16843286; // 0x1010216
     field public static final int key = 16843240; // 0x10101e8
-    field public static final int keyBackground = 16843315; // 0x1010233
-    field public static final int keyEdgeFlags = 16843333; // 0x1010245
-    field public static final int keyHeight = 16843326; // 0x101023e
-    field public static final int keyIcon = 16843340; // 0x101024c
-    field public static final int keyLabel = 16843339; // 0x101024b
-    field public static final int keyOutputText = 16843338; // 0x101024a
-    field public static final int keyPreviewHeight = 16843321; // 0x1010239
-    field public static final int keyPreviewLayout = 16843319; // 0x1010237
-    field public static final int keyPreviewOffset = 16843320; // 0x1010238
+    field public static final deprecated int keyBackground = 16843315; // 0x1010233
+    field public static final deprecated int keyEdgeFlags = 16843333; // 0x1010245
+    field public static final deprecated int keyHeight = 16843326; // 0x101023e
+    field public static final deprecated int keyIcon = 16843340; // 0x101024c
+    field public static final deprecated int keyLabel = 16843339; // 0x101024b
+    field public static final deprecated int keyOutputText = 16843338; // 0x101024a
+    field public static final deprecated int keyPreviewHeight = 16843321; // 0x1010239
+    field public static final deprecated int keyPreviewLayout = 16843319; // 0x1010237
+    field public static final deprecated int keyPreviewOffset = 16843320; // 0x1010238
     field public static final int keySet = 16843739; // 0x10103db
-    field public static final int keyTextColor = 16843318; // 0x1010236
-    field public static final int keyTextSize = 16843316; // 0x1010234
-    field public static final int keyWidth = 16843325; // 0x101023d
+    field public static final deprecated int keyTextColor = 16843318; // 0x1010236
+    field public static final deprecated int keyTextSize = 16843316; // 0x1010234
+    field public static final deprecated int keyWidth = 16843325; // 0x101023d
     field public static final int keyboardLayout = 16843691; // 0x10103ab
-    field public static final int keyboardMode = 16843341; // 0x101024d
+    field public static final deprecated int keyboardMode = 16843341; // 0x101024d
     field public static final int keyboardNavigationCluster = 16844096; // 0x1010540
     field public static final int keycode = 16842949; // 0x10100c5
     field public static final int killAfterRestore = 16843420; // 0x101029c
     field public static final int label = 16842753; // 0x1010001
     field public static final int labelFor = 16843718; // 0x10103c6
-    field public static final int labelTextSize = 16843317; // 0x1010235
+    field public static final deprecated int labelTextSize = 16843317; // 0x1010235
     field public static final int languageTag = 16844040; // 0x1010508
     field public static final int largeHeap = 16843610; // 0x101035a
     field public static final int largeScreens = 16843398; // 0x1010286
@@ -940,6 +940,7 @@
     field public static final int menuCategory = 16843230; // 0x10101de
     field public static final int mimeType = 16842790; // 0x1010026
     field public static final int min = 16844089; // 0x1010539
+    field public static final int minAspectRatio = 16844193; // 0x10105a1
     field public static final int minDate = 16843583; // 0x101033f
     field public static final int minEms = 16843098; // 0x101015a
     field public static final int minHeight = 16843072; // 0x1010140
@@ -1046,12 +1047,12 @@
     field public static final int pointerIcon = 16844041; // 0x1010509
     field public static final int popupAnimationStyle = 16843465; // 0x10102c9
     field public static final int popupBackground = 16843126; // 0x1010176
-    field public static final int popupCharacters = 16843332; // 0x1010244
+    field public static final deprecated int popupCharacters = 16843332; // 0x1010244
     field public static final int popupElevation = 16843916; // 0x101048c
     field public static final int popupEnterTransition = 16844063; // 0x101051f
     field public static final int popupExitTransition = 16844064; // 0x1010520
-    field public static final int popupKeyboard = 16843331; // 0x1010243
-    field public static final int popupLayout = 16843323; // 0x101023b
+    field public static final deprecated int popupKeyboard = 16843331; // 0x1010243
+    field public static final deprecated int popupLayout = 16843323; // 0x101023b
     field public static final int popupMenuStyle = 16843520; // 0x1010300
     field public static final int popupTheme = 16843945; // 0x10104a9
     field public static final int popupWindowStyle = 16842870; // 0x1010076
@@ -1150,7 +1151,7 @@
     field public static final int roundIcon = 16844076; // 0x101052c
     field public static final int rowCount = 16843637; // 0x1010375
     field public static final int rowDelay = 16843216; // 0x10101d0
-    field public static final int rowEdgeFlags = 16843329; // 0x1010241
+    field public static final deprecated int rowEdgeFlags = 16843329; // 0x1010241
     field public static final int rowHeight = 16843058; // 0x1010132
     field public static final int rowOrderPreserved = 16843638; // 0x1010376
     field public static final int saveEnabled = 16842983; // 0x10100e7
@@ -1286,7 +1287,7 @@
     field public static final int state_focused = 16842908; // 0x101009c
     field public static final int state_hovered = 16843623; // 0x1010367
     field public static final int state_last = 16842918; // 0x10100a6
-    field public static final int state_long_pressable = 16843324; // 0x101023c
+    field public static final deprecated int state_long_pressable = 16843324; // 0x101023c
     field public static final int state_middle = 16842917; // 0x10100a5
     field public static final int state_multiline = 16843597; // 0x101034d
     field public static final int state_pressed = 16842919; // 0x10100a7
@@ -1524,9 +1525,9 @@
     field public static final int versionCodeMajor = 16844150; // 0x1010576
     field public static final int versionMajor = 16844151; // 0x1010577
     field public static final int versionName = 16843292; // 0x101021c
-    field public static final int verticalCorrection = 16843322; // 0x101023a
+    field public static final deprecated int verticalCorrection = 16843322; // 0x101023a
     field public static final int verticalDivider = 16843054; // 0x101012e
-    field public static final int verticalGap = 16843328; // 0x1010240
+    field public static final deprecated int verticalGap = 16843328; // 0x1010240
     field public static final int verticalScrollbarPosition = 16843572; // 0x1010334
     field public static final int verticalSpacing = 16843029; // 0x1010115
     field public static final int viewportHeight = 16843779; // 0x1010403
@@ -1894,7 +1895,7 @@
     field public static final int input = 16908297; // 0x1020009
     field public static final int inputArea = 16908318; // 0x102001e
     field public static final int inputExtractEditText = 16908325; // 0x1020025
-    field public static final int keyboardView = 16908326; // 0x1020026
+    field public static final deprecated int keyboardView = 16908326; // 0x1020026
     field public static final int list = 16908298; // 0x102000a
     field public static final int list_container = 16908351; // 0x102003f
     field public static final int mask = 16908334; // 0x102002e
@@ -2605,7 +2606,7 @@
     field public static final int Widget_Holo_WebView = 16973993; // 0x10300a9
     field public static final int Widget_ImageButton = 16973862; // 0x1030026
     field public static final int Widget_ImageWell = 16973861; // 0x1030025
-    field public static final int Widget_KeyboardView = 16973911; // 0x1030057
+    field public static final deprecated int Widget_KeyboardView = 16973911; // 0x1030057
     field public static final int Widget_ListPopupWindow = 16973957; // 0x1030085
     field public static final int Widget_ListView = 16973870; // 0x103002e
     field public static final int Widget_ListView_DropDown = 16973872; // 0x1030030
@@ -3933,6 +3934,7 @@
     method public android.app.PendingIntent getRunningServiceControlPanel(android.content.ComponentName) throws java.lang.SecurityException;
     method public deprecated java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException;
     method public deprecated java.util.List<android.app.ActivityManager.RunningTaskInfo> getRunningTasks(int) throws java.lang.SecurityException;
+    method public boolean isActivityStartAllowedOnDisplay(android.content.Context, int, android.content.Intent);
     method public boolean isBackgroundRestricted();
     method public deprecated boolean isInLockTaskMode();
     method public boolean isLowRamDevice();
@@ -4432,11 +4434,12 @@
   }
 
   public final class AutomaticZenRule implements android.os.Parcelable {
-    ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean);
-    ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, android.service.notification.ZenPolicy, boolean);
+    ctor public deprecated AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean);
+    ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.content.ComponentName, android.net.Uri, android.service.notification.ZenPolicy, int, boolean);
     ctor public AutomaticZenRule(android.os.Parcel);
     method public int describeContents();
     method public android.net.Uri getConditionId();
+    method public android.content.ComponentName getConfigurationActivity();
     method public long getCreationTime();
     method public int getInterruptionFilter();
     method public java.lang.String getName();
@@ -4444,6 +4447,7 @@
     method public android.service.notification.ZenPolicy getZenPolicy();
     method public boolean isEnabled();
     method public void setConditionId(android.net.Uri);
+    method public void setConfigurationActivity(android.content.ComponentName);
     method public void setEnabled(boolean);
     method public void setInterruptionFilter(int);
     method public void setName(java.lang.String);
@@ -5381,11 +5385,11 @@
     method public android.graphics.drawable.Icon getIcon();
     method public android.app.RemoteInput[] getRemoteInputs();
     method public int getSemanticAction();
+    method public boolean isContextual();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.Notification.Action> CREATOR;
     field public static final int SEMANTIC_ACTION_ARCHIVE = 5; // 0x5
     field public static final int SEMANTIC_ACTION_CALL = 10; // 0xa
-    field public static final int SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION = 11; // 0xb
     field public static final int SEMANTIC_ACTION_DELETE = 4; // 0x4
     field public static final int SEMANTIC_ACTION_MARK_AS_READ = 2; // 0x2
     field public static final int SEMANTIC_ACTION_MARK_AS_UNREAD = 3; // 0x3
@@ -5410,6 +5414,7 @@
     method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Extender);
     method public android.os.Bundle getExtras();
     method public android.app.Notification.Action.Builder setAllowGeneratedReplies(boolean);
+    method public android.app.Notification.Action.Builder setContextual(boolean);
     method public android.app.Notification.Action.Builder setSemanticAction(int);
   }
 
@@ -5727,7 +5732,6 @@
 
   public final class NotificationChannelGroup implements android.os.Parcelable {
     ctor public NotificationChannelGroup(java.lang.String, java.lang.CharSequence);
-    method public boolean canOverlayApps();
     method public android.app.NotificationChannelGroup clone();
     method public int describeContents();
     method public java.util.List<android.app.NotificationChannel> getChannels();
@@ -5735,7 +5739,6 @@
     method public java.lang.String getId();
     method public java.lang.CharSequence getName();
     method public boolean isBlocked();
-    method public void setAllowAppOverlay(boolean);
     method public void setDescription(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.NotificationChannelGroup> CREATOR;
@@ -5743,6 +5746,7 @@
 
   public class NotificationManager {
     method public java.lang.String addAutomaticZenRule(android.app.AutomaticZenRule);
+    method public boolean areAppOverlaysAllowed();
     method public boolean areNotificationsEnabled();
     method public boolean canNotifyAsPackage(java.lang.String);
     method public void cancel(int);
@@ -5772,16 +5776,19 @@
     method public void notifyAsPackage(java.lang.String, java.lang.String, int, android.app.Notification);
     method public boolean removeAutomaticZenRule(java.lang.String);
     method public void revokeNotificationDelegate();
+    method public void setAutomaticZenRuleState(java.lang.String, android.service.notification.Condition);
     method public final void setInterruptionFilter(int);
     method public void setNotificationDelegate(java.lang.String);
     method public void setNotificationPolicy(android.app.NotificationManager.Policy);
     method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
     field public static final java.lang.String ACTION_APP_BLOCK_STATE_CHANGED = "android.app.action.APP_BLOCK_STATE_CHANGED";
+    field public static final java.lang.String ACTION_AUTOMATIC_ZEN_RULE = "android.app.action.AUTOMATIC_ZEN_RULE";
     field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
     field public static final java.lang.String ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED = "android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED";
     field public static final java.lang.String ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED = "android.app.action.NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED";
+    field public static final java.lang.String EXTRA_AUTOMATIC_RULE_ID = "android.app.extra.AUTOMATIC_RULE_ID";
     field public static final java.lang.String EXTRA_BLOCKED_STATE = "android.app.extra.BLOCKED_STATE";
     field public static final java.lang.String EXTRA_NOTIFICATION_CHANNEL_GROUP_ID = "android.app.extra.NOTIFICATION_CHANNEL_GROUP_ID";
     field public static final java.lang.String EXTRA_NOTIFICATION_CHANNEL_ID = "android.app.extra.NOTIFICATION_CHANNEL_ID";
@@ -5797,6 +5804,8 @@
     field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3
     field public static final int INTERRUPTION_FILTER_PRIORITY = 2; // 0x2
     field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0
+    field public static final java.lang.String META_DATA_AUTOMATIC_RULE_TYPE = "android.app.automatic.ruleType";
+    field public static final java.lang.String META_DATA_RULE_INSTANCE_LIMIT = "android.app.zen.automatic.ruleInstanceLimit";
   }
 
   public static class NotificationManager.Policy implements android.os.Parcelable {
@@ -10191,6 +10200,7 @@
     field public static final java.lang.String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED";
     field public static final java.lang.String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED";
     field public static final java.lang.String ACTION_PASTE = "android.intent.action.PASTE";
+    field public static final java.lang.String ACTION_PERMISSION_USAGE_DETAILS = "android.intent.action.PERMISSION_USAGE_DETAILS";
     field public static final java.lang.String ACTION_PICK = "android.intent.action.PICK";
     field public static final java.lang.String ACTION_PICK_ACTIVITY = "android.intent.action.PICK_ACTIVITY";
     field public static final java.lang.String ACTION_POWER_CONNECTED = "android.intent.action.ACTION_POWER_CONNECTED";
@@ -10260,6 +10270,7 @@
     field public static final java.lang.String CATEGORY_OPENABLE = "android.intent.category.OPENABLE";
     field public static final java.lang.String CATEGORY_PREFERENCE = "android.intent.category.PREFERENCE";
     field public static final java.lang.String CATEGORY_SAMPLE_CODE = "android.intent.category.SAMPLE_CODE";
+    field public static final java.lang.String CATEGORY_SECONDARY_HOME = "android.intent.category.SECONDARY_HOME";
     field public static final java.lang.String CATEGORY_SELECTED_ALTERNATIVE = "android.intent.category.SELECTED_ALTERNATIVE";
     field public static final java.lang.String CATEGORY_TAB = "android.intent.category.TAB";
     field public static final java.lang.String CATEGORY_TEST = "android.intent.category.TEST";
@@ -10314,6 +10325,7 @@
     field public static final java.lang.String EXTRA_NOT_UNKNOWN_SOURCE = "android.intent.extra.NOT_UNKNOWN_SOURCE";
     field public static final java.lang.String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI";
     field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
+    field public static final java.lang.String EXTRA_PERMISSION_USAGE_PERMISSIONS = "android.intent.extra.PERMISSION_USAGE_PERMISSIONS";
     field public static final java.lang.String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
     field public static final java.lang.String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
     field public static final java.lang.String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY";
@@ -11294,6 +11306,7 @@
     method public void updateSessionAppLabel(int, java.lang.CharSequence);
     field public static final java.lang.String ACTION_SESSION_COMMITTED = "android.content.pm.action.SESSION_COMMITTED";
     field public static final java.lang.String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
+    field public static final java.lang.String ACTION_SESSION_UPDATED = "android.content.pm.action.SESSION_UPDATED";
     field public static final java.lang.String EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME";
     field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
     field public static final java.lang.String EXTRA_SESSION = "android.content.pm.extra.SESSION";
@@ -11358,13 +11371,20 @@
     method public android.net.Uri getReferrerUri();
     method public int getSessionId();
     method public long getSize();
+    method public int getStagedSessionErrorCode();
     method public boolean isActive();
     method public boolean isMultiPackage();
     method public boolean isSealed();
+    method public boolean isSessionApplied();
+    method public boolean isSessionFailed();
+    method public boolean isSessionReady();
     method public boolean isStaged();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int ACTIVATION_FAILED = 2; // 0x2
     field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionInfo> CREATOR;
     field public static final int INVALID_ID = -1; // 0xffffffff
+    field public static final int NO_ERROR = 0; // 0x0
+    field public static final int VERIFICATION_FAILED = 1; // 0x1
   }
 
   public static class PackageInstaller.SessionParams implements android.os.Parcelable {
@@ -11557,18 +11577,19 @@
     field public static final java.lang.String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
     field public static final java.lang.String FEATURE_EMBEDDED = "android.hardware.type.embedded";
     field public static final java.lang.String FEATURE_ETHERNET = "android.hardware.ethernet";
-    field public static final java.lang.String FEATURE_FACE = "android.hardware.face";
+    field public static final java.lang.String FEATURE_FACE = "android.hardware.biometrics.face";
     field public static final java.lang.String FEATURE_FAKETOUCH = "android.hardware.faketouch";
     field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
     field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand";
-    field public static final java.lang.String FEATURE_FINGERPRINT = "android.hardware.fingerprint";
+    field public static final java.lang.String FEATURE_FINGERPRINT = "android.hardware.biometrics.fingerprint";
+    field public static final java.lang.String FEATURE_FINGERPRINT_PRE_29 = "android.hardware.fingerprint";
     field public static final java.lang.String FEATURE_FREEFORM_WINDOW_MANAGEMENT = "android.software.freeform_window_management";
     field public static final java.lang.String FEATURE_GAMEPAD = "android.hardware.gamepad";
     field public static final java.lang.String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors";
     field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen";
     field public static final java.lang.String FEATURE_INPUT_METHODS = "android.software.input_methods";
     field public static final java.lang.String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
-    field public static final java.lang.String FEATURE_IRIS = "android.hardware.iris";
+    field public static final java.lang.String FEATURE_IRIS = "android.hardware.biometrics.iris";
     field public static final java.lang.String FEATURE_LEANBACK = "android.software.leanback";
     field public static final java.lang.String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
     field public static final java.lang.String FEATURE_LIVE_TV = "android.software.live_tv";
@@ -11580,8 +11601,11 @@
     field public static final java.lang.String FEATURE_MICROPHONE = "android.hardware.microphone";
     field public static final java.lang.String FEATURE_MIDI = "android.software.midi";
     field public static final java.lang.String FEATURE_NFC = "android.hardware.nfc";
+    field public static final java.lang.String FEATURE_NFC_BEAM = "android.sofware.nfc.beam";
     field public static final java.lang.String FEATURE_NFC_HOST_CARD_EMULATION = "android.hardware.nfc.hce";
     field public static final java.lang.String FEATURE_NFC_HOST_CARD_EMULATION_NFCF = "android.hardware.nfc.hcef";
+    field public static final java.lang.String FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE = "android.hardware.nfc.ese";
+    field public static final java.lang.String FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC = "android.hardware.nfc.uicc";
     field public static final java.lang.String FEATURE_OPENGLES_EXTENSION_PACK = "android.hardware.opengles.aep";
     field public static final java.lang.String FEATURE_PC = "android.hardware.type.pc";
     field public static final java.lang.String FEATURE_PICTURE_IN_PICTURE = "android.software.picture_in_picture";
@@ -14118,8 +14142,11 @@
 
   public final class Insets {
     method public static android.graphics.Insets add(android.graphics.Insets, android.graphics.Insets);
+    method public static android.graphics.Insets max(android.graphics.Insets, android.graphics.Insets);
+    method public static android.graphics.Insets min(android.graphics.Insets, android.graphics.Insets);
     method public static android.graphics.Insets of(int, int, int, int);
     method public static android.graphics.Insets of(android.graphics.Rect);
+    method public static android.graphics.Insets subtract(android.graphics.Insets, android.graphics.Insets);
     field public static final android.graphics.Insets NONE;
     field public final int bottom;
     field public final int left;
@@ -14853,6 +14880,7 @@
     method public int getAmbientShadowColor();
     method public int getBottom();
     method public float getCameraDistance();
+    method public boolean getClipToBounds();
     method public boolean getClipToOutline();
     method public float getElevation();
     method public int getHeight();
@@ -14873,6 +14901,7 @@
     method public float getTranslationY();
     method public float getTranslationZ();
     method public long getUniqueId();
+    method public boolean getUseCompositingLayer();
     method public int getWidth();
     method public boolean hasDisplayList();
     method public boolean hasIdentityMatrix();
@@ -22381,7 +22410,7 @@
     field public int visibleTopInsets;
   }
 
-  public class Keyboard {
+  public deprecated class Keyboard {
     ctor public Keyboard(android.content.Context, int);
     ctor public Keyboard(android.content.Context, int, int, int, int);
     ctor public Keyboard(android.content.Context, int, int);
@@ -22455,7 +22484,7 @@
     field public int verticalGap;
   }
 
-  public class KeyboardView extends android.view.View implements android.view.View.OnClickListener {
+  public deprecated class KeyboardView extends android.view.View implements android.view.View.OnClickListener {
     ctor public KeyboardView(android.content.Context, android.util.AttributeSet);
     ctor public KeyboardView(android.content.Context, android.util.AttributeSet, int);
     ctor public KeyboardView(android.content.Context, android.util.AttributeSet, int, int);
@@ -23361,12 +23390,13 @@
     method public android.media.AudioPresentation.Builder setProgramId(int);
   }
 
-  public class AudioRecord implements android.media.AudioRouting {
+  public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting {
     ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
     method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public deprecated void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
     method protected void finalize();
     method public java.util.List<android.media.MicrophoneInfo> getActiveMicrophones() throws java.io.IOException;
+    method public android.media.AudioRecordingConfiguration getActiveRecordingConfiguration();
     method public int getAudioFormat();
     method public int getAudioSessionId();
     method public int getAudioSource();
@@ -23391,6 +23421,7 @@
     method public int read(float[], int, int, int);
     method public int read(java.nio.ByteBuffer, int);
     method public int read(java.nio.ByteBuffer, int, int);
+    method public void registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback);
     method public void release();
     method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public deprecated void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
@@ -23402,6 +23433,7 @@
     method public void startRecording() throws java.lang.IllegalStateException;
     method public void startRecording(android.media.MediaSyncEvent) throws java.lang.IllegalStateException;
     method public void stop() throws java.lang.IllegalStateException;
+    method public void unregisterAudioRecordingCallback(android.media.AudioManager.AudioRecordingCallback);
     field public static final int ERROR = -1; // 0xffffffff
     field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
     field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa
@@ -23444,14 +23476,24 @@
   public final class AudioRecordingConfiguration implements android.os.Parcelable {
     method public int describeContents();
     method public android.media.AudioDeviceInfo getAudioDevice();
+    method public int getAudioSource();
     method public int getClientAudioSessionId();
     method public int getClientAudioSource();
+    method public java.util.List<android.media.audiofx.AudioEffect.Descriptor> getClientEffects();
     method public android.media.AudioFormat getClientFormat();
+    method public java.util.List<android.media.audiofx.AudioEffect.Descriptor> getEffects();
     method public android.media.AudioFormat getFormat();
+    method public boolean isClientSilenced();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.media.AudioRecordingConfiguration> CREATOR;
   }
 
+  public abstract interface AudioRecordingMonitor {
+    method public abstract android.media.AudioRecordingConfiguration getActiveRecordingConfiguration();
+    method public abstract void registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback);
+    method public abstract void unregisterAudioRecordingCallback(android.media.AudioManager.AudioRecordingCallback);
+  }
+
   public abstract interface AudioRouting {
     method public abstract void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public abstract android.media.AudioDeviceInfo getPreferredDevice();
@@ -24903,6 +24945,25 @@
     field public static final int TYPE_STRING = 4; // 0x4
   }
 
+  public final class MediaItem2 implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getEndPosition();
+    method public android.media.MediaMetadata getMetadata();
+    method public long getStartPosition();
+    method public void setMetadata(android.media.MediaMetadata);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.media.MediaItem2> CREATOR;
+    field public static final long POSITION_UNKNOWN = 576460752303423487L; // 0x7ffffffffffffffL
+  }
+
+  public static class MediaItem2.Builder {
+    ctor public MediaItem2.Builder();
+    method public android.media.MediaItem2 build();
+    method public android.media.MediaItem2.Builder setEndPosition(long);
+    method public android.media.MediaItem2.Builder setMetadata(android.media.MediaMetadata);
+    method public android.media.MediaItem2.Builder setStartPosition(long);
+  }
+
   public final class MediaMetadata implements android.os.Parcelable {
     method public boolean containsKey(java.lang.String);
     method public int describeContents();
@@ -24977,8 +25038,9 @@
     field public static final int RATING_KEY_BY_USER = 268435457; // 0x10000001
   }
 
-  public class MediaMetadataRetriever {
+  public class MediaMetadataRetriever implements java.lang.AutoCloseable {
     ctor public MediaMetadataRetriever();
+    method public void close();
     method public java.lang.String extractMetadata(int);
     method public byte[] getEmbeddedPicture();
     method public android.graphics.Bitmap getFrameAtIndex(int, android.media.MediaMetadataRetriever.BitmapParams);
@@ -25300,24 +25362,24 @@
     method public java.lang.Object clearNextDataSources();
     method public void clearPendingCommands();
     method public void close();
-    method public java.lang.Object deselectTrack(int);
+    method public java.lang.Object deselectTrack(android.media.DataSourceDesc, int);
     method public android.media.AudioAttributes getAudioAttributes();
     method public int getAudioSessionId();
-    method public long getBufferedPosition();
+    method public long getBufferedPosition(android.media.DataSourceDesc);
     method public android.media.DataSourceDesc getCurrentDataSource();
     method public long getCurrentPosition();
-    method public long getDuration();
+    method public long getDuration(android.media.DataSourceDesc);
     method public float getMaxPlayerVolume();
     method public android.os.PersistableBundle getMetrics();
     method public android.media.PlaybackParams getPlaybackParams();
     method public float getPlayerVolume();
     method public android.media.AudioDeviceInfo getPreferredDevice();
     method public android.media.AudioDeviceInfo getRoutedDevice();
-    method public int getSelectedTrack(int);
+    method public int getSelectedTrack(android.media.DataSourceDesc, int);
     method public int getState();
     method public android.media.SyncParams getSyncParams();
     method public android.media.MediaTimestamp getTimestamp();
-    method public java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo();
+    method public java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo(android.media.DataSourceDesc);
     method public android.media.VideoSize getVideoSize();
     method public boolean isLooping();
     method public java.lang.Object loopCurrent(boolean);
@@ -25330,7 +25392,7 @@
     method public void reset();
     method public java.lang.Object seekTo(long);
     method public java.lang.Object seekTo(long, int);
-    method public java.lang.Object selectTrack(int);
+    method public java.lang.Object selectTrack(android.media.DataSourceDesc, int);
     method public java.lang.Object setAudioAttributes(android.media.AudioAttributes);
     method public java.lang.Object setAudioSessionId(int);
     method public java.lang.Object setAuxEffectSendLevel(float);
@@ -25452,11 +25514,12 @@
     field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1
   }
 
-  public class MediaRecorder implements android.media.AudioRouting {
+  public class MediaRecorder implements android.media.AudioRecordingMonitor android.media.AudioRouting {
     ctor public MediaRecorder();
     method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method protected void finalize();
     method public java.util.List<android.media.MicrophoneInfo> getActiveMicrophones() throws java.io.IOException;
+    method public android.media.AudioRecordingConfiguration getActiveRecordingConfiguration();
     method public static final int getAudioSourceMax();
     method public int getMaxAmplitude() throws java.lang.IllegalStateException;
     method public android.os.PersistableBundle getMetrics();
@@ -25465,6 +25528,7 @@
     method public android.view.Surface getSurface();
     method public void pause() throws java.lang.IllegalStateException;
     method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
+    method public void registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback);
     method public void release();
     method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public void reset();
@@ -25500,6 +25564,7 @@
     method public void setVideoSource(int) throws java.lang.IllegalStateException;
     method public void start() throws java.lang.IllegalStateException;
     method public void stop() throws java.lang.IllegalStateException;
+    method public void unregisterAudioRecordingCallback(android.media.AudioManager.AudioRecordingCallback);
     field public static final int MEDIA_ERROR_SERVER_DIED = 100; // 0x64
     field public static final int MEDIA_RECORDER_ERROR_UNKNOWN = 1; // 0x1
     field public static final int MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800; // 0x320
@@ -26065,7 +26130,12 @@
 
   public class ThumbnailUtils {
     ctor public ThumbnailUtils();
-    method public static android.graphics.Bitmap createVideoThumbnail(java.lang.String, int);
+    method public static deprecated android.graphics.Bitmap createAudioThumbnail(java.lang.String, int);
+    method public static android.graphics.Bitmap createAudioThumbnail(java.io.File, android.util.Size, android.os.CancellationSignal) throws java.io.IOException;
+    method public static deprecated android.graphics.Bitmap createImageThumbnail(java.lang.String, int);
+    method public static android.graphics.Bitmap createImageThumbnail(java.io.File, android.util.Size, android.os.CancellationSignal) throws java.io.IOException;
+    method public static deprecated android.graphics.Bitmap createVideoThumbnail(java.lang.String, int);
+    method public static android.graphics.Bitmap createVideoThumbnail(java.io.File, android.util.Size, android.os.CancellationSignal) throws java.io.IOException;
     method public static android.graphics.Bitmap extractThumbnail(android.graphics.Bitmap, int, int);
     method public static android.graphics.Bitmap extractThumbnail(android.graphics.Bitmap, int, int, int);
     field public static final int OPTIONS_RECYCLE_INPUT = 2; // 0x2
@@ -28200,7 +28270,7 @@
     method public void addDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
     method public boolean bindProcessToNetwork(android.net.Network);
     method public android.net.Network getActiveNetwork();
-    method public android.net.NetworkInfo getActiveNetworkInfo();
+    method public deprecated android.net.NetworkInfo getActiveNetworkInfo();
     method public deprecated android.net.NetworkInfo[] getAllNetworkInfo();
     method public android.net.Network[] getAllNetworks();
     method public deprecated boolean getBackgroundDataSetting();
@@ -28211,7 +28281,7 @@
     method public int getMultipathPreference(android.net.Network);
     method public android.net.NetworkCapabilities getNetworkCapabilities(android.net.Network);
     method public deprecated android.net.NetworkInfo getNetworkInfo(int);
-    method public android.net.NetworkInfo getNetworkInfo(android.net.Network);
+    method public deprecated android.net.NetworkInfo getNetworkInfo(android.net.Network);
     method public deprecated int getNetworkPreference();
     method public byte[] getNetworkWatchlistConfigHash();
     method public static deprecated android.net.Network getProcessDefaultNetwork();
@@ -28245,14 +28315,14 @@
     field public static final deprecated int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
     field public static final java.lang.String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL";
     field public static final java.lang.String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL";
-    field public static final java.lang.String EXTRA_EXTRA_INFO = "extraInfo";
-    field public static final java.lang.String EXTRA_IS_FAILOVER = "isFailover";
+    field public static final deprecated java.lang.String EXTRA_EXTRA_INFO = "extraInfo";
+    field public static final deprecated java.lang.String EXTRA_IS_FAILOVER = "isFailover";
     field public static final java.lang.String EXTRA_NETWORK = "android.net.extra.NETWORK";
     field public static final deprecated java.lang.String EXTRA_NETWORK_INFO = "networkInfo";
     field public static final java.lang.String EXTRA_NETWORK_REQUEST = "android.net.extra.NETWORK_REQUEST";
     field public static final java.lang.String EXTRA_NETWORK_TYPE = "networkType";
     field public static final java.lang.String EXTRA_NO_CONNECTIVITY = "noConnectivity";
-    field public static final java.lang.String EXTRA_OTHER_NETWORK_INFO = "otherNetwork";
+    field public static final deprecated java.lang.String EXTRA_OTHER_NETWORK_INFO = "otherNetwork";
     field public static final java.lang.String EXTRA_REASON = "reason";
     field public static final int MULTIPATH_PREFERENCE_HANDOVER = 1; // 0x1
     field public static final int MULTIPATH_PREFERENCE_PERFORMANCE = 4; // 0x4
@@ -28308,6 +28378,11 @@
     field public int serverAddress;
   }
 
+  public class InetAddresses {
+    method public static boolean isNumericAddress(java.lang.String);
+    method public static java.net.InetAddress parseNumericAddress(java.lang.String);
+  }
+
   public final class IpPrefix implements android.os.Parcelable {
     method public boolean contains(java.net.InetAddress);
     method public int describeContents();
@@ -28399,6 +28474,7 @@
     method public android.net.ProxyInfo getHttpProxy();
     method public java.lang.String getInterfaceName();
     method public java.util.List<android.net.LinkAddress> getLinkAddresses();
+    method public int getMtu();
     method public java.lang.String getPrivateDnsServerName();
     method public java.util.List<android.net.RouteInfo> getRoutes();
     method public boolean isPrivateDnsActive();
@@ -28547,10 +28623,10 @@
     field public static final int TRANSPORT_WIFI_AWARE = 5; // 0x5
   }
 
-  public class NetworkInfo implements android.os.Parcelable {
+  public deprecated class NetworkInfo implements android.os.Parcelable {
     method public int describeContents();
     method public deprecated android.net.NetworkInfo.DetailedState getDetailedState();
-    method public java.lang.String getExtraInfo();
+    method public deprecated java.lang.String getExtraInfo();
     method public deprecated java.lang.String getReason();
     method public deprecated android.net.NetworkInfo.State getState();
     method public deprecated int getSubtype();
@@ -28566,7 +28642,7 @@
     field public static final android.os.Parcelable.Creator<android.net.NetworkInfo> CREATOR;
   }
 
-  public static final class NetworkInfo.DetailedState extends java.lang.Enum {
+  public static final deprecated class NetworkInfo.DetailedState extends java.lang.Enum {
     method public static android.net.NetworkInfo.DetailedState valueOf(java.lang.String);
     method public static final android.net.NetworkInfo.DetailedState[] values();
     enum_constant public static final android.net.NetworkInfo.DetailedState AUTHENTICATING;
@@ -28584,7 +28660,7 @@
     enum_constant public static final android.net.NetworkInfo.DetailedState VERIFYING_POOR_LINK;
   }
 
-  public static final class NetworkInfo.State extends java.lang.Enum {
+  public static final deprecated class NetworkInfo.State extends java.lang.Enum {
     method public static android.net.NetworkInfo.State valueOf(java.lang.String);
     method public static final android.net.NetworkInfo.State[] values();
     enum_constant public static final android.net.NetworkInfo.State CONNECTED;
@@ -29504,6 +29580,7 @@
     method public int getWifiState();
     method public boolean is5GHzBandSupported();
     method public boolean isDeviceToApRttSupported();
+    method public boolean isDppSupported();
     method public boolean isEnhancedPowerReportingSupported();
     method public boolean isOweSupported();
     method public boolean isP2pSupported();
@@ -29638,21 +29715,20 @@
     field public static final android.os.Parcelable.Creator<android.net.wifi.WifiNetworkSuggestion> CREATOR;
   }
 
-  public deprecated class WpsInfo implements android.os.Parcelable {
-    ctor public deprecated WpsInfo();
-    ctor public deprecated WpsInfo(android.net.wifi.WpsInfo);
-    method public deprecated int describeContents();
-    method public deprecated java.lang.String toString();
-    method public deprecated void writeToParcel(android.os.Parcel, int);
-    field public deprecated java.lang.String BSSID;
-    field public static final deprecated android.os.Parcelable.Creator<android.net.wifi.WpsInfo> CREATOR;
-    field public static final deprecated int DISPLAY = 1; // 0x1
-    field public static final deprecated int INVALID = 4; // 0x4
-    field public static final deprecated int KEYPAD = 2; // 0x2
-    field public static final deprecated int LABEL = 3; // 0x3
-    field public static final deprecated int PBC = 0; // 0x0
-    field public deprecated java.lang.String pin;
-    field public deprecated int setup;
+  public class WpsInfo implements android.os.Parcelable {
+    ctor public WpsInfo();
+    ctor public WpsInfo(android.net.wifi.WpsInfo);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public java.lang.String BSSID;
+    field public static final android.os.Parcelable.Creator<android.net.wifi.WpsInfo> CREATOR;
+    field public static final int DISPLAY = 1; // 0x1
+    field public static final int INVALID = 4; // 0x4
+    field public static final int KEYPAD = 2; // 0x2
+    field public static final int LABEL = 3; // 0x3
+    field public static final int PBC = 0; // 0x0
+    field public java.lang.String pin;
+    field public int setup;
   }
 
 }
@@ -29676,8 +29752,8 @@
 
   public class DiscoverySession implements java.lang.AutoCloseable {
     method public void close();
-    method public android.net.NetworkSpecifier createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle);
-    method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String);
+    method public deprecated android.net.NetworkSpecifier createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle);
+    method public deprecated android.net.NetworkSpecifier createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String);
     method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]);
   }
 
@@ -29700,7 +29776,10 @@
     method public void onIdentityChanged(byte[]);
   }
 
-  public class PeerHandle {
+  public final class PeerHandle implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.net.wifi.aware.PeerHandle> CREATOR;
   }
 
   public final class PublishConfig implements android.os.Parcelable {
@@ -29762,6 +29841,21 @@
     field public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1; // 0x1
   }
 
+  public static class WifiAwareManager.NetworkSpecifierBuilder {
+    ctor public WifiAwareManager.NetworkSpecifierBuilder();
+    method public android.net.NetworkSpecifier build();
+    method public android.net.wifi.aware.WifiAwareManager.NetworkSpecifierBuilder setDiscoverySession(android.net.wifi.aware.DiscoverySession);
+    method public android.net.wifi.aware.WifiAwareManager.NetworkSpecifierBuilder setPeerHandle(android.net.wifi.aware.PeerHandle);
+    method public android.net.wifi.aware.WifiAwareManager.NetworkSpecifierBuilder setPskPassphrase(java.lang.String);
+  }
+
+  public final class WifiAwareNetworkInfo implements android.os.Parcelable android.net.TransportInfo {
+    method public int describeContents();
+    method public java.net.Inet6Address getPeerIpv6Addr();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.net.wifi.aware.WifiAwareNetworkInfo> CREATOR;
+  }
+
   public class WifiAwareSession implements java.lang.AutoCloseable {
     method public void close();
     method public android.net.NetworkSpecifier createNetworkSpecifierOpen(int, byte[]);
@@ -29944,6 +30038,7 @@
     ctor public WifiP2pGroup(android.net.wifi.p2p.WifiP2pGroup);
     method public int describeContents();
     method public java.util.Collection<android.net.wifi.p2p.WifiP2pDevice> getClientList();
+    method public int getFrequency();
     method public java.lang.String getInterface();
     method public java.lang.String getNetworkName();
     method public android.net.wifi.p2p.WifiP2pDevice getOwner();
@@ -30220,15 +30315,16 @@
     method public deprecated void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage);
     method public void enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle);
     method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context);
+    method public java.util.List<java.lang.String> getSupportedOffHostSecureElements();
     method public boolean ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler);
-    method public boolean invokeBeam(android.app.Activity);
+    method public deprecated boolean invokeBeam(android.app.Activity);
     method public boolean isEnabled();
-    method public boolean isNdefPushEnabled();
-    method public void setBeamPushUris(android.net.Uri[], android.app.Activity);
-    method public void setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity);
-    method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...);
-    method public void setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...);
-    method public void setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...);
+    method public deprecated boolean isNdefPushEnabled();
+    method public deprecated void setBeamPushUris(android.net.Uri[], android.app.Activity);
+    method public deprecated void setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity);
+    method public deprecated void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...);
+    method public deprecated void setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...);
+    method public deprecated void setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...);
     field public static final java.lang.String ACTION_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED";
     field public static final java.lang.String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
     field public static final java.lang.String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED";
@@ -30255,15 +30351,15 @@
     field public static final int STATE_TURNING_ON = 2; // 0x2
   }
 
-  public static abstract interface NfcAdapter.CreateBeamUrisCallback {
+  public static abstract deprecated interface NfcAdapter.CreateBeamUrisCallback {
     method public abstract android.net.Uri[] createBeamUris(android.nfc.NfcEvent);
   }
 
-  public static abstract interface NfcAdapter.CreateNdefMessageCallback {
+  public static abstract deprecated interface NfcAdapter.CreateNdefMessageCallback {
     method public abstract android.nfc.NdefMessage createNdefMessage(android.nfc.NfcEvent);
   }
 
-  public static abstract interface NfcAdapter.OnNdefPushCompleteCallback {
+  public static abstract deprecated interface NfcAdapter.OnNdefPushCompleteCallback {
     method public abstract void onNdefPushComplete(android.nfc.NfcEvent);
   }
 
@@ -30311,8 +30407,10 @@
     method public boolean isDefaultServiceForCategory(android.content.ComponentName, java.lang.String);
     method public boolean registerAidsForService(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>);
     method public boolean removeAidsForService(android.content.ComponentName, java.lang.String);
+    method public boolean setOffHostForService(android.content.ComponentName, java.lang.String);
     method public boolean setPreferredService(android.app.Activity, android.content.ComponentName);
     method public boolean supportsAidPrefixRegistration();
+    method public boolean unsetOffHostForService(android.content.ComponentName);
     method public boolean unsetPreferredService(android.app.Activity);
     field public static final java.lang.String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
     field public static final java.lang.String CATEGORY_OTHER = "other";
@@ -35103,6 +35201,7 @@
 
   public final class StorageVolume implements android.os.Parcelable {
     method public deprecated android.content.Intent createAccessIntent(java.lang.String);
+    method public android.content.Intent createOpenDocumentTreeIntent();
     method public int describeContents();
     method public java.lang.String getDescription(android.content.Context);
     method public java.lang.String getState();
@@ -36368,6 +36467,12 @@
     field public static final java.lang.String CACHED_NUMBER_TYPE = "numbertype";
     field public static final java.lang.String CACHED_PHOTO_ID = "photo_id";
     field public static final java.lang.String CACHED_PHOTO_URI = "photo_uri";
+    field public static final java.lang.String CALL_ID_APP_NAME = "call_id_app_name";
+    field public static final java.lang.String CALL_ID_DESCRIPTION = "call_id_description";
+    field public static final java.lang.String CALL_ID_DETAILS = "call_id_details";
+    field public static final java.lang.String CALL_ID_NAME = "call_id_name";
+    field public static final java.lang.String CALL_ID_NUISANCE_CONFIDENCE = "call_id_nuisance_confidence";
+    field public static final java.lang.String CALL_ID_PACKAGE_NAME = "call_id_package_name";
     field public static final java.lang.String CALL_SCREENING_APP_NAME = "call_screening_app_name";
     field public static final java.lang.String CALL_SCREENING_COMPONENT_NAME = "call_screening_component_name";
     field public static final android.net.Uri CONTENT_FILTER_URI;
@@ -37782,7 +37887,11 @@
     method public static java.lang.String getVolumeName(android.net.Uri);
     method public static android.provider.MediaStore.PendingSession openPending(android.content.Context, android.net.Uri);
     method public static android.net.Uri setIncludePending(android.net.Uri);
+    method public static android.net.Uri setIncludeTrashed(android.net.Uri);
     method public static android.net.Uri setRequireOriginal(android.net.Uri);
+    method public static void trash(android.content.Context, android.net.Uri);
+    method public static void trash(android.content.Context, android.net.Uri, long);
+    method public static void untrash(android.content.Context, android.net.Uri);
     field public static final java.lang.String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
     field public static final java.lang.String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE";
     field public static final java.lang.String ACTION_REVIEW = "android.provider.action.REVIEW";
@@ -37961,6 +38070,7 @@
   }
 
   public static abstract interface MediaStore.DownloadColumns implements android.provider.MediaStore.MediaColumns {
+    field public static final java.lang.String DESCRIPTION = "description";
     field public static final java.lang.String DOWNLOAD_URI = "download_uri";
     field public static final java.lang.String REFERER_URI = "referer_uri";
   }
@@ -38005,6 +38115,7 @@
     field public static final deprecated java.lang.String MINI_THUMB_MAGIC = "mini_thumb_magic";
     field public static final java.lang.String ORIENTATION = "orientation";
     field public static final deprecated java.lang.String PICASA_ID = "picasa_id";
+    field public static final java.lang.String SECONDARY_BUCKET_ID = "secondary_bucket_id";
   }
 
   public static final class MediaStore.Images.Media implements android.provider.MediaStore.Images.ImageColumns {
@@ -38049,11 +38160,13 @@
   public static abstract interface MediaStore.MediaColumns implements android.provider.BaseColumns {
     field public static final deprecated java.lang.String DATA = "_data";
     field public static final java.lang.String DATE_ADDED = "date_added";
+    field public static final java.lang.String DATE_EXPIRES = "date_expires";
     field public static final java.lang.String DATE_MODIFIED = "date_modified";
     field public static final java.lang.String DISPLAY_NAME = "_display_name";
     field public static final java.lang.String HASH = "_hash";
     field public static final java.lang.String HEIGHT = "height";
     field public static final java.lang.String IS_PENDING = "is_pending";
+    field public static final java.lang.String IS_TRASHED = "is_trashed";
     field public static final java.lang.String MIME_TYPE = "mime_type";
     field public static final java.lang.String OWNER_PACKAGE_NAME = "owner_package_name";
     field public static final java.lang.String SIZE = "_size";
@@ -38129,6 +38242,7 @@
     field public static final deprecated java.lang.String LONGITUDE = "longitude";
     field public static final deprecated java.lang.String MINI_THUMB_MAGIC = "mini_thumb_magic";
     field public static final java.lang.String RESOLUTION = "resolution";
+    field public static final java.lang.String SECONDARY_BUCKET_ID = "secondary_bucket_id";
     field public static final java.lang.String TAGS = "tags";
   }
 
@@ -38305,6 +38419,11 @@
     field public static final java.lang.String VALUE = "value";
   }
 
+  public static final class Settings.Panel {
+    field public static final java.lang.String ACTION_INTERNET_CONNECTIVITY = "android.settings.panel.action.INTERNET_CONNECTIVITY";
+    field public static final java.lang.String ACTION_VOLUME = "android.settings.panel.action.VOLUME";
+  }
+
   public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
     ctor public Settings.Secure();
     method public static float getFloat(android.content.ContentResolver, java.lang.String, float);
@@ -38622,22 +38741,23 @@
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String CURRENT = "current";
     field public static final java.lang.String DEFAULT_SORT_ORDER = "name ASC";
-    field public static final java.lang.String MCC = "mcc";
+    field public static final deprecated java.lang.String MCC = "mcc";
     field public static final java.lang.String MMSC = "mmsc";
     field public static final java.lang.String MMSPORT = "mmsport";
     field public static final java.lang.String MMSPROXY = "mmsproxy";
-    field public static final java.lang.String MNC = "mnc";
-    field public static final java.lang.String MVNO_MATCH_DATA = "mvno_match_data";
-    field public static final java.lang.String MVNO_TYPE = "mvno_type";
+    field public static final deprecated java.lang.String MNC = "mnc";
+    field public static final deprecated java.lang.String MVNO_MATCH_DATA = "mvno_match_data";
+    field public static final deprecated java.lang.String MVNO_TYPE = "mvno_type";
     field public static final java.lang.String NAME = "name";
     field public static final java.lang.String NETWORK_TYPE_BITMASK = "network_type_bitmask";
-    field public static final java.lang.String NUMERIC = "numeric";
+    field public static final deprecated java.lang.String NUMERIC = "numeric";
     field public static final java.lang.String PASSWORD = "password";
     field public static final java.lang.String PORT = "port";
     field public static final java.lang.String PROTOCOL = "protocol";
     field public static final java.lang.String PROXY = "proxy";
     field public static final java.lang.String ROAMING_PROTOCOL = "roaming_protocol";
     field public static final java.lang.String SERVER = "server";
+    field public static final android.net.Uri SIM_APN_URI;
     field public static final java.lang.String SUBSCRIPTION_ID = "sub_id";
     field public static final java.lang.String TYPE = "type";
     field public static final java.lang.String USER = "user";
@@ -40987,10 +41107,10 @@
     field public final java.lang.String summary;
   }
 
-  public abstract class ConditionProviderService extends android.app.Service {
+  public abstract deprecated class ConditionProviderService extends android.app.Service {
     ctor public ConditionProviderService();
-    method public final void notifyCondition(android.service.notification.Condition);
-    method public final void notifyConditions(android.service.notification.Condition...);
+    method public final deprecated void notifyCondition(android.service.notification.Condition);
+    method public final deprecated void notifyConditions(android.service.notification.Condition...);
     method public android.os.IBinder onBind(android.content.Intent);
     method public abstract void onConnected();
     method public void onRequestConditions(int);
@@ -40998,10 +41118,10 @@
     method public abstract void onUnsubscribe(android.net.Uri);
     method public static final void requestRebind(android.content.ComponentName);
     method public final void requestUnbind();
-    field public static final java.lang.String EXTRA_RULE_ID = "android.service.notification.extra.RULE_ID";
-    field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
-    field public static final java.lang.String META_DATA_RULE_INSTANCE_LIMIT = "android.service.zen.automatic.ruleInstanceLimit";
-    field public static final java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
+    field public static final deprecated java.lang.String EXTRA_RULE_ID = "android.service.notification.extra.RULE_ID";
+    field public static final deprecated java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
+    field public static final deprecated java.lang.String META_DATA_RULE_INSTANCE_LIMIT = "android.service.zen.automatic.ruleInstanceLimit";
+    field public static final deprecated java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
   }
 
@@ -41077,12 +41197,12 @@
 
   public static class NotificationListenerService.Ranking {
     ctor public NotificationListenerService.Ranking();
-    method public boolean audiblyAlerted();
     method public boolean canShowBadge();
     method public android.app.NotificationChannel getChannel();
     method public int getImportance();
     method public java.lang.CharSequence getImportanceExplanation();
     method public java.lang.String getKey();
+    method public long getLastAudiblyAlertedMillis();
     method public java.lang.String getOverrideGroupKey();
     method public int getRank();
     method public int getSuppressedVisualEffects();
@@ -41193,10 +41313,12 @@
     method public android.graphics.drawable.Icon getIcon();
     method public java.lang.CharSequence getLabel();
     method public int getState();
+    method public java.lang.CharSequence getSubtitle();
     method public void setContentDescription(java.lang.CharSequence);
     method public void setIcon(android.graphics.drawable.Icon);
     method public void setLabel(java.lang.CharSequence);
     method public void setState(int);
+    method public void setSubtitle(java.lang.CharSequence);
     method public void updateTile();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.quicksettings.Tile> CREATOR;
@@ -41453,6 +41575,7 @@
     method protected void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public int getDesiredMinimumHeight();
     method public int getDesiredMinimumWidth();
+    method public android.content.Context getDisplayContext();
     method public android.view.SurfaceHolder getSurfaceHolder();
     method public boolean isPreview();
     method public boolean isVisible();
@@ -41858,13 +41981,13 @@
     method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
     method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
     method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
-    method public static void setegid(int) throws android.system.ErrnoException;
+    method public static deprecated void setegid(int) throws android.system.ErrnoException;
     method public static void setenv(java.lang.String, java.lang.String, boolean) throws android.system.ErrnoException;
-    method public static void seteuid(int) throws android.system.ErrnoException;
-    method public static void setgid(int) throws android.system.ErrnoException;
+    method public static deprecated void seteuid(int) throws android.system.ErrnoException;
+    method public static deprecated void setgid(int) throws android.system.ErrnoException;
     method public static int setsid() throws android.system.ErrnoException;
     method public static void setsockoptInt(java.io.FileDescriptor, int, int, int) throws android.system.ErrnoException;
-    method public static void setuid(int) throws android.system.ErrnoException;
+    method public static deprecated void setuid(int) throws android.system.ErrnoException;
     method public static void setxattr(java.lang.String, java.lang.String, byte[], int) throws android.system.ErrnoException;
     method public static void shutdown(java.io.FileDescriptor, int) throws android.system.ErrnoException;
     method public static java.io.FileDescriptor socket(int, int, int) throws android.system.ErrnoException;
@@ -42218,7 +42341,9 @@
     field public static final int SIOCGIFBRDADDR;
     field public static final int SIOCGIFDSTADDR;
     field public static final int SIOCGIFNETMASK;
+    field public static final int SOCK_CLOEXEC;
     field public static final int SOCK_DGRAM;
+    field public static final int SOCK_NONBLOCK;
     field public static final int SOCK_RAW;
     field public static final int SOCK_SEQPACKET;
     field public static final int SOCK_STREAM;
@@ -42525,6 +42650,7 @@
     method public static java.lang.String capabilitiesToString(int);
     method public android.telecom.PhoneAccountHandle getAccountHandle();
     method public int getCallCapabilities();
+    method public android.telecom.CallIdentification getCallIdentification();
     method public int getCallProperties();
     method public java.lang.String getCallerDisplayName();
     method public int getCallerDisplayNamePresentation();
@@ -42603,14 +42729,42 @@
     field public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5
   }
 
+  public final class CallIdentification implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.lang.String getCallScreeningAppName();
+    method public java.lang.String getCallScreeningPackageName();
+    method public java.lang.String getDescription();
+    method public java.lang.String getDetails();
+    method public java.lang.String getName();
+    method public int getNuisanceConfidence();
+    method public android.graphics.drawable.Icon getPhoto();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CONFIDENCE_LIKELY_NOT_NUISANCE = -1; // 0xffffffff
+    field public static final int CONFIDENCE_LIKELY_NUISANCE = 1; // 0x1
+    field public static final int CONFIDENCE_NOT_NUISANCE = -2; // 0xfffffffe
+    field public static final int CONFIDENCE_NUISANCE = 2; // 0x2
+    field public static final int CONFIDENCE_UNKNOWN = 0; // 0x0
+    field public static final android.os.Parcelable.Creator<android.telecom.CallIdentification> CREATOR;
+  }
+
+  public static class CallIdentification.Builder {
+    ctor public CallIdentification.Builder();
+    method public android.telecom.CallIdentification build();
+    method public android.telecom.CallIdentification.Builder setDescription(java.lang.String);
+    method public android.telecom.CallIdentification.Builder setDetails(java.lang.String);
+    method public android.telecom.CallIdentification.Builder setName(java.lang.String);
+    method public android.telecom.CallIdentification.Builder setNuisanceConfidence(int);
+    method public android.telecom.CallIdentification.Builder setPhoto(android.graphics.drawable.Icon);
+  }
+
   public abstract class CallRedirectionService extends android.app.Service {
     ctor public CallRedirectionService();
     method public final void cancelCall();
     method public final android.os.IBinder onBind(android.content.Intent);
-    method public abstract void onPlaceCall(android.net.Uri, android.telecom.PhoneAccountHandle);
+    method public abstract void onPlaceCall(android.net.Uri, android.telecom.PhoneAccountHandle, boolean);
     method public final boolean onUnbind(android.content.Intent);
     method public final void placeCallUnmodified();
-    method public final void redirectCall(android.net.Uri, android.telecom.PhoneAccountHandle);
+    method public final void redirectCall(android.net.Uri, android.telecom.PhoneAccountHandle, boolean);
     field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.CallRedirectionService";
   }
 
@@ -42618,6 +42772,7 @@
     ctor public CallScreeningService();
     method public android.os.IBinder onBind(android.content.Intent);
     method public abstract void onScreenCall(android.telecom.Call.Details);
+    method public final void provideCallIdentification(android.telecom.Call.Details, android.telecom.CallIdentification);
     method public final void respondToCall(android.telecom.Call.Details, android.telecom.CallScreeningService.CallResponse);
     field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.CallScreeningService";
   }
@@ -42798,6 +42953,7 @@
     field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
     field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
     field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
+    field public static final java.lang.String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE";
     field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
     field public static final int PROPERTY_IS_RTT = 256; // 0x100
@@ -43212,7 +43368,6 @@
     method public java.lang.String getVoiceMailNumber(android.telecom.PhoneAccountHandle);
     method public boolean handleMmi(java.lang.String);
     method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
-    method public boolean isDefaultCallScreeningApp(android.content.ComponentName);
     method public boolean isInCall();
     method public boolean isInManagedCall();
     method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
@@ -43221,7 +43376,6 @@
     method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
     method public void placeCall(android.net.Uri, android.os.Bundle);
     method public void registerPhoneAccount(android.telecom.PhoneAccount);
-    method public void requestChangeDefaultCallScreeningApp(android.content.ComponentName);
     method public void showInCallScreen(boolean);
     method public void silenceRinger();
     method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
@@ -43299,9 +43453,12 @@
 
   public static final class VideoProfile.CameraCapabilities implements android.os.Parcelable {
     ctor public VideoProfile.CameraCapabilities(int, int);
+    ctor public VideoProfile.CameraCapabilities(int, int, boolean, float);
     method public int describeContents();
     method public int getHeight();
+    method public float getMaxZoom();
     method public int getWidth();
+    method public boolean isZoomSupported();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telecom.VideoProfile.CameraCapabilities> CREATOR;
   }
@@ -43554,6 +43711,10 @@
     field public static final java.lang.String KEY_MONTHLY_DATA_CYCLE_DAY_INT = "monthly_data_cycle_day_int";
     field public static final java.lang.String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
     field public static final java.lang.String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
+    field public static final java.lang.String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT = "opportunistic_network_entry_threshold_rsrp_int";
+    field public static final java.lang.String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT = "opportunistic_network_entry_threshold_rssnr_int";
+    field public static final java.lang.String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT = "opportunistic_network_exit_threshold_rsrp_int";
+    field public static final java.lang.String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT = "opportunistic_network_exit_threshold_rssnr_int";
     field public static final java.lang.String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
     field public static final java.lang.String KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY = "radio_restart_failure_causes_int_array";
     field public static final java.lang.String KEY_RCS_CONFIG_SERVER_URL_STRING = "rcs_config_server_url_string";
@@ -44211,6 +44372,7 @@
   public class SubscriptionInfo implements android.os.Parcelable {
     method public android.graphics.Bitmap createIconBitmap(android.content.Context);
     method public int describeContents();
+    method public int getCarrierId();
     method public java.lang.CharSequence getCarrierName();
     method public java.lang.String getCountryIso();
     method public int getDataRoaming();
@@ -44258,6 +44420,8 @@
     method public void removeOnOpportunisticSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
     method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
     method public boolean removeSubscriptionsFromGroup(int[]);
+    method public boolean setMetered(boolean, int);
+    method public boolean setOpportunistic(boolean, int);
     method public java.lang.String setSubscriptionGroup(int[]);
     method public void setSubscriptionOverrideCongested(int, boolean, long);
     method public void setSubscriptionOverrideUnmetered(int, boolean, long);
@@ -46876,7 +47040,7 @@
     ctor public deprecated Scene(android.view.ViewGroup, android.view.ViewGroup);
     method public void enter();
     method public void exit();
-    method public static android.transition.Scene getCurrentScene(android.view.View);
+    method public static android.transition.Scene getCurrentScene(android.view.ViewGroup);
     method public static android.transition.Scene getSceneForLayout(android.view.ViewGroup, int, android.content.Context);
     method public android.view.ViewGroup getSceneRoot();
     method public void setEnterAction(java.lang.Runnable);
@@ -49513,6 +49677,7 @@
     method public android.graphics.Rect getClipBounds();
     method public boolean getClipBounds(android.graphics.Rect);
     method public final boolean getClipToOutline();
+    method public final android.view.contentcapture.ContentCaptureSession getContentCaptureSession();
     method public java.lang.CharSequence getContentDescription();
     method public final android.content.Context getContext();
     method protected android.view.ContextMenu.ContextMenuInfo getContextMenuInfo();
@@ -49849,6 +50014,7 @@
     method public void setClickable(boolean);
     method public void setClipBounds(android.graphics.Rect);
     method public void setClipToOutline(boolean);
+    method public void setContentCaptureSession(android.view.contentcapture.ContentCaptureSession);
     method public void setContentDescription(java.lang.CharSequence);
     method public void setContextClickable(boolean);
     method public void setDefaultFocusHighlightEnabled(boolean);
@@ -51778,6 +51944,7 @@
   public abstract class Animation implements java.lang.Cloneable {
     ctor public Animation();
     ctor public Animation(android.content.Context, android.util.AttributeSet);
+    method public void addAnimationListener(android.view.animation.Animation.AnimationListener);
     method protected void applyTransformation(float, android.view.animation.Transformation);
     method public void cancel();
     method protected android.view.animation.Animation clone() throws java.lang.CloneNotSupportedException;
@@ -51802,6 +51969,7 @@
     method public void initialize(int, int, int, int);
     method public boolean isFillEnabled();
     method public boolean isInitialized();
+    method public void removeAnimationListener(android.view.animation.Animation.AnimationListener);
     method public void reset();
     method protected float resolveSize(int, float, int, int);
     method public void restrictDuration(long);
@@ -52109,17 +52277,56 @@
 
 package android.view.contentcapture {
 
+  public final class ContentCaptureContext implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureContext> CREATOR;
+  }
+
+  public static final class ContentCaptureContext.Builder {
+    ctor public ContentCaptureContext.Builder();
+    method public android.view.contentcapture.ContentCaptureContext build();
+    method public android.view.contentcapture.ContentCaptureContext.Builder setExtras(android.os.Bundle);
+    method public android.view.contentcapture.ContentCaptureContext.Builder setUri(android.net.Uri);
+  }
+
   public final class ContentCaptureManager {
     method public android.content.ComponentName getServiceComponentName();
     method public boolean isContentCaptureEnabled();
-    method public android.view.ViewStructure newVirtualViewStructure(android.view.autofill.AutofillId, int);
-    method public void notifyViewAppeared(android.view.ViewStructure);
-    method public void notifyViewDisappeared(android.view.autofill.AutofillId);
-    method public void notifyViewTextChanged(android.view.autofill.AutofillId, java.lang.CharSequence, int);
+    method public void removeUserData(android.view.contentcapture.UserDataRemovalRequest);
     method public void setContentCaptureEnabled(boolean);
+  }
+
+  public abstract class ContentCaptureSession implements java.lang.AutoCloseable {
+    method public void close();
+    method public final android.view.contentcapture.ContentCaptureSession createContentCaptureSession(android.view.contentcapture.ContentCaptureContext);
+    method public final void destroy();
+    method public final android.view.contentcapture.ContentCaptureSessionId getContentCaptureSessionId();
+    method public final void notifyViewAppeared(android.view.ViewStructure);
+    method public final void notifyViewDisappeared(android.view.autofill.AutofillId);
+    method public final void notifyViewTextChanged(android.view.autofill.AutofillId, java.lang.CharSequence, int);
     field public static final int FLAG_USER_INPUT = 1; // 0x1
   }
 
+  public final class ContentCaptureSessionId implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureSessionId> CREATOR;
+  }
+
+  public final class UserDataRemovalRequest implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.contentcapture.UserDataRemovalRequest> CREATOR;
+  }
+
+  public static final class UserDataRemovalRequest.Builder {
+    ctor public UserDataRemovalRequest.Builder();
+    method public android.view.contentcapture.UserDataRemovalRequest.Builder addUri(android.net.Uri, boolean);
+    method public android.view.contentcapture.UserDataRemovalRequest build();
+    method public android.view.contentcapture.UserDataRemovalRequest.Builder forEverything();
+  }
+
 }
 
 package android.view.inputmethod {
@@ -56321,6 +56528,7 @@
     method public boolean isCursorVisible();
     method public boolean isElegantTextHeight();
     method public boolean isFallbackLineSpacing();
+    method public final boolean isHorizontallyScrolling();
     method public boolean isInputMethodTarget();
     method public boolean isSingleLine();
     method public boolean isSuggestionsEnabled();
diff --git a/api/system-current.txt b/api/system-current.txt
index a2e5121..7d889a2 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -27,6 +27,7 @@
     field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
     field public static final java.lang.String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE";
     field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE";
+    field public static final java.lang.String BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE = "android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE";
     field public static final java.lang.String BIND_PRINT_RECOMMENDATION_SERVICE = "android.permission.BIND_PRINT_RECOMMENDATION_SERVICE";
     field public static final java.lang.String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
     field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE";
@@ -61,6 +62,7 @@
     field public static final java.lang.String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
     field public static final java.lang.String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS";
     field public static final java.lang.String GET_PROCESS_STATE_AND_OOM_SCORE = "android.permission.GET_PROCESS_STATE_AND_OOM_SCORE";
+    field public static final java.lang.String GET_RUNTIME_PERMISSIONS = "android.permission.GET_RUNTIME_PERMISSIONS";
     field public static final java.lang.String GET_TOP_ACTIVITY_INFO = "android.permission.GET_TOP_ACTIVITY_INFO";
     field public static final java.lang.String GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS = "android.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS";
     field public static final java.lang.String GRANT_RUNTIME_PERMISSIONS = "android.permission.GRANT_RUNTIME_PERMISSIONS";
@@ -93,6 +95,8 @@
     field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
     field public static final java.lang.String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
     field public static final java.lang.String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
+    field public static final java.lang.String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
+    field public static final java.lang.String MANAGE_SENSOR_PRIVACY = "android.permission.MANAGE_SENSOR_PRIVACY";
     field public static final java.lang.String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
     field public static final java.lang.String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
     field public static final java.lang.String MANAGE_USB = "android.permission.MANAGE_USB";
@@ -204,6 +208,10 @@
     field public static final int config_sendPackageName = 17891328; // 0x1110000
   }
 
+  public static final class R.color {
+    field public static final int system_notification_accent_color = 17170460; // 0x106001c
+  }
+
   public static final class R.dimen {
     field public static final int config_mediaMetadataBitmapMaxSize = 17104904; // 0x1050008
     field public static final int config_restrictedIconSize = 17104903; // 0x1050007
@@ -852,6 +860,7 @@
     method public void addRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
     method public boolean addRoleHolderFromController(java.lang.String, java.lang.String);
     method public void clearRoleHoldersAsUser(java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
+    method public java.util.List<java.lang.String> getHeldRolesFromController(java.lang.String);
     method public java.util.List<java.lang.String> getRoleHolders(java.lang.String);
     method public java.util.List<java.lang.String> getRoleHoldersAsUser(java.lang.String, android.os.UserHandle);
     method public void removeOnRoleHoldersChangedListenerAsUser(android.app.role.OnRoleHoldersChangedListener, android.os.UserHandle);
@@ -918,6 +927,9 @@
     method public java.util.Map<java.lang.String, java.lang.Integer> getAppStandbyBuckets();
     method public void registerAppUsageObserver(int, java.lang.String[], long, java.util.concurrent.TimeUnit, android.app.PendingIntent);
     method public void registerUsageSessionObserver(int, java.lang.String[], long, java.util.concurrent.TimeUnit, long, java.util.concurrent.TimeUnit, android.app.PendingIntent, android.app.PendingIntent);
+    method public void reportUsageStart(android.app.Activity, java.lang.String);
+    method public void reportUsageStart(android.app.Activity, java.lang.String, long);
+    method public void reportUsageStop(android.app.Activity, java.lang.String);
     method public void setAppStandbyBucket(java.lang.String, int);
     method public void setAppStandbyBuckets(java.util.Map<java.lang.String, java.lang.Integer>);
     method public void unregisterAppUsageObserver(int);
@@ -1031,6 +1043,7 @@
     field public static final java.lang.String OEM_LOCK_SERVICE = "oem_lock";
     field public static final java.lang.String PERMISSION_SERVICE = "permission";
     field public static final java.lang.String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
+    field public static final java.lang.String ROLLBACK_SERVICE = "rollback";
     field public static final java.lang.String SECURE_ELEMENT_SERVICE = "secure_element";
     field public static final java.lang.String STATS_MANAGER = "stats";
     field public static final java.lang.String SYSTEM_UPDATE_SERVICE = "system_update";
@@ -1062,6 +1075,7 @@
     field public static final java.lang.String ACTION_MANAGE_PERMISSIONS = "android.intent.action.MANAGE_PERMISSIONS";
     field public static final java.lang.String ACTION_MANAGE_PERMISSION_APPS = "android.intent.action.MANAGE_PERMISSION_APPS";
     field public static final java.lang.String ACTION_MASTER_CLEAR_NOTIFICATION = "android.intent.action.MASTER_CLEAR_NOTIFICATION";
+    field public static final java.lang.String ACTION_PACKAGE_ROLLBACK_EXECUTED = "android.intent.action.PACKAGE_ROLLBACK_EXECUTED";
     field public static final java.lang.String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED";
     field public static final java.lang.String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
     field public static final java.lang.String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
@@ -1197,6 +1211,7 @@
     method public void setAllocateAggressive(boolean);
     method public void setAllowDowngrade(boolean);
     method public void setDontKillApp(boolean);
+    method public void setEnableRollback();
     method public void setGrantedRuntimePermissions(java.lang.String[]);
     method public void setInstallAsInstantApp(boolean);
     method public void setInstallAsVirtualPreload();
@@ -1214,7 +1229,6 @@
   public abstract class PackageManager {
     method public abstract void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
     method public abstract boolean arePermissionsIndividuallyControlled();
-    method public boolean canSuspendPackage(java.lang.String);
     method public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(java.lang.String);
     method public android.content.pm.ApplicationInfo getApplicationInfoAsUser(java.lang.String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.dex.ArtManager getArtManager();
@@ -1228,6 +1242,7 @@
     method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
     method public abstract int getIntentVerificationStatusAsUser(java.lang.String, int);
     method public abstract int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle);
+    method public java.lang.String[] getUnsuspendablePackages(java.lang.String[]);
     method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract int installExistingPackage(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int installExistingPackage(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -1391,6 +1406,41 @@
 
 }
 
+package android.content.rollback {
+
+  public final class PackageRollbackInfo implements android.os.Parcelable {
+    ctor public PackageRollbackInfo(java.lang.String, android.content.rollback.PackageRollbackInfo.PackageVersion, android.content.rollback.PackageRollbackInfo.PackageVersion);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.rollback.PackageRollbackInfo> CREATOR;
+    field public final android.content.rollback.PackageRollbackInfo.PackageVersion higherVersion;
+    field public final android.content.rollback.PackageRollbackInfo.PackageVersion lowerVersion;
+    field public final java.lang.String packageName;
+  }
+
+  public static class PackageRollbackInfo.PackageVersion {
+    ctor public PackageRollbackInfo.PackageVersion(long);
+    field public final long versionCode;
+  }
+
+  public final class RollbackInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.rollback.RollbackInfo> CREATOR;
+    field public final android.content.rollback.PackageRollbackInfo targetPackage;
+  }
+
+  public final class RollbackManager {
+    method public void executeRollback(android.content.rollback.RollbackInfo, android.content.IntentSender);
+    method public void expireRollbackForPackage(java.lang.String);
+    method public android.content.rollback.RollbackInfo getAvailableRollback(java.lang.String);
+    method public java.util.List<java.lang.String> getPackagesWithAvailableRollbacks();
+    method public java.util.List<android.content.rollback.RollbackInfo> getRecentlyExecutedRollbacks();
+    method public void reloadPersistedData();
+  }
+
+}
+
 package android.hardware {
 
   public final class Sensor {
@@ -2514,7 +2564,37 @@
   }
 
   public class UsbManager {
+    method public java.util.List<android.hardware.usb.UsbPort> getPorts();
     method public void grantPermission(android.hardware.usb.UsbDevice, java.lang.String);
+    field public static final java.lang.String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED";
+  }
+
+  public final class UsbPort {
+    method public android.hardware.usb.UsbPortStatus getStatus();
+    method public void setRoles(int, int);
+  }
+
+  public final class UsbPortStatus implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getCurrentDataRole();
+    method public int getCurrentMode();
+    method public int getCurrentPowerRole();
+    method public int getSupportedRoleCombinations();
+    method public boolean isConnected();
+    method public boolean isRoleCombinationSupported(int, int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.hardware.usb.UsbPortStatus> CREATOR;
+    field public static final int DATA_ROLE_DEVICE = 2; // 0x2
+    field public static final int DATA_ROLE_HOST = 1; // 0x1
+    field public static final int DATA_ROLE_NONE = 0; // 0x0
+    field public static final int MODE_AUDIO_ACCESSORY = 4; // 0x4
+    field public static final int MODE_DEBUG_ACCESSORY = 8; // 0x8
+    field public static final int MODE_DFP = 2; // 0x2
+    field public static final int MODE_NONE = 0; // 0x0
+    field public static final int MODE_UFP = 1; // 0x1
+    field public static final int POWER_ROLE_NONE = 0; // 0x0
+    field public static final int POWER_ROLE_SINK = 2; // 0x2
+    field public static final int POWER_ROLE_SOURCE = 1; // 0x1
   }
 
 }
@@ -2526,6 +2606,81 @@
     method public void onLocationBatch(java.util.List<android.location.Location>);
   }
 
+  public final class GnssMeasurementCorrections implements android.os.Parcelable {
+    method public int describeContents();
+    method public double getAltitudeMeters();
+    method public double getLatitudeDegrees();
+    method public double getLongitudeDegrees();
+    method public java.util.List<android.location.GnssSingleSatCorrection> getSingleSatCorrectionList();
+    method public long getToaGpsNanosecondsOfWeek();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.location.GnssMeasurementCorrections> CREATOR;
+  }
+
+  public static class GnssMeasurementCorrections.Builder {
+    ctor public GnssMeasurementCorrections.Builder();
+    method public android.location.GnssMeasurementCorrections build();
+    method public android.location.GnssMeasurementCorrections.Builder setAltitudeMeters(double);
+    method public android.location.GnssMeasurementCorrections.Builder setLatitudeDegrees(double);
+    method public android.location.GnssMeasurementCorrections.Builder setLongitudeDegrees(double);
+    method public android.location.GnssMeasurementCorrections.Builder setSingleSatCorrectionList(java.util.List<android.location.GnssSingleSatCorrection>);
+    method public android.location.GnssMeasurementCorrections.Builder setToaGpsNanosecondsOfWeek(long);
+  }
+
+  public final class GnssReflectingPlane implements android.os.Parcelable {
+    method public int describeContents();
+    method public double getAltitudeMeters();
+    method public double getAzimuthDegrees();
+    method public double getLatitudeDegrees();
+    method public double getLongitudeDegrees();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.location.GnssReflectingPlane> CREATOR;
+  }
+
+  public static class GnssReflectingPlane.Builder {
+    ctor public GnssReflectingPlane.Builder();
+    method public android.location.GnssReflectingPlane build();
+    method public android.location.GnssReflectingPlane.Builder setAltitudeMeters(double);
+    method public android.location.GnssReflectingPlane.Builder setAzimuthDegrees(double);
+    method public android.location.GnssReflectingPlane.Builder setLatitudeDegrees(double);
+    method public android.location.GnssReflectingPlane.Builder setLongitudeDegrees(double);
+  }
+
+  public final class GnssSingleSatCorrection implements android.os.Parcelable {
+    method public int describeContents();
+    method public float getCarrierFrequencyHz();
+    method public int getConstellationType();
+    method public float getExcessPathLengthMeters();
+    method public float getExcessPathLengthUncertaintyMeters();
+    method public android.location.GnssReflectingPlane getReflectingPlane();
+    method public int getSatId();
+    method public int getSingleSatCorrectionFlags();
+    method public boolean hasExcessPathLength();
+    method public boolean hasExcessPathLengthUncertainty();
+    method public boolean hasReflectingPlane();
+    method public boolean hasSatelliteLineOfSight();
+    method public boolean isSatelliteLineOfSight();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.location.GnssSingleSatCorrection> CREATOR;
+    field public static final int HAS_EXCESS_PATH_LENGTH_MASK = 2; // 0x2
+    field public static final int HAS_EXCESS_PATH_LENGTH_UNC_MASK = 4; // 0x4
+    field public static final int HAS_REFLECTING_PLANE_MASK = 8; // 0x8
+    field public static final int HAS_SAT_IS_LOS_MASK = 1; // 0x1
+  }
+
+  public static class GnssSingleSatCorrection.Builder {
+    ctor public GnssSingleSatCorrection.Builder();
+    method public android.location.GnssSingleSatCorrection build();
+    method public android.location.GnssSingleSatCorrection.Builder setCarrierFrequencyHz(float);
+    method public android.location.GnssSingleSatCorrection.Builder setConstellationType(int);
+    method public android.location.GnssSingleSatCorrection.Builder setExcessPathLengthMeters(float);
+    method public android.location.GnssSingleSatCorrection.Builder setExcessPathLengthUncertaintyMeters(float);
+    method public android.location.GnssSingleSatCorrection.Builder setReflectingPlane(android.location.GnssReflectingPlane);
+    method public android.location.GnssSingleSatCorrection.Builder setSatId(int);
+    method public android.location.GnssSingleSatCorrection.Builder setSatIsLos(boolean);
+    method public android.location.GnssSingleSatCorrection.Builder setSingleSatCorrectionFlags(int);
+  }
+
   public class GpsClock implements android.os.Parcelable {
     method public int describeContents();
     method public double getBiasInNs();
@@ -2762,7 +2917,11 @@
     method public deprecated boolean addGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener);
     method public void flushGnssBatch();
     method public int getGnssBatchSize();
+    method public int getGnssCapabilities();
+    method public java.lang.String getLocationControllerExtraPackage();
     method public java.lang.String getNetworkProviderPackage();
+    method public void injectGnssMeasurementCorrections(android.location.GnssMeasurementCorrections);
+    method public boolean isLocationControllerExtraPackageEnabled();
     method public boolean isLocationEnabledForUser(android.os.UserHandle);
     method public boolean isProviderEnabledForUser(java.lang.String, android.os.UserHandle);
     method public boolean registerGnssBatchedLocationCallback(long, boolean, android.location.BatchedLocationCallback, android.os.Handler);
@@ -2770,6 +2929,8 @@
     method public deprecated void removeGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener);
     method public void requestLocationUpdates(android.location.LocationRequest, android.location.LocationListener, android.os.Looper);
     method public void requestLocationUpdates(android.location.LocationRequest, android.app.PendingIntent);
+    method public void setLocationControllerExtraPackage(java.lang.String);
+    method public void setLocationControllerExtraPackageEnabled(boolean);
     method public void setLocationEnabledForUser(boolean, android.os.UserHandle);
     method public boolean setProviderEnabledForUser(java.lang.String, boolean, android.os.UserHandle);
     method public boolean unregisterGnssBatchedLocationCallback(android.location.BatchedLocationCallback);
@@ -2868,7 +3029,8 @@
     field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1
     field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4
     field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2
-    field public static final int FLAG_FROM_KEY = 4096; // 0x1000
+    field public static final int FLAG_FROM_KEY = 65536; // 0x10000
+    field public static final int SUCCESS = 0; // 0x0
   }
 
   public static abstract class AudioManager.AudioServerStateCallback {
@@ -2898,7 +3060,7 @@
     field public static final int PLAYER_TYPE_UNKNOWN = -1; // 0xffffffff
   }
 
-  public class AudioRecord implements android.media.AudioRouting {
+  public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting {
     ctor public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException;
   }
 
@@ -2985,8 +3147,10 @@
     method public int detachMixes(java.util.List<android.media.audiopolicy.AudioMix>);
     method public int getFocusDuckingBehavior();
     method public int getStatus();
+    method public int removeUidDeviceAffinity(int);
     method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
     method public void setRegistration(java.lang.String);
+    method public int setUidDeviceAffinity(int, java.util.List<android.media.AudioDeviceInfo>);
     method public java.lang.String toLogFriendlyString();
     field public static final int FOCUS_POLICY_DUCKING_DEFAULT = 0; // 0x0
     field public static final int FOCUS_POLICY_DUCKING_IN_APP = 0; // 0x0
@@ -3386,7 +3550,20 @@
     ctor public LinkAddress(java.lang.String);
   }
 
+  public final class LinkProperties implements android.os.Parcelable {
+    ctor public LinkProperties();
+    method public boolean addRoute(android.net.RouteInfo);
+    method public void clear();
+    method public void setDnsServers(java.util.Collection<java.net.InetAddress>);
+    method public void setDomains(java.lang.String);
+    method public void setHttpProxy(android.net.ProxyInfo);
+    method public void setInterfaceName(java.lang.String);
+    method public void setLinkAddresses(java.util.Collection<android.net.LinkAddress>);
+    method public void setMtu(int);
+  }
+
   public final class NetworkCapabilities implements android.os.Parcelable {
+    method public int getSignalStrength();
     field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
   }
 
@@ -3406,6 +3583,10 @@
     method public abstract void onRequestScores(android.net.NetworkKey[]);
   }
 
+  public static class NetworkRequest.Builder {
+    method public android.net.NetworkRequest.Builder setSignalStrength(int);
+  }
+
   public class NetworkScoreManager {
     method public boolean clearScores() throws java.lang.SecurityException;
     method public void disableScoring() throws java.lang.SecurityException;
@@ -3480,6 +3661,26 @@
 
 package android.net.wifi {
 
+  public abstract class DppStatusCallback {
+    ctor public DppStatusCallback();
+    method public abstract void onConfiguratorSuccess(int);
+    method public abstract void onEnrolleeSuccess(int);
+    method public abstract void onFailure(int);
+    method public abstract void onProgress(int);
+    field public static final int DPP_EVENT_FAILURE = -7; // 0xfffffff9
+    field public static final int DPP_EVENT_FAILURE_AUTHENTICATION = -2; // 0xfffffffe
+    field public static final int DPP_EVENT_FAILURE_BUSY = -5; // 0xfffffffb
+    field public static final int DPP_EVENT_FAILURE_CONFIGURATION = -4; // 0xfffffffc
+    field public static final int DPP_EVENT_FAILURE_INVALID_NETWORK = -9; // 0xfffffff7
+    field public static final int DPP_EVENT_FAILURE_INVALID_URI = -1; // 0xffffffff
+    field public static final int DPP_EVENT_FAILURE_NOT_COMPATIBLE = -3; // 0xfffffffd
+    field public static final int DPP_EVENT_FAILURE_NOT_SUPPORTED = -8; // 0xfffffff8
+    field public static final int DPP_EVENT_FAILURE_TIMEOUT = -6; // 0xfffffffa
+    field public static final int DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0; // 0x0
+    field public static final int DPP_EVENT_PROGRESS_RESPONSE_PENDING = 1; // 0x1
+    field public static final int DPP_EVENT_SUCCESS_CONFIGURATION_SENT = 0; // 0x0
+  }
+
   public deprecated class RttManager {
     method public void disableResponder(android.net.wifi.RttManager.ResponderCallback);
     method public void enableResponder(android.net.wifi.RttManager.ResponderCallback);
@@ -3697,7 +3898,6 @@
     method public void disable(int, android.net.wifi.WifiManager.ActionListener);
     method public void disableEphemeralNetwork(java.lang.String);
     method public void forget(int, android.net.wifi.WifiManager.ActionListener);
-    method public java.util.List<android.net.wifi.WifiConfiguration> getAllMatchingWifiConfigs(java.util.List<android.net.wifi.ScanResult>);
     method public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
     method public android.net.wifi.WifiConfiguration getWifiApConfiguration();
     method public int getWifiApState();
@@ -3707,13 +3907,23 @@
     method public boolean isWifiScannerSupported();
     method public void registerNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback, android.os.Handler);
     method public void save(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener);
+    method public void setDeviceMobilityState(int);
     method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
+    method public void startDppAsConfiguratorInitiator(java.lang.String, int, int, android.os.Handler, android.net.wifi.DppStatusCallback);
+    method public void startDppAsEnrolleeInitiator(java.lang.String, android.os.Handler, android.net.wifi.DppStatusCallback);
     method public boolean startScan(android.os.WorkSource);
+    method public void stopDppSession();
     method public void unregisterNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback);
     field public static final int CHANGE_REASON_ADDED = 0; // 0x0
     field public static final int CHANGE_REASON_CONFIG_CHANGE = 2; // 0x2
     field public static final int CHANGE_REASON_REMOVED = 1; // 0x1
     field public static final java.lang.String CONFIGURED_NETWORKS_CHANGED_ACTION = "android.net.wifi.CONFIGURED_NETWORKS_CHANGE";
+    field public static final int DEVICE_MOBILITY_STATE_HIGH_MVMT = 1; // 0x1
+    field public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2; // 0x2
+    field public static final int DEVICE_MOBILITY_STATE_STATIONARY = 3; // 0x3
+    field public static final int DEVICE_MOBILITY_STATE_UNKNOWN = 0; // 0x0
+    field public static final int DPP_NETWORK_ROLE_AP = 1; // 0x1
+    field public static final int DPP_NETWORK_ROLE_STA = 0; // 0x0
     field public static final java.lang.String EXTRA_CHANGE_REASON = "changeReason";
     field public static final java.lang.String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
     field public static final java.lang.String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
@@ -3884,7 +4094,11 @@
 package android.net.wifi.aware {
 
   public class DiscoverySession implements java.lang.AutoCloseable {
-    method public android.net.NetworkSpecifier createNetworkSpecifierPmk(android.net.wifi.aware.PeerHandle, byte[]);
+    method public deprecated android.net.NetworkSpecifier createNetworkSpecifierPmk(android.net.wifi.aware.PeerHandle, byte[]);
+  }
+
+  public static class WifiAwareManager.NetworkSpecifierBuilder {
+    method public android.net.wifi.aware.WifiAwareManager.NetworkSpecifierBuilder setPmk(byte[]);
   }
 
   public class WifiAwareSession implements java.lang.AutoCloseable {
@@ -4280,6 +4494,7 @@
     field public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10; // 0xa
     field public static final int PAYLOAD_MISMATCHED_TYPE_ERROR = 6; // 0x6
     field public static final int PAYLOAD_SIZE_MISMATCH_ERROR = 11; // 0xb
+    field public static final int PAYLOAD_TIMESTAMP_ERROR = 51; // 0x33
     field public static final int POST_INSTALL_RUNNER_ERROR = 5; // 0x5
     field public static final int SUCCESS = 0; // 0x0
     field public static final int UPDATED_BUT_NOT_ACTIVE = 52; // 0x34
@@ -4376,6 +4591,7 @@
     method public void allocateBytes(java.util.UUID, long, int) throws java.io.IOException;
     method public void allocateBytes(java.io.FileDescriptor, long, int) throws java.io.IOException;
     method public long getAllocatableBytes(java.util.UUID, int) throws java.io.IOException;
+    method public static boolean hasIsolatedStorage();
     field public static final int FLAG_ALLOCATE_AGGRESSIVE = 1; // 0x1
   }
 
@@ -4383,6 +4599,16 @@
 
 package android.permission {
 
+  public abstract class PermissionControllerService extends android.app.Service {
+    ctor public PermissionControllerService();
+    method public final void attachBaseContext(android.content.Context);
+    method public final android.os.IBinder onBind(android.content.Intent);
+    method public abstract int onCountPermissionApps(java.util.List<java.lang.String>, boolean, boolean);
+    method public abstract java.util.List<android.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(java.lang.String);
+    method public abstract void onRevokeRuntimePermission(java.lang.String, java.lang.String);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
+  }
+
   public final class PermissionManager {
     method public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions();
   }
@@ -4403,16 +4629,6 @@
     field public static final android.os.Parcelable.Creator<android.permission.RuntimePermissionPresentationInfo> CREATOR;
   }
 
-  public abstract class RuntimePermissionPresenterService extends android.app.Service {
-    ctor public RuntimePermissionPresenterService();
-    method public final void attachBaseContext(android.content.Context);
-    method public final android.os.IBinder onBind(android.content.Intent);
-    method public abstract int onCountPermissionApps(java.util.List<java.lang.String>, boolean, boolean);
-    method public abstract java.util.List<android.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(java.lang.String);
-    method public abstract void onRevokeRuntimePermission(java.lang.String, java.lang.String);
-    field public static final java.lang.String SERVICE_INTERFACE = "android.permission.RuntimePermissionPresenterService";
-  }
-
 }
 
 package android.permissionpresenterservice {
@@ -4528,6 +4744,18 @@
     field public static final java.lang.String STATE = "state";
   }
 
+  public final class DeviceConfig {
+    method public static void addOnPropertyChangedListener(java.lang.String, java.util.concurrent.Executor, android.provider.DeviceConfig.OnPropertyChangedListener);
+    method public static java.lang.String getProperty(java.lang.String, java.lang.String);
+    method public static void removeOnPropertyChangedListener(android.provider.DeviceConfig.OnPropertyChangedListener);
+    method public static void resetToDefaults(int, java.lang.String);
+    method public static boolean setProperty(java.lang.String, java.lang.String, java.lang.String, boolean);
+  }
+
+  public static abstract interface DeviceConfig.OnPropertyChangedListener {
+    method public abstract void onPropertyChanged(java.lang.String, java.lang.String, java.lang.String);
+  }
+
   public final class DocumentsContract {
     method public static boolean isManageMode(android.net.Uri);
     method public static android.net.Uri setManageMode(android.net.Uri);
@@ -4647,6 +4875,7 @@
 
   public final class Settings {
     field public static final java.lang.String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
+    field public static final java.lang.String ACTION_LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS = "android.settings.LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS";
     field public static final java.lang.String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS";
   }
 
@@ -4973,7 +5202,7 @@
 
 package android.service.contentcapture {
 
-  public final class ContentCaptureEventsRequest implements android.os.Parcelable {
+  public final deprecated class ContentCaptureEventsRequest implements android.os.Parcelable {
     method public int describeContents();
     method public java.util.List<android.view.contentcapture.ContentCaptureEvent> getEvents();
     method public void writeToParcel(android.os.Parcel, int);
@@ -4984,34 +5213,19 @@
     ctor public ContentCaptureService();
     method public final java.util.Set<android.content.ComponentName> getContentCaptureDisabledActivities();
     method public final java.util.Set<java.lang.String> getContentCaptureDisabledPackages();
-    method public void onActivitySnapshot(android.service.contentcapture.InteractionSessionId, android.service.contentcapture.SnapshotData);
-    method public abstract void onContentCaptureEventsRequest(android.service.contentcapture.InteractionSessionId, android.service.contentcapture.ContentCaptureEventsRequest);
-    method public void onCreateInteractionSession(android.service.contentcapture.InteractionContext, android.service.contentcapture.InteractionSessionId);
-    method public void onDestroyInteractionSession(android.service.contentcapture.InteractionSessionId);
+    method public void onActivitySnapshot(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.SnapshotData);
+    method public void onConnected();
+    method public void onContentCaptureEvent(android.view.contentcapture.ContentCaptureSessionId, android.view.contentcapture.ContentCaptureEvent);
+    method public deprecated void onContentCaptureEventsRequest(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.ContentCaptureEventsRequest);
+    method public void onCreateContentCaptureSession(android.view.contentcapture.ContentCaptureContext, android.view.contentcapture.ContentCaptureSessionId);
+    method public void onDestroyContentCaptureSession(android.view.contentcapture.ContentCaptureSessionId);
+    method public void onDisconnected();
     method public final void setActivityContentCaptureEnabled(android.content.ComponentName, boolean);
     method public final void setContentCaptureWhitelist(java.util.List<java.lang.String>, java.util.List<android.content.ComponentName>);
     method public final void setPackageContentCaptureEnabled(java.lang.String, boolean);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.contentcapture.ContentCaptureService";
   }
 
-  public final class InteractionContext implements android.os.Parcelable {
-    method public int describeContents();
-    method public android.content.ComponentName getActivityComponent();
-    method public int getDisplayId();
-    method public int getFlags();
-    method public int getTaskId();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.contentcapture.InteractionContext> CREATOR;
-    field public static final int FLAG_DISABLED_BY_APP = 1; // 0x1
-    field public static final int FLAG_DISABLED_BY_FLAG_SECURE = 2; // 0x2
-  }
-
-  public final class InteractionSessionId implements android.os.Parcelable {
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.contentcapture.InteractionSessionId> CREATOR;
-  }
-
   public final class SnapshotData implements android.os.Parcelable {
     method public int describeContents();
     method public android.app.assist.AssistContent getAssistContent();
@@ -5025,6 +5239,16 @@
 
 package android.service.euicc {
 
+  public final class DownloadSubscriptionResult implements android.os.Parcelable {
+    ctor public DownloadSubscriptionResult(int, int, int);
+    method public int describeContents();
+    method public int getCardId();
+    method public int getResolvableErrors();
+    method public int getResult();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.euicc.DownloadSubscriptionResult> CREATOR;
+  }
+
   public final class EuiccProfileInfo implements android.os.Parcelable {
     method public int describeContents();
     method public android.service.carrier.CarrierIdentifier getCarrierIdentifier();
@@ -5078,7 +5302,8 @@
     ctor public EuiccService();
     method public android.os.IBinder onBind(android.content.Intent);
     method public abstract int onDeleteSubscription(int, java.lang.String);
-    method public abstract int onDownloadSubscription(int, android.telephony.euicc.DownloadableSubscription, boolean, boolean);
+    method public abstract android.service.euicc.DownloadSubscriptionResult onDownloadSubscription(int, android.telephony.euicc.DownloadableSubscription, boolean, boolean, android.os.Bundle);
+    method public deprecated int onDownloadSubscription(int, android.telephony.euicc.DownloadableSubscription, boolean, boolean);
     method public abstract int onEraseSubscriptions(int);
     method public abstract android.service.euicc.GetDefaultDownloadableSubscriptionListResult onGetDefaultDownloadableSubscriptionList(int, boolean);
     method public abstract android.service.euicc.GetDownloadableSubscriptionMetadataResult onGetDownloadableSubscriptionMetadata(int, android.telephony.euicc.DownloadableSubscription, boolean);
@@ -5093,19 +5318,25 @@
     field public static final java.lang.String ACTION_BIND_CARRIER_PROVISIONING_SERVICE = "android.service.euicc.action.BIND_CARRIER_PROVISIONING_SERVICE";
     field public static final java.lang.String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS = "android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
     field public static final java.lang.String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.service.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
-    field public static final java.lang.String ACTION_RESOLVE_CONFIRMATION_CODE = "android.service.euicc.action.RESOLVE_CONFIRMATION_CODE";
+    field public static final deprecated java.lang.String ACTION_RESOLVE_CONFIRMATION_CODE = "android.service.euicc.action.RESOLVE_CONFIRMATION_CODE";
     field public static final java.lang.String ACTION_RESOLVE_DEACTIVATE_SIM = "android.service.euicc.action.RESOLVE_DEACTIVATE_SIM";
     field public static final java.lang.String ACTION_RESOLVE_NO_PRIVILEGES = "android.service.euicc.action.RESOLVE_NO_PRIVILEGES";
+    field public static final java.lang.String ACTION_RESOLVE_RESOLVABLE_ERRORS = "android.service.euicc.action.RESOLVE_RESOLVABLE_ERRORS";
     field public static final java.lang.String CATEGORY_EUICC_UI = "android.service.euicc.category.EUICC_UI";
     field public static final java.lang.String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService";
+    field public static final java.lang.String EXTRA_RESOLUTION_ALLOW_POLICY_RULES = "android.service.euicc.extra.RESOLUTION_ALLOW_POLICY_RULES";
     field public static final java.lang.String EXTRA_RESOLUTION_CALLING_PACKAGE = "android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE";
     field public static final java.lang.String EXTRA_RESOLUTION_CONFIRMATION_CODE = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE";
     field public static final java.lang.String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED";
     field public static final java.lang.String EXTRA_RESOLUTION_CONSENT = "android.service.euicc.extra.RESOLUTION_CONSENT";
+    field public static final java.lang.String EXTRA_RESOLVABLE_ERRORS = "android.service.euicc.extra.RESOLVABLE_ERRORS";
+    field public static final int RESOLVABLE_ERROR_CONFIRMATION_CODE = 1; // 0x1
+    field public static final int RESOLVABLE_ERROR_POLICY_RULES = 2; // 0x2
     field public static final int RESULT_FIRST_USER = 1; // 0x1
     field public static final int RESULT_MUST_DEACTIVATE_SIM = -1; // 0xffffffff
-    field public static final int RESULT_NEED_CONFIRMATION_CODE = -2; // 0xfffffffe
+    field public static final deprecated int RESULT_NEED_CONFIRMATION_CODE = -2; // 0xfffffffe
     field public static final int RESULT_OK = 0; // 0x0
+    field public static final int RESULT_RESOLVABLE_ERRORS = -2; // 0xfffffffe
   }
 
   public static abstract class EuiccService.OtaStatusChangedCallback {
@@ -5591,6 +5822,14 @@
     ctor public PhoneAccountSuggestion(android.telecom.PhoneAccountHandle, int, boolean);
   }
 
+  public class PhoneAccountSuggestionService extends android.app.Service {
+    ctor public PhoneAccountSuggestionService();
+    method public void onAccountSuggestionRequest(java.lang.String);
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public final void suggestPhoneAccounts(java.lang.String, java.util.List<android.telecom.PhoneAccountSuggestion>);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.PhoneAccountSuggestionService";
+  }
+
   public final class RemoteConference {
     method public deprecated void setAudioState(android.telecom.AudioState);
   }
@@ -5658,7 +5897,7 @@
     method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(java.lang.String);
     method public boolean isInEmergencyCall();
     method public boolean isRinging();
-    method public boolean setDefaultDialer(java.lang.String);
+    method public deprecated boolean setDefaultDialer(java.lang.String);
     field public static final java.lang.String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
     field public static final java.lang.String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
     field public static final java.lang.String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
@@ -5684,6 +5923,83 @@
     field public static final java.lang.String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
   }
 
+  public class DisconnectCause {
+    field public static final int ALREADY_DIALING = 72; // 0x48
+    field public static final int ANSWERED_ELSEWHERE = 52; // 0x34
+    field public static final int BUSY = 4; // 0x4
+    field public static final int CALLING_DISABLED = 74; // 0x4a
+    field public static final int CALL_BARRED = 20; // 0x14
+    field public static final int CALL_PULLED = 51; // 0x33
+    field public static final int CANT_CALL_WHILE_RINGING = 73; // 0x49
+    field public static final int CDMA_ACCESS_BLOCKED = 35; // 0x23
+    field public static final int CDMA_ACCESS_FAILURE = 32; // 0x20
+    field public static final int CDMA_ALREADY_ACTIVATED = 49; // 0x31
+    field public static final int CDMA_DROP = 27; // 0x1b
+    field public static final int CDMA_INTERCEPT = 28; // 0x1c
+    field public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 26; // 0x1a
+    field public static final int CDMA_NOT_EMERGENCY = 34; // 0x22
+    field public static final int CDMA_PREEMPTED = 33; // 0x21
+    field public static final int CDMA_REORDER = 29; // 0x1d
+    field public static final int CDMA_RETRY_ORDER = 31; // 0x1f
+    field public static final int CDMA_SO_REJECT = 30; // 0x1e
+    field public static final int CONGESTION = 5; // 0x5
+    field public static final int CS_RESTRICTED = 22; // 0x16
+    field public static final int CS_RESTRICTED_EMERGENCY = 24; // 0x18
+    field public static final int CS_RESTRICTED_NORMAL = 23; // 0x17
+    field public static final int DATA_DISABLED = 54; // 0x36
+    field public static final int DATA_LIMIT_REACHED = 55; // 0x37
+    field public static final int DIALED_CALL_FORWARDING_WHILE_ROAMING = 57; // 0x39
+    field public static final int DIALED_MMI = 39; // 0x27
+    field public static final int DIAL_LOW_BATTERY = 62; // 0x3e
+    field public static final int DIAL_MODIFIED_TO_DIAL = 48; // 0x30
+    field public static final int DIAL_MODIFIED_TO_DIAL_VIDEO = 66; // 0x42
+    field public static final int DIAL_MODIFIED_TO_SS = 47; // 0x2f
+    field public static final int DIAL_MODIFIED_TO_USSD = 46; // 0x2e
+    field public static final int DIAL_VIDEO_MODIFIED_TO_DIAL = 69; // 0x45
+    field public static final int DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 70; // 0x46
+    field public static final int DIAL_VIDEO_MODIFIED_TO_SS = 67; // 0x43
+    field public static final int DIAL_VIDEO_MODIFIED_TO_USSD = 68; // 0x44
+    field public static final int EMERGENCY_PERM_FAILURE = 64; // 0x40
+    field public static final int EMERGENCY_TEMP_FAILURE = 63; // 0x3f
+    field public static final int ERROR_UNSPECIFIED = 36; // 0x24
+    field public static final int FDN_BLOCKED = 21; // 0x15
+    field public static final int ICC_ERROR = 19; // 0x13
+    field public static final int IMEI_NOT_ACCEPTED = 58; // 0x3a
+    field public static final int IMS_ACCESS_BLOCKED = 60; // 0x3c
+    field public static final int IMS_MERGED_SUCCESSFULLY = 45; // 0x2d
+    field public static final int IMS_SIP_ALTERNATE_EMERGENCY_CALL = 71; // 0x47
+    field public static final int INCOMING_MISSED = 1; // 0x1
+    field public static final int INCOMING_REJECTED = 16; // 0x10
+    field public static final int INVALID_CREDENTIALS = 10; // 0xa
+    field public static final int INVALID_NUMBER = 7; // 0x7
+    field public static final int LIMIT_EXCEEDED = 15; // 0xf
+    field public static final int LOCAL = 3; // 0x3
+    field public static final int LOST_SIGNAL = 14; // 0xe
+    field public static final int LOW_BATTERY = 61; // 0x3d
+    field public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 53; // 0x35
+    field public static final int MMI = 6; // 0x6
+    field public static final int NORMAL = 2; // 0x2
+    field public static final int NORMAL_UNSPECIFIED = 65; // 0x41
+    field public static final int NOT_DISCONNECTED = 0; // 0x0
+    field public static final int NOT_VALID = -1; // 0xffffffff
+    field public static final int NO_PHONE_NUMBER_SUPPLIED = 38; // 0x26
+    field public static final int NUMBER_UNREACHABLE = 8; // 0x8
+    field public static final int OTASP_PROVISIONING_IN_PROCESS = 76; // 0x4c
+    field public static final int OUTGOING_CANCELED = 44; // 0x2c
+    field public static final int OUTGOING_FAILURE = 43; // 0x2b
+    field public static final int OUT_OF_NETWORK = 11; // 0xb
+    field public static final int OUT_OF_SERVICE = 18; // 0x12
+    field public static final int POWER_OFF = 17; // 0x11
+    field public static final int SERVER_ERROR = 12; // 0xc
+    field public static final int SERVER_UNREACHABLE = 9; // 0x9
+    field public static final int TIMED_OUT = 13; // 0xd
+    field public static final int TOO_MANY_ONGOING_CALLS = 75; // 0x4b
+    field public static final int UNOBTAINABLE_NUMBER = 25; // 0x19
+    field public static final int VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED = 50; // 0x32
+    field public static final int VOICEMAIL_NUMBER_MISSING = 40; // 0x28
+    field public static final int WIFI_LOST = 59; // 0x3b
+  }
+
   public class MbmsDownloadSession implements java.lang.AutoCloseable {
     field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_ACTION = "android.telephony.action.EmbmsDownload";
   }
@@ -5730,16 +6046,15 @@
   public abstract class NetworkService extends android.app.Service {
     ctor public NetworkService();
     method protected abstract android.telephony.NetworkService.NetworkServiceProvider createNetworkServiceProvider(int);
-    field public static final java.lang.String NETWORK_SERVICE_EXTRA_SLOT_ID = "android.telephony.extra.SLOT_ID";
     field public static final java.lang.String NETWORK_SERVICE_INTERFACE = "android.telephony.NetworkService";
   }
 
-  public class NetworkService.NetworkServiceProvider {
+  public abstract class NetworkService.NetworkServiceProvider implements java.lang.AutoCloseable {
     ctor public NetworkService.NetworkServiceProvider(int);
+    method public abstract void close();
     method public void getNetworkRegistrationState(int, android.telephony.NetworkServiceCallback);
     method public final int getSlotId();
     method public final void notifyNetworkRegistrationStateChanged();
-    method protected void onDestroy();
   }
 
   public class NetworkServiceCallback {
@@ -5773,14 +6088,134 @@
   }
 
   public class PhoneStateListener {
+    method public void onCallDisconnectCauseChanged(int, int);
+    method public void onPreciseCallStateChanged(android.telephony.PreciseCallState);
     method public void onRadioPowerStateChanged(int);
     method public void onSrvccStateChanged(int);
     method public void onVoiceActivationStateChanged(int);
+    field public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
+    field public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
     field public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
     field public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000
     field public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000
   }
 
+  public final class PreciseCallState implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getBackgroundCallState();
+    method public int getForegroundCallState();
+    method public int getRingingCallState();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.PreciseCallState> CREATOR;
+    field public static final int PRECISE_CALL_STATE_ACTIVE = 1; // 0x1
+    field public static final int PRECISE_CALL_STATE_ALERTING = 4; // 0x4
+    field public static final int PRECISE_CALL_STATE_DIALING = 3; // 0x3
+    field public static final int PRECISE_CALL_STATE_DISCONNECTED = 7; // 0x7
+    field public static final int PRECISE_CALL_STATE_DISCONNECTING = 8; // 0x8
+    field public static final int PRECISE_CALL_STATE_HOLDING = 2; // 0x2
+    field public static final int PRECISE_CALL_STATE_IDLE = 0; // 0x0
+    field public static final int PRECISE_CALL_STATE_INCOMING = 5; // 0x5
+    field public static final int PRECISE_CALL_STATE_NOT_VALID = -1; // 0xffffffff
+    field public static final int PRECISE_CALL_STATE_WAITING = 6; // 0x6
+  }
+
+  public class PreciseDisconnectCause {
+    field public static final int ACCESS_CLASS_BLOCKED = 260; // 0x104
+    field public static final int ACCESS_INFORMATION_DISCARDED = 43; // 0x2b
+    field public static final int ACM_LIMIT_EXCEEDED = 68; // 0x44
+    field public static final int BEARER_CAPABILITY_NOT_AUTHORIZED = 57; // 0x39
+    field public static final int BEARER_NOT_AVAIL = 58; // 0x3a
+    field public static final int BEARER_SERVICE_NOT_IMPLEMENTED = 65; // 0x41
+    field public static final int BUSY = 17; // 0x11
+    field public static final int CALL_BARRED = 240; // 0xf0
+    field public static final int CALL_REJECTED = 21; // 0x15
+    field public static final int CDMA_ACCESS_BLOCKED = 1009; // 0x3f1
+    field public static final int CDMA_ACCESS_FAILURE = 1006; // 0x3ee
+    field public static final int CDMA_DROP = 1001; // 0x3e9
+    field public static final int CDMA_INTERCEPT = 1002; // 0x3ea
+    field public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 1000; // 0x3e8
+    field public static final int CDMA_NOT_EMERGENCY = 1008; // 0x3f0
+    field public static final int CDMA_PREEMPTED = 1007; // 0x3ef
+    field public static final int CDMA_REORDER = 1003; // 0x3eb
+    field public static final int CDMA_RETRY_ORDER = 1005; // 0x3ed
+    field public static final int CDMA_SO_REJECT = 1004; // 0x3ec
+    field public static final int CHANNEL_NOT_AVAIL = 44; // 0x2c
+    field public static final int CHANNEL_UNACCEPTABLE = 6; // 0x6
+    field public static final int CONDITIONAL_IE_ERROR = 100; // 0x64
+    field public static final int DESTINATION_OUT_OF_ORDER = 27; // 0x1b
+    field public static final int ERROR_UNSPECIFIED = 65535; // 0xffff
+    field public static final int FACILITY_REJECTED = 29; // 0x1d
+    field public static final int FDN_BLOCKED = 241; // 0xf1
+    field public static final int IMEI_NOT_ACCEPTED = 243; // 0xf3
+    field public static final int IMSI_UNKNOWN_IN_VLR = 242; // 0xf2
+    field public static final int INCOMING_CALLS_BARRED_WITHIN_CUG = 55; // 0x37
+    field public static final int INCOMPATIBLE_DESTINATION = 88; // 0x58
+    field public static final int INFORMATION_ELEMENT_NON_EXISTENT = 99; // 0x63
+    field public static final int INTERWORKING_UNSPECIFIED = 127; // 0x7f
+    field public static final int INVALID_MANDATORY_INFORMATION = 96; // 0x60
+    field public static final int INVALID_NUMBER_FORMAT = 28; // 0x1c
+    field public static final int INVALID_TRANSACTION_IDENTIFIER = 81; // 0x51
+    field public static final int MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 101; // 0x65
+    field public static final int MESSAGE_TYPE_NON_IMPLEMENTED = 97; // 0x61
+    field public static final int MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 98; // 0x62
+    field public static final int NETWORK_DETACH = 261; // 0x105
+    field public static final int NETWORK_OUT_OF_ORDER = 38; // 0x26
+    field public static final int NETWORK_REJECT = 252; // 0xfc
+    field public static final int NETWORK_RESP_TIMEOUT = 251; // 0xfb
+    field public static final int NORMAL = 16; // 0x10
+    field public static final int NORMAL_UNSPECIFIED = 31; // 0x1f
+    field public static final int NOT_VALID = -1; // 0xffffffff
+    field public static final int NO_ANSWER_FROM_USER = 19; // 0x13
+    field public static final int NO_CIRCUIT_AVAIL = 34; // 0x22
+    field public static final int NO_DISCONNECT_CAUSE_AVAILABLE = 0; // 0x0
+    field public static final int NO_ROUTE_TO_DESTINATION = 3; // 0x3
+    field public static final int NO_USER_RESPONDING = 18; // 0x12
+    field public static final int NO_VALID_SIM = 249; // 0xf9
+    field public static final int NUMBER_CHANGED = 22; // 0x16
+    field public static final int OEM_CAUSE_1 = 61441; // 0xf001
+    field public static final int OEM_CAUSE_10 = 61450; // 0xf00a
+    field public static final int OEM_CAUSE_11 = 61451; // 0xf00b
+    field public static final int OEM_CAUSE_12 = 61452; // 0xf00c
+    field public static final int OEM_CAUSE_13 = 61453; // 0xf00d
+    field public static final int OEM_CAUSE_14 = 61454; // 0xf00e
+    field public static final int OEM_CAUSE_15 = 61455; // 0xf00f
+    field public static final int OEM_CAUSE_2 = 61442; // 0xf002
+    field public static final int OEM_CAUSE_3 = 61443; // 0xf003
+    field public static final int OEM_CAUSE_4 = 61444; // 0xf004
+    field public static final int OEM_CAUSE_5 = 61445; // 0xf005
+    field public static final int OEM_CAUSE_6 = 61446; // 0xf006
+    field public static final int OEM_CAUSE_7 = 61447; // 0xf007
+    field public static final int OEM_CAUSE_8 = 61448; // 0xf008
+    field public static final int OEM_CAUSE_9 = 61449; // 0xf009
+    field public static final int ONLY_DIGITAL_INFORMATION_BEARER_AVAILABLE = 70; // 0x46
+    field public static final int OPERATOR_DETERMINED_BARRING = 8; // 0x8
+    field public static final int OUT_OF_SRV = 248; // 0xf8
+    field public static final int PREEMPTION = 25; // 0x19
+    field public static final int PROTOCOL_ERROR_UNSPECIFIED = 111; // 0x6f
+    field public static final int QOS_NOT_AVAIL = 49; // 0x31
+    field public static final int RADIO_ACCESS_FAILURE = 253; // 0xfd
+    field public static final int RADIO_INTERNAL_ERROR = 250; // 0xfa
+    field public static final int RADIO_LINK_FAILURE = 254; // 0xfe
+    field public static final int RADIO_LINK_LOST = 255; // 0xff
+    field public static final int RADIO_OFF = 247; // 0xf7
+    field public static final int RADIO_RELEASE_ABNORMAL = 259; // 0x103
+    field public static final int RADIO_RELEASE_NORMAL = 258; // 0x102
+    field public static final int RADIO_SETUP_FAILURE = 257; // 0x101
+    field public static final int RADIO_UPLINK_FAILURE = 256; // 0x100
+    field public static final int RECOVERY_ON_TIMER_EXPIRED = 102; // 0x66
+    field public static final int REQUESTED_FACILITY_NOT_IMPLEMENTED = 69; // 0x45
+    field public static final int REQUESTED_FACILITY_NOT_SUBSCRIBED = 50; // 0x32
+    field public static final int RESOURCES_UNAVAILABLE_OR_UNSPECIFIED = 47; // 0x2f
+    field public static final int SEMANTICALLY_INCORRECT_MESSAGE = 95; // 0x5f
+    field public static final int SERVICE_OPTION_NOT_AVAILABLE = 63; // 0x3f
+    field public static final int SERVICE_OR_OPTION_NOT_IMPLEMENTED = 79; // 0x4f
+    field public static final int STATUS_ENQUIRY = 30; // 0x1e
+    field public static final int SWITCHING_CONGESTION = 42; // 0x2a
+    field public static final int TEMPORARY_FAILURE = 41; // 0x29
+    field public static final int UNOBTAINABLE_NUMBER = 1; // 0x1
+    field public static final int USER_NOT_MEMBER_OF_CUG = 87; // 0x57
+  }
+
   public class ServiceState implements android.os.Parcelable {
     method public android.telephony.NetworkRegistrationState getNetworkRegistrationState(int, int);
     method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates();
@@ -5818,11 +6253,13 @@
 
   public class SubscriptionInfo implements android.os.Parcelable {
     method public java.util.List<android.telephony.UiccAccessRule> getAccessRules();
+    method public int getCardId();
   }
 
   public class SubscriptionManager {
     method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
     method public void requestEmbeddedSubscriptionInfoListRefresh();
+    method public void requestEmbeddedSubscriptionInfoListRefresh(int);
     method public void setDefaultDataSubId(int);
     method public void setDefaultSmsSubId(int);
     field public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
@@ -5884,12 +6321,13 @@
     method public deprecated boolean getDataEnabled(int);
     method public boolean getEmergencyCallbackMode();
     method public java.lang.String getIsimDomain();
-    method public int getPreferredNetworkType(int);
+    method public int getPreferredNetworkTypeBitmap();
     method public int getRadioPowerState();
     method public int getSimApplicationState();
     method public int getSimCardState();
     method public int getSupportedRadioAccessFamily();
     method public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
+    method public android.telephony.UiccCardInfo[] getUiccCardsInfo();
     method public android.telephony.UiccSlotInfo[] getUiccSlotsInfo();
     method public android.os.Bundle getVisualVoicemailSettings();
     method public int getVoiceActivationState();
@@ -5913,6 +6351,7 @@
     method public void setDataActivationState(int);
     method public deprecated void setDataEnabled(int, boolean);
     method public void setDataRoamingEnabled(boolean);
+    method public boolean setPreferredNetworkTypeBitmap(int);
     method public boolean setRadio(boolean);
     method public boolean setRadioPower(boolean);
     method public void setSimPowerState(int);
@@ -5938,29 +6377,6 @@
     field public static final java.lang.String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
     field public static final int INVALID_CARD_ID = -1; // 0xffffffff
     field public static final long MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS = 60000L; // 0xea60L
-    field public static final int NETWORK_MODE_CDMA_EVDO = 4; // 0x4
-    field public static final int NETWORK_MODE_CDMA_NO_EVDO = 5; // 0x5
-    field public static final int NETWORK_MODE_EVDO_NO_CDMA = 6; // 0x6
-    field public static final int NETWORK_MODE_GLOBAL = 7; // 0x7
-    field public static final int NETWORK_MODE_GSM_ONLY = 1; // 0x1
-    field public static final int NETWORK_MODE_GSM_UMTS = 3; // 0x3
-    field public static final int NETWORK_MODE_LTE_CDMA_EVDO = 8; // 0x8
-    field public static final int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA = 10; // 0xa
-    field public static final int NETWORK_MODE_LTE_GSM_WCDMA = 9; // 0x9
-    field public static final int NETWORK_MODE_LTE_ONLY = 11; // 0xb
-    field public static final int NETWORK_MODE_LTE_TDSCDMA = 15; // 0xf
-    field public static final int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 22; // 0x16
-    field public static final int NETWORK_MODE_LTE_TDSCDMA_GSM = 17; // 0x11
-    field public static final int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA = 20; // 0x14
-    field public static final int NETWORK_MODE_LTE_TDSCDMA_WCDMA = 19; // 0x13
-    field public static final int NETWORK_MODE_LTE_WCDMA = 12; // 0xc
-    field public static final int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 21; // 0x15
-    field public static final int NETWORK_MODE_TDSCDMA_GSM = 16; // 0x10
-    field public static final int NETWORK_MODE_TDSCDMA_GSM_WCDMA = 18; // 0x12
-    field public static final int NETWORK_MODE_TDSCDMA_ONLY = 13; // 0xd
-    field public static final int NETWORK_MODE_TDSCDMA_WCDMA = 14; // 0xe
-    field public static final int NETWORK_MODE_WCDMA_ONLY = 2; // 0x2
-    field public static final int NETWORK_MODE_WCDMA_PREF = 0; // 0x0
     field public static final int NETWORK_TYPE_BITMASK_1xRTT = 128; // 0x80
     field public static final int NETWORK_TYPE_BITMASK_CDMA = 16; // 0x10
     field public static final int NETWORK_TYPE_BITMASK_EDGE = 4; // 0x4
@@ -6008,6 +6424,18 @@
     field public static final android.os.Parcelable.Creator<android.telephony.UiccAccessRule> CREATOR;
   }
 
+  public class UiccCardInfo implements android.os.Parcelable {
+    ctor public UiccCardInfo(boolean, int, java.lang.String, java.lang.String, int);
+    method public int describeContents();
+    method public int getCardId();
+    method public java.lang.String getEid();
+    method public java.lang.String getIccId();
+    method public int getSlotIndex();
+    method public boolean isEuicc();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.UiccCardInfo> CREATOR;
+  }
+
   public class UiccSlotInfo implements android.os.Parcelable {
     ctor public UiccSlotInfo(boolean, boolean, java.lang.String, int, int, boolean);
     method public int describeContents();
@@ -6079,20 +6507,19 @@
   public abstract class DataService extends android.app.Service {
     ctor public DataService();
     method public abstract android.telephony.data.DataService.DataServiceProvider createDataServiceProvider(int);
-    field public static final java.lang.String DATA_SERVICE_EXTRA_SLOT_ID = "android.telephony.data.extra.SLOT_ID";
     field public static final java.lang.String DATA_SERVICE_INTERFACE = "android.telephony.data.DataService";
     field public static final int REQUEST_REASON_HANDOVER = 3; // 0x3
     field public static final int REQUEST_REASON_NORMAL = 1; // 0x1
     field public static final int REQUEST_REASON_SHUTDOWN = 2; // 0x2
   }
 
-  public class DataService.DataServiceProvider {
+  public abstract class DataService.DataServiceProvider implements java.lang.AutoCloseable {
     ctor public DataService.DataServiceProvider(int);
+    method public abstract void close();
     method public void deactivateDataCall(int, int, android.telephony.data.DataServiceCallback);
     method public void getDataCallList(android.telephony.data.DataServiceCallback);
     method public final int getSlotId();
     method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
-    method protected void onDestroy();
     method public void setDataProfile(java.util.List<android.telephony.data.DataProfile>, boolean, android.telephony.data.DataServiceCallback);
     method public void setInitialAttachApn(android.telephony.data.DataProfile, boolean, android.telephony.data.DataServiceCallback);
     method public void setupDataCall(int, android.telephony.data.DataProfile, boolean, boolean, int, android.net.LinkProperties, android.telephony.data.DataServiceCallback);
@@ -6197,12 +6624,17 @@
     method public void getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent);
     method public int getOtaStatus();
     field public static final java.lang.String ACTION_OTA_STATUS_CHANGED = "android.telephony.euicc.action.OTA_STATUS_CHANGED";
+    field public static final java.lang.String ACTION_PROFILE_SELECTION = "android.telephony.euicc.action.PROFILE_SELECTION";
     field public static final java.lang.String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
+    field public static final int EUICC_ACTIVATION_TYPE_BACKUP = 2; // 0x2
+    field public static final int EUICC_ACTIVATION_TYPE_DEFAULT = 1; // 0x1
+    field public static final int EUICC_ACTIVATION_TYPE_TRANSFER = 3; // 0x3
     field public static final int EUICC_OTA_FAILED = 2; // 0x2
     field public static final int EUICC_OTA_IN_PROGRESS = 1; // 0x1
     field public static final int EUICC_OTA_NOT_NEEDED = 4; // 0x4
     field public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5; // 0x5
     field public static final int EUICC_OTA_SUCCEEDED = 3; // 0x3
+    field public static final java.lang.String EXTRA_ACTIVATION_TYPE = "android.telephony.euicc.extra.ACTIVATION_TYPE";
     field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS";
     field public static final java.lang.String EXTRA_FORCE_PROVISION = "android.telephony.euicc.extra.FORCE_PROVISION";
   }
@@ -6326,6 +6758,7 @@
     field public static final int DIALSTRING_SS_CONF = 1; // 0x1
     field public static final int DIALSTRING_USSD = 2; // 0x2
     field public static final java.lang.String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
+    field public static final java.lang.String EXTRA_ADDITIONAL_SIP_INVITE_FIELDS = "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS";
     field public static final java.lang.String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
     field public static final java.lang.String EXTRA_CHILD_NUMBER = "ChildNum";
     field public static final java.lang.String EXTRA_CNA = "cna";
@@ -6614,8 +7047,8 @@
     field public static final int CODE_SIP_SERVER_TIMEOUT = 353; // 0x161
     field public static final int CODE_SIP_SERVICE_UNAVAILABLE = 352; // 0x160
     field public static final int CODE_SIP_TEMPRARILY_UNAVAILABLE = 336; // 0x150
-    field public static final int CODE_SIP_TRANSACTION_DOES_NOT_EXIST = 343; // 0x157
     field public static final int CODE_SIP_TOO_MANY_HOPS = 374; // 0x176
+    field public static final int CODE_SIP_TRANSACTION_DOES_NOT_EXIST = 343; // 0x157
     field public static final int CODE_SIP_UNDECIPHERABLE = 378; // 0x17a
     field public static final int CODE_SIP_USER_MARKED_UNWANTED = 365; // 0x16d
     field public static final int CODE_SIP_USER_REJECTED = 361; // 0x169
@@ -7281,6 +7714,18 @@
 
 package android.view.contentcapture {
 
+  public final class ContentCaptureContext implements android.os.Parcelable {
+    method public android.content.ComponentName getActivityComponent();
+    method public int getDisplayId();
+    method public android.os.Bundle getExtras();
+    method public int getFlags();
+    method public android.view.contentcapture.ContentCaptureSessionId getParentSessionId();
+    method public int getTaskId();
+    method public android.net.Uri getUri();
+    field public static final int FLAG_DISABLED_BY_APP = 1; // 0x1
+    field public static final int FLAG_DISABLED_BY_FLAG_SECURE = 2; // 0x2
+  }
+
   public final class ContentCaptureEvent implements android.os.Parcelable {
     method public int describeContents();
     method public long getEventTime();
@@ -7291,13 +7736,20 @@
     method public android.view.contentcapture.ViewNode getViewNode();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR;
-    field public static final deprecated int TYPE_ACTIVITY_PAUSED = 3; // 0x3
-    field public static final deprecated int TYPE_ACTIVITY_RESUMED = 2; // 0x2
-    field public static final deprecated int TYPE_ACTIVITY_STARTED = 1; // 0x1
-    field public static final deprecated int TYPE_ACTIVITY_STOPPED = 4; // 0x4
-    field public static final int TYPE_VIEW_APPEARED = 5; // 0x5
-    field public static final int TYPE_VIEW_DISAPPEARED = 6; // 0x6
-    field public static final int TYPE_VIEW_TEXT_CHANGED = 7; // 0x7
+    field public static final int TYPE_VIEW_APPEARED = 1; // 0x1
+    field public static final int TYPE_VIEW_DISAPPEARED = 2; // 0x2
+    field public static final int TYPE_VIEW_TEXT_CHANGED = 3; // 0x3
+  }
+
+  public final class UserDataRemovalRequest implements android.os.Parcelable {
+    method public java.lang.String getPackageName();
+    method public java.util.List<android.view.contentcapture.UserDataRemovalRequest.UriRequest> getUriRequests();
+    method public boolean isForEverything();
+  }
+
+  public final class UserDataRemovalRequest.UriRequest {
+    method public android.net.Uri getUri();
+    method public boolean isRecursive();
   }
 
   public final class ViewNode extends android.app.assist.AssistStructure.ViewNode {
@@ -7475,6 +7927,7 @@
     method public void callDrawGlFunction(android.graphics.Canvas, long, java.lang.Runnable);
     method public boolean canInvokeDrawGlFunctor(android.view.View);
     method public void detachDrawGlFunctor(android.view.View, long);
+    method public void drawWebViewFunctor(android.graphics.Canvas, int);
     method public android.app.Application getApplication();
     method public java.lang.String getDataDirectorySuffix();
     method public java.lang.String getErrorString(android.content.Context, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 627ef22..1401cbb 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -72,6 +72,7 @@
     method public void resizeStack(int, android.graphics.Rect) throws java.lang.SecurityException;
     method public void resizeStack(int, android.graphics.Rect, boolean);
     method public void resizeTask(int, android.graphics.Rect);
+    method public void setDisplayToSingleTaskInstance(int);
     method public void setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException;
     method public void setTaskWindowingModeSplitScreenPrimary(int, int, boolean, boolean, android.graphics.Rect, boolean) throws java.lang.SecurityException;
     method public void startSystemLockTaskMode(int);
@@ -182,7 +183,6 @@
     method public int getUserLockedFields();
     method public void lockFields(int);
     method public void setBlocked(boolean);
-    field public static final int USER_LOCKED_ALLOW_APP_OVERLAY = 2; // 0x2
   }
 
   public class NotificationManager {
@@ -639,6 +639,11 @@
     method public static boolean isEncodingLinearPcm(int);
   }
 
+  public final class AudioRecordingConfiguration implements android.os.Parcelable {
+    ctor public AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, java.lang.String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]);
+    ctor public AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, java.lang.String);
+  }
+
   public final class BufferingParams implements android.os.Parcelable {
     method public int describeContents();
     method public int getInitialMarkMs();
@@ -655,11 +660,6 @@
     method public android.media.BufferingParams.Builder setResumePlaybackMarkMs(int);
   }
 
-  public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation {
-    method public android.media.BufferingParams getBufferingParams();
-    method public void setBufferingParams(android.media.BufferingParams);
-  }
-
   public final class PlaybackParams implements android.os.Parcelable {
     method public int getAudioStretchMode();
     method public android.media.PlaybackParams setAudioStretchMode(int);
@@ -1109,6 +1109,20 @@
     method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int) throws java.lang.Exception;
   }
 
+  public final class CompositeUserData implements android.os.Parcelable {
+    ctor public CompositeUserData(android.service.autofill.UserData, android.service.autofill.UserData);
+    method public int describeContents();
+    method public java.lang.String[] getCategoryIds();
+    method public android.os.Bundle getDefaultFieldClassificationArgs();
+    method public java.lang.String getFieldClassificationAlgorithm();
+    method public java.lang.String getFieldClassificationAlgorithmForCategory(java.lang.String);
+    method public android.util.ArrayMap<java.lang.String, java.lang.String> getFieldClassificationAlgorithms();
+    method public android.util.ArrayMap<java.lang.String, android.os.Bundle> getFieldClassificationArgs();
+    method public java.lang.String[] getValues();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.CompositeUserData> CREATOR;
+  }
+
   public final class CustomDescription implements android.os.Parcelable {
     method public android.util.SparseArray<android.service.autofill.InternalOnClickAction> getActions();
   }
@@ -1197,7 +1211,7 @@
     field public static final java.lang.String KEY_USER_SENTIMENT = "key_user_sentiment";
   }
 
-  public abstract class ConditionProviderService extends android.app.Service {
+  public abstract deprecated class ConditionProviderService extends android.app.Service {
     method public boolean isBound();
   }
 
@@ -1287,6 +1301,14 @@
     ctor public PhoneAccountSuggestion(android.telecom.PhoneAccountHandle, int, boolean);
   }
 
+  public class PhoneAccountSuggestionService extends android.app.Service {
+    ctor public PhoneAccountSuggestionService();
+    method public void onAccountSuggestionRequest(java.lang.String);
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public final void suggestPhoneAccounts(java.lang.String, java.util.List<android.telecom.PhoneAccountSuggestion>);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.PhoneAccountSuggestionService";
+  }
+
 }
 
 package android.telephony {
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index e3748f1..3defdc5 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -247,7 +247,7 @@
         }
 
         try {
-            mBmgr.dataChanged(pkg);
+            mBmgr.dataChangedForUser(userId, pkg);
         } catch (RemoteException e) {
             System.err.println(e.toString());
             System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -264,7 +264,8 @@
         }
         if (allPkgs.size() > 0) {
             try {
-                mBmgr.fullTransportBackup(allPkgs.toArray(new String[allPkgs.size()]));
+                mBmgr.fullTransportBackupForUser(
+                        userId, allPkgs.toArray(new String[allPkgs.size()]));
             } catch (RemoteException e) {
                 System.err.println(e.toString());
                 System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -393,7 +394,7 @@
                     installedPackages.stream().map(p -> p.packageName).toArray(String[]::new);
             String[] filteredPackages = {};
             try {
-                filteredPackages = mBmgr.filterAppsEligibleForBackup(packages);
+                filteredPackages = mBmgr.filterAppsEligibleForBackupForUser(userId, packages);
             } catch (RemoteException e) {
                 System.err.println(e.toString());
                 System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -498,11 +499,11 @@
             }
 
             if ("-c".equals(which)) {
-                doTransportByComponent();
+                doTransportByComponent(userId);
                 return;
             }
 
-            String old = mBmgr.selectBackupTransport(which);
+            String old = mBmgr.selectBackupTransportForUser(userId, which);
             if (old == null) {
                 System.out.println("Unknown transport '" + which
                         + "' specified; no changes made.");
@@ -516,7 +517,7 @@
         }
     }
 
-    private void doTransportByComponent() {
+    private void doTransportByComponent(@UserIdInt int userId) {
         String which = nextArg();
         if (which == null) {
             showUsage();
@@ -526,7 +527,9 @@
         final CountDownLatch latch = new CountDownLatch(1);
 
         try {
-            mBmgr.selectBackupTransportAsync(ComponentName.unflattenFromString(which),
+            mBmgr.selectBackupTransportAsyncForUser(
+                    userId,
+                    ComponentName.unflattenFromString(which),
                     new ISelectBackupTransportCallback.Stub() {
                         @Override
                         public void onSuccess(String transportName) {
@@ -567,7 +570,7 @@
         }
 
         try {
-            mBmgr.clearBackupData(transport, pkg);
+            mBmgr.clearBackupDataForUser(userId, transport, pkg);
             System.out.println("Wiped backup data for " + pkg + " on " + transport);
         } catch (RemoteException e) {
             System.err.println(e.toString());
@@ -599,7 +602,8 @@
         InitObserver observer = new InitObserver();
         try {
             System.out.println("Initializing transports: " + transports);
-            mBmgr.initializeTransports(transports.toArray(new String[transports.size()]), observer);
+            mBmgr.initializeTransportsForUser(
+                    userId, transports.toArray(new String[transports.size()]), observer);
             observer.waitForCompletion(30*1000L);
             System.out.println("Initialization result: " + observer.result);
         } catch (RemoteException e) {
@@ -611,13 +615,13 @@
     private void doList(@UserIdInt int userId) {
         String arg = nextArg();     // sets, transports, packages set#
         if ("transports".equals(arg)) {
-            doListTransports();
+            doListTransports(userId);
             return;
         }
 
         // The rest of the 'list' options work with a restore session on the current transport
         try {
-            mRestore = mBmgr.beginRestoreSession(null, null);
+            mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null);
             if (mRestore == null) {
                 System.err.println(BMGR_NOT_RUNNING_ERR);
                 return;
@@ -634,19 +638,19 @@
         }
     }
 
-    private void doListTransports() {
+    private void doListTransports(@UserIdInt int userId) {
         String arg = nextArg();
 
         try {
             if ("-c".equals(arg)) {
-                for (ComponentName transport : mBmgr.listAllTransportComponents()) {
+                for (ComponentName transport : mBmgr.listAllTransportComponentsForUser(userId)) {
                     System.out.println(transport.flattenToShortString());
                 }
                 return;
             }
 
-            String current = mBmgr.getCurrentTransport();
-            String[] transports = mBmgr.listAllTransports();
+            String current = mBmgr.getCurrentTransportForUser(userId);
+            String[] transports = mBmgr.listAllTransportsForUser(userId);
             if (transports == null || transports.length == 0) {
                 System.out.println("No transports available.");
                 return;
@@ -756,7 +760,7 @@
                     filter.add(arg);
                 }
 
-                doRestoreAll(token, filter);
+                doRestoreAll(userId, token, filter);
             } catch (NumberFormatException e) {
                 showUsage();
                 return;
@@ -769,12 +773,12 @@
         System.err.println("'restore <token> <package>'.");
     }
 
-    private void doRestoreAll(long token, HashSet<String> filter) {
+    private void doRestoreAll(@UserIdInt int userId, long token, HashSet<String> filter) {
         RestoreObserver observer = new RestoreObserver();
 
         try {
             boolean didRestore = false;
-            mRestore = mBmgr.beginRestoreSession(null, null);
+            mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null);
             if (mRestore == null) {
                 System.err.println(BMGR_NOT_RUNNING_ERR);
                 return;
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 498eebc..04d4f9a 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -114,7 +114,7 @@
         virtual void shutdown() {}
     };
 
-    BootAnimation(sp<Callbacks> callbacks);
+    explicit BootAnimation(sp<Callbacks> callbacks);
 
     sp<SurfaceComposerClient> session() const;
 
@@ -128,7 +128,7 @@
 
     class TimeCheckThread : public Thread {
     public:
-        TimeCheckThread(BootAnimation* bootAnimation);
+        explicit TimeCheckThread(BootAnimation* bootAnimation);
         virtual ~TimeCheckThread();
     private:
         virtual status_t    readyToRun();
diff --git a/cmds/bu/src/com/android/commands/bu/Backup.java b/cmds/bu/src/com/android/commands/bu/Backup.java
index 834658d..373677e 100644
--- a/cmds/bu/src/com/android/commands/bu/Backup.java
+++ b/cmds/bu/src/com/android/commands/bu/Backup.java
@@ -16,13 +16,17 @@
 
 package com.android.commands.bu;
 
+import android.annotation.UserIdInt;
 import android.app.backup.IBackupManager;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.system.OsConstants;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.IOException;
 import java.util.ArrayList;
 
@@ -33,35 +37,50 @@
     int mNextArg;
     IBackupManager mBackupManager;
 
+    @VisibleForTesting
+    Backup(IBackupManager backupManager) {
+        mBackupManager = backupManager;
+    }
+
+    Backup() {
+        mBackupManager = IBackupManager.Stub.asInterface(ServiceManager.getService("backup"));
+    }
+
     public static void main(String[] args) {
-        Log.d(TAG, "Beginning: " + args[0]);
-        mArgs = args;
         try {
-            new Backup().run();
+            new Backup().run(args);
         } catch (Exception e) {
             Log.e(TAG, "Error running backup/restore", e);
         }
         Log.d(TAG, "Finished.");
     }
 
-    public void run() {
-        mBackupManager = IBackupManager.Stub.asInterface(ServiceManager.getService("backup"));
+    public void run(String[] args) {
         if (mBackupManager == null) {
             Log.e(TAG, "Can't obtain Backup Manager binder");
             return;
         }
 
+        Log.d(TAG, "Beginning: " + args[0]);
+        mArgs = args;
+
+        int userId = parseUserId();
+        if (!isBackupActiveForUser(userId)) {
+            Log.e(TAG, "BackupManager is not available for user " + userId);
+            return;
+        }
+
         String arg = nextArg();
         if (arg.equals("backup")) {
-            doBackup(OsConstants.STDOUT_FILENO);
+            doBackup(OsConstants.STDOUT_FILENO, userId);
         } else if (arg.equals("restore")) {
-            doRestore(OsConstants.STDIN_FILENO);
+            doRestore(OsConstants.STDIN_FILENO, userId);
         } else {
             showUsage();
         }
     }
 
-    private void doBackup(int socketFd) {
+    private void doBackup(int socketFd, @UserIdInt int userId) {
         ArrayList<String> packages = new ArrayList<String>();
         boolean saveApks = false;
         boolean saveObbs = false;
@@ -105,6 +124,10 @@
                     doKeyValue = true;
                 } else if ("-nokeyvalue".equals(arg)) {
                     doKeyValue = false;
+                } else if ("-user".equals(arg)) {
+                    // User ID has been processed in run(), ignore the next argument.
+                    nextArg();
+                    continue;
                 } else {
                     Log.w(TAG, "Unknown backup flag " + arg);
                     continue;
@@ -128,7 +151,7 @@
         try {
             fd = ParcelFileDescriptor.adoptFd(socketFd);
             String[] packArray = new String[packages.size()];
-            mBackupManager.adbBackup(fd, saveApks, saveObbs, saveShared, doWidgets, doEverything,
+            mBackupManager.adbBackup(userId, fd, saveApks, saveObbs, saveShared, doWidgets, doEverything,
                     allIncludesSystem, doCompress, doKeyValue, packages.toArray(packArray));
         } catch (RemoteException e) {
             Log.e(TAG, "Unable to invoke backup manager for backup");
@@ -143,12 +166,12 @@
         }
     }
 
-    private void doRestore(int socketFd) {
+    private void doRestore(int socketFd, @UserIdInt int userId) {
         // No arguments to restore
         ParcelFileDescriptor fd = null;
         try {
             fd = ParcelFileDescriptor.adoptFd(socketFd);
-            mBackupManager.adbRestore(fd);
+            mBackupManager.adbRestore(userId, fd);
         } catch (RemoteException e) {
             Log.e(TAG, "Unable to invoke backup manager for restore");
         } finally {
@@ -160,11 +183,31 @@
         }
     }
 
+    private @UserIdInt int parseUserId() {
+        for (int argNumber = 0; argNumber < mArgs.length - 1; argNumber++) {
+            if ("-user".equals(mArgs[argNumber])) {
+                return UserHandle.parseUserArg(mArgs[argNumber + 1]);
+            }
+        }
+
+        return UserHandle.USER_SYSTEM;
+    }
+
+    private boolean isBackupActiveForUser(int userId) {
+        try {
+            return mBackupManager.isBackupServiceActive(userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not access BackupManager: " + e.toString());
+            return false;
+        }
+    }
+
     private static void showUsage() {
-        System.err.println(" backup [-f FILE] [-apk|-noapk] [-obb|-noobb] [-shared|-noshared] [-all]");
-        System.err.println("        [-system|-nosystem] [-keyvalue|-nokeyvalue] [PACKAGE...]");
+        System.err.println(" backup [-user USER_ID] [-f FILE] [-apk|-noapk] [-obb|-noobb] [-shared|-noshared]");
+        System.err.println("        [-all] [-system|-nosystem] [-keyvalue|-nokeyvalue] [PACKAGE...]");
         System.err.println("     write an archive of the device's data to FILE [default=backup.adb]");
         System.err.println("     package list optional if -all/-shared are supplied");
+        System.err.println("     -user: user ID for which to perform the operation (default - system user)");
         System.err.println("     -apk/-noapk: do/don't back up .apk files (default -noapk)");
         System.err.println("     -obb/-noobb: do/don't back up .obb files (default -noobb)");
         System.err.println("     -shared|-noshared: do/don't back up shared storage (default -noshared)");
@@ -172,7 +215,8 @@
         System.err.println("     -system|-nosystem: include system apps in -all (default -system)");
         System.err.println("     -keyvalue|-nokeyvalue: include apps that perform key/value backups.");
         System.err.println("         (default -nokeyvalue)");
-        System.err.println(" restore FILE             restore device contents from FILE");
+        System.err.println(" restore [-user USER_ID] FILE       restore device contents from FILE");
+        System.err.println("     -user: user ID for which to perform the operation (default - system user)");
     }
 
     private String nextArg() {
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index d7922bc..abe18ba 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -12,14 +12,27 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-cc_library {
-    name: "libidmap2",
-    host_supported: true,
+cc_defaults {
+    name: "idmap2_defaults",
     tidy: true,
+    tidy_checks: [
+        "android-*",
+        "misc-*",
+        "modernize-*",
+        "readability-*",
+    ],
     tidy_flags: [
         "-system-headers",
-// b/120024673       "-warnings-as-errors=*",
+        "-warnings-as-errors=*",
     ],
+}
+
+cc_library {
+    name: "libidmap2",
+    defaults: [
+        "idmap2_defaults",
+    ],
+    host_supported: true,
     srcs: [
         "libidmap2/BinaryStreamVisitor.cpp",
         "libidmap2/CommandLineOptions.cpp",
@@ -60,12 +73,13 @@
 
 cc_test {
     name: "idmap2_tests",
-    host_supported: true,
-    tidy: true,
-    tidy_flags: [
-        "-system-headers",
-// b/120024673        "-warnings-as-errors=*",
+    defaults: [
+        "idmap2_defaults",
     ],
+    tidy_checks: [
+        "-readability-magic-numbers",
+    ],
+    host_supported: true,
     srcs: [
         "tests/BinaryStreamVisitorTests.cpp",
         "tests/CommandLineOptionsTests.cpp",
@@ -114,12 +128,10 @@
 
 cc_binary {
     name: "idmap2",
-    host_supported: true,
-    tidy: true,
-    tidy_flags: [
-        "-system-headers",
-// b/120024673        "-warnings-as-errors=*",
+    defaults: [
+        "idmap2_defaults",
     ],
+    host_supported: true,
     srcs: [
         "idmap2/Create.cpp",
         "idmap2/Dump.cpp",
@@ -156,19 +168,11 @@
 
 cc_binary {
     name: "idmap2d",
+    defaults: [
+        "idmap2_defaults",
+    ],
     host_supported: false,
-    tidy: true,
-    tidy_checks: [
-        // remove google-default-arguments or clang-tidy will complain about
-        // the auto-generated file IIdmap2.cpp
-        "-google-default-arguments",
-    ],
-    tidy_flags: [
-        "-system-headers",
-// b/120024673        "-warnings-as-errors=*",
-    ],
     srcs: [
-        ":idmap2_aidl",
         "idmap2d/Idmap2Service.cpp",
         "idmap2d/Main.cpp",
     ],
@@ -181,9 +185,30 @@
         "libutils",
         "libziparchive",
     ],
+    static_libs: [
+        "libidmap2daidl",
+    ],
     init_rc: ["idmap2d/idmap2d.rc"],
 }
 
+cc_library_static {
+    name: "libidmap2daidl",
+    defaults: [
+        "idmap2_defaults",
+    ],
+    tidy: false,
+    host_supported: false,
+    srcs: [
+        ":idmap2_aidl",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+    aidl: {
+        export_aidl_headers: true,
+    },
+}
+
 filegroup {
     name: "idmap2_aidl",
     srcs: [
diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp
index 291eaeb..b075673 100644
--- a/cmds/idmap2/idmap2/Create.cpp
+++ b/cmds/idmap2/idmap2/Create.cpp
@@ -32,9 +32,12 @@
 using android::idmap2::BinaryStreamVisitor;
 using android::idmap2::CommandLineOptions;
 using android::idmap2::Idmap;
+using android::idmap2::utils::kIdmapFilePermissionMask;
 
 bool Create(const std::vector<std::string>& args, std::ostream& out_error) {
-  std::string target_apk_path, overlay_apk_path, idmap_path;
+  std::string target_apk_path;
+  std::string overlay_apk_path;
+  std::string idmap_path;
 
   const CommandLineOptions opts =
       CommandLineOptions("idmap2 create")
@@ -68,7 +71,7 @@
     return false;
   }
 
-  umask(0133);  // u=rw,g=r,o=r
+  umask(kIdmapFilePermissionMask);
   std::ofstream fout(idmap_path);
   if (fout.fail()) {
     out_error << "failed to open idmap path " << idmap_path << std::endl;
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index 020c443..cfb5dd5 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -36,6 +36,7 @@
 
 #include "idmap2/CommandLineOptions.h"
 #include "idmap2/Idmap.h"
+#include "idmap2/Result.h"
 #include "idmap2/Xml.h"
 #include "idmap2/ZipFile.h"
 
@@ -53,39 +54,42 @@
 using android::idmap2::CommandLineOptions;
 using android::idmap2::IdmapHeader;
 using android::idmap2::ResourceId;
+using android::idmap2::Result;
 using android::idmap2::Xml;
 using android::idmap2::ZipFile;
 using android::util::Utf16ToUtf8;
 
 namespace {
-std::pair<bool, ResourceId> WARN_UNUSED ParseResReference(const AssetManager2& am,
-                                                          const std::string& res,
-                                                          const std::string& fallback_package) {
+
+Result<ResourceId> WARN_UNUSED ParseResReference(const AssetManager2& am, const std::string& res,
+                                                 const std::string& fallback_package) {
+  static constexpr const int kBaseHex = 16;
+
   // first, try to parse as a hex number
   char* endptr = nullptr;
   ResourceId resid;
-  resid = strtol(res.c_str(), &endptr, 16);
+  resid = strtol(res.c_str(), &endptr, kBaseHex);
   if (*endptr == '\0') {
-    return std::make_pair(true, resid);
+    return {resid};
   }
 
   // next, try to parse as a package:type/name string
   resid = am.GetResourceId(res, "", fallback_package);
   if (is_valid_resid(resid)) {
-    return std::make_pair(true, resid);
+    return {resid};
   }
 
   // end of the road: res could not be parsed
-  return std::make_pair(false, 0);
+  return {};
 }
 
-std::pair<bool, std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId resid) {
+Result<std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId resid) {
   Res_value value;
   ResTable_config config;
   uint32_t flags;
   ApkAssetsCookie cookie = am.GetResource(resid, false, 0, &value, &config, &flags);
   if (cookie == kInvalidCookie) {
-    return std::make_pair(false, "");
+    return {};
   }
 
   std::string out;
@@ -123,37 +127,39 @@
       out.append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data));
       break;
   }
-  return std::make_pair(true, out);
+  return {out};
 }
 
-std::pair<bool, std::string> GetTargetPackageNameFromManifest(const std::string& apk_path) {
+Result<std::string> GetTargetPackageNameFromManifest(const std::string& apk_path) {
   const auto zip = ZipFile::Open(apk_path);
   if (!zip) {
-    return std::make_pair(false, "");
+    return {};
   }
   const auto entry = zip->Uncompress("AndroidManifest.xml");
   if (!entry) {
-    return std::make_pair(false, "");
+    return {};
   }
   const auto xml = Xml::Create(entry->buf, entry->size);
   if (!xml) {
-    return std::make_pair(false, "");
+    return {};
   }
   const auto tag = xml->FindTag("overlay");
   if (!tag) {
-    return std::make_pair(false, "");
+    return {};
   }
   const auto iter = tag->find("targetPackage");
   if (iter == tag->end()) {
-    return std::make_pair(false, "");
+    return {};
   }
-  return std::make_pair(true, iter->second);
+  return {iter->second};
 }
 }  // namespace
 
 bool Lookup(const std::vector<std::string>& args, std::ostream& out_error) {
   std::vector<std::string> idmap_paths;
-  std::string config_str, resid_str;
+  std::string config_str;
+  std::string resid_str;
+
   const CommandLineOptions opts =
       CommandLineOptions("idmap2 lookup")
           .MandatoryOption("--idmap-path", "input: path to idmap file to load", &idmap_paths)
@@ -195,14 +201,14 @@
       }
       apk_assets.push_back(std::move(target_apk));
 
-      bool lookup_ok;
-      std::tie(lookup_ok, target_package_name) =
+      const Result<std::string> package_name =
           GetTargetPackageNameFromManifest(idmap_header->GetOverlayPath().to_string());
-      if (!lookup_ok) {
+      if (!package_name) {
         out_error << "error: failed to parse android:targetPackage from overlay manifest"
                   << std::endl;
         return false;
       }
+      target_package_name = *package_name;
     } else if (target_path != idmap_header->GetTargetPath()) {
       out_error << "error: different target APKs (expected target APK " << target_path << " but "
                 << idmap_path << " has target APK " << idmap_header->GetTargetPath() << ")"
@@ -227,21 +233,18 @@
   am.SetApkAssets(raw_pointer_apk_assets);
   am.SetConfiguration(config);
 
-  ResourceId resid;
-  bool lookup_ok;
-  std::tie(lookup_ok, resid) = ParseResReference(am, resid_str, target_package_name);
-  if (!lookup_ok) {
+  const Result<ResourceId> resid = ParseResReference(am, resid_str, target_package_name);
+  if (!resid) {
     out_error << "error: failed to parse resource ID" << std::endl;
     return false;
   }
 
-  std::string value;
-  std::tie(lookup_ok, value) = GetValue(am, resid);
-  if (!lookup_ok) {
-    out_error << StringPrintf("error: resource 0x%08x not found", resid) << std::endl;
+  const Result<std::string> value = GetValue(am, *resid);
+  if (!value) {
+    out_error << StringPrintf("error: resource 0x%08x not found", *resid) << std::endl;
     return false;
   }
-  std::cout << value << std::endl;
+  std::cout << *value << std::endl;
 
   return true;
 }
diff --git a/cmds/idmap2/idmap2/Main.cpp b/cmds/idmap2/idmap2/Main.cpp
index 5d9ea77..445fac5 100644
--- a/cmds/idmap2/idmap2/Main.cpp
+++ b/cmds/idmap2/idmap2/Main.cpp
@@ -29,10 +29,12 @@
 
 using android::idmap2::CommandLineOptions;
 
-typedef std::map<std::string, std::function<int(const std::vector<std::string>&, std::ostream&)>>
-    NameToFunctionMap;
+using NameToFunctionMap =
+    std::map<std::string, std::function<bool(const std::vector<std::string>&, std::ostream&)>>;
 
-static void PrintUsage(const NameToFunctionMap& commands, std::ostream& out) {
+namespace {
+
+void PrintUsage(const NameToFunctionMap& commands, std::ostream& out) {
   out << "usage: idmap2 [";
   for (auto iter = commands.cbegin(); iter != commands.cend(); iter++) {
     if (iter != commands.cbegin()) {
@@ -43,6 +45,8 @@
   out << "]" << std::endl;
 }
 
+}  // namespace
+
 int main(int argc, char** argv) {
   const NameToFunctionMap commands = {
       {"create", Create}, {"dump", Dump}, {"lookup", Lookup}, {"scan", Scan}, {"verify", Verify},
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
index 00c49e3..4918747 100644
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ b/cmds/idmap2/idmap2/Scan.cpp
@@ -44,7 +44,7 @@
   const auto predicate = [](unsigned char type, const std::string& path) -> bool {
     static constexpr size_t kExtLen = 4;  // strlen(".apk")
     return type == DT_REG && path.size() > kExtLen &&
-           !path.compare(path.size() - kExtLen, kExtLen, ".apk");
+           path.compare(path.size() - kExtLen, kExtLen, ".apk") == 0;
   };
   // pass apk paths through a set to filter out duplicates
   std::set<std::string> paths;
@@ -56,14 +56,15 @@
     }
     paths.insert(apk_paths->cbegin(), apk_paths->cend());
   }
-  return std::unique_ptr<std::vector<std::string>>(
-      new std::vector<std::string>(paths.cbegin(), paths.cend()));
+  return std::make_unique<std::vector<std::string>>(paths.cbegin(), paths.cend());
 }
 }  // namespace
 
 bool Scan(const std::vector<std::string>& args, std::ostream& out_error) {
   std::vector<std::string> input_directories;
-  std::string target_package_name, target_apk_path, output_directory;
+  std::string target_package_name;
+  std::string target_apk_path;
+  std::string output_directory;
   bool recursive = false;
 
   const CommandLineOptions opts =
@@ -112,7 +113,7 @@
     }
 
     auto iter = tag->find("isStatic");
-    if (iter == tag->end() || std::stoul(iter->second) == 0u) {
+    if (iter == tag->end() || std::stoul(iter->second) == 0U) {
       continue;
     }
 
@@ -136,15 +137,15 @@
   }
 
   std::stringstream stream;
-  for (auto iter = interesting_apks.cbegin(); iter != interesting_apks.cend(); ++iter) {
-    const std::string idmap_path = Idmap::CanonicalIdmapPathFor(output_directory, *iter);
+  for (const auto& apk : interesting_apks) {
+    const std::string idmap_path = Idmap::CanonicalIdmapPathFor(output_directory, apk);
     std::stringstream dev_null;
     if (!Verify(std::vector<std::string>({"--idmap-path", idmap_path}), dev_null) &&
         !Create(std::vector<std::string>({
                     "--target-apk-path",
                     target_apk_path,
                     "--overlay-apk-path",
-                    *iter,
+                    apk,
                     "--idmap-path",
                     idmap_path,
                 }),
diff --git a/cmds/idmap2/idmap2/Verify.cpp b/cmds/idmap2/idmap2/Verify.cpp
index b5fa438..4d4a0e7 100644
--- a/cmds/idmap2/idmap2/Verify.cpp
+++ b/cmds/idmap2/idmap2/Verify.cpp
@@ -27,6 +27,7 @@
 
 bool Verify(const std::vector<std::string>& args, std::ostream& out_error) {
   std::string idmap_path;
+
   const CommandLineOptions opts =
       CommandLineOptions("idmap2 verify")
           .MandatoryOption("--idmap-path", "input: path to idmap file to verify", &idmap_path);
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index cf72cb9..d2e46e1 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -39,10 +39,11 @@
 using android::idmap2::BinaryStreamVisitor;
 using android::idmap2::Idmap;
 using android::idmap2::IdmapHeader;
+using android::idmap2::utils::kIdmapFilePermissionMask;
 
 namespace {
 
-static constexpr const char* kIdmapCacheDir = "/data/resource-cache";
+constexpr const char* kIdmapCacheDir = "/data/resource-cache";
 
 Status ok() {
   return Status::ok();
@@ -55,8 +56,7 @@
 
 }  // namespace
 
-namespace android {
-namespace os {
+namespace android::os {
 
 Status Idmap2Service::getIdmapPath(const std::string& overlay_apk_path,
                                    int32_t user_id ATTRIBUTE_UNUSED, std::string* _aidl_return) {
@@ -69,13 +69,24 @@
                                   int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) {
   assert(_aidl_return);
   const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
-  if (unlink(idmap_path.c_str()) == 0) {
-    *_aidl_return = true;
-    return ok();
-  } else {
+  if (unlink(idmap_path.c_str()) != 0) {
     *_aidl_return = false;
     return error("failed to unlink " + idmap_path + ": " + strerror(errno));
   }
+  *_aidl_return = true;
+  return ok();
+}
+
+Status Idmap2Service::verifyIdmap(const std::string& overlay_apk_path,
+                                  int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) {
+  assert(_aidl_return);
+  const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
+  std::ifstream fin(idmap_path);
+  const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
+  fin.close();
+  std::stringstream dev_null;
+  *_aidl_return = header && header->IsUpToDate(dev_null);
+  return ok();
 }
 
 Status Idmap2Service::createIdmap(const std::string& target_apk_path,
@@ -90,17 +101,6 @@
 
   _aidl_return->reset(nullptr);
 
-  const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
-  std::ifstream fin(idmap_path);
-  const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
-  fin.close();
-  // do not reuse error stream from IsUpToDate below, or error messages will be
-  // polluted with irrelevant data
-  std::stringstream dev_null;
-  if (header && header->IsUpToDate(dev_null)) {
-    return ok();
-  }
-
   const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
   if (!target_apk) {
     return error("failed to load apk " + target_apk_path);
@@ -118,7 +118,8 @@
     return error(err.str());
   }
 
-  umask(0133);  // u=rw,g=r,o=r
+  umask(kIdmapFilePermissionMask);
+  const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
   std::ofstream fout(idmap_path);
   if (fout.fail()) {
     return error("failed to open idmap path " + idmap_path);
@@ -130,9 +131,8 @@
     return error("failed to write to idmap path " + idmap_path);
   }
 
-  _aidl_return->reset(new std::string(idmap_path));
+  *_aidl_return = std::make_unique<std::string>(idmap_path);
   return ok();
 }
 
-}  // namespace os
-}  // namespace android
+}  // namespace android::os
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h
index 2b32042..e0bc22e 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.h
+++ b/cmds/idmap2/idmap2d/Idmap2Service.h
@@ -25,8 +25,8 @@
 
 #include "android/os/BnIdmap2.h"
 
-namespace android {
-namespace os {
+namespace android::os {
+
 class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 {
  public:
   static char const* getServiceName() {
@@ -39,11 +39,14 @@
   binder::Status removeIdmap(const std::string& overlay_apk_path, int32_t user_id,
                              bool* _aidl_return);
 
+  binder::Status verifyIdmap(const std::string& overlay_apk_path, int32_t user_id,
+                             bool* _aidl_return);
+
   binder::Status createIdmap(const std::string& target_apk_path,
                              const std::string& overlay_apk_path, int32_t user_id,
                              std::unique_ptr<std::string>* _aidl_return);
 };
-}  // namespace os
-}  // namespace android
+
+}  // namespace android::os
 
 #endif  // IDMAP2_IDMAP2D_IDMAP2SERVICE_H_
diff --git a/cmds/idmap2/idmap2d/Main.cpp b/cmds/idmap2/idmap2d/Main.cpp
index d64a87b..4393dcc 100644
--- a/cmds/idmap2/idmap2d/Main.cpp
+++ b/cmds/idmap2/idmap2d/Main.cpp
@@ -37,7 +37,7 @@
 using android::os::Idmap2Service;
 
 int main(int argc ATTRIBUTE_UNUSED, char** argv ATTRIBUTE_UNUSED) {
-  IPCThreadState::self()->disableBackgroundScheduling(true);
+  IPCThreadState::disableBackgroundScheduling(true);
   status_t ret = BinderService<Idmap2Service>::publish();
   if (ret != android::OK) {
     return EXIT_FAILURE;
diff --git a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
index 5d19610..d475417 100644
--- a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
+++ b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
@@ -22,6 +22,7 @@
 interface IIdmap2 {
   @utf8InCpp String getIdmapPath(@utf8InCpp String overlayApkPath, int userId);
   boolean removeIdmap(@utf8InCpp String overlayApkPath, int userId);
+  boolean verifyIdmap(@utf8InCpp String overlayApkPath, int userId);
   @nullable @utf8InCpp String createIdmap(@utf8InCpp String targetApkPath,
                                           @utf8InCpp String overlayApkPath, int userId);
 }
diff --git a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
index 2368aea..2c3e9d3 100644
--- a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
+++ b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
@@ -23,8 +23,7 @@
 
 #include "idmap2/Idmap.h"
 
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
 
 class BinaryStreamVisitor : public Visitor {
  public:
@@ -43,7 +42,6 @@
   std::ostream& stream_;
 };
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
 
 #endif  // IDMAP2_INCLUDE_IDMAP2_BINARYSTREAMVISITOR_H_
diff --git a/cmds/idmap2/include/idmap2/CommandLineOptions.h b/cmds/idmap2/include/idmap2/CommandLineOptions.h
index f3aa68b..b93e716 100644
--- a/cmds/idmap2/include/idmap2/CommandLineOptions.h
+++ b/cmds/idmap2/include/idmap2/CommandLineOptions.h
@@ -23,8 +23,7 @@
 #include <string>
 #include <vector>
 
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
 
 /*
  * Utility class to convert a command line, including options (--path foo.txt),
@@ -65,7 +64,6 @@
   std::string name_;
 };
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
 
 #endif  // IDMAP2_INCLUDE_IDMAP2_COMMANDLINEOPTIONS_H_
diff --git a/cmds/idmap2/include/idmap2/FileUtils.h b/cmds/idmap2/include/idmap2/FileUtils.h
index 05c6d31..5c41c49 100644
--- a/cmds/idmap2/include/idmap2/FileUtils.h
+++ b/cmds/idmap2/include/idmap2/FileUtils.h
@@ -22,9 +22,10 @@
 #include <string>
 #include <vector>
 
-namespace android {
-namespace idmap2 {
-namespace utils {
+namespace android::idmap2::utils {
+
+constexpr const mode_t kIdmapFilePermissionMask = 0133;  // u=rw,g=r,o=r
+
 typedef std::function<bool(unsigned char type /* DT_* from dirent.h */, const std::string& path)>
     FindFilesPredicate;
 std::unique_ptr<std::vector<std::string>> FindFiles(const std::string& root, bool recurse,
@@ -34,8 +35,6 @@
 
 std::unique_ptr<std::string> ReadFile(const std::string& path);
 
-}  // namespace utils
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2::utils
 
 #endif  // IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index 837b7c5..b989e4c 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -57,8 +57,7 @@
 #include "androidfw/ResourceTypes.h"
 #include "androidfw/StringPiece.h"
 
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
 
 class Idmap;
 class Visitor;
@@ -271,7 +270,6 @@
   virtual void visit(const IdmapData::TypeEntry& type_entry) = 0;
 };
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
 
 #endif  // IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
index 88a835b..323796b 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -18,22 +18,17 @@
 #define IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_
 
 #include <string>
-#include <utility>
 
 #include "android-base/macros.h"
 #include "androidfw/AssetManager2.h"
 
 #include "idmap2/Idmap.h"
+#include "idmap2/Result.h"
 
-namespace android {
-namespace idmap2 {
-namespace utils {
+namespace android::idmap2::utils {
 
-std::pair<bool, std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am,
-                                                            ResourceId resid);
+Result<std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am, ResourceId resid);
 
-}  // namespace utils
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2::utils
 
 #endif  // IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_
diff --git a/cmds/idmap2/include/idmap2/Result.h b/cmds/idmap2/include/idmap2/Result.h
new file mode 100644
index 0000000..6189ea3
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/Result.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_RESULT_H_
+#define IDMAP2_INCLUDE_IDMAP2_RESULT_H_
+
+#include <optional>
+
+namespace android::idmap2 {
+
+template <typename T>
+using Result = std::optional<T>;
+
+static constexpr std::nullopt_t kResultError = std::nullopt;
+
+}  // namespace android::idmap2
+
+#endif  // IDMAP2_INCLUDE_IDMAP2_RESULT_H_
diff --git a/cmds/idmap2/include/idmap2/Xml.h b/cmds/idmap2/include/idmap2/Xml.h
index 9ab5ec4..dd89dee 100644
--- a/cmds/idmap2/include/idmap2/Xml.h
+++ b/cmds/idmap2/include/idmap2/Xml.h
@@ -25,8 +25,7 @@
 #include "androidfw/ResourceTypes.h"
 #include "utils/String16.h"
 
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
 
 class Xml {
  public:
@@ -45,7 +44,6 @@
   DISALLOW_COPY_AND_ASSIGN(Xml);
 };
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
 
 #endif  // IDMAP2_INCLUDE_IDMAP2_XML_H_
diff --git a/cmds/idmap2/include/idmap2/ZipFile.h b/cmds/idmap2/include/idmap2/ZipFile.h
index 328bd36..8f50e36 100644
--- a/cmds/idmap2/include/idmap2/ZipFile.h
+++ b/cmds/idmap2/include/idmap2/ZipFile.h
@@ -19,13 +19,12 @@
 
 #include <memory>
 #include <string>
-#include <utility>
 
 #include "android-base/macros.h"
+#include "idmap2/Result.h"
 #include "ziparchive/zip_archive.h"
 
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
 
 struct MemoryChunk {
   size_t size;
@@ -43,7 +42,7 @@
   static std::unique_ptr<const ZipFile> Open(const std::string& path);
 
   std::unique_ptr<const MemoryChunk> Uncompress(const std::string& entryPath) const;
-  std::pair<bool, uint32_t> Crc(const std::string& entryPath) const;
+  Result<uint32_t> Crc(const std::string& entryPath) const;
 
   ~ZipFile();
 
@@ -56,7 +55,6 @@
   DISALLOW_COPY_AND_ASSIGN(ZipFile);
 };
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
 
 #endif  // IDMAP2_INCLUDE_IDMAP2_ZIPFILE_H_
diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
index 29969a2..9651328 100644
--- a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
+++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
@@ -22,8 +22,7 @@
 
 #include "idmap2/BinaryStreamVisitor.h"
 
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
 
 void BinaryStreamVisitor::Write16(uint16_t value) {
   uint16_t x = htodl(value);
@@ -64,18 +63,17 @@
   Write16(header.GetTypeCount());
 }
 
-void BinaryStreamVisitor::visit(const IdmapData::TypeEntry& te) {
-  const uint16_t entryCount = te.GetEntryCount();
+void BinaryStreamVisitor::visit(const IdmapData::TypeEntry& type_entry) {
+  const uint16_t entryCount = type_entry.GetEntryCount();
 
-  Write16(te.GetTargetTypeId());
-  Write16(te.GetOverlayTypeId());
+  Write16(type_entry.GetTargetTypeId());
+  Write16(type_entry.GetOverlayTypeId());
   Write16(entryCount);
-  Write16(te.GetEntryOffset());
+  Write16(type_entry.GetEntryOffset());
   for (uint16_t i = 0; i < entryCount; i++) {
-    EntryId entry_id = te.GetEntry(i);
+    EntryId entry_id = type_entry.GetEntry(i);
     Write32(entry_id != kNoEntry ? static_cast<uint32_t>(entry_id) : kPadding);
   }
 }
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/CommandLineOptions.cpp b/cmds/idmap2/libidmap2/CommandLineOptions.cpp
index 28c3797..cabc8f3 100644
--- a/cmds/idmap2/libidmap2/CommandLineOptions.cpp
+++ b/cmds/idmap2/libidmap2/CommandLineOptions.cpp
@@ -26,13 +26,11 @@
 
 #include "idmap2/CommandLineOptions.h"
 
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
 
 std::unique_ptr<std::vector<std::string>> CommandLineOptions::ConvertArgvToVector(
     int argc, const char** argv) {
-  return std::unique_ptr<std::vector<std::string>>(
-      new std::vector<std::string>(argv + 1, argv + argc));
+  return std::make_unique<std::vector<std::string>>(argv + 1, argv + argc);
 }
 
 CommandLineOptions& CommandLineOptions::OptionalFlag(const std::string& name,
@@ -111,8 +109,8 @@
   }
 
   if (!mandatory_opts.empty()) {
-    for (auto iter = mandatory_opts.cbegin(); iter != mandatory_opts.cend(); ++iter) {
-      outError << "error: " << *iter << ": missing mandatory option" << std::endl;
+    for (const auto& opt : mandatory_opts) {
+      outError << "error: " << opt << ": missing mandatory option" << std::endl;
     }
     Usage(outError);
     return false;
@@ -159,5 +157,4 @@
   }
 }
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/FileUtils.cpp b/cmds/idmap2/libidmap2/FileUtils.cpp
index 4ac4c04..0255727 100644
--- a/cmds/idmap2/libidmap2/FileUtils.cpp
+++ b/cmds/idmap2/libidmap2/FileUtils.cpp
@@ -15,10 +15,10 @@
  */
 
 #include <dirent.h>
-#include <errno.h>
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <cerrno>
 #include <fstream>
 #include <memory>
 #include <string>
@@ -27,19 +27,17 @@
 
 #include "idmap2/FileUtils.h"
 
-namespace android {
-namespace idmap2 {
-namespace utils {
+namespace android::idmap2::utils {
 
 std::unique_ptr<std::vector<std::string>> FindFiles(const std::string& root, bool recurse,
                                                     const FindFilesPredicate& predicate) {
   DIR* dir = opendir(root.c_str());
-  if (!dir) {
+  if (dir == nullptr) {
     return nullptr;
   }
   std::unique_ptr<std::vector<std::string>> vector(new std::vector<std::string>());
   struct dirent* dirent;
-  while ((dirent = readdir(dir))) {
+  while ((dirent = readdir(dir)) != nullptr) {
     const std::string path = root + "/" + dirent->d_name;
     if (predicate(dirent->d_type, path)) {
       vector->push_back(path);
@@ -68,8 +66,10 @@
 }
 
 std::unique_ptr<std::string> ReadFile(int fd) {
+  static constexpr const size_t kBufSize = 1024;
+
   std::unique_ptr<std::string> str(new std::string());
-  char buf[1024];
+  char buf[kBufSize];
   ssize_t r;
   while ((r = read(fd, buf, sizeof(buf))) > 0) {
     str->append(buf, r);
@@ -77,6 +77,4 @@
   return r == 0 ? std::move(str) : nullptr;
 }
 
-}  // namespace utils
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2::utils
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 5a47e30..37d6af8 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -33,29 +33,37 @@
 
 #include "idmap2/Idmap.h"
 #include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
 #include "idmap2/ZipFile.h"
 
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
+
+namespace {
 
 #define EXTRACT_TYPE(resid) ((0x00ff0000 & (resid)) >> 16)
 
 #define EXTRACT_ENTRY(resid) (0x0000ffff & (resid))
 
-struct MatchingResources {
+class MatchingResources {
+ public:
   void Add(ResourceId target_resid, ResourceId overlay_resid) {
     TypeId target_typeid = EXTRACT_TYPE(target_resid);
-    if (map.find(target_typeid) == map.end()) {
-      map.emplace(target_typeid, std::set<std::pair<ResourceId, ResourceId>>());
+    if (map_.find(target_typeid) == map_.end()) {
+      map_.emplace(target_typeid, std::set<std::pair<ResourceId, ResourceId>>());
     }
-    map[target_typeid].insert(std::make_pair(target_resid, overlay_resid));
+    map_[target_typeid].insert(std::make_pair(target_resid, overlay_resid));
   }
 
+  inline const std::map<TypeId, std::set<std::pair<ResourceId, ResourceId>>>& Map() const {
+    return map_;
+  }
+
+ private:
   // target type id -> set { pair { overlay entry id, overlay entry id } }
-  std::map<TypeId, std::set<std::pair<ResourceId, ResourceId>>> map;
+  std::map<TypeId, std::set<std::pair<ResourceId, ResourceId>>> map_;
 };
 
-static bool WARN_UNUSED Read16(std::istream& stream, uint16_t* out) {
+bool WARN_UNUSED Read16(std::istream& stream, uint16_t* out) {
   uint16_t value;
   if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint16_t))) {
     *out = dtohl(value);
@@ -64,7 +72,7 @@
   return false;
 }
 
-static bool WARN_UNUSED Read32(std::istream& stream, uint32_t* out) {
+bool WARN_UNUSED Read32(std::istream& stream, uint32_t* out) {
   uint32_t value;
   if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint32_t))) {
     *out = dtohl(value);
@@ -74,7 +82,7 @@
 }
 
 // a string is encoded as a kIdmapStringLength char array; the array is always null-terminated
-static bool WARN_UNUSED ReadString(std::istream& stream, char out[kIdmapStringLength]) {
+bool WARN_UNUSED ReadString(std::istream& stream, char out[kIdmapStringLength]) {
   char buf[kIdmapStringLength];
   memset(buf, 0, sizeof(buf));
   if (!stream.read(buf, sizeof(buf))) {
@@ -87,7 +95,7 @@
   return true;
 }
 
-static ResourceId NameToResid(const AssetManager2& am, const std::string& name) {
+ResourceId NameToResid(const AssetManager2& am, const std::string& name) {
   return am.GetResourceId(name);
 }
 
@@ -100,7 +108,7 @@
 // relying on a hard-coded index. This however requires storing the package name in the idmap
 // header, which in turn requires incrementing the idmap version. Because the initial version of
 // idmap2 is compatible with idmap, this will have to wait for now.
-static const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) {
+const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) {
   const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages();
   if (packages.empty()) {
     return nullptr;
@@ -109,6 +117,8 @@
   return loaded_arsc.GetPackageById(id);
 }
 
+}  // namespace
+
 std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& stream) {
   std::unique_ptr<IdmapHeader> idmap_header(new IdmapHeader());
 
@@ -143,18 +153,16 @@
     return false;
   }
 
-  bool status;
-  uint32_t target_crc;
-  std::tie(status, target_crc) = target_zip->Crc("resources.arsc");
-  if (!status) {
+  Result<uint32_t> target_crc = target_zip->Crc("resources.arsc");
+  if (!target_crc) {
     out_error << "error: failed to get target crc" << std::endl;
     return false;
   }
 
-  if (target_crc_ != target_crc) {
+  if (target_crc_ != *target_crc) {
     out_error << base::StringPrintf(
                      "error: bad target crc: idmap version 0x%08x, file system version 0x%08x",
-                     target_crc_, target_crc)
+                     target_crc_, *target_crc)
               << std::endl;
     return false;
   }
@@ -165,17 +173,16 @@
     return false;
   }
 
-  uint32_t overlay_crc;
-  std::tie(status, overlay_crc) = overlay_zip->Crc("resources.arsc");
-  if (!status) {
+  Result<uint32_t> overlay_crc = overlay_zip->Crc("resources.arsc");
+  if (!overlay_crc) {
     out_error << "error: failed to get overlay crc" << std::endl;
     return false;
   }
 
-  if (overlay_crc_ != overlay_crc) {
+  if (overlay_crc_ != *overlay_crc) {
     out_error << base::StringPrintf(
                      "error: bad overlay crc: idmap version 0x%08x, file system version 0x%08x",
-                     overlay_crc_, overlay_crc)
+                     overlay_crc_, *overlay_crc)
               << std::endl;
     return false;
   }
@@ -198,8 +205,9 @@
 std::unique_ptr<const IdmapData::TypeEntry> IdmapData::TypeEntry::FromBinaryStream(
     std::istream& stream) {
   std::unique_ptr<IdmapData::TypeEntry> data(new IdmapData::TypeEntry());
-
-  uint16_t target_type16, overlay_type16, entry_count;
+  uint16_t target_type16;
+  uint16_t overlay_type16;
+  uint16_t entry_count;
   if (!Read16(stream, &target_type16) || !Read16(stream, &overlay_type16) ||
       !Read16(stream, &entry_count) || !Read16(stream, &data->entry_offset_)) {
     return nullptr;
@@ -284,25 +292,25 @@
   }
 
   const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc();
-  if (!target_arsc) {
+  if (target_arsc == nullptr) {
     out_error << "error: failed to load target resources.arsc" << std::endl;
     return nullptr;
   }
 
   const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc();
-  if (!overlay_arsc) {
+  if (overlay_arsc == nullptr) {
     out_error << "error: failed to load overlay resources.arsc" << std::endl;
     return nullptr;
   }
 
   const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc);
-  if (!target_pkg) {
+  if (target_pkg == nullptr) {
     out_error << "error: failed to load target package from resources.arsc" << std::endl;
     return nullptr;
   }
 
   const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc);
-  if (!overlay_pkg) {
+  if (overlay_pkg == nullptr) {
     out_error << "error: failed to load overlay package from resources.arsc" << std::endl;
     return nullptr;
   }
@@ -322,17 +330,20 @@
   std::unique_ptr<IdmapHeader> header(new IdmapHeader());
   header->magic_ = kIdmapMagic;
   header->version_ = kIdmapCurrentVersion;
-  bool crc_status;
-  std::tie(crc_status, header->target_crc_) = target_zip->Crc("resources.arsc");
-  if (!crc_status) {
+
+  Result<uint32_t> crc = target_zip->Crc("resources.arsc");
+  if (!crc) {
     out_error << "error: failed to get zip crc for target" << std::endl;
     return nullptr;
   }
-  std::tie(crc_status, header->overlay_crc_) = overlay_zip->Crc("resources.arsc");
-  if (!crc_status) {
+  header->target_crc_ = *crc;
+
+  crc = overlay_zip->Crc("resources.arsc");
+  if (!crc) {
     out_error << "error: failed to get zip crc for overlay" << std::endl;
     return nullptr;
   }
+  header->overlay_crc_ = *crc;
 
   if (target_apk_path.size() > sizeof(header->target_path_)) {
     out_error << "error: target apk path \"" << target_apk_path << "\" longer that maximum size "
@@ -358,15 +369,14 @@
   const auto end = overlay_pkg->end();
   for (auto iter = overlay_pkg->begin(); iter != end; ++iter) {
     const ResourceId overlay_resid = *iter;
-    bool lookup_ok;
-    std::string name;
-    std::tie(lookup_ok, name) = utils::ResToTypeEntryName(overlay_asset_manager, overlay_resid);
-    if (!lookup_ok) {
+    Result<std::string> name = utils::ResToTypeEntryName(overlay_asset_manager, overlay_resid);
+    if (!name) {
       continue;
     }
     // prepend "<package>:" to turn name into "<package>:<type>/<name>"
-    name = base::StringPrintf("%s:%s", target_pkg->GetPackageName().c_str(), name.c_str());
-    const ResourceId target_resid = NameToResid(target_asset_manager, name);
+    const std::string full_name =
+        base::StringPrintf("%s:%s", target_pkg->GetPackageName().c_str(), name->c_str());
+    const ResourceId target_resid = NameToResid(target_asset_manager, full_name);
     if (target_resid == 0) {
       continue;
     }
@@ -375,8 +385,8 @@
 
   // encode idmap data
   std::unique_ptr<IdmapData> data(new IdmapData());
-  const auto types_end = matching_resources.map.cend();
-  for (auto ti = matching_resources.map.cbegin(); ti != types_end; ++ti) {
+  const auto types_end = matching_resources.Map().cend();
+  for (auto ti = matching_resources.Map().cbegin(); ti != types_end; ++ti) {
     auto ei = ti->second.cbegin();
     std::unique_ptr<IdmapData::TypeEntry> type(new IdmapData::TypeEntry());
     type->target_type_id_ = EXTRACT_TYPE(ei->first);
@@ -439,5 +449,4 @@
   }
 }
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
index 492e6f0..fc967799 100644
--- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
@@ -15,7 +15,6 @@
  */
 
 #include <string>
-#include <utility>
 
 #include "android-base/macros.h"
 #include "android-base/stringprintf.h"
@@ -23,9 +22,9 @@
 
 #include "idmap2/PrettyPrintVisitor.h"
 #include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
 
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
 
 #define RESID(pkg, type, entry) (((pkg) << 24) | ((type) << 16) | (entry))
 
@@ -49,30 +48,28 @@
   last_seen_package_id_ = header.GetTargetPackageId();
 }
 
-void PrettyPrintVisitor::visit(const IdmapData::TypeEntry& te) {
+void PrettyPrintVisitor::visit(const IdmapData::TypeEntry& type_entry) {
   const bool target_package_loaded = !target_am_.GetApkAssets().empty();
-  for (uint16_t i = 0; i < te.GetEntryCount(); i++) {
-    const EntryId entry = te.GetEntry(i);
+  for (uint16_t i = 0; i < type_entry.GetEntryCount(); i++) {
+    const EntryId entry = type_entry.GetEntry(i);
     if (entry == kNoEntry) {
       continue;
     }
 
     const ResourceId target_resid =
-        RESID(last_seen_package_id_, te.GetTargetTypeId(), te.GetEntryOffset() + i);
-    const ResourceId overlay_resid = RESID(last_seen_package_id_, te.GetOverlayTypeId(), entry);
+        RESID(last_seen_package_id_, type_entry.GetTargetTypeId(), type_entry.GetEntryOffset() + i);
+    const ResourceId overlay_resid =
+        RESID(last_seen_package_id_, type_entry.GetOverlayTypeId(), entry);
 
     stream_ << base::StringPrintf("0x%08x -> 0x%08x", target_resid, overlay_resid);
     if (target_package_loaded) {
-      bool lookup_ok;
-      std::string name;
-      std::tie(lookup_ok, name) = utils::ResToTypeEntryName(target_am_, target_resid);
-      if (lookup_ok) {
-        stream_ << " " << name;
+      Result<std::string> name = utils::ResToTypeEntryName(target_am_, target_resid);
+      if (name) {
+        stream_ << " " << *name;
       }
     }
     stream_ << std::endl;
   }
 }
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
index 57cfc8e..ec2decf 100644
--- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -16,7 +16,6 @@
 
 #include <cstdarg>
 #include <string>
-#include <utility>
 
 #include "android-base/macros.h"
 #include "android-base/stringprintf.h"
@@ -24,11 +23,11 @@
 
 #include "idmap2/RawPrintVisitor.h"
 #include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
 
 using android::ApkAssets;
 
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
 
 // verbatim copy fomr PrettyPrintVisitor.cpp, move to common utils
 #define RESID(pkg, type, entry) (((pkg) << 24) | ((type) << 16) | (entry))
@@ -59,30 +58,30 @@
   last_seen_package_id_ = header.GetTargetPackageId();
 }
 
-void RawPrintVisitor::visit(const IdmapData::TypeEntry& te) {
+void RawPrintVisitor::visit(const IdmapData::TypeEntry& type_entry) {
   const bool target_package_loaded = !target_am_.GetApkAssets().empty();
 
-  print(static_cast<uint16_t>(te.GetTargetTypeId()), "target type");
-  print(static_cast<uint16_t>(te.GetOverlayTypeId()), "overlay type");
-  print(static_cast<uint16_t>(te.GetEntryCount()), "entry count");
-  print(static_cast<uint16_t>(te.GetEntryOffset()), "entry offset");
+  print(static_cast<uint16_t>(type_entry.GetTargetTypeId()), "target type");
+  print(static_cast<uint16_t>(type_entry.GetOverlayTypeId()), "overlay type");
+  print(static_cast<uint16_t>(type_entry.GetEntryCount()), "entry count");
+  print(static_cast<uint16_t>(type_entry.GetEntryOffset()), "entry offset");
 
-  for (uint16_t i = 0; i < te.GetEntryCount(); i++) {
-    const EntryId entry = te.GetEntry(i);
+  for (uint16_t i = 0; i < type_entry.GetEntryCount(); i++) {
+    const EntryId entry = type_entry.GetEntry(i);
     if (entry == kNoEntry) {
       print(kPadding, "no entry");
     } else {
-      const ResourceId target_resid =
-          RESID(last_seen_package_id_, te.GetTargetTypeId(), te.GetEntryOffset() + i);
-      const ResourceId overlay_resid = RESID(last_seen_package_id_, te.GetOverlayTypeId(), entry);
-      bool lookup_ok = false;
-      std::string name;
+      const ResourceId target_resid = RESID(last_seen_package_id_, type_entry.GetTargetTypeId(),
+                                            type_entry.GetEntryOffset() + i);
+      const ResourceId overlay_resid =
+          RESID(last_seen_package_id_, type_entry.GetOverlayTypeId(), entry);
+      Result<std::string> name;
       if (target_package_loaded) {
-        std::tie(lookup_ok, name) = utils::ResToTypeEntryName(target_am_, target_resid);
+        name = utils::ResToTypeEntryName(target_am_, target_resid);
       }
-      if (lookup_ok) {
+      if (name) {
         print(static_cast<uint32_t>(entry), "0x%08x -> 0x%08x %s", target_resid, overlay_resid,
-              name.c_str());
+              name->c_str());
       } else {
         print(static_cast<uint32_t>(entry), "0x%08x -> 0x%08x", target_resid, overlay_resid);
       }
@@ -127,5 +126,4 @@
   offset_ += kIdmapStringLength;
 }
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp
index e98f843..04e0ec7 100644
--- a/cmds/idmap2/libidmap2/ResourceUtils.cpp
+++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp
@@ -15,25 +15,22 @@
  */
 
 #include <string>
-#include <utility>
 
 #include "androidfw/StringPiece.h"
 #include "androidfw/Util.h"
 
 #include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
 
 using android::StringPiece16;
 using android::util::Utf16ToUtf8;
 
-namespace android {
-namespace idmap2 {
-namespace utils {
+namespace android::idmap2::utils {
 
-std::pair<bool, std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am,
-                                                            ResourceId resid) {
+Result<std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am, ResourceId resid) {
   AssetManager2::ResourceName name;
   if (!am.GetResourceName(resid, &name)) {
-    return std::make_pair(false, "");
+    return {};
   }
   std::string out;
   if (name.type != nullptr) {
@@ -47,9 +44,7 @@
   } else {
     out += Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len));
   }
-  return std::make_pair(true, out);
+  return {out};
 }
 
-}  // namespace utils
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2::utils
diff --git a/cmds/idmap2/libidmap2/Xml.cpp b/cmds/idmap2/libidmap2/Xml.cpp
index 5543722..0075a92 100644
--- a/cmds/idmap2/libidmap2/Xml.cpp
+++ b/cmds/idmap2/libidmap2/Xml.cpp
@@ -21,8 +21,7 @@
 
 #include "idmap2/Xml.h"
 
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
 
 std::unique_ptr<const Xml> Xml::Create(const uint8_t* data, size_t size, bool copyData) {
   std::unique_ptr<Xml> xml(new Xml());
@@ -78,5 +77,4 @@
   xml_.uninit();
 }
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/ZipFile.cpp b/cmds/idmap2/libidmap2/ZipFile.cpp
index 3f2079a..15ec3f9 100644
--- a/cmds/idmap2/libidmap2/ZipFile.cpp
+++ b/cmds/idmap2/libidmap2/ZipFile.cpp
@@ -16,12 +16,11 @@
 
 #include <memory>
 #include <string>
-#include <utility>
 
+#include "idmap2/Result.h"
 #include "idmap2/ZipFile.h"
 
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
 
 std::unique_ptr<MemoryChunk> MemoryChunk::Allocate(size_t size) {
   void* ptr = ::operator new(sizeof(MemoryChunk) + size);
@@ -57,11 +56,10 @@
   return chunk;
 }
 
-std::pair<bool, uint32_t> ZipFile::Crc(const std::string& entryPath) const {
+Result<uint32_t> ZipFile::Crc(const std::string& entryPath) const {
   ::ZipEntry entry;
   int32_t status = ::FindEntry(handle_, ::ZipString(entryPath.c_str()), &entry);
-  return std::make_pair(status == 0, entry.crc32);
+  return status == 0 ? Result<uint32_t>(entry.crc32) : kResultError;
 }
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
index 8b552dc..2698ac0 100644
--- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
+++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
@@ -29,11 +29,9 @@
 
 #include "TestHelpers.h"
 
-using ::testing::IsNull;
 using ::testing::NotNull;
 
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
 
 TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) {
   std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
@@ -52,14 +50,14 @@
 
   ASSERT_EQ(idmap1->GetHeader()->GetTargetCrc(), idmap2->GetHeader()->GetTargetCrc());
   ASSERT_EQ(idmap1->GetHeader()->GetTargetPath(), idmap2->GetHeader()->GetTargetPath());
-  ASSERT_EQ(idmap1->GetData().size(), 1u);
+  ASSERT_EQ(idmap1->GetData().size(), 1U);
   ASSERT_EQ(idmap1->GetData().size(), idmap2->GetData().size());
 
   const auto& data1 = idmap1->GetData()[0];
   const auto& data2 = idmap2->GetData()[0];
 
   ASSERT_EQ(data1->GetHeader()->GetTargetPackageId(), data2->GetHeader()->GetTargetPackageId());
-  ASSERT_EQ(data1->GetTypeEntries().size(), 2u);
+  ASSERT_EQ(data1->GetTypeEntries().size(), 2U);
   ASSERT_EQ(data1->GetTypeEntries().size(), data2->GetTypeEntries().size());
   ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(0), data2->GetTypeEntries()[0]->GetEntry(0));
   ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(1), data2->GetTypeEntries()[0]->GetEntry(1));
@@ -125,5 +123,4 @@
   ASSERT_FALSE(success);
 }
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
diff --git a/cmds/idmap2/tests/CommandLineOptionsTests.cpp b/cmds/idmap2/tests/CommandLineOptionsTests.cpp
index b04b256..c27d27a 100644
--- a/cmds/idmap2/tests/CommandLineOptionsTests.cpp
+++ b/cmds/idmap2/tests/CommandLineOptionsTests.cpp
@@ -38,14 +38,11 @@
 
 #include "TestHelpers.h"
 
-using ::testing::NotNull;
-
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
 
 TEST(CommandLineOptionsTests, Flag) {
-  bool foo = true, bar = false;
-
+  bool foo = true;
+  bool bar = false;
   CommandLineOptions opts =
       CommandLineOptions("test").OptionalFlag("--foo", "", &foo).OptionalFlag("--bar", "", &bar);
 
@@ -63,7 +60,8 @@
 }
 
 TEST(CommandLineOptionsTests, MandatoryOption) {
-  std::string foo, bar;
+  std::string foo;
+  std::string bar;
   CommandLineOptions opts = CommandLineOptions("test")
                                 .MandatoryOption("--foo", "", &foo)
                                 .MandatoryOption("--bar", "", &bar);
@@ -92,13 +90,14 @@
   std::ostream fakeStdErr(nullptr);
   bool success = opts.Parse({"--foo", "FOO", "--foo", "BAR"}, fakeStdErr);
   ASSERT_TRUE(success);
-  ASSERT_EQ(args.size(), 2u);
+  ASSERT_EQ(args.size(), 2U);
   ASSERT_EQ(args[0], "FOO");
   ASSERT_EQ(args[1], "BAR");
 }
 
 TEST(CommandLineOptionsTests, OptionalOption) {
-  std::string foo, bar;
+  std::string foo;
+  std::string bar;
   CommandLineOptions opts = CommandLineOptions("test")
                                 .OptionalOption("--foo", "", &foo)
                                 .OptionalOption("--bar", "", &bar);
@@ -123,7 +122,8 @@
 }
 
 TEST(CommandLineOptionsTests, CornerCases) {
-  std::string foo, bar;
+  std::string foo;
+  std::string bar;
   bool baz = false;
   CommandLineOptions opts = CommandLineOptions("test")
                                 .MandatoryOption("--foo", "", &foo)
@@ -150,7 +150,7 @@
       nullptr,
   };
   std::unique_ptr<std::vector<std::string>> v = CommandLineOptions::ConvertArgvToVector(3, argv);
-  ASSERT_EQ(v->size(), 2ul);
+  ASSERT_EQ(v->size(), 2UL);
   ASSERT_EQ((*v)[0], "--foo");
   ASSERT_EQ((*v)[1], "FOO");
 }
@@ -161,12 +161,16 @@
       nullptr,
   };
   std::unique_ptr<std::vector<std::string>> v = CommandLineOptions::ConvertArgvToVector(1, argv);
-  ASSERT_EQ(v->size(), 0ul);
+  ASSERT_EQ(v->size(), 0UL);
 }
 
 TEST(CommandLineOptionsTests, Usage) {
-  std::string arg1, arg2, arg3, arg4;
-  bool arg5 = false, arg6 = false;
+  std::string arg1;
+  std::string arg2;
+  std::string arg3;
+  std::string arg4;
+  bool arg5 = false;
+  bool arg6 = false;
   std::vector<std::string> arg7;
   CommandLineOptions opts = CommandLineOptions("test")
                                 .MandatoryOption("--aa", "description-aa", &arg1)
@@ -188,5 +192,4 @@
             std::string::npos);
 }
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp
index 0c6439a..4bf832a 100644
--- a/cmds/idmap2/tests/FileUtilsTests.cpp
+++ b/cmds/idmap2/tests/FileUtilsTests.cpp
@@ -29,9 +29,7 @@
 
 using ::testing::NotNull;
 
-namespace android {
-namespace idmap2 {
-namespace utils {
+namespace android::idmap2::utils {
 
 TEST(FileUtilsTests, FindFilesFindEverythingNonRecursive) {
   const auto& root = GetTestDataPath();
@@ -39,7 +37,7 @@
                             [](unsigned char type ATTRIBUTE_UNUSED,
                                const std::string& path ATTRIBUTE_UNUSED) -> bool { return true; });
   ASSERT_THAT(v, NotNull());
-  ASSERT_EQ(v->size(), 4u);
+  ASSERT_EQ(v->size(), 4U);
   ASSERT_EQ(
       std::set<std::string>(v->begin(), v->end()),
       std::set<std::string>({root + "/.", root + "/..", root + "/overlay", root + "/target"}));
@@ -48,10 +46,10 @@
 TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) {
   const auto& root = GetTestDataPath();
   auto v = utils::FindFiles(root, true, [](unsigned char type, const std::string& path) -> bool {
-    return type == DT_REG && path.size() > 4 && !path.compare(path.size() - 4, 4, ".apk");
+    return type == DT_REG && path.size() > 4 && path.compare(path.size() - 4, 4, ".apk") == 0;
   });
   ASSERT_THAT(v, NotNull());
-  ASSERT_EQ(v->size(), 4u);
+  ASSERT_EQ(v->size(), 4U);
   ASSERT_EQ(std::set<std::string>(v->begin(), v->end()),
             std::set<std::string>({root + "/target/target.apk", root + "/overlay/overlay.apk",
                                    root + "/overlay/overlay-static-1.apk",
@@ -71,6 +69,4 @@
   close(pipefd[0]);
 }
 
-}  // namespace utils
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2::utils
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index 5c4e857..22f48e9 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -46,20 +46,21 @@
 using ::android::util::ExecuteBinary;
 using ::testing::NotNull;
 
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
 
 class Idmap2BinaryTests : public Idmap2Tests {};
 
-static void AssertIdmap(const Idmap& idmap, const std::string& target_apk_path,
-                        const std::string& overlay_apk_path) {
+namespace {
+
+void AssertIdmap(const Idmap& idmap, const std::string& target_apk_path,
+                 const std::string& overlay_apk_path) {
   // check that the idmap file looks reasonable (IdmapTests is responsible for
   // more in-depth verification)
   ASSERT_EQ(idmap.GetHeader()->GetMagic(), kIdmapMagic);
   ASSERT_EQ(idmap.GetHeader()->GetVersion(), kIdmapCurrentVersion);
   ASSERT_EQ(idmap.GetHeader()->GetTargetPath(), target_apk_path);
   ASSERT_EQ(idmap.GetHeader()->GetOverlayPath(), overlay_apk_path);
-  ASSERT_EQ(idmap.GetData().size(), 1u);
+  ASSERT_EQ(idmap.GetData().size(), 1U);
 }
 
 #define ASSERT_IDMAP(idmap_ref, target_apk_path, overlay_apk_path)                      \
@@ -67,6 +68,8 @@
     ASSERT_NO_FATAL_FAILURE(AssertIdmap(idmap_ref, target_apk_path, overlay_apk_path)); \
   } while (0)
 
+}  // namespace
+
 TEST_F(Idmap2BinaryTests, Create) {
   // clang-format off
   auto result = ExecuteBinary({"idmap2",
@@ -309,5 +312,4 @@
   ASSERT_NE(result->status, EXIT_SUCCESS);
 }
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 0379aa4..963f22e 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -37,8 +37,7 @@
 using ::testing::IsNull;
 using ::testing::NotNull;
 
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
 
 TEST(IdmapTests, TestCanonicalIdmapPathFor) {
   ASSERT_EQ(Idmap::CanonicalIdmapPathFor("/foo", "/vendor/overlay/bar.apk"),
@@ -50,10 +49,10 @@
   std::istringstream stream(raw);
   std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
   ASSERT_THAT(header, NotNull());
-  ASSERT_EQ(header->GetMagic(), 0x504d4449u);
-  ASSERT_EQ(header->GetVersion(), 0x01u);
-  ASSERT_EQ(header->GetTargetCrc(), 0x1234u);
-  ASSERT_EQ(header->GetOverlayCrc(), 0x5678u);
+  ASSERT_EQ(header->GetMagic(), 0x504d4449U);
+  ASSERT_EQ(header->GetVersion(), 0x01U);
+  ASSERT_EQ(header->GetTargetCrc(), 0x1234U);
+  ASSERT_EQ(header->GetOverlayCrc(), 0x5678U);
   ASSERT_EQ(header->GetTargetPath().to_string(), "target.apk");
   ASSERT_EQ(header->GetOverlayPath().to_string(), "overlay.apk");
 }
@@ -77,8 +76,8 @@
 
   std::unique_ptr<const IdmapData::Header> header = IdmapData::Header::FromBinaryStream(stream);
   ASSERT_THAT(header, NotNull());
-  ASSERT_EQ(header->GetTargetPackageId(), 0x7fu);
-  ASSERT_EQ(header->GetTypeCount(), 2u);
+  ASSERT_EQ(header->GetTargetPackageId(), 0x7fU);
+  ASSERT_EQ(header->GetTypeCount(), 2U);
 }
 
 TEST(IdmapTests, CreateIdmapDataResourceTypeFromBinaryStream) {
@@ -89,11 +88,11 @@
 
   std::unique_ptr<const IdmapData::TypeEntry> data = IdmapData::TypeEntry::FromBinaryStream(stream);
   ASSERT_THAT(data, NotNull());
-  ASSERT_EQ(data->GetTargetTypeId(), 0x02u);
-  ASSERT_EQ(data->GetOverlayTypeId(), 0x02u);
-  ASSERT_EQ(data->GetEntryCount(), 1u);
-  ASSERT_EQ(data->GetEntryOffset(), 0u);
-  ASSERT_EQ(data->GetEntry(0), 0u);
+  ASSERT_EQ(data->GetTargetTypeId(), 0x02U);
+  ASSERT_EQ(data->GetOverlayTypeId(), 0x02U);
+  ASSERT_EQ(data->GetEntryCount(), 1U);
+  ASSERT_EQ(data->GetEntryOffset(), 0U);
+  ASSERT_EQ(data->GetEntry(0), 0U);
 }
 
 TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
@@ -104,24 +103,24 @@
 
   std::unique_ptr<const IdmapData> data = IdmapData::FromBinaryStream(stream);
   ASSERT_THAT(data, NotNull());
-  ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fu);
-  ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2u);
+  ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
+  ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2U);
   const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
-  ASSERT_EQ(types.size(), 2u);
+  ASSERT_EQ(types.size(), 2U);
 
-  ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02u);
-  ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02u);
-  ASSERT_EQ(types[0]->GetEntryCount(), 1u);
-  ASSERT_EQ(types[0]->GetEntryOffset(), 0u);
-  ASSERT_EQ(types[0]->GetEntry(0), 0x0000u);
+  ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
+  ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02U);
+  ASSERT_EQ(types[0]->GetEntryCount(), 1U);
+  ASSERT_EQ(types[0]->GetEntryOffset(), 0U);
+  ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);
 
-  ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03u);
-  ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03u);
-  ASSERT_EQ(types[1]->GetEntryCount(), 3u);
-  ASSERT_EQ(types[1]->GetEntryOffset(), 3u);
-  ASSERT_EQ(types[1]->GetEntry(0), 0x0000u);
+  ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03U);
+  ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03U);
+  ASSERT_EQ(types[1]->GetEntryCount(), 3U);
+  ASSERT_EQ(types[1]->GetEntryOffset(), 3U);
+  ASSERT_EQ(types[1]->GetEntry(0), 0x0000U);
   ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
-  ASSERT_EQ(types[1]->GetEntry(2), 0x0001u);
+  ASSERT_EQ(types[1]->GetEntry(2), 0x0001U);
 }
 
 TEST(IdmapTests, CreateIdmapFromBinaryStream) {
@@ -133,35 +132,35 @@
   ASSERT_THAT(idmap, NotNull());
 
   ASSERT_THAT(idmap->GetHeader(), NotNull());
-  ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449u);
-  ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01u);
-  ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234u);
-  ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678u);
+  ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
+  ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U);
+  ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U);
+  ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U);
   ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), "target.apk");
   ASSERT_EQ(idmap->GetHeader()->GetOverlayPath().to_string(), "overlay.apk");
 
   const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
-  ASSERT_EQ(dataBlocks.size(), 1u);
+  ASSERT_EQ(dataBlocks.size(), 1U);
 
   const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
-  ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fu);
-  ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2u);
+  ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
+  ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2U);
   const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
-  ASSERT_EQ(types.size(), 2u);
+  ASSERT_EQ(types.size(), 2U);
 
-  ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02u);
-  ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02u);
-  ASSERT_EQ(types[0]->GetEntryCount(), 1u);
-  ASSERT_EQ(types[0]->GetEntryOffset(), 0u);
-  ASSERT_EQ(types[0]->GetEntry(0), 0x0000u);
+  ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
+  ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02U);
+  ASSERT_EQ(types[0]->GetEntryCount(), 1U);
+  ASSERT_EQ(types[0]->GetEntryOffset(), 0U);
+  ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);
 
-  ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03u);
-  ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03u);
-  ASSERT_EQ(types[1]->GetEntryCount(), 3u);
-  ASSERT_EQ(types[1]->GetEntryOffset(), 3u);
-  ASSERT_EQ(types[1]->GetEntry(0), 0x0000u);
+  ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03U);
+  ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03U);
+  ASSERT_EQ(types[1]->GetEntryCount(), 3U);
+  ASSERT_EQ(types[1]->GetEntryOffset(), 3U);
+  ASSERT_EQ(types[1]->GetEntry(0), 0x0000U);
   ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
-  ASSERT_EQ(types[1]->GetEntry(2), 0x0001u);
+  ASSERT_EQ(types[1]->GetEntry(2), 0x0001U);
 }
 
 TEST(IdmapTests, GracefullyFailToCreateIdmapFromCorruptBinaryStream) {
@@ -189,8 +188,8 @@
   ASSERT_THAT(idmap, NotNull());
 
   ASSERT_THAT(idmap->GetHeader(), NotNull());
-  ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449u);
-  ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01u);
+  ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
+  ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U);
   ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xf5ad1d1d);
   ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xd470336b);
   ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path);
@@ -198,30 +197,30 @@
   ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
 
   const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
-  ASSERT_EQ(dataBlocks.size(), 1u);
+  ASSERT_EQ(dataBlocks.size(), 1U);
 
   const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
 
-  ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fu);
-  ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2u);
+  ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
+  ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2U);
 
   const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
-  ASSERT_EQ(types.size(), 2u);
+  ASSERT_EQ(types.size(), 2U);
 
-  ASSERT_EQ(types[0]->GetTargetTypeId(), 0x01u);
-  ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01u);
-  ASSERT_EQ(types[0]->GetEntryCount(), 1u);
-  ASSERT_EQ(types[0]->GetEntryOffset(), 0u);
-  ASSERT_EQ(types[0]->GetEntry(0), 0x0000u);
+  ASSERT_EQ(types[0]->GetTargetTypeId(), 0x01U);
+  ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
+  ASSERT_EQ(types[0]->GetEntryCount(), 1U);
+  ASSERT_EQ(types[0]->GetEntryOffset(), 0U);
+  ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);
 
-  ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02u);
-  ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02u);
-  ASSERT_EQ(types[1]->GetEntryCount(), 4u);
-  ASSERT_EQ(types[1]->GetEntryOffset(), 3u);
-  ASSERT_EQ(types[1]->GetEntry(0), 0x0000u);
+  ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02U);
+  ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02U);
+  ASSERT_EQ(types[1]->GetEntryCount(), 4U);
+  ASSERT_EQ(types[1]->GetEntryOffset(), 3U);
+  ASSERT_EQ(types[1]->GetEntry(0), 0x0000U);
   ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
-  ASSERT_EQ(types[1]->GetEntry(2), 0x0001u);
-  ASSERT_EQ(types[1]->GetEntry(3), 0x0002u);
+  ASSERT_EQ(types[1]->GetEntry(2), 0x0001U);
+  ASSERT_EQ(types[1]->GetEntry(3), 0x0002U);
 }
 
 TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) {
@@ -346,23 +345,23 @@
   explicit TestVisitor(std::ostream& stream) : stream_(stream) {
   }
 
-  void visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
+  void visit(const Idmap& idmap ATTRIBUTE_UNUSED) override {
     stream_ << "TestVisitor::visit(Idmap)" << std::endl;
   }
 
-  void visit(const IdmapHeader& idmap ATTRIBUTE_UNUSED) {
+  void visit(const IdmapHeader& idmap ATTRIBUTE_UNUSED) override {
     stream_ << "TestVisitor::visit(IdmapHeader)" << std::endl;
   }
 
-  void visit(const IdmapData& idmap ATTRIBUTE_UNUSED) {
+  void visit(const IdmapData& idmap ATTRIBUTE_UNUSED) override {
     stream_ << "TestVisitor::visit(IdmapData)" << std::endl;
   }
 
-  void visit(const IdmapData::Header& idmap ATTRIBUTE_UNUSED) {
+  void visit(const IdmapData::Header& idmap ATTRIBUTE_UNUSED) override {
     stream_ << "TestVisitor::visit(IdmapData::Header)" << std::endl;
   }
 
-  void visit(const IdmapData::TypeEntry& idmap ATTRIBUTE_UNUSED) {
+  void visit(const IdmapData::TypeEntry& idmap ATTRIBUTE_UNUSED) override {
     stream_ << "TestVisitor::visit(IdmapData::TypeEntry)" << std::endl;
   }
 
@@ -391,5 +390,4 @@
             "TestVisitor::visit(IdmapData::TypeEntry)\n");
 }
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
diff --git a/cmds/idmap2/tests/Main.cpp b/cmds/idmap2/tests/Main.cpp
index f2469ea..2b13fed 100644
--- a/cmds/idmap2/tests/Main.cpp
+++ b/cmds/idmap2/tests/Main.cpp
@@ -22,15 +22,13 @@
 
 #include "TestHelpers.h"
 
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
 
-const std::string GetTestDataPath() {
+std::string GetTestDataPath() {
   return base::GetExecutableDirectory() + "/tests/data";
 }
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
 
 int main(int argc, char** argv) {
   ::testing::InitGoogleTest(&argc, argv);
diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
index da97792..7736bc0 100644
--- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
@@ -29,13 +29,11 @@
 
 #include "TestHelpers.h"
 
-using ::testing::IsNull;
 using ::testing::NotNull;
 
 using android::ApkAssets;
 
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
 
 TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) {
   const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
@@ -79,5 +77,4 @@
   ASSERT_NE(stream.str().find("0x7f020000 -> 0x7f020000\n"), std::string::npos);
 }
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index c28ce2e..0318cd2 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -27,11 +27,9 @@
 
 #include "TestHelpers.h"
 
-using ::testing::IsNull;
 using ::testing::NotNull;
 
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
 
 TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
   const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
@@ -80,5 +78,4 @@
   ASSERT_NE(stream.str().find("0000021c: 00000000  0x7f020000 -> 0x7f020000\n"), std::string::npos);
 }
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
diff --git a/cmds/idmap2/tests/ResourceUtilsTests.cpp b/cmds/idmap2/tests/ResourceUtilsTests.cpp
index 0547fa0..ad78685 100644
--- a/cmds/idmap2/tests/ResourceUtilsTests.cpp
+++ b/cmds/idmap2/tests/ResourceUtilsTests.cpp
@@ -16,20 +16,19 @@
 
 #include <memory>
 #include <string>
-#include <utility>
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
 #include "androidfw/ApkAssets.h"
 #include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
 
 #include "TestHelpers.h"
 
 using ::testing::NotNull;
 
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
 
 class ResourceUtilsTests : public Idmap2Tests {
  protected:
@@ -52,18 +51,14 @@
 };
 
 TEST_F(ResourceUtilsTests, ResToTypeEntryName) {
-  bool lookup_ok;
-  std::string name;
-  std::tie(lookup_ok, name) = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000u);
-  ASSERT_TRUE(lookup_ok);
-  ASSERT_EQ(name, "integer/int1");
+  Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000U);
+  ASSERT_TRUE(name);
+  ASSERT_EQ(*name, "integer/int1");
 }
 
 TEST_F(ResourceUtilsTests, ResToTypeEntryNameNoSuchResourceId) {
-  bool lookup_ok;
-  std::tie(lookup_ok, std::ignore) = utils::ResToTypeEntryName(GetAssetManager(), 0x7f123456u);
-  ASSERT_FALSE(lookup_ok);
+  Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f123456U);
+  ASSERT_FALSE(name);
 }
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h
index 18dc541..45525a5 100644
--- a/cmds/idmap2/tests/TestHelpers.h
+++ b/cmds/idmap2/tests/TestHelpers.h
@@ -19,8 +19,7 @@
 
 #include <string>
 
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
 
 const unsigned char idmap_raw_data[] = {
     // IDMAP HEADER
@@ -117,7 +116,7 @@
 
 const unsigned int idmap_raw_data_len = 565;
 
-const std::string GetTestDataPath();
+std::string GetTestDataPath();
 
 class Idmap2Tests : public testing::Test {
  protected:
@@ -162,7 +161,6 @@
   std::string idmap_path_;
 };
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
 
 #endif  // IDMAP2_TESTS_TESTHELPERS_H_
diff --git a/cmds/idmap2/tests/XmlTests.cpp b/cmds/idmap2/tests/XmlTests.cpp
index 97ff03e..fe79d8f 100644
--- a/cmds/idmap2/tests/XmlTests.cpp
+++ b/cmds/idmap2/tests/XmlTests.cpp
@@ -27,8 +27,7 @@
 using ::testing::IsNull;
 using ::testing::NotNull;
 
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
 
 TEST(XmlTests, Create) {
   auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
@@ -58,15 +57,14 @@
 
   auto attrs = xml->FindTag("c");
   ASSERT_THAT(attrs, NotNull());
-  ASSERT_EQ(attrs->size(), 4u);
+  ASSERT_EQ(attrs->size(), 4U);
   ASSERT_EQ(attrs->at("type_string"), "fortytwo");
   ASSERT_EQ(std::stoi(attrs->at("type_int_dec")), 42);
   ASSERT_EQ(std::stoi(attrs->at("type_int_hex")), 42);
-  ASSERT_NE(std::stoul(attrs->at("type_int_boolean")), 0u);
+  ASSERT_NE(std::stoul(attrs->at("type_int_boolean")), 0U);
 
   auto fail = xml->FindTag("does-not-exist");
   ASSERT_THAT(fail, IsNull());
 }
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
diff --git a/cmds/idmap2/tests/ZipFileTests.cpp b/cmds/idmap2/tests/ZipFileTests.cpp
index a504d31..79be43c 100644
--- a/cmds/idmap2/tests/ZipFileTests.cpp
+++ b/cmds/idmap2/tests/ZipFileTests.cpp
@@ -16,8 +16,8 @@
 
 #include <cstdio>  // fclose
 #include <string>
-#include <utility>
 
+#include "idmap2/Result.h"
 #include "idmap2/ZipFile.h"
 
 #include "gmock/gmock.h"
@@ -28,8 +28,7 @@
 using ::testing::IsNull;
 using ::testing::NotNull;
 
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
 
 TEST(ZipFileTests, BasicOpen) {
   auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
@@ -44,14 +43,12 @@
   auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
   ASSERT_THAT(zip, NotNull());
 
-  bool status;
-  uint32_t crc;
-  std::tie(status, crc) = zip->Crc("AndroidManifest.xml");
-  ASSERT_TRUE(status);
-  ASSERT_EQ(crc, 0x762f3d24);
+  Result<uint32_t> crc = zip->Crc("AndroidManifest.xml");
+  ASSERT_TRUE(crc);
+  ASSERT_EQ(*crc, 0x762f3d24);
 
-  std::tie(status, std::ignore) = zip->Crc("does-not-exist");
-  ASSERT_FALSE(status);
+  Result<uint32_t> crc2 = zip->Crc("does-not-exist");
+  ASSERT_FALSE(crc2);
 }
 
 TEST(ZipFileTests, Uncompress) {
@@ -68,5 +65,4 @@
   ASSERT_THAT(fail, IsNull());
 }
 
-}  // namespace idmap2
-}  // namespace android
+}  // namespace android::idmap2
diff --git a/cmds/incident_helper/src/TextParserBase.h b/cmds/incident_helper/src/TextParserBase.h
index 784c181..a6074e7 100644
--- a/cmds/incident_helper/src/TextParserBase.h
+++ b/cmds/incident_helper/src/TextParserBase.h
@@ -30,7 +30,7 @@
 public:
     String8 name;
 
-    TextParserBase(String8 name) : name(name) {};
+    explicit TextParserBase(String8 name) : name(name) {};
     virtual ~TextParserBase() {};
 
     virtual status_t Parse(const int in, const int out) const = 0;
diff --git a/cmds/incident_helper/src/ih_util.h b/cmds/incident_helper/src/ih_util.h
index c02a349..09dc8e6 100644
--- a/cmds/incident_helper/src/ih_util.h
+++ b/cmds/incident_helper/src/ih_util.h
@@ -109,7 +109,7 @@
 class Reader
 {
 public:
-    Reader(const int fd);
+    explicit Reader(const int fd);
     ~Reader();
 
     bool readLine(std::string* line);
@@ -162,7 +162,7 @@
 class Message
 {
 public:
-    Message(Table* table);
+    explicit Message(Table* table);
     ~Message();
 
     // Reconstructs the typical proto message by adding its message fields.
diff --git a/cmds/incidentd/src/IncidentService.h b/cmds/incidentd/src/IncidentService.h
index 6252ad2..c63a183 100644
--- a/cmds/incidentd/src/IncidentService.h
+++ b/cmds/incidentd/src/IncidentService.h
@@ -97,7 +97,7 @@
 // ================================================================================
 class IncidentService : public BnIncidentManager {
 public:
-    IncidentService(const sp<Looper>& handlerLooper);
+    explicit IncidentService(const sp<Looper>& handlerLooper);
     virtual ~IncidentService();
 
     virtual Status reportIncident(const IncidentReportArgs& args);
diff --git a/cmds/incidentd/src/Privacy.h b/cmds/incidentd/src/Privacy.h
index a3df490..a0159d9 100644
--- a/cmds/incidentd/src/Privacy.h
+++ b/cmds/incidentd/src/Privacy.h
@@ -83,7 +83,7 @@
     static PrivacySpec new_spec(int dest);
 
 private:
-    PrivacySpec(uint8_t dest) : dest(dest) {}
+    explicit PrivacySpec(uint8_t dest) : dest(dest) {}
 };
 
 }  // namespace incidentd
diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h
index 45fd944..2a3abd7 100644
--- a/cmds/incidentd/src/Reporter.h
+++ b/cmds/incidentd/src/Reporter.h
@@ -89,7 +89,7 @@
     ReportRequestSet batch;
 
     Reporter();                       // PROD must use this constructor.
-    Reporter(const char* directory);  // For testing purpose only.
+    explicit Reporter(const char* directory);  // For testing purpose only.
     virtual ~Reporter();
 
     // Run the report as described in the batch and args parameters.
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 10d2268..32ec1ba 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -415,7 +415,7 @@
     bool workerDone;
     status_t workerError;
 
-    WorkerThreadData(const WorkerThreadSection* section);
+    explicit WorkerThreadData(const WorkerThreadSection* section);
     virtual ~WorkerThreadData();
 };
 
diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java
index 74edffb..b905e94 100644
--- a/cmds/input/src/com/android/commands/input/Input.java
+++ b/cmds/input/src/com/android/commands/input/Input.java
@@ -16,15 +16,20 @@
 
 package com.android.commands.input;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+
 import android.hardware.input.InputManager;
 import android.os.SystemClock;
-import android.util.Log;
 import android.view.InputDevice;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
 
+import com.android.internal.os.BaseCommand;
+
+import java.io.PrintStream;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -33,9 +38,11 @@
  * desired character output.
  */
 
-public class Input {
+public class Input extends BaseCommand {
     private static final String TAG = "Input";
     private static final String INVALID_ARGUMENTS = "Error: Invalid arguments for command: ";
+    private static final String INVALID_DISPLAY_ARGUMENTS =
+            "Error: Invalid arguments for display ID.";
 
     private static final Map<String, Integer> SOURCES = new HashMap<String, Integer>() {{
         put("keyboard", InputDevice.SOURCE_KEYBOARD);
@@ -50,6 +57,7 @@
         put("joystick", InputDevice.SOURCE_JOYSTICK);
     }};
 
+    private static final Map<String, InputCmd> COMMANDS = new HashMap<String, InputCmd>();
 
     /**
      * Command-line entry point.
@@ -60,217 +68,256 @@
         (new Input()).run(args);
     }
 
-    private void run(String[] args) {
-        if (args.length < 1) {
-            showUsage();
-            return;
-        }
+    Input() {
+        COMMANDS.put("text", new InputText());
+        COMMANDS.put("keyevent", new InputKeyEvent());
+        COMMANDS.put("tap", new InputTap());
+        COMMANDS.put("swipe", new InputSwipe());
+        COMMANDS.put("draganddrop", new InputDragAndDrop());
+        COMMANDS.put("press", new InputPress());
+        COMMANDS.put("roll", new InputRoll());
+    }
 
-        int index = 0;
-        String command = args[index];
+    @Override
+    public void onRun() throws Exception {
+        String arg = nextArgRequired();
         int inputSource = InputDevice.SOURCE_UNKNOWN;
-        if (SOURCES.containsKey(command)) {
-            inputSource = SOURCES.get(command);
-            index++;
-            command = args[index];
-        }
-        final int length = args.length - index;
 
-        try {
-            if (command.equals("text")) {
-                if (length == 2) {
-                    inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
-                    sendText(inputSource, args[index+1]);
-                    return;
+        // Get source (optional).
+        if (SOURCES.containsKey(arg)) {
+            inputSource = SOURCES.get(arg);
+            arg = nextArgRequired();
+        }
+
+        // Get displayId (optional).
+        int displayId = INVALID_DISPLAY;
+        if ("-d".equals(arg)) {
+            displayId = getDisplayId();
+            arg = nextArgRequired();
+        }
+
+        // Get command and run.
+        InputCmd cmd = COMMANDS.get(arg);
+        if (cmd != null) {
+            try {
+                cmd.run(inputSource, displayId);
+                return;
+            } catch (NumberFormatException ex) {
+                throw new IllegalArgumentException(INVALID_ARGUMENTS + arg);
+            }
+        }
+
+        throw new IllegalArgumentException("Error: Unknown command: " + arg);
+    }
+
+    private int getDisplayId() {
+        String displayArg = nextArgRequired();
+        if ("INVALID_DISPLAY".equalsIgnoreCase(displayArg)) {
+            return INVALID_DISPLAY;
+        } else if ("DEFAULT_DISPLAY".equalsIgnoreCase(displayArg)) {
+            return DEFAULT_DISPLAY;
+        } else {
+            try {
+                final int displayId = Integer.parseInt(displayArg);
+                if (displayId == INVALID_DISPLAY) {
+                    return INVALID_DISPLAY;
                 }
-            } else if (command.equals("keyevent")) {
-                if (length >= 2) {
-                    final boolean longpress = "--longpress".equals(args[index + 1]);
-                    final int start = longpress ? index + 2 : index + 1;
-                    inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
-                    if (args.length > start) {
-                        for (int i = start; i < args.length; i++) {
-                            int keyCode = KeyEvent.keyCodeFromString(args[i]);
-                            sendKeyEvent(inputSource, keyCode, longpress);
-                        }
-                        return;
+                return Math.max(displayId, 0);
+            } catch (NumberFormatException e) {
+                throw new IllegalArgumentException(INVALID_DISPLAY_ARGUMENTS);
+            }
+        }
+    }
+
+    class InputText implements InputCmd {
+        @Override
+        public void run(int inputSource, int displayId) {
+            inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
+            sendText(inputSource, nextArgRequired(), displayId);
+        }
+
+        /**
+         * Convert the characters of string text into key event's and send to
+         * device.
+         *
+         * @param text is a string of characters you want to input to the device.
+         */
+        private void sendText(int source, final String text, int displayId) {
+            final StringBuffer buff = new StringBuffer(text);
+            boolean escapeFlag = false;
+            for (int i = 0; i < buff.length(); i++) {
+                if (escapeFlag) {
+                    escapeFlag = false;
+                    if (buff.charAt(i) == 's') {
+                        buff.setCharAt(i, ' ');
+                        buff.deleteCharAt(--i);
                     }
                 }
-            } else if (command.equals("tap")) {
-                if (length == 3) {
-                    inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
-                    sendTap(inputSource, Float.parseFloat(args[index+1]),
-                            Float.parseFloat(args[index+2]));
-                    return;
+                if (buff.charAt(i) == '%') {
+                    escapeFlag = true;
                 }
-            } else if (command.equals("swipe")) {
-                int duration = -1;
-                inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
-                switch (length) {
-                    case 6:
-                        duration = Integer.parseInt(args[index+5]);
-                    case 5:
-                        sendSwipe(inputSource,
-                                Float.parseFloat(args[index+1]), Float.parseFloat(args[index+2]),
-                                Float.parseFloat(args[index+3]), Float.parseFloat(args[index+4]),
-                                duration);
-                        return;
-                }
-            } else if (command.equals("draganddrop")) {
-                int duration = -1;
-                inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
-                switch (length) {
-                    case 6:
-                        duration = Integer.parseInt(args[index+5]);
-                    case 5:
-                        sendDragAndDrop(inputSource,
-                                Float.parseFloat(args[index+1]), Float.parseFloat(args[index+2]),
-                                Float.parseFloat(args[index+3]), Float.parseFloat(args[index+4]),
-                                duration);
-                        return;
-                }
-            } else if (command.equals("press")) {
-                inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
-                if (length == 1) {
-                    sendTap(inputSource, 0.0f, 0.0f);
-                    return;
-                }
-            } else if (command.equals("roll")) {
-                inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
-                if (length == 3) {
-                    sendMove(inputSource, Float.parseFloat(args[index+1]),
-                            Float.parseFloat(args[index+2]));
-                    return;
-                }
-            } else {
-                System.err.println("Error: Unknown command: " + command);
-                showUsage();
-                return;
             }
-        } catch (NumberFormatException ex) {
+
+            final char[] chars = buff.toString().toCharArray();
+            final KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+            final KeyEvent[] events = kcm.getEvents(chars);
+            for (int i = 0; i < events.length; i++) {
+                KeyEvent e = events[i];
+                if (source != e.getSource()) {
+                    e.setSource(source);
+                }
+                e.setDisplayId(displayId);
+                injectKeyEvent(e);
+            }
         }
-        System.err.println(INVALID_ARGUMENTS + command);
-        showUsage();
+    }
+
+    class InputKeyEvent implements InputCmd {
+        @Override
+        public void run(int inputSource, int displayId) {
+            String arg = nextArgRequired();
+            final boolean longpress = "--longpress".equals(arg);
+            if (longpress) {
+                arg = nextArgRequired();
+            }
+
+            do {
+                final int keycode = KeyEvent.keyCodeFromString(arg);
+                sendKeyEvent(inputSource, keycode, longpress, displayId);
+            } while ((arg = nextArg()) != null);
+        }
+
+        private void sendKeyEvent(int inputSource, int keyCode, boolean longpress, int displayId) {
+            final long now = SystemClock.uptimeMillis();
+            int repeatCount = 0;
+
+            KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, repeatCount,
+                    0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0/*flags*/,
+                    inputSource);
+            event.setDisplayId(displayId);
+
+            injectKeyEvent(event);
+            if (longpress) {
+                repeatCount++;
+                injectKeyEvent(KeyEvent.changeTimeRepeat(event, now, repeatCount,
+                        KeyEvent.FLAG_LONG_PRESS));
+            }
+            injectKeyEvent(KeyEvent.changeAction(event, KeyEvent.ACTION_UP));
+        }
+    }
+
+    class InputTap implements InputCmd {
+        @Override
+        public void run(int inputSource, int displayId) {
+            inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+            sendTap(inputSource, Float.parseFloat(nextArgRequired()),
+                    Float.parseFloat(nextArgRequired()), displayId);
+        }
+
+        void sendTap(int inputSource, float x, float y, int displayId) {
+            final long now = SystemClock.uptimeMillis();
+            injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, now, x, y, 1.0f,
+                    displayId);
+            injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, now, x, y, 0.0f, displayId);
+        }
+    }
+
+    class InputPress extends InputTap {
+        @Override
+        public void run(int inputSource, int displayId) {
+            inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
+            sendTap(inputSource, 0.0f, 0.0f, displayId);
+        }
+    }
+
+    class InputSwipe implements InputCmd {
+        @Override
+        public void run(int inputSource, int displayId) {
+            inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+            sendSwipe(inputSource, displayId, false);
+        }
+
+        void sendSwipe(int inputSource, int displayId, boolean isDragDrop) {
+            // Parse two points and duration.
+            final float x1 = Float.parseFloat(nextArgRequired());
+            final float y1 = Float.parseFloat(nextArgRequired());
+            final float x2 = Float.parseFloat(nextArgRequired());
+            final float y2 = Float.parseFloat(nextArgRequired());
+            String durationArg = nextArg();
+            int duration = durationArg != null ? Integer.parseInt(durationArg) : -1;
+            if (duration < 0) {
+                duration = 300;
+            }
+
+            final long down = SystemClock.uptimeMillis();
+            injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, down, down, x1, y1, 1.0f,
+                    displayId);
+            if (isDragDrop) {
+                // long press until drag start.
+                try {
+                    Thread.sleep(ViewConfiguration.getLongPressTimeout());
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+            long now = SystemClock.uptimeMillis();
+            final long endTime = down + duration;
+            while (now < endTime) {
+                final long elapsedTime = now - down;
+                final float alpha = (float) elapsedTime / duration;
+                injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, down, now,
+                        lerp(x1, x2, alpha), lerp(y1, y2, alpha), 1.0f, displayId);
+                now = SystemClock.uptimeMillis();
+            }
+            injectMotionEvent(inputSource, MotionEvent.ACTION_UP, down, now, x2, y2, 0.0f,
+                    displayId);
+        }
+    }
+
+    class InputDragAndDrop extends InputSwipe {
+        @Override
+        public void run(int inputSource, int displayId) {
+            inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+            sendSwipe(inputSource, displayId, true);
+        }
+    }
+
+    class InputRoll implements InputCmd {
+        @Override
+        public void run(int inputSource, int displayId) {
+            inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
+            sendMove(inputSource, Float.parseFloat(nextArgRequired()),
+                    Float.parseFloat(nextArgRequired()), displayId);
+        }
+
+        /**
+         * Sends a simple zero-pressure move event.
+         *
+         * @param inputSource the InputDevice.SOURCE_* sending the input event
+         * @param dx change in x coordinate due to move
+         * @param dy change in y coordinate due to move
+         */
+        private void sendMove(int inputSource, float dx, float dy, int displayId) {
+            final long now = SystemClock.uptimeMillis();
+            injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, now, dx, dy, 0.0f,
+                    displayId);
+        }
     }
 
     /**
-     * Convert the characters of string text into key event's and send to
-     * device.
-     *
-     * @param text is a string of characters you want to input to the device.
+     * Abstract class for command
+     * use nextArgRequired or nextArg to check next argument if necessary.
      */
-    private void sendText(int source, String text) {
-
-        StringBuffer buff = new StringBuffer(text);
-
-        boolean escapeFlag = false;
-        for (int i=0; i<buff.length(); i++) {
-            if (escapeFlag) {
-                escapeFlag = false;
-                if (buff.charAt(i) == 's') {
-                    buff.setCharAt(i, ' ');
-                    buff.deleteCharAt(--i);
-                }
-            }
-            if (buff.charAt(i) == '%') {
-                escapeFlag = true;
-            }
-        }
-
-        char[] chars = buff.toString().toCharArray();
-
-        KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
-        KeyEvent[] events = kcm.getEvents(chars);
-        for(int i = 0; i < events.length; i++) {
-            KeyEvent e = events[i];
-            if (source != e.getSource()) {
-                e.setSource(source);
-            }
-            injectKeyEvent(e);
-        }
+    private interface InputCmd {
+        void run(int inputSource, int displayId);
     }
 
-    private void sendKeyEvent(int inputSource, int keyCode, boolean longpress) {
-        long now = SystemClock.uptimeMillis();
-        injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0, 0,
-                KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource));
-        if (longpress) {
-            injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 1, 0,
-                    KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_LONG_PRESS,
-                    inputSource));
-        }
-        injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_UP, keyCode, 0, 0,
-                KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource));
-    }
-
-    private void sendTap(int inputSource, float x, float y) {
-        long now = SystemClock.uptimeMillis();
-        injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x, y, 1.0f);
-        injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x, y, 0.0f);
-    }
-
-    private void sendSwipe(int inputSource, float x1, float y1, float x2, float y2, int duration) {
-        if (duration < 0) {
-            duration = 300;
-        }
-        long now = SystemClock.uptimeMillis();
-        injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x1, y1, 1.0f);
-        long startTime = now;
-        long endTime = startTime + duration;
-        while (now < endTime) {
-            long elapsedTime = now - startTime;
-            float alpha = (float) elapsedTime / duration;
-            injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, lerp(x1, x2, alpha),
-                    lerp(y1, y2, alpha), 1.0f);
-            now = SystemClock.uptimeMillis();
-        }
-        injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x2, y2, 0.0f);
-    }
-
-    private void sendDragAndDrop(int inputSource, float x1, float y1, float x2, float y2,
-            int dragDuration) {
-        if (dragDuration < 0) {
-            dragDuration = 300;
-        }
-        long now = SystemClock.uptimeMillis();
-        injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x1, y1, 1.0f);
-        try {
-            Thread.sleep(ViewConfiguration.getLongPressTimeout());
-        } catch (InterruptedException e) {
-            throw new RuntimeException(e);
-        }
-        now = SystemClock.uptimeMillis();
-        long startTime = now;
-        long endTime = startTime + dragDuration;
-        while (now < endTime) {
-            long elapsedTime = now - startTime;
-            float alpha = (float) elapsedTime / dragDuration;
-            injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, lerp(x1, x2, alpha),
-                    lerp(y1, y2, alpha), 1.0f);
-            now = SystemClock.uptimeMillis();
-        }
-        injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x2, y2, 0.0f);
-    }
-
-    /**
-     * Sends a simple zero-pressure move event.
-     *
-     * @param inputSource the InputDevice.SOURCE_* sending the input event
-     * @param dx change in x coordinate due to move
-     * @param dy change in y coordinate due to move
-     */
-    private void sendMove(int inputSource, float dx, float dy) {
-        long now = SystemClock.uptimeMillis();
-        injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, dx, dy, 0.0f);
-    }
-
-    private void injectKeyEvent(KeyEvent event) {
-        Log.i(TAG, "injectKeyEvent: " + event);
+    private static void injectKeyEvent(KeyEvent event) {
         InputManager.getInstance().injectInputEvent(event,
                 InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
     }
 
-    private int getInputDeviceId(int inputSource) {
+    private static int getInputDeviceId(int inputSource) {
         final int DEFAULT_DEVICE_ID = 0;
         int[] devIds = InputDevice.getDeviceIds();
         for (int devId : devIds) {
@@ -287,22 +334,27 @@
      *
      * @param inputSource the InputDevice.SOURCE_* sending the input event
      * @param action the MotionEvent.ACTION_* for the event
+     * @param downTime the value of the ACTION_DOWN event happened
      * @param when the value of SystemClock.uptimeMillis() at which the event happened
      * @param x x coordinate of event
      * @param y y coordinate of event
      * @param pressure pressure of event
      */
-    private void injectMotionEvent(int inputSource, int action, long when, float x, float y, float pressure) {
+    private static void injectMotionEvent(int inputSource, int action, long downTime, long when,
+            float x, float y, float pressure, int displayId) {
         final float DEFAULT_SIZE = 1.0f;
         final int DEFAULT_META_STATE = 0;
         final float DEFAULT_PRECISION_X = 1.0f;
         final float DEFAULT_PRECISION_Y = 1.0f;
         final int DEFAULT_EDGE_FLAGS = 0;
-        MotionEvent event = MotionEvent.obtain(when, when, action, x, y, pressure, DEFAULT_SIZE,
+        MotionEvent event = MotionEvent.obtain(downTime, when, action, x, y, pressure, DEFAULT_SIZE,
                 DEFAULT_META_STATE, DEFAULT_PRECISION_X, DEFAULT_PRECISION_Y,
                 getInputDeviceId(inputSource), DEFAULT_EDGE_FLAGS);
         event.setSource(inputSource);
-        Log.i(TAG, "injectMotionEvent: " + event);
+        if (displayId == INVALID_DISPLAY && (inputSource & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+            displayId = DEFAULT_DISPLAY;
+        }
+        event.setDisplayId(displayId);
         InputManager.getInstance().injectInputEvent(event,
                 InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
     }
@@ -315,24 +367,29 @@
         return inputSource == InputDevice.SOURCE_UNKNOWN ? defaultSource : inputSource;
     }
 
-    private void showUsage() {
-        System.err.println("Usage: input [<source>] <command> [<arg>...]");
-        System.err.println();
-        System.err.println("The sources are: ");
+    @Override
+    public void onShowUsage(PrintStream out) {
+        out.println("Usage: input [<source>] [-d DISPLAY_ID] <command> [<arg>...]");
+        out.println();
+        out.println("The sources are: ");
         for (String src : SOURCES.keySet()) {
-            System.err.println("      " + src);
+            out.println("      " + src);
         }
-        System.err.println();
-        System.err.println("The commands and default sources are:");
-        System.err.println("      text <string> (Default: touchscreen)");
-        System.err.println("      keyevent [--longpress] <key code number or name> ..."
+        out.println();
+        out.printf("-d: specify the display ID.\n"
+                + "      (Default: %d for key event, %d for motion event if not specified.)",
+                INVALID_DISPLAY, DEFAULT_DISPLAY);
+        out.println();
+        out.println("The commands and default sources are:");
+        out.println("      text <string> (Default: touchscreen)");
+        out.println("      keyevent [--longpress] <key code number or name> ..."
                 + " (Default: keyboard)");
-        System.err.println("      tap <x> <y> (Default: touchscreen)");
-        System.err.println("      swipe <x1> <y1> <x2> <y2> [duration(ms)]"
+        out.println("      tap <x> <y> (Default: touchscreen)");
+        out.println("      swipe <x1> <y1> <x2> <y2> [duration(ms)]"
                 + " (Default: touchscreen)");
-        System.err.println("      draganddrop <x1> <y1> <x2> <y2> [duration(ms)]"
+        out.println("      draganddrop <x1> <y1> <x2> <y2> [duration(ms)]"
                 + " (Default: touchscreen)");
-        System.err.println("      press (Default: trackball)");
-        System.err.println("      roll <dx> <dy> (Default: trackball)");
+        out.println("      press (Default: trackball)");
+        out.println("      roll <dx> <dy> (Default: trackball)");
     }
 }
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index 783c8c4..be5a5bf 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -282,14 +282,31 @@
                 StorageManager.DEBUG_VIRTUAL_DISK);
     }
 
-    public void runIsolatedStorage() throws RemoteException {
-        final boolean enableIsolatedStorage = Boolean.parseBoolean(nextArg());
+    public void runIsolatedStorage() {
+        final int value;
+        final int mask = StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON
+                | StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF;
+        switch (nextArg()) {
+            case "on":
+            case "true":
+                value = StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON;
+                break;
+            case "off":
+                value = StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF;
+                break;
+            case "default":
+            case "false":
+                value = 0;
+                break;
+            default:
+                return;
+        }
+
         // Toggling isolated-storage state will result in a device reboot. So to avoid this command
         // from erroring out (DeadSystemException), call setDebugFlags() in a separate thread.
         new Thread(() -> {
             try {
-                mSm.setDebugFlags(enableIsolatedStorage ? StorageManager.DEBUG_ISOLATED_STORAGE : 0,
-                        StorageManager.DEBUG_ISOLATED_STORAGE);
+                mSm.setDebugFlags(value, mask);
             } catch (RemoteException e) {
                 Log.e(TAG, "Encountered an error!", e);
             }
@@ -334,7 +351,7 @@
         System.err.println("");
         System.err.println("       sm set-emulate-fbe [true|false]");
         System.err.println("");
-        System.err.println("       sm set-isolated-storage [true|false]");
+        System.err.println("       sm set-isolated-storage [on|off|default]");
         System.err.println("");
         return 1;
     }
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 0114ff4..59b2aa6 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -136,7 +136,7 @@
         "android.hardware.power@1.0",
         "android.hardware.power@1.1",
         "android.hardware.power.stats@1.0",
-        "android.hardware.thermal@1.0",
+        "android.hardware.thermal@2.0",
         "libpackagelistparser",
         "libsysutils",
         "libcutils",
@@ -216,6 +216,7 @@
         "tests/anomaly/AnomalyTracker_test.cpp",
         "tests/ConfigManager_test.cpp",
         "tests/external/puller_util_test.cpp",
+	"tests/external/StatsPuller_test.cpp",
         "tests/indexed_priority_queue_test.cpp",
         "tests/LogEntryMatcher_test.cpp",
         "tests/LogEvent_test.cpp",
@@ -331,6 +332,8 @@
         "src/stats_log.proto",
         "src/statsd_config.proto",
         "src/atoms.proto",
+        "src/shell/shell_config.proto",
+        "src/shell/shell_data.proto",
     ],
 
     static_libs: [
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index a981997..69cb264 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -293,7 +293,7 @@
 
     // Then, check stats-data directory to see there's any file containing
     // ConfigMetricsReport from previous shutdowns to concatenate to reports.
-    StorageManager::appendConfigMetricsReport(key, proto);
+    StorageManager::appendConfigMetricsReport(key, proto, erase_data);
 
     auto it = mMetricsManagers.find(key);
     if (it != mMetricsManagers.end()) {
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 04173b2..3107b4d 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -296,6 +296,7 @@
                                  ADB_DUMP, &proto);
         proto.end(reportsListToken);
         proto.flush(out);
+        proto.clear();
     }
 }
 
@@ -359,7 +360,11 @@
             if (mShellSubscriber == nullptr) {
                 mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager);
             }
-            mShellSubscriber->startNewSubscription(in, out, resultReceiver);
+            int timeoutSec = -1;
+            if (argCount >= 2) {
+                timeoutSec = atoi(args[1].c_str());
+            }
+            mShellSubscriber->startNewSubscription(in, out, resultReceiver, timeoutSec);
             return NO_ERROR;
         }
     }
@@ -466,23 +471,12 @@
         name.assign(args[1].c_str(), args[1].size());
         good = true;
     } else if (argCount == 3) {
-        // If it's a userdebug or eng build, then the shell user can
-        // impersonate other uids.
-        if (mEngBuild) {
-            const char* s = args[1].c_str();
-            if (*s != '\0') {
-                char* end = NULL;
-                uid = strtol(s, &end, 0);
-                if (*end == '\0') {
-                    name.assign(args[2].c_str(), args[2].size());
-                    good = true;
-                }
-            }
-        } else {
-            dprintf(out,
-                    "The metrics can only be dumped for other UIDs on eng or userdebug "
-                    "builds.\n");
+        good = getUidFromArgs(args, 1, uid);
+        if (!good) {
+            dprintf(out, "Invalid UID. Note that the metrics can only be dumped for "
+                         "other UIDs on eng or userdebug builds.\n");
         }
+        name.assign(args[2].c_str(), args[2].size());
     }
     if (!good) {
         print_cmd_help(out);
@@ -518,23 +512,12 @@
                 name.assign(args[2].c_str(), args[2].size());
                 good = true;
             } else if (argCount == 4) {
-                // If it's a userdebug or eng build, then the shell user can
-                // impersonate other uids.
-                if (mEngBuild) {
-                    const char* s = args[2].c_str();
-                    if (*s != '\0') {
-                        char* end = NULL;
-                        uid = strtol(s, &end, 0);
-                        if (*end == '\0') {
-                            name.assign(args[3].c_str(), args[3].size());
-                            good = true;
-                        }
-                    }
-                } else {
-                    dprintf(err,
-                            "The config can only be set for other UIDs on eng or userdebug "
-                            "builds.\n");
+                good = getUidFromArgs(args, 2, uid);
+                if (!good) {
+                    dprintf(err, "Invalid UID. Note that the config can only be set for "
+                                 "other UIDs on eng or userdebug builds.\n");
                 }
+                name.assign(args[3].c_str(), args[3].size());
             } else if (argCount == 2 && args[1] == "remove") {
                 good = true;
             }
@@ -612,23 +595,12 @@
             name.assign(args[1].c_str(), args[1].size());
             good = true;
         } else if (argCount == 3) {
-            // If it's a userdebug or eng build, then the shell user can
-            // impersonate other uids.
-            if (mEngBuild) {
-                const char* s = args[1].c_str();
-                if (*s != '\0') {
-                    char* end = NULL;
-                    uid = strtol(s, &end, 0);
-                    if (*end == '\0') {
-                        name.assign(args[2].c_str(), args[2].size());
-                        good = true;
-                    }
-                }
-            } else {
-                dprintf(out,
-                        "The metrics can only be dumped for other UIDs on eng or userdebug "
-                        "builds.\n");
+            good = getUidFromArgs(args, 1, uid);
+            if (!good) {
+                dprintf(out, "Invalid UID. Note that the metrics can only be dumped for "
+                             "other UIDs on eng or userdebug builds.\n");
             }
+            name.assign(args[2].c_str(), args[2].size());
         }
         if (good) {
             vector<uint8_t> data;
@@ -714,18 +686,14 @@
         state = atoi(args[2].c_str());
         good = true;
     } else if (argCount == 4) {
-        uid = atoi(args[1].c_str());
-        // If it's a userdebug or eng build, then the shell user can impersonate other uids.
-        // Otherwise, the uid must match the actual caller's uid.
-        if (mEngBuild || (uid >= 0 && (uid_t)uid == IPCThreadState::self()->getCallingUid())) {
-            label = atoi(args[2].c_str());
-            state = atoi(args[3].c_str());
-            good = true;
-        } else {
+        good = getUidFromArgs(args, 1, uid);
+        if (!good) {
             dprintf(out,
-                    "Selecting a UID for writing AppBreadcrumb can only be done for other UIDs "
-                    "on eng or userdebug builds.\n");
+                    "Invalid UID. Note that selecting a UID for writing AppBreadcrumb can only be "
+                    "done for other UIDs on eng or userdebug builds.\n");
         }
+        label = atoi(args[2].c_str());
+        state = atoi(args[3].c_str());
     }
     if (good) {
         dprintf(out, "Logging AppBreadcrumbReported(%d, %d, %d) to statslog.\n", uid, label, state);
@@ -740,7 +708,7 @@
 status_t StatsService::cmd_print_pulled_metrics(int out, const Vector<String8>& args) {
     int s = atoi(args[1].c_str());
     vector<shared_ptr<LogEvent> > stats;
-    if (mPullerManager->Pull(s, getElapsedRealtimeNs(), &stats)) {
+    if (mPullerManager->Pull(s, &stats)) {
         for (const auto& it : stats) {
             dprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
         }
@@ -792,6 +760,28 @@
     }
 }
 
+bool StatsService::getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid) {
+    const char* s = args[uidArgIndex].c_str();
+    if (*s == '\0') {
+        return false;
+    }
+    char* endc = NULL;
+    int64_t longUid = strtol(s, &endc, 0);
+    if (*endc != '\0') {
+        return false;
+    }
+    int32_t goodUid = static_cast<int32_t>(longUid);
+    if (longUid < 0 || static_cast<uint64_t>(longUid) != static_cast<uid_t>(goodUid)) {
+        return false;  // It was not of uid_t type.
+    }
+    uid = goodUid;
+
+    int32_t callingUid = IPCThreadState::self()->getCallingUid();
+    return mEngBuild // UserDebug/EngBuild are allowed to impersonate uids.
+            || (callingUid == goodUid) // Anyone can 'impersonate' themselves.
+            || (callingUid == AID_ROOT && goodUid == AID_SHELL); // ROOT can impersonate SHELL.
+}
+
 Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version,
                                       const vector<String16>& version_string,
                                       const vector<String16>& app,
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index cd4d601..135a3c9 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -291,6 +291,15 @@
     status_t cmd_print_logs(int outFd, const Vector<String8>& args);
 
     /**
+     * Writes the value of args[uidArgIndex] into uid.
+     * Returns whether the uid is reasonable (type uid_t) and whether
+     * 1. it is equal to the calling uid, or
+     * 2. the device is mEngBuild, or
+     * 3. the caller is AID_ROOT and the uid is AID_SHELL (i.e. ROOT can impersonate SHELL).
+     */
+    bool getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid);
+
+    /**
      * Adds a configuration after checking permissions and obtaining UID from binder call.
      */
     bool addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config);
@@ -340,6 +349,7 @@
     FRIEND_TEST(StatsServiceTest, TestAddConfig_simple);
     FRIEND_TEST(StatsServiceTest, TestAddConfig_empty);
     FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid);
+    FRIEND_TEST(StatsServiceTest, TestGetUidFromArgs);
     FRIEND_TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp);
     FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade);
     FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval);
diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.h b/cmds/statsd/src/anomaly/AlarmMonitor.h
index 3badb1f..bca858e 100644
--- a/cmds/statsd/src/anomaly/AlarmMonitor.h
+++ b/cmds/statsd/src/anomaly/AlarmMonitor.h
@@ -42,7 +42,7 @@
  * Timestamps are in seconds since epoch in a uint32, so will fail in year 2106.
  */
 struct InternalAlarm : public RefBase {
-    InternalAlarm(uint32_t timestampSec) : timestampSec(timestampSec) {
+    explicit InternalAlarm(uint32_t timestampSec) : timestampSec(timestampSec) {
     }
 
     const uint32_t timestampSec;
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 410bd19..5c53a3a 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -169,6 +169,10 @@
         DocsUIRootVisitedReported docs_ui_root_visited = 110;
         DocsUIStartupMsReported docs_ui_startup_ms = 111;
         DocsUIUserActionReported docs_ui_user_action_reported = 112;
+        WifiEnabledStateChanged wifi_enabled_state_changed = 113;
+        WifiRunningStateChanged wifi_running_state_changed = 114;
+        AppCompacted app_compacted = 115;
+        NetworkDnsEventReported network_dns_event_Reported = 116;
     }
 
     // Pulled events will start at field 10000.
@@ -871,6 +875,39 @@
 }
 
 /**
+ * Logs when Wifi is toggled on/off.
+ * Note that Wifi may still perform certain functions (e.g. location scanning) even when disabled.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
+ */
+message WifiEnabledStateChanged {
+    enum State {
+        OFF = 0;
+        ON = 1;
+    }
+    optional State state = 1;
+}
+
+/**
+ * Logs when an app causes Wifi to run. In this context, 'to run' means to use Wifi Client Mode.
+ * TODO: Include support for Hotspot, perhaps by using an extra field to denote 'mode'.
+ * Note that Wifi Scanning is monitored separately in WifiScanStateChanged.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
+ */
+message WifiRunningStateChanged {
+    repeated AttributionNode attribution_node = 1;
+
+    enum State {
+        OFF = 0;
+        ON = 1;
+    }
+    optional State state = 2;
+}
+
+/**
  * Logs wifi locks held by an app.
  *
  * Logged from:
@@ -890,7 +927,7 @@
  * Logs wifi signal strength changes.
  *
  * Logged from:
- *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ *   frameworks/opt/net/wifi/service/java/com/android/server/wifi/ClientModeImpl.java
  */
 message WifiSignalStrengthChanged {
     // Signal strength, from frameworks/base/core/proto/android/telephony/enums.proto.
@@ -901,7 +938,7 @@
  * Logs wifi scans performed by an app.
  *
  * Logged from:
- *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ *   frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
  */
 message WifiScanStateChanged {
     repeated AttributionNode attribution_node = 1;
@@ -917,7 +954,7 @@
  * Logs wifi multicast locks held by an app
  *
  * Logged from:
- *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ *   frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiMulticastLockManager.java
  */
 message WifiMulticastLockStateChanged {
     repeated AttributionNode attribution_node = 1;
@@ -927,6 +964,8 @@
         ON = 1;
     }
     optional State state = 2;
+
+    optional string tag = 3;
 }
 
 /**
@@ -1222,7 +1261,7 @@
         STATE_CONNECTED = 1;
     }
     optional State state = 6;
-    optional int64 last_connect_duration_ms = 7;
+    optional int64 last_connect_duration_millis = 7;
 }
 
 
@@ -2627,6 +2666,8 @@
  *
  * Pulled from StatsCompanionService for all managed processes (from ActivityManagerServie)
  * and for selected native processes.
+ *
+ * Pulling this atom resets high-water mark counters for all processes.
  */
 message ProcessMemoryHighWaterMark {
     // The uid if available. -1 means not available.
@@ -2748,13 +2789,14 @@
  *   frameworks/base/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp
  */
 message Temperature {
-    // The type of temperature being reported. Eg. CPU, GPU, SKIN, BATTERY.
+    // The type of temperature being reported. Eg. CPU, GPU, SKIN, BATTERY, BCL_.
     optional android.os.TemperatureTypeEnum sensor_location = 1;
 
     // The name of the temperature source. Eg. CPU0
     optional string sensor_name = 2;
 
     // Temperature in tenths of a degree C.
+    // For BCL, it is decimillivolt, decimilliamps, and percentage * 10.
     optional int32 temperature_deci_celsius = 3;
 }
 
@@ -3435,10 +3477,10 @@
  * Pulls on-device BatteryStats power use calculations for the overall device.
  */
 message DeviceCalculatedPowerUse {
-    // Power used by the device in mAh, as computed by BatteryStats, since BatteryStats last reset
-    // (i.e. roughly since device was last significantly charged).
-    // Currently, this is BatteryStatsHelper.getComputedPower() (not getTotalPower()).
-    optional float computed_power_milli_amp_hours = 1;
+    // Power used by the device in nAs (i.e. nanocoulombs (nC)), as computed by BatteryStats, since
+    // BatteryStats last reset (i.e. roughly since device was last significantly charged).
+    // Currently, this is from BatteryStatsHelper.getComputedPower() (not getTotalPower()).
+    optional int64 computed_power_nano_amp_secs = 1;
 }
 
 /**
@@ -3450,9 +3492,9 @@
     // Uid being blamed. Note: isolated uids have already been mapped to host uid.
     optional int32 uid = 1 [(is_uid) = true];
 
-    // Power used by this uid in mAh, as computed by BatteryStats, since BatteryStats last reset
-    // (i.e. roughly since device was last significantly charged).
-    optional float power_milli_amp_hours = 2;
+    // Power used by this uid in nAs (i.e. nanocoulombs (nC)), as computed by BatteryStats, since
+    // BatteryStats last reset (i.e. roughly since device was last significantly charged).
+    optional int64 power_nano_amp_secs = 2;
 }
 
 /**
@@ -3486,9 +3528,9 @@
     }
     optional DrainType drain_type = 1;
 
-    // Power used by this item in mAh, as computed by BatteryStats, since BatteryStats last reset
-    // (i.e. roughly since device was last significantly charged).
-    optional float power_milli_amp_hours = 2;
+    // Power used by this item in nAs (i.e. nanocoulombs (nC)), as computed by BatteryStats, since
+    // BatteryStats last reset (i.e. roughly since device was last significantly charged).
+    optional int64 power_nano_amp_secs = 2;
 }
 
 /**
@@ -3612,3 +3654,113 @@
 message DocsUIInvalidScopedAccessRequestReported {
     optional android.stats.docsui.InvalidScopedAccess type = 1;
 }
+
+/**
+ * Logs when an app's memory is compacted.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ */
+message AppCompacted {
+  // The pid of the process being compacted.
+  optional int32 pid = 1;
+
+  // The name of the process being compacted.
+  optional string process_name = 2;
+
+  // The type of compaction.
+  enum Action {
+    UNKNOWN = 0;
+    SOME = 1;
+    FULL = 2;
+  }
+  optional Action action = 3;
+
+  // Total RSS in kilobytes consumed by the process prior to compaction.
+  optional int64 before_rss_total_kilobytes = 4;
+
+  // File RSS in kilobytes consumed by the process prior to compaction.
+  optional int64 before_rss_file_kilobytes = 5;
+
+  // Anonymous RSS in kilobytes consumed by the process prior to compaction.
+  optional int64 before_rss_anon_kilobytes = 6;
+
+  // Swap in kilobytes consumed by the process prior to compaction.
+  optional int64 before_swap_kilobytes = 7;
+
+  // Total RSS in kilobytes consumed by the process after compaction.
+  optional int64 after_rss_total_kilobytes = 8;
+
+  // File RSS in kilobytes consumed by the process after compaction.
+  optional int64 after_rss_file_kilobytes = 9;
+
+  // Anonymous RSS in kilobytes consumed by the process after compaction.
+  optional int64 after_rss_anon_kilobytes = 10;
+
+  // Swap in kilobytes consumed by the process after compaction.
+  optional int64 after_swap_kilobytes = 11;
+
+  // The time taken to perform compaction in milliseconds.
+  optional int64 time_to_compact_millis = 12;
+
+  // The last compaction action performed for this app.
+  optional Action last_action = 13;
+
+  // The last time that compaction was attempted on this process in milliseconds
+  // since boot, not including sleep (see SystemClock.uptimeMillis()).
+  optional int64 last_compact_timestamp_ms_since_boot = 14;
+
+  // The oom_score_adj at the time of compaction.
+  optional int32 oom_score_adj = 15;
+
+  // The process state at the time of compaction.
+  optional android.app.ProcessStateEnum process_state = 16 [default = PROCESS_STATE_UNKNOWN];
+}
+
+/**
+ * Logs the latency period(in microseconds) and the return code of
+ * the DNS(Domain Name System) lookups.
+ * These 4 methods(GETADDRINFO,GETHOSTBYNAME,GETHOSTBYADDR,RES_NSEND)
+ * to get info(address or hostname) from DNS server(or DNS cache).
+ * Logged from:
+ *   /system/netd/server/DnsProxyListener.cpp
+ */
+message NetworkDnsEventReported {
+    // The types of the DNS lookups, as defined in
+    //system/netd/server/binder/android/net/metrics/INetdEventListener.aidl
+    enum EventType {
+        EVENT_UNKNOWN = 0;
+        EVENT_GETADDRINFO = 1;
+        EVENT_GETHOSTBYNAME = 2;
+        EVENT_GETHOSTBYADDR = 3;
+        EVENT_RES_NSEND = 4;
+    }
+    optional EventType event_type = 1;
+
+    // The return value of the DNS resolver for each DNS lookups.
+    //bionic/libc/include/netdb.h
+    //system/netd/resolv/include/netd_resolv/resolv.h
+    enum ReturnCode {
+        EAI_NOERR = 0;
+        EAI_ADDRFAMILY = 1;
+        EAI_AGAIN = 2;
+        EAI_BADFLAGS = 3;
+        EAI_FAIL = 4;
+        EAI_FAMILY = 5;
+        EAI_MEMORY = 6;
+        EAI_NODATA = 7;
+        EAI_NONAME = 8;
+        EAI_SERVICE = 9;
+        EAI_SOCKTYPE = 10;
+        EAI_SYSTEM = 11;
+        EAI_BADHINTS = 12;
+        EAI_PROTOCOL = 13;
+        EAI_OVERFLOW = 14;
+        RESOLV_TIMEOUT = 255;
+        EAI_MAX = 256;
+    }
+    optional ReturnCode return_code = 2;
+
+    // The latency period(in microseconds) it took for this DNS lookup to complete.
+    optional int32 latency_micros = 3;
+}
diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h
index a6f88af..2c88147 100644
--- a/cmds/statsd/src/condition/ConditionWizard.h
+++ b/cmds/statsd/src/condition/ConditionWizard.h
@@ -29,7 +29,7 @@
 class ConditionWizard : public virtual android::RefBase {
 public:
     ConditionWizard(){};  // for testing
-    ConditionWizard(std::vector<sp<ConditionTracker>>& conditionTrackers)
+    explicit ConditionWizard(std::vector<sp<ConditionTracker>>& conditionTrackers)
         : mAllConditions(conditionTrackers){};
 
     virtual ~ConditionWizard(){};
diff --git a/cmds/statsd/src/config/ConfigKey.h b/cmds/statsd/src/config/ConfigKey.h
index dc79519..4cc9393 100644
--- a/cmds/statsd/src/config/ConfigKey.h
+++ b/cmds/statsd/src/config/ConfigKey.h
@@ -33,7 +33,7 @@
 class ConfigKey {
 public:
     ConfigKey();
-    explicit ConfigKey(const ConfigKey& that);
+    ConfigKey(const ConfigKey& that);
     ConfigKey(int uid, const int64_t& id);
     ~ConfigKey();
 
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 16b7e79..5fea90b 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -106,14 +106,14 @@
         // Add to set.
         mConfigs[key.GetUid()].insert(key);
 
-        for (sp<ConfigListener> listener : mListeners) {
+        for (const sp<ConfigListener>& listener : mListeners) {
             broadcastList.push_back(listener);
         }
     }
 
     const int64_t timestampNs = getElapsedRealtimeNs();
     // Tell everyone
-    for (sp<ConfigListener> listener : broadcastList) {
+    for (const sp<ConfigListener>& listener : broadcastList) {
         listener->OnConfigUpdated(timestampNs, key, config);
     }
 }
@@ -137,7 +137,7 @@
         if (uidIt != mConfigs.end() && uidIt->second.find(key) != uidIt->second.end()) {
             // Remove from map
             uidIt->second.erase(key);
-            for (sp<ConfigListener> listener : mListeners) {
+            for (const sp<ConfigListener>& listener : mListeners) {
                 broadcastList.push_back(listener);
             }
         }
@@ -153,7 +153,7 @@
         remove_saved_configs(key);
     }
 
-    for (sp<ConfigListener> listener:broadcastList) {
+    for (const sp<ConfigListener>& listener:broadcastList) {
         listener->OnConfigRemoved(key);
     }
 }
@@ -183,7 +183,7 @@
 
         mConfigs.erase(uidIt);
 
-        for (sp<ConfigListener> listener : mListeners) {
+        for (const sp<ConfigListener>& listener : mListeners) {
             broadcastList.push_back(listener);
         }
     }
@@ -191,7 +191,7 @@
     // Remove separately so if they do anything in the callback they can't mess up our iteration.
     for (auto& key : removed) {
         // Tell everyone
-        for (sp<ConfigListener> listener:broadcastList) {
+        for (const sp<ConfigListener>& listener:broadcastList) {
             listener->OnConfigRemoved(key);
         }
     }
@@ -213,7 +213,7 @@
         }
 
         mConfigReceivers.clear();
-        for (sp<ConfigListener> listener : mListeners) {
+        for (const sp<ConfigListener>& listener : mListeners) {
             broadcastList.push_back(listener);
         }
     }
@@ -221,7 +221,7 @@
     // Remove separately so if they do anything in the callback they can't mess up our iteration.
     for (auto& key : removed) {
         // Tell everyone
-        for (sp<ConfigListener> listener:broadcastList) {
+        for (const sp<ConfigListener>& listener:broadcastList) {
             listener->OnConfigRemoved(key);
         }
     }
diff --git a/cmds/statsd/src/external/PowerStatsPuller.h b/cmds/statsd/src/external/PowerStatsPuller.h
index dd5ff8f..6f15bd6 100644
--- a/cmds/statsd/src/external/PowerStatsPuller.h
+++ b/cmds/statsd/src/external/PowerStatsPuller.h
@@ -28,6 +28,8 @@
 class PowerStatsPuller : public StatsPuller {
 public:
     PowerStatsPuller();
+
+private:
     bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
 };
 
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.h b/cmds/statsd/src/external/ResourceHealthManagerPuller.h
index 9b238ea..f650fcc 100644
--- a/cmds/statsd/src/external/ResourceHealthManagerPuller.h
+++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.h
@@ -28,7 +28,9 @@
  */
 class ResourceHealthManagerPuller : public StatsPuller {
 public:
-    ResourceHealthManagerPuller(int tagId);
+    explicit ResourceHealthManagerPuller(int tagId);
+
+private:
     bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
 };
 
diff --git a/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp b/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp
index 33a17de..53709f1 100644
--- a/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp
+++ b/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp
@@ -17,7 +17,7 @@
 #define DEBUG false  // STOPSHIP if true
 #include "Log.h"
 
-#include <android/hardware/thermal/1.0/IThermal.h>
+#include <android/hardware/thermal/2.0/IThermal.h>
 #include "external/ResourceThermalManagerPuller.h"
 #include "external/StatsPuller.h"
 
@@ -31,10 +31,11 @@
 using android::hardware::hidl_death_recipient;
 using android::hardware::hidl_vec;
 using android::hidl::base::V1_0::IBase;
-using android::hardware::thermal::V1_0::IThermal;
-using android::hardware::thermal::V1_0::Temperature;
-using android::hardware::thermal::V1_0::ThermalStatus;
-using android::hardware::thermal::V1_0::ThermalStatusCode;
+using ::android::hardware::thermal::V2_0::IThermal;
+using ::android::hardware::thermal::V2_0::Temperature;
+using ::android::hardware::thermal::V2_0::TemperatureType;
+using ::android::hardware::thermal::V1_0::ThermalStatus;
+using ::android::hardware::thermal::V1_0::ThermalStatusCode;
 using android::hardware::Return;
 using android::hardware::Void;
 
@@ -49,7 +50,7 @@
 namespace statsd {
 
 bool getThermalHalLocked();
-sp<android::hardware::thermal::V1_0::IThermal> gThermalHal = nullptr;
+sp<android::hardware::thermal::V2_0::IThermal> gThermalHal = nullptr;
 std::mutex gThermalHalMutex;
 
 struct ThermalHalDeathRecipient : virtual public hidl_death_recipient {
@@ -107,7 +108,7 @@
     data->clear();
     bool resultSuccess = true;
 
-    Return<void> ret = gThermalHal->getTemperatures(
+    Return<void> ret = gThermalHal->getCurrentTemperatures(false, TemperatureType::SKIN,
             [&](ThermalStatus status, const hidl_vec<Temperature>& temps) {
         if (status.code != ThermalStatusCode::SUCCESS) {
             ALOGE("Failed to get temperatures from ThermalHAL. Status: %d", status.code);
@@ -121,7 +122,7 @@
                 ptr->write((static_cast<int>(temps[i].type)));
                 ptr->write(temps[i].name);
                 // Convert the temperature to an int.
-                int32_t temp = static_cast<int>(temps[i].currentValue * 10);
+                int32_t temp = static_cast<int>(temps[i].value * 10);
                 ptr->write(temp);
                 ptr->init();
                 data->push_back(ptr);
diff --git a/cmds/statsd/src/external/ResourceThermalManagerPuller.h b/cmds/statsd/src/external/ResourceThermalManagerPuller.h
index 13c675a..5313792 100644
--- a/cmds/statsd/src/external/ResourceThermalManagerPuller.h
+++ b/cmds/statsd/src/external/ResourceThermalManagerPuller.h
@@ -29,6 +29,8 @@
 class ResourceThermalManagerPuller : public StatsPuller {
 public:
     ResourceThermalManagerPuller();
+
+private:
     bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
 };
 
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.h b/cmds/statsd/src/external/StatsCompanionServicePuller.h
index 0a49732..2e13320 100644
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.h
+++ b/cmds/statsd/src/external/StatsCompanionServicePuller.h
@@ -25,14 +25,14 @@
 
 class StatsCompanionServicePuller : public StatsPuller {
 public:
-    StatsCompanionServicePuller(int tagId);
-    bool PullInternal(vector<std::shared_ptr<LogEvent> >* data) override;
+    explicit StatsCompanionServicePuller(int tagId);
 
     void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) override;
 
 private:
     Mutex mStatsCompanionServiceLock;
     sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+    bool PullInternal(vector<std::shared_ptr<LogEvent> >* data) override;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index 7043d66..c7c22ee 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -34,48 +34,52 @@
 
 StatsPuller::StatsPuller(const int tagId)
     : mTagId(tagId) {
-    // Pullers can cause significant impact to system health and battery.
-    // So that we don't pull too frequently.
-    mCoolDownNs = StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.coolDownNs;
-    VLOG("Puller for tag %d created. Cooldown set to %lld", mTagId, (long long)mCoolDownNs);
 }
 
-bool StatsPuller::Pull(const int64_t elapsedTimeNs, std::vector<std::shared_ptr<LogEvent>>* data) {
+bool StatsPuller::Pull(std::vector<std::shared_ptr<LogEvent>>* data) {
     lock_guard<std::mutex> lock(mLock);
-    int64_t wallClockTimeNs = getWallClockNs();
+    int64_t elapsedTimeNs = getElapsedRealtimeNs();
     StatsdStats::getInstance().notePull(mTagId);
-    if (elapsedTimeNs - mLastPullTimeNs < mCoolDownNs) {
-        (*data) = mCachedData;
-        StatsdStats::getInstance().notePullFromCache(mTagId);
-        StatsdStats::getInstance().notePullDelay(mTagId, getElapsedRealtimeNs() - elapsedTimeNs);
-        return true;
+    const bool shouldUseCache = elapsedTimeNs - mLastPullTimeNs <
+                                StatsPullerManager::kAllPullAtomInfo.at(mTagId).coolDownNs;
+    if (shouldUseCache) {
+        if (mHasGoodData) {
+            (*data) = mCachedData;
+            StatsdStats::getInstance().notePullFromCache(mTagId);
+        }
+        return mHasGoodData;
     }
-    if (mMinPullIntervalNs > elapsedTimeNs - mLastPullTimeNs) {
-        mMinPullIntervalNs = elapsedTimeNs - mLastPullTimeNs;
-        StatsdStats::getInstance().updateMinPullIntervalSec(mTagId,
-                                                            mMinPullIntervalNs / NS_PER_SEC);
+
+    if (mLastPullTimeNs > 0) {
+        StatsdStats::getInstance().updateMinPullIntervalSec(
+                mTagId, (elapsedTimeNs - mLastPullTimeNs) / NS_PER_SEC);
     }
     mCachedData.clear();
     mLastPullTimeNs = elapsedTimeNs;
-    int64_t pullStartTimeNs = getElapsedRealtimeNs();
-    bool ret = PullInternal(&mCachedData);
-    if (!ret) {
-        mCachedData.clear();
-        return false;
+    mHasGoodData = PullInternal(&mCachedData);
+    if (!mHasGoodData) {
+        return mHasGoodData;
     }
-    StatsdStats::getInstance().notePullTime(mTagId, getElapsedRealtimeNs() - pullStartTimeNs);
-    for (const shared_ptr<LogEvent>& data : mCachedData) {
-        data->setElapsedTimestampNs(elapsedTimeNs);
-        data->setLogdWallClockTimestampNs(wallClockTimeNs);
+    const int64_t pullDurationNs = getElapsedRealtimeNs() - elapsedTimeNs;
+    StatsdStats::getInstance().notePullTime(mTagId, pullDurationNs);
+    const bool pullTimeOut =
+            pullDurationNs > StatsPullerManager::kAllPullAtomInfo.at(mTagId).pullTimeoutNs;
+    if (pullTimeOut) {
+        // Something went wrong. Discard the data.
+        clearCacheLocked();
+        mHasGoodData = false;
+        StatsdStats::getInstance().notePullTimeout(mTagId);
+        ALOGW("Pull for atom %d exceeds timeout %lld nano seconds.", mTagId,
+              (long long)pullDurationNs);
+        return mHasGoodData;
     }
 
     if (mCachedData.size() > 0) {
         mapAndMergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
-        (*data) = mCachedData;
     }
 
-    StatsdStats::getInstance().notePullDelay(mTagId, getElapsedRealtimeNs() - elapsedTimeNs);
-    return ret;
+    (*data) = mCachedData;
+    return mHasGoodData;
 }
 
 int StatsPuller::ForceClearCache() {
@@ -84,6 +88,10 @@
 
 int StatsPuller::clearCache() {
     lock_guard<std::mutex> lock(mLock);
+    return clearCacheLocked();
+}
+
+int StatsPuller::clearCacheLocked() {
     int ret = mCachedData.size();
     mCachedData.clear();
     mLastPullTimeNs = 0;
@@ -91,7 +99,8 @@
 }
 
 int StatsPuller::ClearCacheIfNecessary(int64_t timestampNs) {
-    if (timestampNs - mLastPullTimeNs > mCoolDownNs) {
+    if (timestampNs - mLastPullTimeNs >
+        StatsPullerManager::kAllPullAtomInfo.at(mTagId).coolDownNs) {
         return clearCache();
     } else {
         return 0;
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index cafd797..c83c4f8 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -18,7 +18,6 @@
 
 #include <android/os/IStatsCompanionService.h>
 #include <utils/RefBase.h>
-#include <utils/String16.h>
 #include <mutex>
 #include <vector>
 #include "packages/UidMap.h"
@@ -33,14 +32,20 @@
 
 class StatsPuller : public virtual RefBase {
 public:
-    StatsPuller(const int tagId);
+    explicit StatsPuller(const int tagId);
 
     virtual ~StatsPuller() {}
 
-    // Pulls the data. The returned data will have elapsedTimeNs set as timeNs
-    // and will have wallClockTimeNs set as current wall clock time.
-    // Return true if the pull is successful.
-    bool Pull(const int64_t timeNs, std::vector<std::shared_ptr<LogEvent>>* data);
+    // Pulls the most recent data.
+    // The data may be served from cache if consecutive pulls come within
+    // predefined cooldown time.
+    // Returns true if the pull was successful.
+    // Returns false when
+    //   1) the pull fails
+    //   2) pull takes longer than mPullTimeoutNs (intrinsic to puller)
+    // If a metric wants to make any change to the data, like timestamps, it
+    // should make a copy as this data may be shared with multiple metrics.
+    bool Pull(std::vector<std::shared_ptr<LogEvent>>* data);
 
     // Clear cache immediately
     int ForceClearCache();
@@ -53,29 +58,30 @@
     virtual void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService){};
 
 protected:
-    // The atom tag id this puller pulls
     const int mTagId;
 
 private:
     mutable std::mutex mLock;
-    // Minimum time before this puller does actual pull again.
-    // If a pull request comes before cooldown, a cached version from purevious pull
-    // will be returned.
-    // The actual value should be determined by individual pullers.
-    int64_t mCoolDownNs;
-    // For puller stats
-    int64_t mMinPullIntervalNs = LONG_MAX;
 
+    // Real puller impl.
     virtual bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) = 0;
 
-    // Cache of data from last pull. If next request comes before cool down finishes,
-    // cached data will be returned.
-    std::vector<std::shared_ptr<LogEvent>> mCachedData;
+    bool mHasGoodData = false;
 
     int64_t mLastPullTimeNs;
 
+    // Cache of data from last pull. If next request comes before cool down finishes,
+    // cached data will be returned.
+    // Cached data is cleared when
+    //   1) A pull fails
+    //   2) A new pull request comes after cooldown time.
+    //   3) clearCache is called.
+    std::vector<std::shared_ptr<LogEvent>> mCachedData;
+
     int clearCache();
 
+    int clearCacheLocked();
+
     static sp<UidMap> mUidMap;
 };
 
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index c070ca3..4a716cf 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -53,195 +53,172 @@
 const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
         // wifi_bytes_transfer
         {android::util::WIFI_BYTES_TRANSFER,
-         {{2, 3, 4, 5},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER)}},
+         {.additiveFields = {2, 3, 4, 5},
+          .puller = new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER)}},
         // wifi_bytes_transfer_by_fg_bg
         {android::util::WIFI_BYTES_TRANSFER_BY_FG_BG,
-         {{3, 4, 5, 6},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}},
+         {.additiveFields = {3, 4, 5, 6},
+          .puller = new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}},
         // mobile_bytes_transfer
         {android::util::MOBILE_BYTES_TRANSFER,
-         {{2, 3, 4, 5},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}},
+         {.additiveFields = {2, 3, 4, 5},
+          .puller = new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}},
         // mobile_bytes_transfer_by_fg_bg
         {android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
-         {{3, 4, 5, 6},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}},
+         {.additiveFields = {3, 4, 5, 6},
+          .puller =
+                  new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}},
         // bluetooth_bytes_transfer
         {android::util::BLUETOOTH_BYTES_TRANSFER,
-         {{2, 3},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}},
+         {.additiveFields = {2, 3},
+          .puller = new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}},
         // kernel_wakelock
         {android::util::KERNEL_WAKELOCK,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
+         {.puller = new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
         // subsystem_sleep_state
-        {android::util::SUBSYSTEM_SLEEP_STATE,
-         {{}, 1 * NS_PER_SEC, new SubsystemSleepStatePuller()}},
+        {android::util::SUBSYSTEM_SLEEP_STATE, {.puller = new SubsystemSleepStatePuller()}},
         // on_device_power_measurement
-        {android::util::ON_DEVICE_POWER_MEASUREMENT, {{}, 1 * NS_PER_SEC, new PowerStatsPuller()}},
+        {android::util::ON_DEVICE_POWER_MEASUREMENT, {.puller = new PowerStatsPuller()}},
         // cpu_time_per_freq
         {android::util::CPU_TIME_PER_FREQ,
-         {{3}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
+         {.additiveFields = {3},
+          .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
         // cpu_time_per_uid
         {android::util::CPU_TIME_PER_UID,
-         {{2, 3},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}},
+         {.additiveFields = {2, 3},
+          .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}},
         // cpu_time_per_uid_freq
         // the throttling is 3sec, handled in
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         {android::util::CPU_TIME_PER_UID_FREQ,
-         {{4},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}},
+         {.additiveFields = {4},
+          .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}},
         // cpu_active_time
         // the throttling is 3sec, handled in
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         {android::util::CPU_ACTIVE_TIME,
-         {{2}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
+         {.additiveFields = {2},
+          .puller = new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
         // cpu_cluster_time
         // the throttling is 3sec, handled in
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         {android::util::CPU_CLUSTER_TIME,
-         {{3}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
+         {.additiveFields = {3},
+          .puller = new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
         // wifi_activity_energy_info
         {android::util::WIFI_ACTIVITY_INFO,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
+         {.puller = new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
         // modem_activity_info
         {android::util::MODEM_ACTIVITY_INFO,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
+         {.puller = new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
         // bluetooth_activity_info
         {android::util::BLUETOOTH_ACTIVITY_INFO,
-         {{},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
+         {.puller = new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
         // system_elapsed_realtime
         {android::util::SYSTEM_ELAPSED_REALTIME,
-         {{},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
+         {.pullTimeoutNs = NS_PER_SEC / 2,
+          .coolDownNs = NS_PER_SEC,
+          .puller = new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
         // system_uptime
         {android::util::SYSTEM_UPTIME,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
+         {.puller = new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
         // remaining_battery_capacity
         {android::util::REMAINING_BATTERY_CAPACITY,
-         {{},
-          1 * NS_PER_SEC,
-          new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
+         {.puller = new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
         // full_battery_capacity
         {android::util::FULL_BATTERY_CAPACITY,
-         {{},
-          1 * NS_PER_SEC,
-          new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
+         {.puller = new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
         // battery_voltage
         {android::util::BATTERY_VOLTAGE,
-         {{}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
-        // battery_level
+         {.puller = new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
+        // battery_voltage
         {android::util::BATTERY_LEVEL,
-         {{}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_LEVEL)}},
+         {.puller = new ResourceHealthManagerPuller(android::util::BATTERY_LEVEL)}},
         // process_memory_state
         {android::util::PROCESS_MEMORY_STATE,
-         {{4, 5, 6, 7, 8, 9},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
+         {.additiveFields = {4, 5, 6, 7, 8, 9},
+          .puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
         // native_process_memory_state
         {android::util::NATIVE_PROCESS_MEMORY_STATE,
-         {{3, 4, 5, 6},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::NATIVE_PROCESS_MEMORY_STATE)}},
+         {.additiveFields = {3, 4, 5, 6},
+          .puller = new StatsCompanionServicePuller(android::util::NATIVE_PROCESS_MEMORY_STATE)}},
         {android::util::PROCESS_MEMORY_HIGH_WATER_MARK,
-         {{3},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
+         {.additiveFields = {3},
+          .puller =
+                  new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
         // temperature
-        {android::util::TEMPERATURE, {{}, 1 * NS_PER_SEC, new ResourceThermalManagerPuller()}},
+        {android::util::TEMPERATURE, {.puller = new ResourceThermalManagerPuller()}},
         // binder_calls
         {android::util::BINDER_CALLS,
-         {{4, 5, 6, 8, 12},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::BINDER_CALLS)}},
+         {.additiveFields = {4, 5, 6, 8, 12},
+          .puller = new StatsCompanionServicePuller(android::util::BINDER_CALLS)}},
         // binder_calls_exceptions
         {android::util::BINDER_CALLS_EXCEPTIONS,
-         {{},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}},
+         {.puller = new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}},
         // looper_stats
         {android::util::LOOPER_STATS,
-         {{5, 6, 7, 8, 9},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::LOOPER_STATS)}},
+         {.additiveFields = {5, 6, 7, 8, 9},
+          .puller = new StatsCompanionServicePuller(android::util::LOOPER_STATS)}},
         // Disk Stats
         {android::util::DISK_STATS,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_STATS)}},
+         {.puller = new StatsCompanionServicePuller(android::util::DISK_STATS)}},
         // Directory usage
         {android::util::DIRECTORY_USAGE,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
+         {.puller = new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
         // Size of app's code, data, and cache
         {android::util::APP_SIZE,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::APP_SIZE)}},
+         {.puller = new StatsCompanionServicePuller(android::util::APP_SIZE)}},
         // Size of specific categories of files. Eg. Music.
         {android::util::CATEGORY_SIZE,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
+         {.puller = new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
         // Number of fingerprints registered to each user.
         {android::util::NUM_FINGERPRINTS,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}},
+         {.puller = new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}},
         // ProcStats.
         {android::util::PROC_STATS,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS)}},
+         {.puller = new StatsCompanionServicePuller(android::util::PROC_STATS)}},
         // ProcStatsPkgProc.
         {android::util::PROC_STATS_PKG_PROC,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS_PKG_PROC)}},
+         {.puller = new StatsCompanionServicePuller(android::util::PROC_STATS_PKG_PROC)}},
         // Disk I/O stats per uid.
         {android::util::DISK_IO,
-         {{2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
-          3 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::DISK_IO)}},
+         {.additiveFields = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
+          .coolDownNs = 3 * NS_PER_SEC,
+          .puller = new StatsCompanionServicePuller(android::util::DISK_IO)}},
         // PowerProfile constants for power model calculations.
         {android::util::POWER_PROFILE,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::POWER_PROFILE)}},
+         {.puller = new StatsCompanionServicePuller(android::util::POWER_PROFILE)}},
         // Process cpu stats. Min cool-down is 5 sec, inline with what AcitivityManagerService uses.
         {android::util::PROCESS_CPU_TIME,
-         {{} /* additive fields */,
-          5 * NS_PER_SEC /* min cool-down in seconds*/,
-          new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}},
+         {.coolDownNs = 5 * NS_PER_SEC /* min cool-down in seconds*/,
+          .puller = new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}},
         {android::util::CPU_TIME_PER_THREAD_FREQ,
-         {{7, 9, 11, 13, 15, 17, 19, 21},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}},
+         {.additiveFields = {7, 9, 11, 13, 15, 17, 19, 21},
+          .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}},
         // DeviceCalculatedPowerUse.
         {android::util::DEVICE_CALCULATED_POWER_USE,
-         {{},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_USE)}},
+         {.puller = new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_USE)}},
         // DeviceCalculatedPowerBlameUid.
         {android::util::DEVICE_CALCULATED_POWER_BLAME_UID,
-         {{},  // BatteryStats already merged isolated with host ids so it's unnecessary here.
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_UID)}},
+         {.puller = new StatsCompanionServicePuller(
+                  android::util::DEVICE_CALCULATED_POWER_BLAME_UID)}},
         // DeviceCalculatedPowerBlameOther.
         {android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER,
-         {{},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}},
+         {.puller = new StatsCompanionServicePuller(
+                  android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}},
         // BuildInformation.
         {android::util::BUILD_INFORMATION,
-         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::BUILD_INFORMATION)}},
+         {.puller = new StatsCompanionServicePuller(android::util::BUILD_INFORMATION)}},
 };
 
 StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
 }
 
-bool StatsPullerManager::Pull(const int tagId, const int64_t timeNs,
-                                  vector<shared_ptr<LogEvent>>* data) {
+bool StatsPullerManager::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
     VLOG("Initiating pulling %d", tagId);
 
     if (kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end()) {
-        bool ret = kAllPullAtomInfo.find(tagId)->second.puller->Pull(timeNs, data);
+        bool ret = kAllPullAtomInfo.find(tagId)->second.puller->Pull(data);
         VLOG("pulled %d items", (int)data->size());
         return ret;
     } else {
@@ -333,8 +310,9 @@
     }
 }
 
-void StatsPullerManager::OnAlarmFired(const int64_t currentTimeNs) {
+void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) {
     AutoMutex _l(mLock);
+    int64_t wallClockNs = getWallClockNs();
 
     int64_t minNextPullTimeNs = NO_ALARM_UPDATE;
 
@@ -344,7 +322,7 @@
         vector<ReceiverInfo*> receivers = vector<ReceiverInfo*>();
         if (pair.second.size() != 0) {
             for (ReceiverInfo& receiverInfo : pair.second) {
-                if (receiverInfo.nextPullTimeNs <= currentTimeNs) {
+                if (receiverInfo.nextPullTimeNs <= elapsedTimeNs) {
                     receivers.push_back(&receiverInfo);
                 } else {
                     if (receiverInfo.nextPullTimeNs < minNextPullTimeNs) {
@@ -360,22 +338,38 @@
 
     for (const auto& pullInfo : needToPull) {
         vector<shared_ptr<LogEvent>> data;
-        if (Pull(pullInfo.first, currentTimeNs, &data)) {
-            for (const auto& receiverInfo : pullInfo.second) {
-                sp<PullDataReceiver> receiverPtr = receiverInfo->receiver.promote();
-                if (receiverPtr != nullptr) {
-                    receiverPtr->onDataPulled(data);
-                    // we may have just come out of a coma, compute next pull time
-                    receiverInfo->nextPullTimeNs =
-                            (currentTimeNs - receiverInfo->nextPullTimeNs) /
-                                receiverInfo->intervalNs * receiverInfo->intervalNs +
-                            receiverInfo->intervalNs + receiverInfo->nextPullTimeNs;
-                    if (receiverInfo->nextPullTimeNs < minNextPullTimeNs) {
-                        minNextPullTimeNs = receiverInfo->nextPullTimeNs;
-                    }
-                } else {
-                    VLOG("receiver already gone.");
+        if (!Pull(pullInfo.first, &data)) {
+            VLOG("pull failed at %lld, will try again later", (long long)elapsedTimeNs);
+            continue;
+        }
+        StatsdStats::getInstance().notePullDelay(pullInfo.first,
+                                                 getElapsedRealtimeNs() - elapsedTimeNs);
+
+        // Convention is to mark pull atom timestamp at request time.
+        // If we pull at t0, puller starts at t1, finishes at t2, and send back
+        // at t3, we mark t0 as its timestamp, which should correspond to its
+        // triggering event, such as condition change at t0.
+        // Here the triggering event is alarm fired from AlarmManager.
+        // In ValueMetricProducer and GaugeMetricProducer we do same thing
+        // when pull on condition change, etc.
+        for (auto& event : data) {
+            event->setElapsedTimestampNs(elapsedTimeNs);
+            event->setLogdWallClockTimestampNs(wallClockNs);
+        }
+
+        for (const auto& receiverInfo : pullInfo.second) {
+            sp<PullDataReceiver> receiverPtr = receiverInfo->receiver.promote();
+            if (receiverPtr != nullptr) {
+                receiverPtr->onDataPulled(data);
+                // we may have just come out of a coma, compute next pull time
+                int numBucketsAhead =
+                        (elapsedTimeNs - receiverInfo->nextPullTimeNs) / receiverInfo->intervalNs;
+                receiverInfo->nextPullTimeNs += (numBucketsAhead + 1) * receiverInfo->intervalNs;
+                if (receiverInfo->nextPullTimeNs < minNextPullTimeNs) {
+                    minNextPullTimeNs = receiverInfo->nextPullTimeNs;
                 }
+            } else {
+                VLOG("receiver already gone.");
             }
         }
     }
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 3350736..807e4af 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -26,6 +26,7 @@
 #include <vector>
 #include "PullDataReceiver.h"
 #include "StatsPuller.h"
+#include "guardrail/StatsdStats.h"
 #include "logd/LogEvent.h"
 
 namespace android {
@@ -36,11 +37,19 @@
     // The field numbers of the fields that need to be summed when merging
     // isolated uid with host uid.
     std::vector<int> additiveFields;
-    // How long should the puller wait before doing an actual pull again. Default
-    // 1 sec. Set this to 0 if this is handled elsewhere.
+    // Minimum time before this puller does actual pull again.
+    // Pullers can cause significant impact to system health and battery.
+    // So that we don't pull too frequently.
+    // If a pull request comes before cooldown, a cached version from previous pull
+    // will be returned.
     int64_t coolDownNs = 1 * NS_PER_SEC;
     // The actual puller
     sp<StatsPuller> puller;
+    // Max time allowed to pull this atom.
+    // We cannot reliably kill a pull thread. So we don't terminate the puller.
+    // The data is discarded if the pull takes longer than this and mHasGoodData
+    // marked as false.
+    int64_t pullTimeoutNs = StatsdStats::kPullMaxDelayNs;
 } PullAtomInfo;
 
 class StatsPullerManager : public virtual RefBase {
@@ -61,13 +70,18 @@
     // Verify if we know how to pull for this matcher
     bool PullerForMatcherExists(int tagId) const;
 
-    void OnAlarmFired(const int64_t timeNs);
+    void OnAlarmFired(int64_t elapsedTimeNs);
 
-    // Use respective puller to pull the data. The returned data will have
-    // elapsedTimeNs set as timeNs and will have wallClockTimeNs set as current
-    // wall clock time.
-    virtual bool Pull(const int tagId, const int64_t timeNs,
-                      vector<std::shared_ptr<LogEvent>>* data);
+    // Pulls the most recent data.
+    // The data may be served from cache if consecutive pulls come within
+    // mCoolDownNs.
+    // Returns true if the pull was successful.
+    // Returns false when
+    //   1) the pull fails
+    //   2) pull takes longer than mPullTimeoutNs (intrinsic to puller)
+    // If the metric wants to make any change to the data, like timestamps, they
+    // should make a copy as this data may be shared with multiple metrics.
+    virtual bool Pull(int tagId, vector<std::shared_ptr<LogEvent>>* data);
 
     // Clear pull data cache immediately.
     int ForceClearPullerCache();
diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
index c8c3920..d822959 100644
--- a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
+++ b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
@@ -281,7 +281,7 @@
                              (long long)state.residencyInMsecSinceBoot,
                              (long long)state.totalTransitions,
                              state.supportedOnlyInSuspend ? 1 : 0);
-                        for (auto voter : state.voters) {
+                        for (const auto& voter : state.voters) {
                             auto voterPtr = make_shared<LogEvent>(
                                 android::util::SUBSYSTEM_SLEEP_STATE,
                                 wallClockTimestampNs, elapsedTimestampNs);
diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.h b/cmds/statsd/src/external/SubsystemSleepStatePuller.h
index 17ce5b4..87f5f02 100644
--- a/cmds/statsd/src/external/SubsystemSleepStatePuller.h
+++ b/cmds/statsd/src/external/SubsystemSleepStatePuller.h
@@ -29,6 +29,8 @@
 class SubsystemSleepStatePuller : public StatsPuller {
 public:
     SubsystemSleepStatePuller();
+
+private:
     bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
 };
 
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 3e5e82f..f4d0144 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -373,6 +373,16 @@
     mPulledAtomStats[pullAtomId].dataError++;
 }
 
+void StatsdStats::notePullTimeout(int pullAtomId) {
+    lock_guard<std::mutex> lock(mLock);
+    mPulledAtomStats[pullAtomId].pullTimeout++;
+}
+
+void StatsdStats::notePullExceedMaxDelay(int pullAtomId) {
+    lock_guard<std::mutex> lock(mLock);
+    mPulledAtomStats[pullAtomId].pullExceedMaxDelay++;
+}
+
 void StatsdStats::noteAtomLogged(int atomId, int32_t timeSec) {
     lock_guard<std::mutex> lock(mLock);
 
@@ -429,6 +439,8 @@
         pullStats.second.maxPullDelayNs = 0;
         pullStats.second.numPullDelay = 0;
         pullStats.second.dataError = 0;
+        pullStats.second.pullTimeout = 0;
+        pullStats.second.pullExceedMaxDelay = 0;
     }
 }
 
@@ -535,13 +547,16 @@
     dprintf(out, "********Pulled Atom stats***********\n");
     for (const auto& pair : mPulledAtomStats) {
         dprintf(out,
-                "Atom %d->(total pull)%ld, (pull from cache)%ld, (min pull interval)%ld, (average "
-                "pull time nanos)%lld, (max pull time nanos)%lld, (average pull delay nanos)%lld, "
-                "(max pull delay nanos)%lld, (data error)%ld\n",
+                "Atom %d->(total pull)%ld, (pull from cache)%ld, (min pull interval)%ld \n"
+                "  (average pull time nanos)%lld, (max pull time nanos)%lld, (average pull delay "
+                "nanos)%lld, "
+                "  (max pull delay nanos)%lld, (data error)%ld\n"
+                "  (pull timeout)%ld, (pull exceed max delay)%ld\n",
                 (int)pair.first, (long)pair.second.totalPull, (long)pair.second.totalPullFromCache,
                 (long)pair.second.minPullIntervalSec, (long long)pair.second.avgPullTimeNs,
                 (long long)pair.second.maxPullTimeNs, (long long)pair.second.avgPullDelayNs,
-                (long long)pair.second.maxPullDelayNs, pair.second.dataError);
+                (long long)pair.second.maxPullDelayNs, pair.second.dataError,
+                pair.second.pullTimeout, pair.second.pullExceedMaxDelay);
     }
 
     if (mAnomalyAlarmRegisteredStats > 0) {
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 3157037..dc647f8 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -144,6 +144,8 @@
     // How long to try to clear puller cache from last time
     static const long kPullerCacheClearIntervalSec = 1;
 
+    // Max time to do a pull.
+    static const int64_t kPullMaxDelayNs = 10 * NS_PER_SEC;
     /**
      * Report a new config has been received and report the static stats about the config.
      *
@@ -296,6 +298,16 @@
     void notePullDelay(int pullAtomId, int64_t pullDelayNs);
 
     /*
+     * Records pull exceeds timeout for the puller.
+     */
+    void notePullTimeout(int pullAtomId);
+
+    /*
+     * Records pull exceeds max delay for a metric.
+     */
+    void notePullExceedMaxDelay(int pullAtomId);
+
+    /*
      * Records when system server restarts.
      */
     void noteSystemServerRestart(int32_t timeSec);
@@ -335,6 +347,8 @@
         int64_t maxPullDelayNs = 0;
         long numPullDelay = 0;
         long dataError = 0;
+        long pullTimeout = 0;
+        long pullExceedMaxDelay = 0;
     } PulledAtomStats;
 
 private:
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 8d61aba..2ff8aa1 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -41,6 +41,14 @@
     }
 }
 
+LogEvent::LogEvent(const LogEvent& event) {
+    mTagId = event.mTagId;
+    mLogUid = event.mLogUid;
+    mElapsedTimestampNs = event.mElapsedTimestampNs;
+    mLogdTimestampNs = event.mLogdTimestampNs;
+    mValues = event.mValues;
+}
+
 LogEvent::LogEvent(const StatsLogEventWrapper& statsLogEventWrapper, int workChainIndex) {
     mTagId = statsLogEventWrapper.getTagId();
     mLogdTimestampNs = statsLogEventWrapper.getWallClockTimeNs();
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 5408d17..43e6e4f 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -207,12 +207,15 @@
         return &mValues;
     }
 
+    inline LogEvent makeCopy() {
+        return LogEvent(*this);
+    }
+
 private:
     /**
-     * Don't copy, it's slower. If we really need this we can add it but let's try to
-     * avoid it.
+     * Only use this if copy is absolutely needed.
      */
-    explicit LogEvent(const LogEvent&);
+    LogEvent(const LogEvent&);
 
     /**
      * Parses a log_msg into a LogEvent object.
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 14f2de0..7cc57c1 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -49,7 +49,7 @@
 const int FIELD_ID_BUCKET_SIZE = 10;
 const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
 const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
-const int FIELD_ID_IS_ACTIVE = 13;
+const int FIELD_ID_IS_ACTIVE = 14;
 
 // for CountMetricDataWrapper
 const int FIELD_ID_DATA = 1;
@@ -154,7 +154,7 @@
         flushIfNeededLocked(dumpTimeNs);
     }
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+    protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
 
 
     if (mPastBuckets.empty()) {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 7797bd9..69bafc3 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -48,7 +48,7 @@
 const int FIELD_ID_BUCKET_SIZE = 10;
 const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
 const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
-const int FIELD_ID_IS_ACTIVE = 13;
+const int FIELD_ID_IS_ACTIVE = 14;
 // for DurationMetricDataWrapper
 const int FIELD_ID_DATA = 1;
 // for DurationMetricData
@@ -463,7 +463,7 @@
         flushIfNeededLocked(dumpTimeNs);
     }
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+    protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
 
     if (mPastBuckets.empty()) {
         VLOG(" Duration metric, empty return");
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 31a4361..c53c4ce 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -44,7 +44,7 @@
 // for StatsLogReport
 const int FIELD_ID_ID = 1;
 const int FIELD_ID_EVENT_METRICS = 4;
-const int FIELD_ID_IS_ACTIVE = 13;
+const int FIELD_ID_IS_ACTIVE = 14;
 // for EventMetricDataWrapper
 const int FIELD_ID_DATA = 1;
 // for EventMetricData
@@ -110,7 +110,7 @@
                                              std::set<string> *str_set,
                                              ProtoOutputStream* protoOutput) {
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+    protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
     if (mProto->size() <= 0) {
         return;
     }
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 03e42ce..67a1a47 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -49,7 +49,7 @@
 const int FIELD_ID_BUCKET_SIZE = 10;
 const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
 const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
-const int FIELD_ID_IS_ACTIVE = 13;
+const int FIELD_ID_IS_ACTIVE = 14;
 // for GaugeMetricDataWrapper
 const int FIELD_ID_DATA = 1;
 const int FIELD_ID_SKIPPED = 2;
@@ -68,15 +68,12 @@
 const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 7;
 const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 8;
 
-GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric,
-                                         const int conditionIndex,
-                                         const sp<ConditionWizard>& wizard,
-                                         const int whatMatcherIndex,
-                                         const sp<EventMatcherWizard>& matcherWizard,
-                                         const int pullTagId,
-                                         const int triggerAtomId, const int atomId,
-                                         const int64_t timeBaseNs, const int64_t startTimeNs,
-                                         const sp<StatsPullerManager>& pullerManager)
+GaugeMetricProducer::GaugeMetricProducer(
+        const ConfigKey& key, const GaugeMetric& metric, const int conditionIndex,
+        const sp<ConditionWizard>& wizard, const int whatMatcherIndex,
+        const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int triggerAtomId,
+        const int atomId, const int64_t timeBaseNs, const int64_t startTimeNs,
+        const sp<StatsPullerManager>& pullerManager)
     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
       mWhatMatcherIndex(whatMatcherIndex),
       mEventMatcherWizard(matcherWizard),
@@ -86,6 +83,8 @@
       mAtomId(atomId),
       mIsPulled(pullTagId != -1),
       mMinBucketSizeNs(metric.min_bucket_size_nanos()),
+      mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC
+                                                      : StatsdStats::kPullMaxDelayNs),
       mDimensionSoftLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
                                           StatsdStats::kAtomDimensionKeySizeLimitMap.end()
                                   ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).first
@@ -194,7 +193,7 @@
     }
 
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+    protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
 
     if (mPastBuckets.empty()) {
         return;
@@ -338,14 +337,24 @@
         return;
     }
     vector<std::shared_ptr<LogEvent>> allData;
-    if (!mPullerManager->Pull(mPullTagId, timestampNs, &allData)) {
+    if (!mPullerManager->Pull(mPullTagId, &allData)) {
         ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
         return;
     }
+    const int64_t pullDelayNs = getElapsedRealtimeNs() - timestampNs;
+    if (pullDelayNs > mMaxPullDelayNs) {
+        ALOGE("Pull finish too late for atom %d", mPullTagId);
+        StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
+        StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
+        return;
+    }
+    StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
     for (const auto& data : allData) {
-        if (mEventMatcherWizard->matchLogEvent(
-                *data, mWhatMatcherIndex) == MatchingState::kMatched) {
-            onMatchedLogEventLocked(mWhatMatcherIndex, *data);
+        LogEvent localCopy = data->makeCopy();
+        localCopy.setElapsedTimestampNs(timestampNs);
+        if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) ==
+            MatchingState::kMatched) {
+            onMatchedLogEventLocked(mWhatMatcherIndex, localCopy);
         }
     }
 }
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 6e3530b..a1a5061 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -160,6 +160,8 @@
 
     GaugeMetric::SamplingType mSamplingType;
 
+    const int64_t mMaxPullDelayNs;
+
     // apply a whitelist on the original input
     std::shared_ptr<vector<FieldValue>> getGaugeFields(const LogEvent& event);
 
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index ac34f47..dd969c0 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -412,7 +412,7 @@
 // Returns the total byte size of all metrics managed by a single config source.
 size_t MetricsManager::byteSize() {
     size_t totalSize = 0;
-    for (auto metricProducer : mAllMetricProducers) {
+    for (const auto& metricProducer : mAllMetricProducers) {
         totalSize += metricProducer->byteSize();
     }
     return totalSize;
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 1f22a6a..9a8e3bd 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -52,7 +52,7 @@
 const int FIELD_ID_BUCKET_SIZE = 10;
 const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
 const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
-const int FIELD_ID_IS_ACTIVE = 13;
+const int FIELD_ID_IS_ACTIVE = 14;
 // for ValueMetricDataWrapper
 const int FIELD_ID_DATA = 1;
 const int FIELD_ID_SKIPPED = 2;
@@ -103,7 +103,9 @@
       mValueDirection(metric.value_direction()),
       mSkipZeroDiffOutput(metric.skip_zero_diff_output()),
       mUseZeroDefaultBase(metric.use_zero_default_base()),
-      mHasGlobalBase(false) {
+      mHasGlobalBase(false),
+      mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC
+                                                      : StatsdStats::kPullMaxDelayNs) {
     int64_t bucketSizeMills = 0;
     if (metric.has_bucket()) {
         bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket());
@@ -192,7 +194,7 @@
         flushIfNeededLocked(dumpTimeNs);
     }
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+    protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
 
     if (mPastBuckets.empty() && mSkippedBuckets.empty()) {
         return;
@@ -340,19 +342,32 @@
 
 void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
     vector<std::shared_ptr<LogEvent>> allData;
-    if (mPullerManager->Pull(mPullTagId, timestampNs, &allData)) {
-        for (const auto& data : allData) {
-            if (mEventMatcherWizard->matchLogEvent(
-                *data, mWhatMatcherIndex) == MatchingState::kMatched) {
-                onMatchedLogEventLocked(mWhatMatcherIndex, *data);
-            }
-        }
-        mHasGlobalBase = true;
-    } else {
-        // for pulled data, every pull is needed. So we reset the base if any
-        // pull fails.
+    if (!mPullerManager->Pull(mPullTagId, &allData)) {
+        ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
         resetBase();
+        return;
     }
+    const int64_t pullDelayNs = getElapsedRealtimeNs() - timestampNs;
+    if (pullDelayNs > mMaxPullDelayNs) {
+        ALOGE("Pull finish too late for atom %d, longer than %lld", mPullTagId,
+              (long long)mMaxPullDelayNs);
+        StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
+        StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
+        resetBase();
+        return;
+    }
+    StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
+
+    for (const auto& data : allData) {
+        // make a copy before doing and changes
+        LogEvent localCopy = data->makeCopy();
+        localCopy.setElapsedTimestampNs(timestampNs);
+        if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) ==
+            MatchingState::kMatched) {
+            onMatchedLogEventLocked(mWhatMatcherIndex, localCopy);
+        }
+    }
+    mHasGlobalBase = true;
 }
 
 int64_t ValueMetricProducer::calcPreviousBucketEndTime(const int64_t currentTimeNs) {
@@ -381,10 +396,11 @@
             return;
         }
         for (const auto& data : allData) {
-            if (mEventMatcherWizard->matchLogEvent(*data, mWhatMatcherIndex) ==
+            LogEvent localCopy = data->makeCopy();
+            if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) ==
                 MatchingState::kMatched) {
-                data->setElapsedTimestampNs(bucketEndTime);
-                onMatchedLogEventLocked(mWhatMatcherIndex, *data);
+                localCopy.setElapsedTimestampNs(bucketEndTime);
+                onMatchedLogEventLocked(mWhatMatcherIndex, localCopy);
             }
         }
         mHasGlobalBase = true;
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 4991af4..4865aee 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -183,6 +183,8 @@
     // diff against.
     bool mHasGlobalBase;
 
+    const int64_t mMaxPullDelayNs;
+
     FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition);
     FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
     FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
@@ -207,6 +209,8 @@
     FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase);
     FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures);
     FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey);
+    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFail);
+    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index b317361..180a1ae 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -598,7 +598,7 @@
         }
         noReportMetricIds.insert(no_report_metric);
     }
-    for (auto it : allMetricProducers) {
+    for (const auto& it : allMetricProducers) {
         uidMap.addListener(it);
     }
     return true;
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 59f3f04..e9c43cd 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -152,7 +152,7 @@
     // listener removes itself before we call it. It's then the listener's job to handle it (expect
     // the callback to be called after listener is removed, and the listener should properly
     // ignore it).
-    for (auto weakPtr : broadcastList) {
+    for (const auto& weakPtr : broadcastList) {
         auto strongPtr = weakPtr.promote();
         if (strongPtr != NULL) {
             strongPtr->onUidMapReceived(timestamp);
@@ -200,7 +200,7 @@
         StatsdStats::getInstance().setUidMapChanges(mChanges.size());
     }
 
-    for (auto weakPtr : broadcastList) {
+    for (const auto& weakPtr : broadcastList) {
         auto strongPtr = weakPtr.promote();
         if (strongPtr != NULL) {
             strongPtr->notifyAppUpgrade(timestamp, appName, uid, versionCode);
@@ -269,7 +269,7 @@
         getListenerListCopyLocked(&broadcastList);
     }
 
-    for (auto weakPtr : broadcastList) {
+    for (const auto& weakPtr : broadcastList) {
         auto strongPtr = weakPtr.promote();
         if (strongPtr != NULL) {
             strongPtr->notifyAppRemoved(timestamp, app, uid);
diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp
index dffff7a..52d5ffc 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.cpp
+++ b/cmds/statsd/src/shell/ShellSubscriber.cpp
@@ -30,7 +30,8 @@
 
 const static int FIELD_ID_ATOM = 1;
 
-void ShellSubscriber::startNewSubscription(int in, int out, sp<IResultReceiver> resultReceiver) {
+void ShellSubscriber::startNewSubscription(int in, int out, sp<IResultReceiver> resultReceiver,
+                                           int timeoutSec) {
     VLOG("start new shell subscription");
     {
         std::lock_guard<std::mutex> lock(mMutex);
@@ -50,11 +51,18 @@
     // Read config forever until EOF is reached. Clients may send multiple configs -- each new
     // config replace the previous one.
     readConfig(in);
+    VLOG("timeout : %d", timeoutSec);
 
     // Now we have read an EOF we now wait for the semaphore until the client exits.
     VLOG("Now wait for client to exit");
     std::unique_lock<std::mutex> lk(mMutex);
-    mShellDied.wait(lk, [this, resultReceiver] { return mResultReceiver != resultReceiver; });
+
+    if (timeoutSec > 0) {
+        mShellDied.wait_for(lk, timeoutSec * 1s,
+                            [this, resultReceiver] { return mResultReceiver != resultReceiver; });
+    } else {
+        mShellDied.wait(lk, [this, resultReceiver] { return mResultReceiver != resultReceiver; });
+    }
 }
 
 void ShellSubscriber::updateConfig(const ShellSubscription& config) {
@@ -131,8 +139,7 @@
                     VLOG("pull atom %d now", pullInfo.mPullerMatcher.atom_id());
 
                     vector<std::shared_ptr<LogEvent>> data;
-                    mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), nowMillis * 1000000L,
-                                     &data);
+                    mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), &data);
                     VLOG("pulled %zu atoms", data.size());
                     if (data.size() > 0) {
                         writeToOutputLocked(data, pullInfo.mPullerMatcher);
diff --git a/cmds/statsd/src/shell/ShellSubscriber.h b/cmds/statsd/src/shell/ShellSubscriber.h
index 5401f31..8e54a8b 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.h
+++ b/cmds/statsd/src/shell/ShellSubscriber.h
@@ -65,7 +65,8 @@
     /**
      * Start a new subscription.
      */
-    void startNewSubscription(int inFd, int outFd, sp<IResultReceiver> resultReceiver);
+    void startNewSubscription(int inFd, int outFd, sp<IResultReceiver> resultReceiver,
+                              int timeoutSec);
 
     void binderDied(const wp<IBinder>& who);
 
diff --git a/cmds/statsd/src/socket/StatsSocketListener.h b/cmds/statsd/src/socket/StatsSocketListener.h
index 73e4d33..b8185d2 100644
--- a/cmds/statsd/src/socket/StatsSocketListener.h
+++ b/cmds/statsd/src/socket/StatsSocketListener.h
@@ -35,7 +35,7 @@
 
 class StatsSocketListener : public SocketListener, public virtual android::RefBase {
 public:
-    StatsSocketListener(const sp<LogListener>& listener);
+    explicit StatsSocketListener(const sp<LogListener>& listener);
 
     virtual ~StatsSocketListener();
 
@@ -51,4 +51,4 @@
 };
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index a6f27c8..e8de875 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -220,7 +220,9 @@
 
   optional DimensionsValue dimensions_path_in_condition = 12;
 
-  optional bool is_active = 13;
+  // DO NOT USE field 13.
+
+  optional bool is_active = 14;
 }
 
 message UidMapping {
@@ -399,6 +401,8 @@
         optional int64 average_pull_delay_nanos = 7;
         optional int64 max_pull_delay_nanos = 8;
         optional int64 data_error = 9;
+        optional int64 pull_timeout = 10;
+        optional int64 pull_exceed_max_delay = 11;
     }
     repeated PulledAtomStats pulled_atom_stats = 10;
 
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index f1310db..7de0bb3 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -64,6 +64,8 @@
 const int FIELD_ID_AVERAGE_PULL_DELAY_NANOS = 7;
 const int FIELD_ID_MAX_PULL_DELAY_NANOS = 8;
 const int FIELD_ID_DATA_ERROR = 9;
+const int FIELD_ID_PULL_TIMEOUT = 10;
+const int FIELD_ID_PULL_EXCEED_MAX_DELAY = 11;
 
 namespace {
 
@@ -450,6 +452,10 @@
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_PULL_DELAY_NANOS,
                        (long long)pair.second.maxPullDelayNs);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DATA_ERROR, (long long)pair.second.dataError);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_TIMEOUT,
+                       (long long)pair.second.pullTimeout);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_EXCEED_MAX_DELAY,
+                       (long long)pair.second.pullExceedMaxDelay);
     protoOutput->end(token);
 }
 
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index f485185..381ac32 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -240,7 +240,10 @@
   optional SamplingType sampling_type = 9 [default = RANDOM_ONE_SAMPLE] ;
 
   optional int64 min_bucket_size_nanos = 10;
+
   optional int64 max_num_gauge_atoms_per_bucket = 11 [default = 10];
+
+  optional int32 max_pull_delay_sec = 13 [default = 10];
 }
 
 message ValueMetric {
@@ -285,6 +288,8 @@
   optional ValueDirection value_direction = 13 [default = INCREASING];
 
   optional bool skip_zero_diff_output = 14 [default = true];
+
+  optional int32 max_pull_delay_sec = 16 [default = 10];
 }
 
 message Alert {
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 2f19a02..90f641a 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -188,7 +188,9 @@
     return false;
 }
 
-void StorageManager::appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto) {
+void StorageManager::appendConfigMetricsReport(const ConfigKey& key,
+                                               ProtoOutputStream* proto,
+                                               bool erasa_data) {
     unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
     if (dir == NULL) {
         VLOG("Path %s does not exist", STATS_DATA_DIR);
@@ -224,8 +226,9 @@
                 close(fd);
             }
 
-            // Remove file from disk after reading.
-            remove(file_name.c_str());
+            if (erasa_data) {
+                remove(file_name.c_str());
+            }
         }
     }
 }
diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h
index 8fbc89e..dcf3bb6 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -68,10 +68,12 @@
     static bool hasConfigMetricsReport(const ConfigKey& key);
 
     /**
-     * Appends ConfigMetricsReport found on disk to the specific proto and
-     * delete it.
+     * Appends the ConfigMetricsReport found on disk to the specifid proto
+     * and, if erase_data, deletes it from disk.
      */
-    static void appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto);
+    static void appendConfigMetricsReport(const ConfigKey& key,
+                                          ProtoOutputStream* proto,
+                                          bool erase_data);
 
     /**
      * Call to load the saved configs from disk.
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 237f8b9..d52be44 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -279,7 +279,10 @@
     // Dump report again. There should be no data since we erased it.
     processor->onDumpReport(cfgKey, 5, true, true /* DO erase data. */, ADB_DUMP, &bytes);
     output.ParseFromArray(bytes.data(), bytes.size());
-    bool noData = (output.reports_size() == 0) || (output.reports(0).metrics_size() == 0);
+    // We don't care whether statsd has a report, as long as it has no count metrics in it.
+    bool noData = output.reports_size() == 0
+            || output.reports(0).metrics_size() == 0
+            || output.reports(0).metrics(0).count_metrics().data_size() == 0;
     EXPECT_TRUE(noData);
 }
 
diff --git a/cmds/statsd/tests/StatsService_test.cpp b/cmds/statsd/tests/StatsService_test.cpp
index a7b4136..560fb9f 100644
--- a/cmds/statsd/tests/StatsService_test.cpp
+++ b/cmds/statsd/tests/StatsService_test.cpp
@@ -58,6 +58,45 @@
             service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
 }
 
+TEST(StatsServiceTest, TestGetUidFromArgs) {
+    Vector<String8> args;
+    args.push(String8("-1"));
+    args.push(String8("0"));
+    args.push(String8("1"));
+    args.push(String8("9999999999999999999999999999999999"));
+    args.push(String8("a1"));
+    args.push(String8(""));
+
+    int32_t uid;
+
+    StatsService service(nullptr);
+    service.mEngBuild = true;
+
+    // "-1"
+    EXPECT_FALSE(service.getUidFromArgs(args, 0, uid));
+
+    // "0"
+    EXPECT_TRUE(service.getUidFromArgs(args, 1, uid));
+    EXPECT_EQ(0, uid);
+
+    // "1"
+    EXPECT_TRUE(service.getUidFromArgs(args, 2, uid));
+    EXPECT_EQ(1, uid);
+
+    // "999999999999999999"
+    EXPECT_FALSE(service.getUidFromArgs(args, 3, uid));
+
+    // "a1"
+    EXPECT_FALSE(service.getUidFromArgs(args, 4, uid));
+
+    // ""
+    EXPECT_FALSE(service.getUidFromArgs(args, 5, uid));
+
+    // For a non-userdebug, uid "1" cannot be impersonated.
+    service.mEngBuild = false;
+    EXPECT_FALSE(service.getUidFromArgs(args, 2, uid));
+}
+
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index 79bed52..960fbda 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -71,12 +71,12 @@
                          const std::shared_ptr<DimToValMap>& currentBucket,
                          const std::set<const MetricDimensionKey>& trueList,
                          const std::set<const MetricDimensionKey>& falseList) {
-    for (MetricDimensionKey key : trueList) {
+    for (const MetricDimensionKey& key : trueList) {
         if (!tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) {
             return false;
         }
     }
-    for (MetricDimensionKey key : falseList) {
+    for (const MetricDimensionKey& key : falseList) {
         if (tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) {
             return false;
         }
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index 2d090e0..d5c358d 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -48,6 +48,7 @@
     *gaugeMetric->mutable_dimensions_in_what() =
         CreateDimensions(android::util::TEMPERATURE, {2/* sensor name field */ });
     gaugeMetric->set_bucket(FIVE_MINUTES);
+    gaugeMetric->set_max_pull_delay_sec(INT_MAX);
     config.set_hash_strings_in_metric_report(false);
 
     return config;
@@ -57,7 +58,7 @@
 
 TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) {
     auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE);
-    int64_t baseTimeNs = 10 * NS_PER_SEC;
+    int64_t baseTimeNs = getElapsedRealtimeNs();
     int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
     int64_t bucketSizeNs =
         TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
@@ -202,7 +203,7 @@
 
 TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents) {
     auto config = CreateStatsdConfig(GaugeMetric::CONDITION_CHANGE_TO_TRUE);
-    int64_t baseTimeNs = 10 * NS_PER_SEC;
+    int64_t baseTimeNs = getElapsedRealtimeNs();
     int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
     int64_t bucketSizeNs =
         TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
@@ -303,7 +304,7 @@
 
 TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm) {
     auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE);
-    int64_t baseTimeNs = 10 * NS_PER_SEC;
+    int64_t baseTimeNs = getElapsedRealtimeNs();
     int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
     int64_t bucketSizeNs =
         TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
index abf1ab1..cab6eac 100644
--- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -50,6 +50,7 @@
     valueMetric->set_bucket(FIVE_MINUTES);
     valueMetric->set_use_absolute_value_on_reset(true);
     valueMetric->set_skip_zero_diff_output(false);
+    valueMetric->set_max_pull_delay_sec(INT_MAX);
     return config;
 }
 
@@ -57,7 +58,7 @@
 
 TEST(ValueMetricE2eTest, TestPulledEvents) {
     auto config = CreateStatsdConfig();
-    int64_t baseTimeNs = 10 * NS_PER_SEC;
+    int64_t baseTimeNs = getElapsedRealtimeNs();
     int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
     int64_t bucketSizeNs =
         TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
@@ -163,7 +164,7 @@
 
 TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm) {
     auto config = CreateStatsdConfig();
-    int64_t baseTimeNs = 10 * NS_PER_SEC;
+    int64_t baseTimeNs = getElapsedRealtimeNs();
     int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
     int64_t bucketSizeNs =
         TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
diff --git a/cmds/statsd/tests/external/StatsPuller_test.cpp b/cmds/statsd/tests/external/StatsPuller_test.cpp
new file mode 100644
index 0000000..76e2097
--- /dev/null
+++ b/cmds/statsd/tests/external/StatsPuller_test.cpp
@@ -0,0 +1,227 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <chrono>
+#include <thread>
+#include <vector>
+#include "../metrics/metrics_test_helper.h"
+#include "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using namespace testing;
+using std::make_shared;
+using std::shared_ptr;
+using std::vector;
+using std::this_thread::sleep_for;
+using testing::Contains;
+
+// cooldown time 1sec.
+int pullTagId = 10014;
+
+bool pullSuccess;
+vector<std::shared_ptr<LogEvent>> pullData;
+long pullDelayNs;
+
+class FakePuller : public StatsPuller {
+public:
+    FakePuller() : StatsPuller(pullTagId){};
+
+private:
+    bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override {
+        (*data) = pullData;
+        sleep_for(std::chrono::nanoseconds(pullDelayNs));
+        return pullSuccess;
+    }
+};
+
+FakePuller puller;
+
+shared_ptr<LogEvent> createSimpleEvent(int64_t eventTimeNs, int64_t value) {
+    shared_ptr<LogEvent> event = make_shared<LogEvent>(pullTagId, eventTimeNs);
+    event->write(value);
+    event->init();
+    return event;
+}
+
+class StatsPullerTest : public ::testing::Test {
+public:
+    StatsPullerTest() {
+    }
+
+    void SetUp() override {
+        puller.ForceClearCache();
+        pullSuccess = false;
+        pullDelayNs = 0;
+        pullData.clear();
+    }
+};
+
+TEST_F(StatsPullerTest, PullSucces) {
+    pullData.push_back(createSimpleEvent(1111L, 33));
+
+    pullSuccess = true;
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_TRUE(puller.Pull(&dataHolder));
+    EXPECT_EQ(1, dataHolder.size());
+    EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+    EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
+    EXPECT_EQ(1, dataHolder[0]->size());
+    EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
+
+    sleep_for(std::chrono::seconds(1));
+
+    pullData.clear();
+    pullData.push_back(createSimpleEvent(2222L, 44));
+
+    pullSuccess = true;
+
+    EXPECT_TRUE(puller.Pull(&dataHolder));
+    EXPECT_EQ(1, dataHolder.size());
+    EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+    EXPECT_EQ(2222L, dataHolder[0]->GetElapsedTimestampNs());
+    EXPECT_EQ(1, dataHolder[0]->size());
+    EXPECT_EQ(44, dataHolder[0]->getValues()[0].mValue.int_value);
+}
+
+TEST_F(StatsPullerTest, PullFailAfterSuccess) {
+    pullData.push_back(createSimpleEvent(1111L, 33));
+
+    pullSuccess = true;
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_TRUE(puller.Pull(&dataHolder));
+    EXPECT_EQ(1, dataHolder.size());
+    EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+    EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
+    EXPECT_EQ(1, dataHolder[0]->size());
+    EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
+
+    sleep_for(std::chrono::seconds(1));
+
+    pullData.clear();
+    pullData.push_back(createSimpleEvent(2222L, 44));
+
+    pullSuccess = false;
+    dataHolder.clear();
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+
+    pullSuccess = true;
+    dataHolder.clear();
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+}
+
+// Test pull takes longer than timeout, 2nd pull happens shorter than cooldown
+TEST_F(StatsPullerTest, PullTakeTooLongAndPullFast) {
+    pullData.push_back(createSimpleEvent(1111L, 33));
+    pullSuccess = true;
+    // timeout is 0.5
+    pullDelayNs = (long)(0.8 * NS_PER_SEC);
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+
+    pullData.clear();
+    pullData.push_back(createSimpleEvent(2222L, 44));
+
+    pullSuccess = true;
+    dataHolder.clear();
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+}
+
+TEST_F(StatsPullerTest, PullFail) {
+    pullData.push_back(createSimpleEvent(1111L, 33));
+
+    pullSuccess = false;
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+}
+
+TEST_F(StatsPullerTest, PullTakeTooLong) {
+    pullData.push_back(createSimpleEvent(1111L, 33));
+
+    pullSuccess = true;
+    pullDelayNs = NS_PER_SEC;
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+}
+
+TEST_F(StatsPullerTest, PullTooFast) {
+    pullData.push_back(createSimpleEvent(1111L, 33));
+
+    pullSuccess = true;
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_TRUE(puller.Pull(&dataHolder));
+    EXPECT_EQ(1, dataHolder.size());
+    EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+    EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
+    EXPECT_EQ(1, dataHolder[0]->size());
+    EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
+
+    pullData.clear();
+    pullData.push_back(createSimpleEvent(2222L, 44));
+
+    pullSuccess = true;
+
+    dataHolder.clear();
+    EXPECT_TRUE(puller.Pull(&dataHolder));
+    EXPECT_EQ(1, dataHolder.size());
+    EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+    EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
+    EXPECT_EQ(1, dataHolder[0]->size());
+    EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
+}
+
+TEST_F(StatsPullerTest, PullFailsAndTooFast) {
+    pullData.push_back(createSimpleEvent(1111L, 33));
+
+    pullSuccess = false;
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+
+    pullData.clear();
+    pullData.push_back(createSimpleEvent(2222L, 44));
+
+    pullSuccess = true;
+
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 67a9f7f..2799107 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -90,6 +90,7 @@
     metric.set_id(metricId);
     metric.set_bucket(ONE_MINUTE);
     metric.mutable_gauge_fields_filter()->set_include_all(false);
+    metric.set_max_pull_delay_sec(INT_MAX);
     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
     gaugeFieldMatcher->set_field(tagId);
     gaugeFieldMatcher->add_child()->set_field(1);
@@ -106,9 +107,8 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
                 event->write(3);
@@ -266,6 +266,7 @@
     GaugeMetric metric;
     metric.set_id(metricId);
     metric.set_bucket(ONE_MINUTE);
+    metric.set_max_pull_delay_sec(INT_MAX);
     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
     gaugeFieldMatcher->set_field(tagId);
     gaugeFieldMatcher->add_child()->set_field(2);
@@ -281,10 +282,9 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
             .WillOnce(Return(false))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, eventUpgradeTimeNs);
                 event->write("some value");
@@ -341,6 +341,7 @@
     GaugeMetric metric;
     metric.set_id(metricId);
     metric.set_bucket(ONE_MINUTE);
+    metric.set_max_pull_delay_sec(INT_MAX);
     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
     gaugeFieldMatcher->set_field(tagId);
     gaugeFieldMatcher->add_child()->set_field(2);
@@ -357,9 +358,8 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
                 event->write("some value");
@@ -420,6 +420,7 @@
     metric.set_bucket(ONE_MINUTE);
     metric.mutable_gauge_fields_filter()->set_include_all(true);
     metric.set_condition(StringToId("APP_DIED"));
+    metric.set_max_pull_delay_sec(INT_MAX);
     auto dim = metric.mutable_dimensions_in_what();
     dim->set_field(tagId);
     dim->add_child()->set_field(1);
@@ -454,9 +455,8 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
                 event->write(1000);
@@ -502,11 +502,12 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(false));
+    EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false));
 
     GaugeMetric metric;
     metric.set_id(metricId);
     metric.set_bucket(ONE_MINUTE);
+    metric.set_max_pull_delay_sec(INT_MAX);
     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
     gaugeFieldMatcher->set_field(tagId);
     gaugeFieldMatcher->add_child()->set_field(2);
@@ -591,6 +592,7 @@
     metric.set_bucket(ONE_MINUTE);
     metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
     metric.mutable_gauge_fields_filter()->set_include_all(false);
+    metric.set_max_pull_delay_sec(INT_MAX);
     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
     gaugeFieldMatcher->set_field(tagId);
     gaugeFieldMatcher->add_child()->set_field(1);
@@ -604,9 +606,8 @@
         new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
                 event->write(4);
@@ -614,8 +615,7 @@
                 data->push_back(event);
                 return true;
             }))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
                 event->write(5);
@@ -664,6 +664,7 @@
     metric.set_bucket(ONE_MINUTE);
     metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
     metric.mutable_gauge_fields_filter()->set_include_all(true);
+    metric.set_max_pull_delay_sec(INT_MAX);
     auto dimensionMatcher = metric.mutable_dimensions_in_what();
     // use field 1 as dimension.
     dimensionMatcher->set_field(tagId);
@@ -678,9 +679,8 @@
         new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3);
                 event->write(3);
@@ -689,8 +689,7 @@
                 data->push_back(event);
                 return true;
             }))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
                 event->write(4);
@@ -699,8 +698,7 @@
                 data->push_back(event);
                 return true;
             }))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
                 event->write(4);
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 5524503..67570fc 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -125,6 +125,7 @@
     metric.set_bucket(ONE_MINUTE);
     metric.mutable_value_field()->set_field(tagId);
     metric.mutable_value_field()->add_child()->set_field(2);
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -136,9 +137,8 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
                 event->write(tagId);
@@ -218,6 +218,7 @@
     metric.set_bucket(ONE_MINUTE);
     metric.mutable_value_field()->set_field(tagId);
     metric.mutable_value_field()->add_child()->set_field(2);
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -232,9 +233,8 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
                 event->write(3);
@@ -315,6 +315,7 @@
     metric.mutable_value_field()->set_field(tagId);
     metric.mutable_value_field()->add_child()->set_field(2);
     metric.set_use_absolute_value_on_reset(true);
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -326,7 +327,7 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(true));
+    EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(true));
 
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       logEventMatcherIndex, eventMatcherWizard, tagId,
@@ -393,6 +394,7 @@
     metric.set_bucket(ONE_MINUTE);
     metric.mutable_value_field()->set_field(tagId);
     metric.mutable_value_field()->add_child()->set_field(2);
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -404,7 +406,7 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(false));
+    EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false));
 
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       logEventMatcherIndex, eventMatcherWizard, tagId,
@@ -469,6 +471,7 @@
     metric.mutable_value_field()->set_field(tagId);
     metric.mutable_value_field()->add_child()->set_field(2);
     metric.set_condition(StringToId("SCREEN_ON"));
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -481,9 +484,8 @@
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
 
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
                 event->write(tagId);
@@ -492,8 +494,7 @@
                 data->push_back(event);
                 return true;
             }))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
                 event->write(tagId);
@@ -599,6 +600,7 @@
     metric.set_bucket(ONE_MINUTE);
     metric.mutable_value_field()->set_field(tagId);
     metric.mutable_value_field()->add_child()->set_field(2);
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -610,10 +612,9 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
             .WillOnce(Return(true))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 149);
                 event->write(tagId);
@@ -661,6 +662,7 @@
     metric.mutable_value_field()->set_field(tagId);
     metric.mutable_value_field()->add_child()->set_field(2);
     metric.set_condition(StringToId("SCREEN_ON"));
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -672,9 +674,8 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
                 event->write(tagId);
@@ -683,8 +684,7 @@
                 data->push_back(event);
                 return true;
             }))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs - 100);
                 event->write(tagId);
@@ -924,6 +924,7 @@
     metric.set_bucket(ONE_MINUTE);
     metric.mutable_value_field()->set_field(tagId);
     metric.mutable_value_field()->add_child()->set_field(2);
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -935,7 +936,7 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(true));
+    EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(true));
 
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       logEventMatcherIndex, eventMatcherWizard, tagId,
@@ -1012,6 +1013,7 @@
     metric.mutable_value_field()->set_field(tagId);
     metric.mutable_value_field()->add_child()->set_field(2);
     metric.set_condition(StringToId("SCREEN_ON"));
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -1024,10 +1026,9 @@
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
 
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
             // condition becomes true
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
                 event->write(tagId);
@@ -1037,8 +1038,7 @@
                 return true;
             }))
             // condition becomes false
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
                 event->write(tagId);
@@ -1098,6 +1098,7 @@
     metric.mutable_value_field()->set_field(tagId);
     metric.mutable_value_field()->add_child()->set_field(2);
     metric.set_condition(StringToId("SCREEN_ON"));
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -1110,10 +1111,9 @@
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillRepeatedly(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
 
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
             // condition becomes true
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
                 event->write(tagId);
@@ -1123,8 +1123,7 @@
                 return true;
             }))
             // condition becomes false
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
                 event->write(tagId);
@@ -1134,8 +1133,7 @@
                 return true;
             }))
             // condition becomes true again
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 25);
                 event->write(tagId);
@@ -1480,6 +1478,7 @@
     metric.mutable_dimensions_in_what()->set_field(tagId);
     metric.mutable_dimensions_in_what()->add_child()->set_field(1);
     metric.set_use_zero_default_base(true);
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -1491,9 +1490,8 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
                 event->write(1);
@@ -1565,6 +1563,7 @@
     metric.mutable_dimensions_in_what()->set_field(tagId);
     metric.mutable_dimensions_in_what()->add_child()->set_field(1);
     metric.set_use_zero_default_base(true);
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -1576,9 +1575,8 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
                 event->write(1);
@@ -1692,6 +1690,7 @@
     metric.mutable_value_field()->add_child()->set_field(2);
     metric.mutable_dimensions_in_what()->set_field(tagId);
     metric.mutable_dimensions_in_what()->add_child()->set_field(1);
+    metric.set_max_pull_delay_sec(INT_MAX);
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -1703,9 +1702,8 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
                 event->write(1);
@@ -1804,6 +1802,128 @@
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
 }
 
+TEST(ValueMetricProducerTest, TestResetBaseOnPullFail) {
+    ValueMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.mutable_value_field()->set_field(tagId);
+    metric.mutable_value_field()->add_child()->set_field(2);
+    metric.set_condition(StringToId("SCREEN_ON"));
+    metric.set_max_pull_delay_sec(INT_MAX);
+
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard =
+            new EventMatcherWizard({new SimpleLogMatchingTracker(
+                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
+                event->write(tagId);
+                event->write(100);
+                event->init();
+                data->push_back(event);
+                return true;
+            }))
+            .WillOnce(Return(false));
+
+    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
+                                      eventMatcherWizard, tagId, bucketStartTimeNs,
+                                      bucketStartTimeNs, pullerManager);
+
+    valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
+
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval& curInterval =
+            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(100, curInterval.base.long_value);
+    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+    valueProducer.onConditionChanged(false, bucketStartTimeNs + 20);
+
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(false, curInterval.hasBase);
+    EXPECT_EQ(false, valueProducer.mHasGlobalBase);
+}
+
+TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) {
+    ValueMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.mutable_value_field()->set_field(tagId);
+    metric.mutable_value_field()->add_child()->set_field(2);
+    metric.set_condition(StringToId("SCREEN_ON"));
+    metric.set_max_pull_delay_sec(0);
+
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard =
+            new EventMatcherWizard({new SimpleLogMatchingTracker(
+                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+                event->write(tagId);
+                event->write(120);
+                event->init();
+                data->push_back(event);
+                return true;
+            }));
+
+    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
+                                      eventMatcherWizard, tagId, bucketStartTimeNs,
+                                      bucketStartTimeNs, pullerManager);
+
+    valueProducer.mCondition = true;
+    valueProducer.mHasGlobalBase = true;
+
+    vector<shared_ptr<LogEvent>> allData;
+    allData.clear();
+    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+    event->write(1);
+    event->write(110);
+    event->init();
+    allData.push_back(event);
+    valueProducer.onDataPulled(allData);
+
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval& curInterval =
+            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(110, curInterval.base.long_value);
+    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(true, valueProducer.mHasGlobalBase);
+
+    valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
+
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(false, curInterval.hasBase);
+    EXPECT_EQ(false, valueProducer.mHasGlobalBase);
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index 5afaba6..97c1072 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -38,8 +38,7 @@
     MOCK_METHOD4(RegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver,
                                         int64_t nextPulltimeNs, int64_t intervalNs));
     MOCK_METHOD2(UnRegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver));
-    MOCK_METHOD3(Pull, bool(const int pullCode, const int64_t timeNs,
-                            vector<std::shared_ptr<LogEvent>>* data));
+    MOCK_METHOD2(Pull, bool(const int pullCode, vector<std::shared_ptr<LogEvent>>* data));
 };
 
 class MockUidMap : public UidMap {
diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
index dd00561..73d1fd7 100644
--- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
+++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
@@ -83,7 +83,7 @@
 
     // mimic a binder thread that a shell subscriber runs on. it would block.
     std::thread reader([&resultReceiver, &fds_config, &fds_data, &shellClient] {
-        shellClient->startNewSubscription(fds_config[0], fds_data[1], resultReceiver);
+        shellClient->startNewSubscription(fds_config[0], fds_data[1], resultReceiver, -1);
     });
     reader.detach();
 
@@ -189,23 +189,22 @@
     sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(10016, _, _))
-            .WillRepeatedly(
-                    Invoke([](int tagId, int64_t timeNs, vector<std::shared_ptr<LogEvent>>* data) {
-                        data->clear();
-                        shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, timeNs);
-                        event->write(kUid1);
-                        event->write(kCpuTime1);
-                        event->init();
-                        data->push_back(event);
-                        // another event
-                        event = make_shared<LogEvent>(tagId, timeNs);
-                        event->write(kUid2);
-                        event->write(kCpuTime2);
-                        event->init();
-                        data->push_back(event);
-                        return true;
-                    }));
+    EXPECT_CALL(*pullerManager, Pull(10016, _))
+            .WillRepeatedly(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, 1111L);
+                event->write(kUid1);
+                event->write(kCpuTime1);
+                event->init();
+                data->push_back(event);
+                // another event
+                event = make_shared<LogEvent>(tagId, 1111L);
+                event->write(kUid2);
+                event->write(kCpuTime2);
+                event->init();
+                data->push_back(event);
+                return true;
+            }));
 
     runShellTest(getPulledConfig(), uidMap, pullerManager, vector<std::shared_ptr<LogEvent>>(),
                  getExpectedShellData());
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
index 8464b8df..91d6490 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
@@ -62,7 +62,7 @@
         if (process.waitFor() == 0) {
             logger.fine("Adb command successful.");
         } else {
-            logger.severe("Abnormal adb shell cmd termination for: " + String.join(",", commands));
+            logger.severe("Abnormal adb shell termination for: " + String.join(",", commands));
             throw new RuntimeException("Error running adb command: " + err.toString());
         }
     }
@@ -118,6 +118,52 @@
         logger.addHandler(handler);
     }
 
+    /**
+     * Attempt to determine whether tool will work with this statsd, i.e. whether statsd is
+     * minCodename or higher.
+     * Algorithm: true if (sdk >= minSdk) || (sdk == minSdk-1 && codeName.startsWith(minCodeName))
+     * If all else fails, assume it will work (letting future commands deal with any errors).
+     */
+    public static boolean isAcceptableStatsd(Logger logger, int minSdk, String minCodename) {
+        BufferedReader in = null;
+        try {
+            File outFileSdk = File.createTempFile("shelltools_sdk", "tmp");
+            outFileSdk.deleteOnExit();
+            runCommand(outFileSdk, logger,
+                    "adb", "shell", "getprop", "ro.build.version.sdk");
+            in = new BufferedReader(new InputStreamReader(new FileInputStream(outFileSdk)));
+            // If NullPointerException/NumberFormatException/etc., just catch and return true.
+            int sdk = Integer.parseInt(in.readLine().trim());
+            if (sdk >= minSdk) {
+                return true;
+            } else if (sdk == minSdk - 1) { // Could be minSdk-1, or could be minSdk development.
+                in.close();
+                File outFileCode = File.createTempFile("shelltools_codename", "tmp");
+                outFileCode.deleteOnExit();
+                runCommand(outFileCode, logger,
+                        "adb", "shell", "getprop", "ro.build.version.codename");
+                in = new BufferedReader(new InputStreamReader(new FileInputStream(outFileCode)));
+                return in.readLine().startsWith(minCodename);
+            } else {
+                return false;
+            }
+        } catch (Exception e) {
+            logger.fine("Could not determine whether statsd version is compatibile "
+                    + "with tool: " + e.toString());
+        } finally {
+            try {
+                if (in != null) {
+                    in.close();
+                }
+            } catch (IOException e) {
+                logger.fine("Could not close temporary file: " + e.toString());
+            }
+        }
+        // Could not determine whether statsd is acceptable version.
+        // Just assume it is; if it isn't, we'll just get future errors via adb and deal with them.
+        return true;
+    }
+
     public static class LocalToolsFormatter extends Formatter {
         public String format(LogRecord record) {
             return record.getMessage() + "\n";
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
index 67fb906..2eb4660 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
@@ -37,6 +37,9 @@
 public class LocalDrive {
     private static final boolean DEBUG = false;
 
+    public static final int MIN_SDK = 29;
+    public static final String MIN_CODENAME = "Q";
+
     public static final long DEFAULT_CONFIG_ID = 56789;
 
     public static final String BINARY_FLAG = "--binary";
@@ -46,7 +49,7 @@
     public static final String HELP_STRING =
         "Usage:\n\n" +
 
-        "statsd_local upload CONFIG_FILE [CONFIG_ID] [--binary]\n" +
+        "statsd_localdrive upload CONFIG_FILE [CONFIG_ID] [--binary]\n" +
         "  Uploads the given statsd config file (in binary or human-readable-text format).\n" +
         "  If a config with this id already exists, removes it first.\n" +
         "    CONFIG_FILE    Location of config file on host.\n" +
@@ -56,12 +59,12 @@
         // Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID
         "\n" +
 
-        "statsd_local update CONFIG_FILE [CONFIG_ID] [--binary]\n" +
+        "statsd_localdrive update CONFIG_FILE [CONFIG_ID] [--binary]\n" +
         "  Same as upload, but does not remove the old config first (if it already exists).\n" +
         // Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID
         "\n" +
 
-        "statsd_local get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]\n" +
+        "statsd_localdrive get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]\n" +
         "  Prints the output statslog data (in binary or human-readable-text format).\n" +
         "    CONFIG_ID      Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
         "    --binary       Output should be in binary, instead of default human-readable text.\n" +
@@ -72,13 +75,13 @@
         //                                                      --include_current_bucket --proto
         "\n" +
 
-        "statsd_local remove [CONFIG_ID]\n" +
+        "statsd_localdrive remove [CONFIG_ID]\n" +
         "  Removes the config.\n" +
         "    CONFIG_ID      Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
         // Equivalent to: adb shell cmd stats config remove SHELL_UID CONFIG_ID
         "\n" +
 
-        "statsd_local clear [CONFIG_ID]\n" +
+        "statsd_localdrive clear [CONFIG_ID]\n" +
         "  Clears the data associated with the config.\n" +
         "    CONFIG_ID      Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
         // Similar to: adb shell cmd stats dump-report SHELL_UID CONFIG_ID
@@ -92,6 +95,12 @@
     public static void main(String[] args) {
         Utils.setUpLogger(sLogger, DEBUG);
 
+        if (!Utils.isAcceptableStatsd(sLogger, MIN_SDK, MIN_CODENAME)) {
+            sLogger.severe("LocalDrive only works with statsd versions for Android "
+                    + MIN_CODENAME + " or higher.");
+            return;
+        }
+
         if (args.length > 0) {
             switch (args[0]) {
                 case "clear":
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
index e3fe928..de818a8 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
@@ -16,207 +16,194 @@
 package com.android.statsd.shelltools.testdrive;
 
 import com.android.internal.os.StatsdConfigProto.AtomMatcher;
+import com.android.internal.os.StatsdConfigProto.EventMetric;
+import com.android.internal.os.StatsdConfigProto.FieldFilter;
+import com.android.internal.os.StatsdConfigProto.GaugeMetric;
 import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
 import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.internal.os.StatsdConfigProto.TimeUnit;
 import com.android.os.AtomsProto.Atom;
 import com.android.os.StatsLog.ConfigMetricsReport;
 import com.android.os.StatsLog.ConfigMetricsReportList;
+import com.android.os.StatsLog.StatsLogReport;
 import com.android.statsd.shelltools.Utils;
 
 import com.google.common.io.Files;
-import com.google.protobuf.TextFormat;
-import com.google.protobuf.TextFormat.ParseException;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 public class TestDrive {
 
-    public static final int PULL_ATOM_START = 10000;
-    public static final long ATOM_MATCHER_ID = 1234567;
+    private static final int METRIC_ID_BASE = 1111;
+    private static final long ATOM_MATCHER_ID_BASE = 1234567;
+    private static final int PULL_ATOM_START = 10000;
+    private static final long CONFIG_ID = 54321;
+    private static final String[] ALLOWED_LOG_SOURCES = {
+        "AID_GRAPHICS",
+        "AID_INCIDENTD",
+        "AID_STATSD",
+        "AID_RADIO",
+        "com.android.systemui",
+        "com.android.vending",
+        "AID_SYSTEM",
+        "AID_ROOT",
+        "AID_BLUETOOTH",
+        "AID_LMKD",
+        "com.android.managedprovisioning"
+    };
+    private static final Logger LOGGER = Logger.getLogger(TestDrive.class.getName());
 
-    public static final long CONFIG_ID = 54321;
-
-    private static boolean mIsPushedAtom = false;
-
-    private static final Logger logger = Logger.getLogger(TestDrive.class.getName());
+    private final Set<Long> mTrackedMetrics = new HashSet<>();
 
     public static void main(String[] args) {
         TestDrive testDrive = new TestDrive();
-        Utils.setUpLogger(logger, false);
+        Set<Integer> trackedAtoms = new HashSet<>();
+        Utils.setUpLogger(LOGGER, false);
+        String remoteConfigPath = null;
 
-        if (args.length != 1) {
-            logger.log(Level.SEVERE, "Usage: ./test_drive <atomId>");
+        if (args.length < 1) {
+            LOGGER.log(Level.SEVERE, "Usage: ./test_drive <atomId1> <atomId2> ... <atomIdN>");
             return;
         }
-        int atomId;
-        try {
-            atomId = Integer.valueOf(args[0]);
-        } catch (NumberFormatException e) {
-            logger.log(Level.SEVERE, "Bad atom id provided: " + args[0]);
-            return;
-        }
-        if (Atom.getDescriptor().findFieldByNumber(atomId) == null) {
-            logger.log(Level.SEVERE, "No such atom found: " + args[0]);
-            return;
-        }
-        mIsPushedAtom = atomId < PULL_ATOM_START;
 
+        for (int i = 0; i < args.length; i++) {
+            try {
+                int atomId = Integer.valueOf(args[i]);
+                if (Atom.getDescriptor().findFieldByNumber(atomId) == null) {
+                    LOGGER.log(Level.SEVERE, "No such atom found: " + args[i]);
+                    continue;
+                }
+                trackedAtoms.add(atomId);
+            } catch (NumberFormatException e) {
+                LOGGER.log(Level.SEVERE, "Bad atom id provided: " + args[i]);
+                continue;
+            }
+        }
 
         try {
-            StatsdConfig config = testDrive.createConfig(atomId);
+            StatsdConfig config = testDrive.createConfig(trackedAtoms);
             if (config == null) {
-                logger.log(Level.SEVERE, "Failed to create valid config.");
+                LOGGER.log(Level.SEVERE, "Failed to create valid config.");
                 return;
             }
-            testDrive.pushConfig(config);
-            logger.info("Pushed the following config to statsd:");
-            logger.info(config.toString());
-            if (mIsPushedAtom) {
-                logger.info(
+            remoteConfigPath = testDrive.pushConfig(config);
+            LOGGER.info("Pushed the following config to statsd:");
+            LOGGER.info(config.toString());
+            if (!hasPulledAtom(trackedAtoms)) {
+                LOGGER.info(
                         "Now please play with the device to trigger the event. All events should "
                                 + "be dumped after 1 min ...");
                 Thread.sleep(60_000);
             } else {
                 // wait for 2 min
-                logger.info("Now wait for 2 minutes ...");
+                LOGGER.info("Now wait for 2 minutes ...");
                 Thread.sleep(120_000);
             }
             testDrive.dumpMetrics();
         } catch (Exception e) {
-            logger.log(Level.SEVERE, "Failed to test drive: " + e.getMessage());
+            LOGGER.log(Level.SEVERE, "Failed to test drive: " + e.getMessage(), e);
         } finally {
             testDrive.removeConfig();
-        }
-    }
-
-    private void pushConfig(StatsdConfig config) throws IOException, InterruptedException {
-        File configFile = File.createTempFile("statsdconfig", ".config");
-        configFile.deleteOnExit();
-        Files.write(config.toByteArray(), configFile);
-        String remotePath = "/data/local/tmp/" + configFile.getName();
-        Utils.runCommand(null, logger, "adb", "push", configFile.getAbsolutePath(), remotePath);
-        Utils.runCommand(null, logger,
-                "adb", "shell", "cat", remotePath, "|", Utils.CMD_UPDATE_CONFIG,
-                String.valueOf(CONFIG_ID));
-    }
-
-    private void removeConfig() {
-        try {
-            Utils.runCommand(null, logger, 
-                    "adb", "shell", Utils.CMD_REMOVE_CONFIG, String.valueOf(CONFIG_ID));
-        } catch (Exception e) {
-            logger.log(Level.SEVERE, "Failed to remove config: " + e.getMessage());
-        }
-    }
-
-    private StatsdConfig createConfig(int atomId) {
-        try {
-            if (mIsPushedAtom) {
-                return createSimpleEventMetricConfig(atomId);
-            } else {
-                return createSimpleGaugeMetricConfig(atomId);
+            if (remoteConfigPath != null) {
+                try {
+                    Utils.runCommand(null, LOGGER, "adb", "shell", "rm", remoteConfigPath);
+                } catch (Exception e) {
+                    LOGGER.log(Level.WARNING,
+                            "Unable to remove remote config file: " + remoteConfigPath, e);
+                }
             }
-        } catch (ParseException e) {
-            logger.log(
-                    Level.SEVERE,
-                    "Failed to parse the config! line: "
-                            + e.getLine()
-                            + " col: "
-                            + e.getColumn()
-                            + " "
-                            + e.getMessage());
         }
-        return null;
     }
 
-    private StatsdConfig createSimpleEventMetricConfig(int atomId) throws ParseException {
-        StatsdConfig.Builder baseBuilder = getSimpleEventMetricBaseConfig();
-        baseBuilder.addAtomMatcher(createAtomMatcher(atomId));
-        return baseBuilder.build();
+    private void dumpMetrics() throws Exception {
+        ConfigMetricsReportList reportList = Utils.getReportList(CONFIG_ID, true, false, LOGGER);
+        // We may get multiple reports. Take the last one.
+        ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1);
+        for (StatsLogReport statsLog : report.getMetricsList()) {
+            if (mTrackedMetrics.contains(statsLog.getMetricId())) {
+                LOGGER.info(statsLog.toString());
+            }
+        }
     }
 
-    private StatsdConfig createSimpleGaugeMetricConfig(int atomId) throws ParseException {
-        StatsdConfig.Builder baseBuilder = getSimpleGaugeMetricBaseConfig();
-        baseBuilder.addAtomMatcher(createAtomMatcher(atomId));
-        return baseBuilder.build();
+    private StatsdConfig createConfig(Set<Integer> atomIds) {
+        long metricId = METRIC_ID_BASE;
+        long atomMatcherId = ATOM_MATCHER_ID_BASE;
+
+        StatsdConfig.Builder builder = StatsdConfig.newBuilder();
+        builder
+            .addAllAllowedLogSource(Arrays.asList(ALLOWED_LOG_SOURCES))
+            .setHashStringsInMetricReport(false);
+
+        for (int atomId : atomIds) {
+            if (isPulledAtom(atomId)) {
+                builder.addAtomMatcher(createAtomMatcher(atomId, atomMatcherId));
+                GaugeMetric.Builder gaugeMetricBuilder = GaugeMetric.newBuilder();
+                gaugeMetricBuilder
+                    .setId(metricId)
+                    .setWhat(atomMatcherId)
+                    .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true).build())
+                    .setBucket(TimeUnit.ONE_MINUTE);
+                builder.addGaugeMetric(gaugeMetricBuilder.build());
+            } else {
+                EventMetric.Builder eventMetricBuilder = EventMetric.newBuilder();
+                eventMetricBuilder
+                    .setId(metricId)
+                    .setWhat(atomMatcherId);
+                builder.addEventMetric(eventMetricBuilder.build());
+                builder.addAtomMatcher(createAtomMatcher(atomId, atomMatcherId));
+            }
+            atomMatcherId++;
+            mTrackedMetrics.add(metricId++);
+        }
+        return builder.build();
     }
 
-    private AtomMatcher createAtomMatcher(int atomId) {
+    private static AtomMatcher createAtomMatcher(int atomId, long matcherId) {
         AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder();
         atomMatcherBuilder
-                .setId(ATOM_MATCHER_ID)
+                .setId(matcherId)
                 .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder().setAtomId(atomId));
         return atomMatcherBuilder.build();
     }
 
-    private StatsdConfig.Builder getSimpleEventMetricBaseConfig() throws ParseException {
-        StatsdConfig.Builder builder = StatsdConfig.newBuilder();
-        TextFormat.merge(EVENT_BASE_CONFIG_SRTR, builder);
-        return builder;
+    private static String pushConfig(StatsdConfig config) throws IOException, InterruptedException {
+        File configFile = File.createTempFile("statsdconfig", ".config");
+        configFile.deleteOnExit();
+        Files.write(config.toByteArray(), configFile);
+        String remotePath = "/data/local/tmp/" + configFile.getName();
+        Utils.runCommand(null, LOGGER, "adb", "push", configFile.getAbsolutePath(), remotePath);
+        Utils.runCommand(null, LOGGER,
+                "adb", "shell", "cat", remotePath, "|", Utils.CMD_UPDATE_CONFIG,
+                String.valueOf(CONFIG_ID));
+        return remotePath;
     }
 
-    private StatsdConfig.Builder getSimpleGaugeMetricBaseConfig() throws ParseException {
-        StatsdConfig.Builder builder = StatsdConfig.newBuilder();
-        TextFormat.merge(GAUGE_BASE_CONFIG_STR, builder);
-        return builder;
-    }
-
-    private void dumpMetrics() throws Exception {
-        ConfigMetricsReportList reportList = Utils.getReportList(CONFIG_ID, true, false, logger);
-        // We may get multiple reports. Take the last one.
-        ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1);
-        // Really should be only one metric.
-        if (report.getMetricsCount() != 1) {
-            logger.log(Level.SEVERE,
-                    "Only one report metric expected, got " + report.getMetricsCount());
-            return;
+    private static void removeConfig() {
+        try {
+            Utils.runCommand(null, LOGGER,
+                    "adb", "shell", Utils.CMD_REMOVE_CONFIG, String.valueOf(CONFIG_ID));
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, "Failed to remove config: " + e.getMessage());
         }
-
-        logger.info("Got following metric data dump:");
-        logger.info(report.getMetrics(0).toString());
     }
 
-    private static final String EVENT_BASE_CONFIG_SRTR =
-            "id: 12345\n"
-                    + "event_metric {\n"
-                    + "  id: 1111\n"
-                    + "  what: 1234567\n"
-                    + "}\n"
-                    + "allowed_log_source: \"AID_GRAPHICS\"\n"
-                    + "allowed_log_source: \"AID_INCIDENTD\"\n"
-                    + "allowed_log_source: \"AID_STATSD\"\n"
-                    + "allowed_log_source: \"AID_RADIO\"\n"
-                    + "allowed_log_source: \"com.android.systemui\"\n"
-                    + "allowed_log_source: \"com.android.vending\"\n"
-                    + "allowed_log_source: \"AID_SYSTEM\"\n"
-                    + "allowed_log_source: \"AID_ROOT\"\n"
-                    + "allowed_log_source: \"AID_BLUETOOTH\"\n"
-                    + "\n"
-                    + "hash_strings_in_metric_report: false";
+    private static boolean isPulledAtom(int atomId) {
+        return atomId >= PULL_ATOM_START;
+    }
 
-    private static final String GAUGE_BASE_CONFIG_STR =
-            "id: 56789\n"
-                    + "gauge_metric {\n"
-                    + "  id: 2222\n"
-                    + "  what: 1234567\n"
-                    + "  gauge_fields_filter {\n"
-                    + "    include_all: true\n"
-                    + "  }\n"
-                    + "  bucket: ONE_MINUTE\n"
-                    + "}\n"
-                    + "allowed_log_source: \"AID_GRAPHICS\"\n"
-                    + "allowed_log_source: \"AID_INCIDENTD\"\n"
-                    + "allowed_log_source: \"AID_STATSD\"\n"
-                    + "allowed_log_source: \"AID_RADIO\"\n"
-                    + "allowed_log_source: \"com.android.systemui\"\n"
-                    + "allowed_log_source: \"com.android.vending\"\n"
-                    + "allowed_log_source: \"AID_SYSTEM\"\n"
-                    + "allowed_log_source: \"AID_ROOT\"\n"
-                    + "allowed_log_source: \"AID_BLUETOOTH\"\n"
-                    + "\n"
-                    + "hash_strings_in_metric_report: false";
-
+    private static boolean hasPulledAtom(Set<Integer> atoms) {
+        for (Integer i : atoms) {
+            if (isPulledAtom(i)) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index a39f5e3..4174ad7 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -51,6 +51,8 @@
     private static final String COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP =
             "add-or-remove-call-companion-app";
     private static final String COMMAND_SET_TEST_AUTO_MODE_APP = "set-test-auto-mode-app";
+    private static final String COMMAND_SET_PHONE_ACCOUNT_SUGGESTION_COMPONENT =
+            "set-phone-acct-suggestion-component";
     private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account";
     private static final String COMMAND_SET_DEFAULT_DIALER = "set-default-dialer";
     private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer";
@@ -64,36 +66,37 @@
 
     @Override
     public void onShowUsage(PrintStream out) {
-        out.println(
-                "usage: telecom [subcommand] [options]\n" +
-                "usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n" +
-                "usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n" +
-                "usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n" +
-                "usage: telecom set-test-call-redirection-app <PACKAGE>\n" +
-                "usage: telecom set-test-call-screening-app <PACKAGE>\n" +
-                "usage: telecom set-test-auto-mode-app <PACKAGE>\n" +
-                "usage: telecom add-or-remove-call-companion-app <PACKAGE> <1/0>\n" +
-                "usage: telecom register-sim-phone-account <COMPONENT> <ID> <USER_SN> <LABEL> <ADDRESS>\n" +
-                "usage: telecom unregister-phone-account <COMPONENT> <ID> <USER_SN>\n" +
-                "usage: telecom set-default-dialer <PACKAGE>\n" +
-                "usage: telecom get-default-dialer\n" +
-                "usage: telecom get-system-dialer\n" +
-                "usage: telecom wait-on-handlers\n" +
-                "\n" +
-                "telecom set-phone-account-enabled: Enables the given phone account, if it has \n" +
-                " already been registered with Telecom.\n" +
-                "\n" +
-                "telecom set-phone-account-disabled: Disables the given phone account, if it \n" +
-                " has already been registered with telecom.\n" +
-                "\n" +
-                "telecom set-default-dialer: Sets the default dialer to the given component. \n" +
-                "\n" +
-                "telecom get-default-dialer: Displays the current default dialer. \n" +
-                "\n" +
-                "telecom get-system-dialer: Displays the current system dialer. \n" +
-                "\n" +
-                "telecom wait-on-handlers: Wait until all handlers finish their work. \n"
-                );
+        out.println("usage: telecom [subcommand] [options]\n"
+                + "usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n"
+                + "usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n"
+                + "usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n"
+                + "usage: telecom set-test-call-redirection-app <PACKAGE>\n"
+                + "usage: telecom set-test-call-screening-app <PACKAGE>\n"
+                + "usage: telecom set-test-auto-mode-app <PACKAGE>\n"
+                + "usage: telecom set-phone-acct-suggestion-component <COMPONENT>\n"
+                + "usage: telecom add-or-remove-call-companion-app <PACKAGE> <1/0>\n"
+                + "usage: telecom register-sim-phone-account <COMPONENT> <ID> <USER_SN>"
+                + " <LABEL> <ADDRESS>\n"
+                + "usage: telecom unregister-phone-account <COMPONENT> <ID> <USER_SN>\n"
+                + "usage: telecom set-default-dialer <PACKAGE>\n"
+                + "usage: telecom get-default-dialer\n"
+                + "usage: telecom get-system-dialer\n"
+                + "usage: telecom wait-on-handlers\n"
+                + "\n"
+                + "telecom set-phone-account-enabled: Enables the given phone account, if it has \n"
+                + " already been registered with Telecom.\n"
+                + "\n"
+                + "telecom set-phone-account-disabled: Disables the given phone account, if it \n"
+                + " has already been registered with telecom.\n"
+                + "\n"
+                + "telecom set-default-dialer: Sets the default dialer to the given component. \n"
+                + "\n"
+                + "telecom get-default-dialer: Displays the current default dialer. \n"
+                + "\n"
+                + "telecom get-system-dialer: Displays the current system dialer. \n"
+                + "\n"
+                + "telecom wait-on-handlers: Wait until all handlers finish their work. \n"
+        );
     }
 
     @Override
@@ -134,6 +137,9 @@
             case COMMAND_SET_TEST_AUTO_MODE_APP:
                 runSetTestAutoModeApp();
                 break;
+            case COMMAND_SET_PHONE_ACCOUNT_SUGGESTION_COMPONENT:
+                runSetTestPhoneAcctSuggestionComponent();
+                break;
             case COMMAND_REGISTER_SIM_PHONE_ACCOUNT:
                 runRegisterSimPhoneAccount();
                 break;
@@ -216,6 +222,11 @@
         mTelecomService.setTestAutoModeApp(packageName);
     }
 
+    private void runSetTestPhoneAcctSuggestionComponent() throws RemoteException {
+        final String componentName = nextArg();
+        mTelecomService.setTestPhoneAcctSuggestionComponent(componentName);
+    }
+
     private void runUnregisterPhoneAccount() throws RemoteException {
         final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
         mTelecomService.unregisterPhoneAccount(handle);
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
index 3d5476d..d862e1c 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
@@ -16,14 +16,16 @@
 
 package com.android.uiautomator.testrunner;
 
+import android.app.ActivityThread;
+import android.content.ContentResolver;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.provider.Settings;
 import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
 
-import com.android.internal.view.IInputMethodManager;
 import com.android.uiautomator.core.UiDevice;
 
 import junit.framework.TestCase;
@@ -44,6 +46,8 @@
 
     private static final String DISABLE_IME = "disable_ime";
     private static final String DUMMY_IME_PACKAGE = "com.android.testing.dummyime";
+    private static final int NOT_A_SUBTYPE_ID = -1;
+
     private UiDevice mUiDevice;
     private Bundle mParams;
     private IAutomationSupport mAutomationSupport;
@@ -124,9 +128,13 @@
         SystemClock.sleep(ms);
     }
 
-    private void setDummyIme() throws RemoteException {
-        IInputMethodManager im = IInputMethodManager.Stub.asInterface(ServiceManager
-                .getService(Context.INPUT_METHOD_SERVICE));
+    private void setDummyIme() {
+        Context context = ActivityThread.currentApplication();
+        if (context == null) {
+            throw new RuntimeException("ActivityThread.currentApplication() is null.");
+        }
+        InputMethodManager im = (InputMethodManager) context.getSystemService(
+                Context.INPUT_METHOD_SERVICE);
         List<InputMethodInfo> infos = im.getInputMethodList();
         String id = null;
         for (InputMethodInfo info : infos) {
@@ -138,10 +146,17 @@
             throw new RuntimeException(String.format(
                     "Required testing fixture missing: IME package (%s)", DUMMY_IME_PACKAGE));
         }
-        im.setInputMethod(null, id);
+        if (context.checkSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+                != PackageManager.PERMISSION_GRANTED) {
+            return;
+        }
+        ContentResolver resolver = context.getContentResolver();
+        Settings.Secure.putInt(resolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
+                NOT_A_SUBTYPE_ID);
+        Settings.Secure.putString(resolver, Settings.Secure.DEFAULT_INPUT_METHOD, id);
     }
 
-    private void restoreActiveIme() throws RemoteException {
+    private void restoreActiveIme() {
         // TODO: figure out a way to restore active IME
         // Currently retrieving active IME requires querying secure settings provider, which is hard
         // to do without a Context; so the caveat here is that to make the post test device usable,
diff --git a/config/dirty-image-objects b/config/dirty-image-objects
index 9b4d199..9e2230b 100644
--- a/config/dirty-image-objects
+++ b/config/dirty-image-objects
@@ -44,7 +44,6 @@
 sun.misc.FormattedFloatingDecimal
 java.util.stream.IntStream
 android.icu.util.TimeZone
-libcore.io.DropBox
 org.apache.harmony.luni.internal.util.TimezoneGetter
 dalvik.system.SocketTagger
 dalvik.system.CloseGuard
@@ -137,7 +136,6 @@
 android.icu.util.ULocale
 dalvik.system.BaseDexClassLoader
 android.icu.text.BreakIterator
-libcore.io.EventLogger
 libcore.net.NetworkSecurityPolicy
 android.icu.text.UnicodeSet
 com.android.org.conscrypt.TrustedCertificateStore$PreloadHolder
diff --git a/config/hiddenapi-greylist-max-p.txt b/config/hiddenapi-greylist-max-p.txt
index e69de29..7840b18 100644
--- a/config/hiddenapi-greylist-max-p.txt
+++ b/config/hiddenapi-greylist-max-p.txt
@@ -0,0 +1,75 @@
+Landroid/app/IInstrumentationWatcher$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IInstrumentationWatcher;
+Landroid/app/ISearchManager$Stub;-><init>()V
+Landroid/app/IUiModeManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IUiModeManager;
+Landroid/app/IUiModeManager;->disableCarMode(I)V
+Landroid/bluetooth/IBluetooth$Stub;-><init>()V
+Landroid/bluetooth/IBluetoothA2dp$Stub;-><init>()V
+Landroid/content/IIntentReceiver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/IIntentReceiver;
+Landroid/content/IIntentSender$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/IIntentSender;
+Landroid/net/IConnectivityManager;->getNetworkInfo(I)Landroid/net/NetworkInfo;
+Landroid/net/IConnectivityManager;->reportInetCondition(II)V
+Landroid/os/BatteryStats$Counter;-><init>()V
+Landroid/os/BatteryStats$HistoryItem;->clear()V
+Landroid/os/BatteryStats$HistoryItem;->next:Landroid/os/BatteryStats$HistoryItem;
+Landroid/os/BatteryStats$HistoryItem;->same(Landroid/os/BatteryStats$HistoryItem;)Z
+Landroid/os/BatteryStats$HistoryItem;->setTo(JBLandroid/os/BatteryStats$HistoryItem;)V
+Landroid/os/BatteryStats$HistoryItem;->setTo(Landroid/os/BatteryStats$HistoryItem;)V
+Landroid/os/BatteryStats$Timer;-><init>()V
+Landroid/os/BatteryStats$Uid$Pkg;-><init>()V
+Landroid/os/BatteryStats$Uid$Proc;-><init>()V
+Landroid/os/BatteryStats$Uid$Sensor;-><init>()V
+Landroid/os/BatteryStats$Uid$Wakelock;-><init>()V
+Landroid/os/BatteryStats;-><init>()V
+Landroid/os/BatteryStats;->getMobileRadioActiveTime(JI)J
+Landroid/os/BatteryStats;->getNetworkActivityBytes(II)J
+Landroid/os/CancellationSignal;->mCancelInProgress:Z
+Landroid/os/CancellationSignal;->mIsCanceled:Z
+Landroid/os/CancellationSignal;->mOnCancelListener:Landroid/os/CancellationSignal$OnCancelListener;
+Landroid/os/CancellationSignal;->mRemote:Landroid/os/ICancellationSignal;
+Landroid/os/CancellationSignal;->waitForCancelFinishedLocked()V
+Landroid/os/IPowerManager;->nap(J)V
+Landroid/os/Parcel;->mCreators:Ljava/util/HashMap;
+Landroid/os/PowerManager;->mHandler:Landroid/os/Handler;
+Landroid/os/Process;->sendSignalQuiet(II)V
+Landroid/os/Registrant;->getHandler()Landroid/os/Handler;
+Landroid/os/RegistrantList;->get(I)Ljava/lang/Object;
+Landroid/os/RemoteCallback;->mHandler:Landroid/os/Handler;
+Landroid/os/storage/IObbActionListener$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/storage/IObbActionListener;
+Landroid/os/SystemProperties;->native_add_change_callback()V
+Landroid/os/SystemProperties;->native_get(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
+Landroid/os/SystemProperties;->native_get_boolean(Ljava/lang/String;Z)Z
+Landroid/os/SystemProperties;->native_get_int(Ljava/lang/String;I)I
+Landroid/os/SystemProperties;->native_set(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/os/UserHandle;->formatUid(Ljava/io/PrintWriter;I)V
+Landroid/os/WorkSource;->sGoneWork:Landroid/os/WorkSource;
+Landroid/os/WorkSource;->sNewbWork:Landroid/os/WorkSource;
+Landroid/os/WorkSource;->sTmpWorkSource:Landroid/os/WorkSource;
+Landroid/os/WorkSource;->updateLocked(Landroid/os/WorkSource;ZZ)Z
+Landroid/service/carrier/ICarrierMessagingCallback$Stub;-><init>()V
+Landroid/service/carrier/ICarrierMessagingService;->filterSms(Landroid/service/carrier/MessagePdu;Ljava/lang/String;IILandroid/service/carrier/ICarrierMessagingCallback;)V
+Landroid/telephony/CarrierMessagingServiceManager;-><init>()V
+Landroid/view/IWindowManager;->setInTouchMode(Z)V
+Landroid/view/IWindowManager;->showStrictModeViolation(Z)V
+Lcom/android/internal/R$styleable;->AndroidManifestActivityAlias:[I
+Lcom/android/internal/R$styleable;->AndroidManifestGrantUriPermission:[I
+Lcom/android/internal/R$styleable;->AndroidManifestInstrumentation:[I
+Lcom/android/internal/R$styleable;->AndroidManifestOriginalPackage:[I
+Lcom/android/internal/R$styleable;->AndroidManifestPathPermission:[I
+Lcom/android/internal/R$styleable;->AndroidManifestPermission:[I
+Lcom/android/internal/R$styleable;->AndroidManifestPermissionGroup:[I
+Lcom/android/internal/R$styleable;->AndroidManifestPermissionTree:[I
+Lcom/android/internal/R$styleable;->AndroidManifestProtectedBroadcast:[I
+Lcom/android/internal/R$styleable;->AndroidManifestSupportsScreens:[I
+Lcom/android/internal/R$styleable;->AndroidManifestUsesConfiguration:[I
+Lcom/android/internal/R$styleable;->AndroidManifestUsesFeature:[I
+Lcom/android/internal/R$styleable;->CycleInterpolator:[I
+Lcom/android/internal/R$styleable;->LinearLayout:[I
+Lcom/android/internal/R$styleable;->MenuView:[I
+Lcom/android/internal/R$styleable;->Searchable:[I
+Lcom/android/internal/R$styleable;->SearchableActionKey:[I
+Lcom/android/internal/telephony/IPhoneSubInfo$Stub;-><init>()V
+Lcom/android/internal/telephony/ITelephonyRegistry;->notifyCallForwardingChanged(Z)V
+Lcom/android/internal/telephony/ITelephonyRegistry;->notifyCellLocation(Landroid/os/Bundle;)V
+Lcom/android/internal/telephony/ITelephonyRegistry;->notifyDataActivity(I)V
+Lcom/android/internal/telephony/ITelephonyRegistry;->notifyOtaspChanged(I)V
+Lcom/android/internal/view/BaseIWindow;-><init>()V
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index bf14438..97dcee6 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -173,7 +173,6 @@
 Landroid/app/IAssistDataReceiver;->onHandleAssistScreenshot(Landroid/graphics/Bitmap;)V
 Landroid/app/IInputForwarder;->forwardEvent(Landroid/view/InputEvent;)Z
 Landroid/app/IInstrumentationWatcher$Stub;-><init>()V
-Landroid/app/IInstrumentationWatcher$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IInstrumentationWatcher;
 Landroid/app/IInstrumentationWatcher;->instrumentationStatus(Landroid/content/ComponentName;ILandroid/os/Bundle;)V
 Landroid/app/INotificationManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/app/INotificationManager$Stub$Proxy;->areNotificationsEnabledForPackage(Ljava/lang/String;I)Z
@@ -191,7 +190,6 @@
 Landroid/app/IProcessObserver$Stub;-><init>()V
 Landroid/app/ISearchManager$Stub$Proxy;->getGlobalSearchActivity()Landroid/content/ComponentName;
 Landroid/app/ISearchManager$Stub$Proxy;->getWebSearchActivity()Landroid/content/ComponentName;
-Landroid/app/ISearchManager$Stub;-><init>()V
 Landroid/app/ISearchManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/ISearchManager;
 Landroid/app/ISearchManager;->getGlobalSearchActivity()Landroid/content/ComponentName;
 Landroid/app/IServiceConnection$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -206,8 +204,6 @@
 Landroid/app/ITransientNotification$Stub;-><init>()V
 Landroid/app/ITransientNotification;->show(Landroid/os/IBinder;)V
 Landroid/app/IUiModeManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-Landroid/app/IUiModeManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IUiModeManager;
-Landroid/app/IUiModeManager;->disableCarMode(I)V
 Landroid/app/IUserSwitchObserver$Stub;-><init>()V
 Landroid/app/IWallpaperManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IWallpaperManager;
 Landroid/app/IWallpaperManager;->getHeightHint(I)I
@@ -250,7 +246,6 @@
 Landroid/app/UserSwitchObserver;-><init>()V
 Landroid/bluetooth/IBluetooth$Stub$Proxy;->getAddress()Ljava/lang/String;
 Landroid/bluetooth/IBluetooth$Stub$Proxy;->getConnectionState(Landroid/bluetooth/BluetoothDevice;)I
-Landroid/bluetooth/IBluetooth$Stub;-><init>()V
 Landroid/bluetooth/IBluetooth$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetooth;
 Landroid/bluetooth/IBluetooth$Stub;->TRANSACTION_enable:I
 Landroid/bluetooth/IBluetooth;->fetchRemoteUuids(Landroid/bluetooth/BluetoothDevice;)Z
@@ -258,7 +253,6 @@
 Landroid/bluetooth/IBluetooth;->getRemoteAlias(Landroid/bluetooth/BluetoothDevice;)Ljava/lang/String;
 Landroid/bluetooth/IBluetooth;->isEnabled()Z
 Landroid/bluetooth/IBluetooth;->sendConnectionStateChange(Landroid/bluetooth/BluetoothDevice;III)V
-Landroid/bluetooth/IBluetoothA2dp$Stub;-><init>()V
 Landroid/bluetooth/IBluetoothA2dp$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetoothA2dp;
 Landroid/bluetooth/IBluetoothA2dp;->connect(Landroid/bluetooth/BluetoothDevice;)Z
 Landroid/bluetooth/IBluetoothA2dp;->disconnect(Landroid/bluetooth/BluetoothDevice;)Z
@@ -307,9 +301,7 @@
 Landroid/content/IIntentReceiver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/IIntentReceiver$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Landroid/content/IIntentReceiver$Stub;-><init>()V
-Landroid/content/IIntentReceiver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/IIntentReceiver;
 Landroid/content/IIntentReceiver;->performReceive(Landroid/content/Intent;ILjava/lang/String;Landroid/os/Bundle;ZZI)V
-Landroid/content/IIntentSender$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/IIntentSender;
 Landroid/content/IOnPrimaryClipChangedListener$Stub;-><init>()V
 Landroid/content/IOnPrimaryClipChangedListener$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/IOnPrimaryClipChangedListener;
 Landroid/content/IRestrictionsManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/IRestrictionsManager;
@@ -554,13 +546,11 @@
 Landroid/net/IConnectivityManager;->getAllNetworkInfo()[Landroid/net/NetworkInfo;
 Landroid/net/IConnectivityManager;->getAllNetworkState()[Landroid/net/NetworkState;
 Landroid/net/IConnectivityManager;->getLastTetherError(Ljava/lang/String;)I
-Landroid/net/IConnectivityManager;->getNetworkInfo(I)Landroid/net/NetworkInfo;
 Landroid/net/IConnectivityManager;->getTetherableIfaces()[Ljava/lang/String;
 Landroid/net/IConnectivityManager;->getTetherableUsbRegexs()[Ljava/lang/String;
 Landroid/net/IConnectivityManager;->getTetherableWifiRegexs()[Ljava/lang/String;
 Landroid/net/IConnectivityManager;->getTetheredIfaces()[Ljava/lang/String;
 Landroid/net/IConnectivityManager;->getTetheringErroredIfaces()[Ljava/lang/String;
-Landroid/net/IConnectivityManager;->reportInetCondition(II)V
 Landroid/net/IConnectivityManager;->startLegacyVpn(Lcom/android/internal/net/VpnProfile;)V
 Landroid/net/INetd$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetd;
 Landroid/net/INetd;->interfaceAddAddress(Ljava/lang/String;Ljava/lang/String;I)V
@@ -634,7 +624,6 @@
 Landroid/os/BatteryManager;->EXTRA_INVALID_CHARGER:Ljava/lang/String;
 Landroid/os/BatteryManager;->EXTRA_MAX_CHARGING_CURRENT:Ljava/lang/String;
 Landroid/os/BatteryManager;->EXTRA_MAX_CHARGING_VOLTAGE:Ljava/lang/String;
-Landroid/os/BatteryStats$Counter;-><init>()V
 Landroid/os/BatteryStats$Counter;->getCountLocked(I)I
 Landroid/os/BatteryStats$HistoryItem;-><init>()V
 Landroid/os/BatteryStats$HistoryItem;->batteryHealth:B
@@ -642,41 +631,31 @@
 Landroid/os/BatteryStats$HistoryItem;->batteryPlugType:B
 Landroid/os/BatteryStats$HistoryItem;->batteryStatus:B
 Landroid/os/BatteryStats$HistoryItem;->batteryVoltage:C
-Landroid/os/BatteryStats$HistoryItem;->clear()V
 Landroid/os/BatteryStats$HistoryItem;->cmd:B
 Landroid/os/BatteryStats$HistoryItem;->CMD_UPDATE:B
-Landroid/os/BatteryStats$HistoryItem;->next:Landroid/os/BatteryStats$HistoryItem;
-Landroid/os/BatteryStats$HistoryItem;->same(Landroid/os/BatteryStats$HistoryItem;)Z
-Landroid/os/BatteryStats$HistoryItem;->setTo(JBLandroid/os/BatteryStats$HistoryItem;)V
-Landroid/os/BatteryStats$HistoryItem;->setTo(Landroid/os/BatteryStats$HistoryItem;)V
 Landroid/os/BatteryStats$HistoryItem;->states2:I
 Landroid/os/BatteryStats$HistoryItem;->states:I
 Landroid/os/BatteryStats$HistoryItem;->time:J
-Landroid/os/BatteryStats$Timer;-><init>()V
 Landroid/os/BatteryStats$Timer;->getCountLocked(I)I
 Landroid/os/BatteryStats$Timer;->getTotalTimeLocked(JI)J
 Landroid/os/BatteryStats$Uid$Pkg$Serv;->getLaunches(I)I
 Landroid/os/BatteryStats$Uid$Pkg$Serv;->getStarts(I)I
 Landroid/os/BatteryStats$Uid$Pkg$Serv;->getStartTime(JI)J
-Landroid/os/BatteryStats$Uid$Pkg;-><init>()V
 Landroid/os/BatteryStats$Uid$Pkg;->getServiceStats()Landroid/util/ArrayMap;
 Landroid/os/BatteryStats$Uid$Pkg;->getWakeupAlarmStats()Landroid/util/ArrayMap;
 Landroid/os/BatteryStats$Uid$Proc$ExcessivePower;-><init>()V
 Landroid/os/BatteryStats$Uid$Proc$ExcessivePower;->overTime:J
 Landroid/os/BatteryStats$Uid$Proc$ExcessivePower;->type:I
 Landroid/os/BatteryStats$Uid$Proc$ExcessivePower;->usedTime:J
-Landroid/os/BatteryStats$Uid$Proc;-><init>()V
 Landroid/os/BatteryStats$Uid$Proc;->countExcessivePowers()I
 Landroid/os/BatteryStats$Uid$Proc;->getExcessivePower(I)Landroid/os/BatteryStats$Uid$Proc$ExcessivePower;
 Landroid/os/BatteryStats$Uid$Proc;->getForegroundTime(I)J
 Landroid/os/BatteryStats$Uid$Proc;->getStarts(I)I
 Landroid/os/BatteryStats$Uid$Proc;->getSystemTime(I)J
 Landroid/os/BatteryStats$Uid$Proc;->getUserTime(I)J
-Landroid/os/BatteryStats$Uid$Sensor;-><init>()V
 Landroid/os/BatteryStats$Uid$Sensor;->getHandle()I
 Landroid/os/BatteryStats$Uid$Sensor;->getSensorTime()Landroid/os/BatteryStats$Timer;
 Landroid/os/BatteryStats$Uid$Sensor;->GPS:I
-Landroid/os/BatteryStats$Uid$Wakelock;-><init>()V
 Landroid/os/BatteryStats$Uid$Wakelock;->getWakeTime(I)Landroid/os/BatteryStats$Timer;
 Landroid/os/BatteryStats$Uid;-><init>()V
 Landroid/os/BatteryStats$Uid;->getAudioTurnedOnTimer()Landroid/os/BatteryStats$Timer;
@@ -693,7 +672,6 @@
 Landroid/os/BatteryStats$Uid;->getWifiMulticastTime(JI)J
 Landroid/os/BatteryStats$Uid;->getWifiRunningTime(JI)J
 Landroid/os/BatteryStats$Uid;->getWifiScanTime(JI)J
-Landroid/os/BatteryStats;-><init>()V
 Landroid/os/BatteryStats;->computeBatteryRealtime(JI)J
 Landroid/os/BatteryStats;->computeBatteryTimeRemaining(J)J
 Landroid/os/BatteryStats;->computeBatteryUptime(JI)J
@@ -701,8 +679,6 @@
 Landroid/os/BatteryStats;->dumpLine(Ljava/io/PrintWriter;ILjava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
 Landroid/os/BatteryStats;->getBatteryUptime(J)J
 Landroid/os/BatteryStats;->getGlobalWifiRunningTime(JI)J
-Landroid/os/BatteryStats;->getMobileRadioActiveTime(JI)J
-Landroid/os/BatteryStats;->getNetworkActivityBytes(II)J
 Landroid/os/BatteryStats;->getNextHistoryLocked(Landroid/os/BatteryStats$HistoryItem;)Z
 Landroid/os/BatteryStats;->getPhoneOnTime(JI)J
 Landroid/os/BatteryStats;->getPhoneSignalStrengthTime(IJI)J
@@ -733,11 +709,6 @@
 Landroid/os/Bundle;->putIBinder(Ljava/lang/String;Landroid/os/IBinder;)V
 Landroid/os/Bundle;->putParcelableList(Ljava/lang/String;Ljava/util/List;)V
 Landroid/os/Bundle;->setDefusable(Landroid/os/Bundle;Z)Landroid/os/Bundle;
-Landroid/os/CancellationSignal;->mCancelInProgress:Z
-Landroid/os/CancellationSignal;->mIsCanceled:Z
-Landroid/os/CancellationSignal;->mOnCancelListener:Landroid/os/CancellationSignal$OnCancelListener;
-Landroid/os/CancellationSignal;->mRemote:Landroid/os/ICancellationSignal;
-Landroid/os/CancellationSignal;->waitForCancelFinishedLocked()V
 Landroid/os/Debug$MemoryInfo;->dalvikPrivateClean:I
 Landroid/os/Debug$MemoryInfo;->dalvikRss:I
 Landroid/os/Debug$MemoryInfo;->dalvikSharedClean:I
@@ -864,7 +835,6 @@
 Landroid/os/IPowerManager$Stub;->TRANSACTION_goToSleep:I
 Landroid/os/IPowerManager;->goToSleep(JII)V
 Landroid/os/IPowerManager;->isInteractive()Z
-Landroid/os/IPowerManager;->nap(J)V
 Landroid/os/IPowerManager;->reboot(ZLjava/lang/String;Z)V
 Landroid/os/IPowerManager;->releaseWakeLock(Landroid/os/IBinder;I)V
 Landroid/os/IPowerManager;->userActivity(JII)V
@@ -912,7 +882,6 @@
 Landroid/os/Parcel$ReadWriteHelper;-><init>()V
 Landroid/os/Parcel;->getGlobalAllocCount()J
 Landroid/os/Parcel;->getGlobalAllocSize()J
-Landroid/os/Parcel;->mCreators:Ljava/util/HashMap;
 Landroid/os/Parcel;->mNativePtr:J
 Landroid/os/Parcel;->readArrayMap(Landroid/util/ArrayMap;Ljava/lang/ClassLoader;)V
 Landroid/os/Parcel;->readArraySet(Ljava/lang/ClassLoader;)Landroid/util/ArraySet;
@@ -953,7 +922,6 @@
 Landroid/os/PowerManager;->goToSleep(JII)V
 Landroid/os/PowerManager;->GO_TO_SLEEP_REASON_TIMEOUT:I
 Landroid/os/PowerManager;->isLightDeviceIdleMode()Z
-Landroid/os/PowerManager;->mHandler:Landroid/os/Handler;
 Landroid/os/PowerManager;->mService:Landroid/os/IPowerManager;
 Landroid/os/PowerManager;->validateWakeLockParameters(ILjava/lang/String;)V
 Landroid/os/PowerManager;->wakeUp(JLjava/lang/String;)V
@@ -985,7 +953,6 @@
 Landroid/os/Process;->readProcFile(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z
 Landroid/os/Process;->readProcLines(Ljava/lang/String;[Ljava/lang/String;[J)V
 Landroid/os/Process;->ROOT_UID:I
-Landroid/os/Process;->sendSignalQuiet(II)V
 Landroid/os/Process;->setArgV0(Ljava/lang/String;)V
 Landroid/os/Process;->setProcessGroup(II)V
 Landroid/os/Process;->SHELL_UID:I
@@ -994,7 +961,6 @@
 Landroid/os/RecoverySystem;->verifyPackageCompatibility(Ljava/io/InputStream;)Z
 Landroid/os/Registrant;-><init>(Landroid/os/Handler;ILjava/lang/Object;)V
 Landroid/os/Registrant;->clear()V
-Landroid/os/Registrant;->getHandler()Landroid/os/Handler;
 Landroid/os/Registrant;->messageForRegistrant()Landroid/os/Message;
 Landroid/os/Registrant;->notifyRegistrant()V
 Landroid/os/Registrant;->notifyRegistrant(Landroid/os/AsyncResult;)V
@@ -1003,14 +969,12 @@
 Landroid/os/RegistrantList;->add(Landroid/os/Handler;ILjava/lang/Object;)V
 Landroid/os/RegistrantList;->add(Landroid/os/Registrant;)V
 Landroid/os/RegistrantList;->addUnique(Landroid/os/Handler;ILjava/lang/Object;)V
-Landroid/os/RegistrantList;->get(I)Ljava/lang/Object;
 Landroid/os/RegistrantList;->notifyRegistrants()V
 Landroid/os/RegistrantList;->notifyRegistrants(Landroid/os/AsyncResult;)V
 Landroid/os/RegistrantList;->notifyResult(Ljava/lang/Object;)V
 Landroid/os/RegistrantList;->remove(Landroid/os/Handler;)V
 Landroid/os/RegistrantList;->removeCleared()V
 Landroid/os/RegistrantList;->size()I
-Landroid/os/RemoteCallback;->mHandler:Landroid/os/Handler;
 Landroid/os/RemoteCallbackList;->mCallbacks:Landroid/util/ArrayMap;
 Landroid/os/RemoteException;->rethrowFromSystemServer()Ljava/lang/RuntimeException;
 Landroid/os/SELinux;->checkSELinuxAccess(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z
@@ -1039,7 +1003,6 @@
 Landroid/os/ShellCommand;->peekNextArg()Ljava/lang/String;
 Landroid/os/StatFs;->mStat:Landroid/system/StructStatVfs;
 Landroid/os/storage/IObbActionListener$Stub;-><init>()V
-Landroid/os/storage/IObbActionListener$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/storage/IObbActionListener;
 Landroid/os/storage/IStorageManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/os/storage/IStorageManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/storage/IStorageManager;
 Landroid/os/storage/StorageEventListener;-><init>()V
@@ -1062,13 +1025,8 @@
 Landroid/os/SystemClock;->currentTimeMicro()J
 Landroid/os/SystemProperties;-><init>()V
 Landroid/os/SystemProperties;->addChangeCallback(Ljava/lang/Runnable;)V
-Landroid/os/SystemProperties;->native_add_change_callback()V
 Landroid/os/SystemProperties;->native_get(Ljava/lang/String;)Ljava/lang/String;
-Landroid/os/SystemProperties;->native_get(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
-Landroid/os/SystemProperties;->native_get_boolean(Ljava/lang/String;Z)Z
-Landroid/os/SystemProperties;->native_get_int(Ljava/lang/String;I)I
 Landroid/os/SystemProperties;->native_get_long(Ljava/lang/String;J)J
-Landroid/os/SystemProperties;->native_set(Ljava/lang/String;Ljava/lang/String;)V
 Landroid/os/SystemProperties;->PROP_NAME_MAX:I
 Landroid/os/SystemProperties;->reportSyspropChanged()V
 Landroid/os/SystemProperties;->sChangeCallbacks:Ljava/util/ArrayList;
@@ -1108,7 +1066,6 @@
 Landroid/os/UserHandle;->AID_SHARED_GID_START:I
 Landroid/os/UserHandle;->CURRENT_OR_SELF:Landroid/os/UserHandle;
 Landroid/os/UserHandle;->ERR_GID:I
-Landroid/os/UserHandle;->formatUid(Ljava/io/PrintWriter;I)V
 Landroid/os/UserHandle;->getAppIdFromSharedAppGid(I)I
 Landroid/os/UserHandle;->getCallingUserId()I
 Landroid/os/UserHandle;->getUid(II)I
@@ -1169,10 +1126,6 @@
 Landroid/os/WorkSource;->mNames:[Ljava/lang/String;
 Landroid/os/WorkSource;->mNum:I
 Landroid/os/WorkSource;->mUids:[I
-Landroid/os/WorkSource;->sGoneWork:Landroid/os/WorkSource;
-Landroid/os/WorkSource;->sNewbWork:Landroid/os/WorkSource;
-Landroid/os/WorkSource;->sTmpWorkSource:Landroid/os/WorkSource;
-Landroid/os/WorkSource;->updateLocked(Landroid/os/WorkSource;ZZ)Z
 Landroid/os/ZygoteStartFailedEx;-><init>(Ljava/lang/String;)V
 Landroid/os/ZygoteStartFailedEx;-><init>(Ljava/lang/Throwable;)V
 Landroid/preference/PreferenceGroupAdapter;->getItem(I)Landroid/preference/Preference;
@@ -1465,8 +1418,6 @@
 Landroid/security/keystore/IKeystoreService;->list(Ljava/lang/String;I)[Ljava/lang/String;
 Landroid/security/keystore/IKeystoreService;->reset()I
 Landroid/security/keystore/IKeystoreService;->ungrant(Ljava/lang/String;I)I
-Landroid/service/carrier/ICarrierMessagingCallback$Stub;-><init>()V
-Landroid/service/carrier/ICarrierMessagingService;->filterSms(Landroid/service/carrier/MessagePdu;Ljava/lang/String;IILandroid/service/carrier/ICarrierMessagingCallback;)V
 Landroid/service/dreams/IDreamManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/dreams/IDreamManager;
 Landroid/service/dreams/IDreamManager;->awaken()V
 Landroid/service/dreams/IDreamManager;->dream()V
@@ -1474,7 +1425,6 @@
 Landroid/service/dreams/IDreamManager;->isDreaming()Z
 Landroid/service/dreams/IDreamManager;->setDreamComponents([Landroid/content/ComponentName;)V
 Landroid/service/euicc/IDeleteSubscriptionCallback;->onComplete(I)V
-Landroid/service/euicc/IDownloadSubscriptionCallback;->onComplete(I)V
 Landroid/service/euicc/IEraseSubscriptionsCallback;->onComplete(I)V
 Landroid/service/euicc/IEuiccService$Stub;-><init>()V
 Landroid/service/euicc/IGetDefaultDownloadableSubscriptionListCallback;->onComplete(Landroid/service/euicc/GetDefaultDownloadableSubscriptionListResult;)V
@@ -1504,7 +1454,6 @@
 Landroid/speech/IRecognitionListener;->onEvent(ILandroid/os/Bundle;)V
 Landroid/telecom/Log;->i(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
 Landroid/telecom/Log;->w(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
-Landroid/telephony/CarrierMessagingServiceManager;-><init>()V
 Landroid/telephony/ims/compat/feature/MMTelFeature;-><init>()V
 Landroid/telephony/ims/compat/ImsService;-><init>()V
 Landroid/telephony/ims/compat/stub/ImsCallSessionImplBase;-><init>()V
@@ -1607,11 +1556,9 @@
 Landroid/view/IWindowManager;->removeRotationWatcher(Landroid/view/IRotationWatcher;)V
 Landroid/view/IWindowManager;->setAnimationScale(IF)V
 Landroid/view/IWindowManager;->setAnimationScales([F)V
-Landroid/view/IWindowManager;->setInTouchMode(Z)V
 Landroid/view/IWindowManager;->setNavBarVirtualKeyHapticFeedbackEnabled(Z)V
 Landroid/view/IWindowManager;->setShelfHeight(ZI)V
 Landroid/view/IWindowManager;->setStrictModeVisualIndicatorPreference(Ljava/lang/String;)V
-Landroid/view/IWindowManager;->showStrictModeViolation(Z)V
 Landroid/view/IWindowManager;->thawRotation()V
 Landroid/view/IWindowSession$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowSession;
 Landroid/view/IWindowSession;->finishDrawing(Landroid/view/IWindow;)V
@@ -2107,7 +2054,6 @@
 Lcom/android/internal/R$styleable;->AnalogClock:[I
 Lcom/android/internal/R$styleable;->AndroidManifest:[I
 Lcom/android/internal/R$styleable;->AndroidManifestActivity:[I
-Lcom/android/internal/R$styleable;->AndroidManifestActivityAlias:[I
 Lcom/android/internal/R$styleable;->AndroidManifestActivity_allowTaskReparenting:I
 Lcom/android/internal/R$styleable;->AndroidManifestActivity_configChanges:I
 Lcom/android/internal/R$styleable;->AndroidManifestActivity_description:I
@@ -2141,21 +2087,13 @@
 Lcom/android/internal/R$styleable;->AndroidManifestApplication_theme:I
 Lcom/android/internal/R$styleable;->AndroidManifestApplication_uiOptions:I
 Lcom/android/internal/R$styleable;->AndroidManifestData:[I
-Lcom/android/internal/R$styleable;->AndroidManifestGrantUriPermission:[I
-Lcom/android/internal/R$styleable;->AndroidManifestInstrumentation:[I
 Lcom/android/internal/R$styleable;->AndroidManifestIntentFilter:[I
 Lcom/android/internal/R$styleable;->AndroidManifestIntentFilter_priority:I
 Lcom/android/internal/R$styleable;->AndroidManifestMetaData:[I
 Lcom/android/internal/R$styleable;->AndroidManifestMetaData_name:I
 Lcom/android/internal/R$styleable;->AndroidManifestMetaData_resource:I
 Lcom/android/internal/R$styleable;->AndroidManifestMetaData_value:I
-Lcom/android/internal/R$styleable;->AndroidManifestOriginalPackage:[I
 Lcom/android/internal/R$styleable;->AndroidManifestPackageVerifier:[I
-Lcom/android/internal/R$styleable;->AndroidManifestPathPermission:[I
-Lcom/android/internal/R$styleable;->AndroidManifestPermission:[I
-Lcom/android/internal/R$styleable;->AndroidManifestPermissionGroup:[I
-Lcom/android/internal/R$styleable;->AndroidManifestPermissionTree:[I
-Lcom/android/internal/R$styleable;->AndroidManifestProtectedBroadcast:[I
 Lcom/android/internal/R$styleable;->AndroidManifestProvider:[I
 Lcom/android/internal/R$styleable;->AndroidManifestService:[I
 Lcom/android/internal/R$styleable;->AndroidManifestService_enabled:I
@@ -2163,9 +2101,6 @@
 Lcom/android/internal/R$styleable;->AndroidManifestService_name:I
 Lcom/android/internal/R$styleable;->AndroidManifestService_permission:I
 Lcom/android/internal/R$styleable;->AndroidManifestService_process:I
-Lcom/android/internal/R$styleable;->AndroidManifestSupportsScreens:[I
-Lcom/android/internal/R$styleable;->AndroidManifestUsesConfiguration:[I
-Lcom/android/internal/R$styleable;->AndroidManifestUsesFeature:[I
 Lcom/android/internal/R$styleable;->AndroidManifestUsesLibrary:[I
 Lcom/android/internal/R$styleable;->AndroidManifestUsesPermission:[I
 Lcom/android/internal/R$styleable;->AndroidManifestUsesPermission_name:I
@@ -2188,7 +2123,6 @@
 Lcom/android/internal/R$styleable;->CompoundButton_button:I
 Lcom/android/internal/R$styleable;->CompoundButton_checked:I
 Lcom/android/internal/R$styleable;->ContactsDataKind:[I
-Lcom/android/internal/R$styleable;->CycleInterpolator:[I
 Lcom/android/internal/R$styleable;->DatePicker:[I
 Lcom/android/internal/R$styleable;->DialogPreference:[I
 Lcom/android/internal/R$styleable;->DialogPreference_dialogTitle:I
@@ -2208,7 +2142,6 @@
 Lcom/android/internal/R$styleable;->KeyboardView:[I
 Lcom/android/internal/R$styleable;->Keyboard_Key:[I
 Lcom/android/internal/R$styleable;->Keyboard_Row:[I
-Lcom/android/internal/R$styleable;->LinearLayout:[I
 Lcom/android/internal/R$styleable;->ListPreference:[I
 Lcom/android/internal/R$styleable;->ListPreference_entries:I
 Lcom/android/internal/R$styleable;->ListView:[I
@@ -2223,7 +2156,6 @@
 Lcom/android/internal/R$styleable;->MapView_apiKey:I
 Lcom/android/internal/R$styleable;->MenuGroup:[I
 Lcom/android/internal/R$styleable;->MenuItem:[I
-Lcom/android/internal/R$styleable;->MenuView:[I
 Lcom/android/internal/R$styleable;->NumberPicker:[I
 Lcom/android/internal/R$styleable;->PopupWindow:[I
 Lcom/android/internal/R$styleable;->PopupWindow_popupAnimationStyle:I
@@ -2250,8 +2182,6 @@
 Lcom/android/internal/R$styleable;->RingtonePreference:[I
 Lcom/android/internal/R$styleable;->ScrollView:[I
 Lcom/android/internal/R$styleable;->ScrollView_fillViewport:I
-Lcom/android/internal/R$styleable;->Searchable:[I
-Lcom/android/internal/R$styleable;->SearchableActionKey:[I
 Lcom/android/internal/R$styleable;->SelectionModeDrawables:[I
 Lcom/android/internal/R$styleable;->Switch:[I
 Lcom/android/internal/R$styleable;->SwitchPreference:[I
@@ -2838,96 +2768,6 @@
 Lcom/android/internal/telephony/Connection;->mIsIncoming:Z
 Lcom/android/internal/telephony/Connection;->mNumberPresentation:I
 Lcom/android/internal/telephony/Connection;->setVideoState(I)V
-Lcom/android/internal/telephony/dataconnection/ApnContext;->getApnType()Ljava/lang/String;
-Lcom/android/internal/telephony/dataconnection/ApnContext;->getReason()Ljava/lang/String;
-Lcom/android/internal/telephony/dataconnection/ApnContext;->getState()Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/dataconnection/ApnContext;->isConnectable()Z
-Lcom/android/internal/telephony/dataconnection/ApnContext;->isDisconnected()Z
-Lcom/android/internal/telephony/dataconnection/ApnContext;->isEnabled()Z
-Lcom/android/internal/telephony/dataconnection/ApnContext;->isReady()Z
-Lcom/android/internal/telephony/dataconnection/ApnContext;->log(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/ApnContext;->mApnType:Ljava/lang/String;
-Lcom/android/internal/telephony/dataconnection/ApnContext;->mRefCount:I
-Lcom/android/internal/telephony/dataconnection/ApnContext;->mRefCountLock:Ljava/lang/Object;
-Lcom/android/internal/telephony/dataconnection/ApnContext;->setState(Lcom/android/internal/telephony/DctConstants$State;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;->mApnContext:Lcom/android/internal/telephony/dataconnection/ApnContext;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->clearSettings()V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->dumpToLog()V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->initConnection(Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;)Z
-Lcom/android/internal/telephony/dataconnection/DataConnection;->log(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mActivatingState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActivatingState;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mActiveState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActiveState;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mConnectionParams:Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mDataRegState:I
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mDct:Lcom/android/internal/telephony/dataconnection/DcTracker;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mDisconnectingErrorCreatingConnection:Lcom/android/internal/telephony/dataconnection/DataConnection$DcDisconnectionErrorCreatingConnection;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mDisconnectingState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcDisconnectingState;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mDisconnectParams:Lcom/android/internal/telephony/dataconnection/DataConnection$DisconnectParams;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mId:I
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mInactiveState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcInactiveState;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mLinkProperties:Landroid/net/LinkProperties;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mNetworkInfo:Landroid/net/NetworkInfo;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mPhone:Lcom/android/internal/telephony/Phone;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mRilRat:I
-Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyAllOfConnected(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyAllOfDisconnectDcRetrying(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyDisconnectCompleted(Lcom/android/internal/telephony/dataconnection/DataConnection$DisconnectParams;Z)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->onConnect(Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->tearDownData(Ljava/lang/Object;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->updateTcpBufferSizes(I)V
-Lcom/android/internal/telephony/dataconnection/DcController;->lr(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcController;->mDcListActiveByCid:Ljava/util/HashMap;
-Lcom/android/internal/telephony/dataconnection/DcController;->mDct:Lcom/android/internal/telephony/dataconnection/DcTracker;
-Lcom/android/internal/telephony/dataconnection/DcController;->mDcTesterDeactivateAll:Lcom/android/internal/telephony/dataconnection/DcTesterDeactivateAll;
-Lcom/android/internal/telephony/dataconnection/DcTracker$RecoveryAction;->isAggressiveRecovery(I)Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->cancelReconnectAlarm(Lcom/android/internal/telephony/dataconnection/ApnContext;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->cleanUpAllConnections(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->createAllApnList()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->getActiveApnTypes()[Ljava/lang/String;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->getOverallState()Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->getUiccRecords(I)Lcom/android/internal/telephony/uicc/IccRecords;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->isConnected()Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->isDisconnected()Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->isOnlySingleDcAllowed(I)Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->log(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->loge(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mAllApnSettings:Ljava/util/ArrayList;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mApnContexts:Ljava/util/concurrent/ConcurrentHashMap;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mAttached:Ljava/util/concurrent/atomic/AtomicBoolean;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mAutoAttachOnCreation:Ljava/util/concurrent/atomic/AtomicBoolean;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mDataConnectionTracker:Landroid/os/Handler;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mDisconnectPendingCount:I
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mIccRecords:Ljava/util/concurrent/atomic/AtomicReference;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mIsPsRestricted:Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mIsScreenOn:Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mNetStatPollEnabled:Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mNetStatPollPeriod:I
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mPhone:Lcom/android/internal/telephony/Phone;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mPrioritySortedApnContexts:Ljava/util/PriorityQueue;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mProvisioningSpinner:Landroid/app/ProgressDialog;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mResolver:Landroid/content/ContentResolver;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mState:Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mSubscriptionManager:Landroid/telephony/SubscriptionManager;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->notifyDataConnection(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->notifyOffApnsOfAvailability(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->onActionIntentDataStallAlarm(Landroid/content/Intent;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->onActionIntentProvisioningApnAlarm(Landroid/content/Intent;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->onRecordsLoadedOrSubIdChanged()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->onSetUserDataEnabled(Z)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->onTrySetupData(Lcom/android/internal/telephony/dataconnection/ApnContext;)Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->onTrySetupData(Ljava/lang/String;)Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->registerSettingsObserver()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->resetPollStats()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->restartDataStallAlarm()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->setInitialAttachApn()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->setPreferredApn(I)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->setRadio(Z)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->setupDataOnConnectableApns(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->startDataStallAlarm(Z)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->startNetStatPoll()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->stopDataStallAlarm()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->stopNetStatPoll()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->unregisterForAllDataDisconnected(Landroid/os/Handler;)V
 Lcom/android/internal/telephony/DctConstants$Activity;->DATAIN:Lcom/android/internal/telephony/DctConstants$Activity;
 Lcom/android/internal/telephony/DctConstants$Activity;->DATAINANDOUT:Lcom/android/internal/telephony/DctConstants$Activity;
 Lcom/android/internal/telephony/DctConstants$Activity;->DATAOUT:Lcom/android/internal/telephony/DctConstants$Activity;
@@ -2939,7 +2779,6 @@
 Lcom/android/internal/telephony/DctConstants$State;->FAILED:Lcom/android/internal/telephony/DctConstants$State;
 Lcom/android/internal/telephony/DctConstants$State;->IDLE:Lcom/android/internal/telephony/DctConstants$State;
 Lcom/android/internal/telephony/DctConstants$State;->RETRYING:Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/DctConstants$State;->SCANNING:Lcom/android/internal/telephony/DctConstants$State;
 Lcom/android/internal/telephony/DctConstants$State;->values()[Lcom/android/internal/telephony/DctConstants$State;
 Lcom/android/internal/telephony/DefaultPhoneNotifier;->mRegistry:Lcom/android/internal/telephony/ITelephonyRegistry;
 Lcom/android/internal/telephony/DriverCall$State;->ACTIVE:Lcom/android/internal/telephony/DriverCall$State;
@@ -3315,7 +3154,6 @@
 Lcom/android/internal/telephony/IPhoneStateListener$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IPhoneStateListener;
 Lcom/android/internal/telephony/IPhoneSubInfo$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Lcom/android/internal/telephony/IPhoneSubInfo$Stub$Proxy;->getDeviceId(Ljava/lang/String;)Ljava/lang/String;
-Lcom/android/internal/telephony/IPhoneSubInfo$Stub;-><init>()V
 Lcom/android/internal/telephony/IPhoneSubInfo$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IPhoneSubInfo;
 Lcom/android/internal/telephony/IPhoneSubInfo$Stub;->TRANSACTION_getDeviceId:I
 Lcom/android/internal/telephony/IPhoneSubInfo;->getIccSerialNumber(Ljava/lang/String;)Ljava/lang/String;
@@ -3362,13 +3200,9 @@
 Lcom/android/internal/telephony/ITelephonyRegistry$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Lcom/android/internal/telephony/ITelephonyRegistry$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephonyRegistry;
 Lcom/android/internal/telephony/ITelephonyRegistry;->listen(Ljava/lang/String;Lcom/android/internal/telephony/IPhoneStateListener;IZ)V
-Lcom/android/internal/telephony/ITelephonyRegistry;->notifyCallForwardingChanged(Z)V
 Lcom/android/internal/telephony/ITelephonyRegistry;->notifyCallState(ILjava/lang/String;)V
 Lcom/android/internal/telephony/ITelephonyRegistry;->notifyCellInfo(Ljava/util/List;)V
-Lcom/android/internal/telephony/ITelephonyRegistry;->notifyCellLocation(Landroid/os/Bundle;)V
-Lcom/android/internal/telephony/ITelephonyRegistry;->notifyDataActivity(I)V
-Lcom/android/internal/telephony/ITelephonyRegistry;->notifyDataConnectionFailed(Ljava/lang/String;Ljava/lang/String;)V
-Lcom/android/internal/telephony/ITelephonyRegistry;->notifyOtaspChanged(I)V
+Lcom/android/internal/telephony/ITelephonyRegistry;->notifyDataConnectionFailed(Ljava/lang/String;)V
 Lcom/android/internal/telephony/IWapPushManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IWapPushManager;
 Lcom/android/internal/telephony/IWapPushManager;->addPackage(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IZZ)Z
 Lcom/android/internal/telephony/IWapPushManager;->deletePackage(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z
@@ -4142,7 +3976,6 @@
 Lcom/android/internal/view/ActionBarPolicy;->hasEmbeddedTabs()Z
 Lcom/android/internal/view/ActionBarPolicy;->mContext:Landroid/content/Context;
 Lcom/android/internal/view/ActionBarPolicy;->showsOverflowMenuButton()Z
-Lcom/android/internal/view/BaseIWindow;-><init>()V
 Lcom/android/internal/view/IInputMethodManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Lcom/android/internal/view/IInputMethodManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/view/IInputMethodManager;
 Lcom/android/internal/view/IInputMethodSession$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/view/IInputMethodSession;
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 3095925..c8a2a9c 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -6137,12 +6137,6 @@
 libcore.io.ClassPathURLStreamHandler
 libcore.io.ClassPathURLStreamHandler$ClassPathURLConnection
 libcore.io.ClassPathURLStreamHandler$ClassPathURLConnection$1
-libcore.io.DropBox
-libcore.io.DropBox$DefaultReporter
-libcore.io.DropBox$Reporter
-libcore.io.EventLogger
-libcore.io.EventLogger$DefaultReporter
-libcore.io.EventLogger$Reporter
 libcore.io.ForwardingOs
 libcore.io.IoBridge
 libcore.io.IoTracker
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 997ed25..e86fa89 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -19,6 +19,7 @@
 import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -794,7 +795,7 @@
      *
      * @see android.R.styleable#AccessibilityService_nonInteractiveUiTimeout
      */
-    public void setNonInteractiveUiTimeoutMillis(int timeout) {
+    public void setNonInteractiveUiTimeoutMillis(@IntRange(from = 0) int timeout) {
         mNonInteractiveUiTimeout = timeout;
     }
 
@@ -821,7 +822,7 @@
      *
      * @see android.R.styleable#AccessibilityService_interactiveUiTimeout
      */
-    public void setInteractiveUiTimeoutMillis(int timeout) {
+    public void setInteractiveUiTimeoutMillis(@IntRange(from = 0) int timeout) {
         mInteractiveUiTimeout = timeout;
     }
 
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 579144b..26c2c0c 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -2226,12 +2226,12 @@
     }
 
     private abstract class AmsTask extends FutureTask<Bundle> implements AccountManagerFuture<Bundle> {
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         final IAccountManagerResponse mResponse;
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         final Handler mHandler;
         final AccountManagerCallback<Bundle> mCallback;
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         final Activity mActivity;
         public AmsTask(Activity activity, Handler handler, AccountManagerCallback<Bundle> callback) {
             super(new Callable<Bundle>() {
@@ -2556,13 +2556,13 @@
         }
         volatile AccountManagerFuture<Bundle> mFuture = null;
         final String mAccountType;
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         final String mAuthTokenType;
         final String[] mFeatures;
         final Bundle mAddAccountOptions;
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         final Bundle mLoginOptions;
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         final AccountManagerCallback<Bundle> mMyCallback;
         private volatile int mNumAccounts = 0;
 
diff --git a/core/java/android/accounts/AuthenticatorDescription.java b/core/java/android/accounts/AuthenticatorDescription.java
index 6875867..2360cec 100644
--- a/core/java/android/accounts/AuthenticatorDescription.java
+++ b/core/java/android/accounts/AuthenticatorDescription.java
@@ -17,6 +17,7 @@
 package android.accounts;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcelable;
 import android.os.Parcel;
 
@@ -77,7 +78,7 @@
         return new AuthenticatorDescription(type);
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private AuthenticatorDescription(String type) {
         this.type = type;
         this.packageName = null;
@@ -88,7 +89,7 @@
         this.customTokens = false;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private AuthenticatorDescription(Parcel source) {
         this.type = source.readString();
         this.packageName = source.readString();
diff --git a/core/java/android/annotation/Px.java b/core/java/android/annotation/Px.java
index a0ef224..ad99fdb 100644
--- a/core/java/android/annotation/Px.java
+++ b/core/java/android/annotation/Px.java
@@ -29,6 +29,8 @@
  * Denotes that a numeric parameter, field or method return value is expected
  * to represent a pixel dimension.
  *
+ * @paramDoc This units of this value are pixels.
+ * @returnDoc This units of this value are pixels.
  * {@hide}
  */
 @Documented
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index b584d5d..5b8261e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -121,7 +121,6 @@
 import android.view.autofill.AutofillManager.AutofillClient;
 import android.view.autofill.AutofillPopupWindow;
 import android.view.autofill.IAutofillWindowPresenter;
-import android.view.contentcapture.ContentCaptureEvent;
 import android.view.contentcapture.ContentCaptureManager;
 import android.widget.AdapterView;
 import android.widget.Toast;
@@ -849,7 +848,7 @@
     @UnsupportedAppUsage
     /*package*/ boolean mWindowAdded = false;
     /*package*/ boolean mVisibleFromServer = false;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     /*package*/ boolean mVisibleFromClient = true;
     /*package*/ ActionBar mActionBar = null;
     private boolean mEnableDefaultActionBarUp;
@@ -925,6 +924,9 @@
 
     private AutofillPopupWindow mAutofillPopupWindow;
 
+    /** @hide */
+    boolean mEnterAnimationComplete;
+
     private static native String getDlWarning();
 
     /** Return the intent that started this activity. */
@@ -1027,28 +1029,39 @@
         return mContentCaptureManager;
     }
 
-    private void notifyContentCaptureManagerIfNeeded(@ContentCaptureEvent.EventType int event) {
+    /** @hide */ private static final int CONTENT_CAPTURE_START = 1;
+    /** @hide */ private static final int CONTENT_CAPTURE_FLUSH = 2;
+    /** @hide */ private static final int CONTENT_CAPTURE_STOP = 3;
+
+    /** @hide */
+    @IntDef(prefix = { "CONTENT_CAPTURE_" }, value = {
+            CONTENT_CAPTURE_START,
+            CONTENT_CAPTURE_FLUSH,
+            CONTENT_CAPTURE_STOP
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface ContentCaptureNotificationType{}
+
+
+    private void notifyContentCaptureManagerIfNeeded(@ContentCaptureNotificationType int type) {
         final ContentCaptureManager cm = getContentCaptureManager();
         if (cm == null || !cm.isContentCaptureEnabled()) {
             return;
         }
-        switch (event) {
-            case ContentCaptureEvent.TYPE_ACTIVITY_CREATED:
+        switch (type) {
+            case CONTENT_CAPTURE_START:
                 //TODO(b/111276913): decide whether the InteractionSessionId should be
-                // saved / restored in the activity bundle.
-                cm.onActivityCreated(mToken, getComponentName());
+                // saved / restored in the activity bundle - probably not
+                cm.onActivityStarted(mToken, getComponentName());
                 break;
-            case ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED:
-                cm.onActivityDestroyed();
+            case CONTENT_CAPTURE_FLUSH:
+                cm.flush();
                 break;
-            case ContentCaptureEvent.TYPE_ACTIVITY_STARTED:
-            case ContentCaptureEvent.TYPE_ACTIVITY_RESUMED:
-            case ContentCaptureEvent.TYPE_ACTIVITY_PAUSED:
-            case ContentCaptureEvent.TYPE_ACTIVITY_STOPPED:
-                cm.onActivityLifecycleEvent(event);
+            case CONTENT_CAPTURE_STOP:
+                cm.onActivityStopped();
                 break;
             default:
-                Log.w(TAG, "notifyContentCaptureManagerIfNeeded(): invalid type " + event);
+                Log.wtf(TAG, "Invalid @ContentCaptureNotificationType: " + type);
         }
     }
 
@@ -1417,7 +1430,6 @@
         mRestoredFromBundle = savedInstanceState != null;
         mCalled = true;
 
-        notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_CREATED);
     }
 
     /**
@@ -1651,7 +1663,7 @@
         if (mAutoFillResetNeeded) {
             getAutofillManager().onVisibleForAutofill();
         }
-        notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_STARTED);
+        notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_START);
     }
 
     /**
@@ -1734,8 +1746,8 @@
                 }
             }
         }
-        notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_RESUMED);
         mCalled = true;
+        notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_FLUSH);
     }
 
     /**
@@ -2128,8 +2140,8 @@
                 mAutoFillIgnoreFirstResumePause = false;
             }
         }
-        notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_PAUSED);
         mCalled = true;
+        notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_FLUSH);
     }
 
     /**
@@ -2317,8 +2329,9 @@
                 getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_CANCEL,
                         mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN));
             }
-            notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_STOPPED);
+            notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_STOP);
         }
+        mEnterAnimationComplete = false;
     }
 
     /**
@@ -2388,9 +2401,6 @@
         }
 
         dispatchActivityDestroyed();
-
-        notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED);
-
     }
 
     /**
@@ -7079,6 +7089,8 @@
      * @hide
      */
     public void dispatchEnterAnimationComplete() {
+        mEnterAnimationComplete = true;
+        mInstrumentation.onEnterAnimationComplete();
         onEnterAnimationComplete();
         if (getWindow() != null && getWindow().getDecorView() != null) {
             getWindow().getDecorView().getViewTreeObserver().dispatchOnEnterAnimationComplete();
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 84c7785..d423260 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1976,6 +1976,32 @@
     }
 
     /**
+     * Check if the context is allowed to start an activity on specified display. Some launch
+     * restrictions may apply to secondary displays that are private, virtual, or owned by the
+     * system, in which case an activity start may throw a {@link SecurityException}. Call this
+     * method prior to starting an activity on a secondary display to check if the current context
+     * has access to it.
+     *
+     * @see ActivityOptions#setLaunchDisplayId(int)
+     * @see android.view.Display.FLAG_PRIVATE
+     * @see android.view.Display.TYPE_VIRTUAL
+     *
+     * @param context Source context, from which an activity will be started.
+     * @param displayId Target display id.
+     * @param intent Intent used to launch an activity.
+     * @return {@code true} if a call to start an activity on the target display is allowed for the
+     * provided context and no {@link SecurityException} will be thrown, {@code false} otherwise.
+     */
+    public boolean isActivityStartAllowedOnDisplay(Context context, int displayId, Intent intent) {
+        try {
+            return getTaskService().isActivityStartAllowedOnDisplay(displayId, intent,
+                    intent.resolveTypeIfNeeded(context.getContentResolver()), context.getUserId());
+        } catch (RemoteException e) {
+            throw new RuntimeException("Failure from system", e);
+        }
+    }
+
+    /**
      * Information you can retrieve about a particular Service that is
      * currently running in the system.
      */
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index b42d53a..0b50916 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -73,6 +73,12 @@
             IBinder whitelistToken, long duration);
 
     /**
+     * Allows for a {@link PendingIntent} to be whitelisted to start activities from background.
+     */
+    public abstract void setPendingIntentAllowBgActivityStarts(
+            IIntentSender target, IBinder whitelistToken, int flags);
+
+    /**
      * Allow DeviceIdleController to tell us about what apps are whitelisted.
      */
     public abstract void setDeviceIdleWhitelist(int[] allAppids, int[] exceptIdleAppids);
@@ -250,7 +256,7 @@
     public abstract int broadcastIntentInPackage(String packageName, int uid, Intent intent,
             String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData,
             Bundle resultExtras, String requiredPermission, Bundle bOptions, boolean serialized,
-            boolean sticky, int userId);
+            boolean sticky, int userId, boolean allowBackgroundActivityStarts);
     public abstract ComponentName startServiceInPackage(int uid, Intent service,
             String resolvedType, boolean fgRequired, String callingPackage, int userId)
             throws TransactionTooLargeException;
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 4d8c856..7eab5db 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -430,4 +430,17 @@
             e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Makes the display with the given id a single task instance display. I.e the display can only
+     * contain one task.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+    public void setDisplayToSingleTaskInstance(int displayId) {
+        try {
+            getService().setDisplayToSingleTaskInstance(displayId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index fe9b1ff..8faf08a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -86,7 +86,6 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Debug;
-import android.os.DropBoxManager;
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.GraphicsEnvironment;
@@ -166,8 +165,6 @@
 import dalvik.system.VMDebug;
 import dalvik.system.VMRuntime;
 
-import libcore.io.DropBox;
-import libcore.io.EventLogger;
 import libcore.io.ForwardingOs;
 import libcore.io.IoUtils;
 import libcore.io.Os;
@@ -3500,7 +3497,7 @@
         return sCurrentBroadcastIntent.get();
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private void handleReceiver(ReceiverData data) {
         // If we are getting ready to gc after going to the background, well
         // we are back active so skip it.
@@ -6155,7 +6152,7 @@
         try {
             synchronized (getGetProviderLock(auth, userId)) {
                 holder = ActivityManager.getService().getContentProvider(
-                        getApplicationThread(), auth, userId, stable);
+                        getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
             }
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
@@ -6726,9 +6723,6 @@
             }
         }
 
-        // add dropbox logging to libcore
-        DropBox.setReporter(new DropBoxReporter());
-
         ViewRootImpl.ConfigChangedCallback configChangedCallback
                 = (Configuration globalConfig) -> {
             synchronized (mResourcesManager) {
@@ -6782,39 +6776,6 @@
         }
     }
 
-    private static class EventLoggingReporter implements EventLogger.Reporter {
-        @Override
-        public void report (int code, Object... list) {
-            EventLog.writeEvent(code, list);
-        }
-    }
-
-    private static class DropBoxReporter implements DropBox.Reporter {
-
-        private DropBoxManager dropBox;
-
-        public DropBoxReporter() {}
-
-        @Override
-        public void addData(String tag, byte[] data, int flags) {
-            ensureInitialized();
-            dropBox.addData(tag, data, flags);
-        }
-
-        @Override
-        public void addText(String tag, String data) {
-            ensureInitialized();
-            dropBox.addText(tag, data);
-        }
-
-        private synchronized void ensureInitialized() {
-            if (dropBox == null) {
-                dropBox = currentActivityThread().getApplication()
-                        .getSystemService(DropBoxManager.class);
-            }
-        }
-    }
-
     private static class AndroidOs extends ForwardingOs {
         /**
          * Install selective syscall interception. For example, this is used to
@@ -6904,9 +6865,6 @@
 
         Environment.initForCurrentUser();
 
-        // Set the reporter for event logging in libcore
-        EventLogger.setReporter(new EventLoggingReporter());
-
         // Make sure TrustedCertificateStore looks in the right place for CA certificates
         final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
         TrustedCertificateStore.setDefaultUserDirectory(configDir);
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 2c435a2..57132a7 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -37,6 +37,7 @@
 import android.view.SurfaceHolder;
 import android.view.SurfaceSession;
 import android.view.SurfaceView;
+import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
@@ -83,6 +84,9 @@
     private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
     private Surface mTmpSurface = new Surface();
 
+    /** The ActivityView is only allowed to contain one task. */
+    private final boolean mSingleTaskInstance;
+
     @UnsupportedAppUsage
     public ActivityView(Context context) {
         this(context, null /* attrs */);
@@ -93,7 +97,13 @@
     }
 
     public ActivityView(Context context, AttributeSet attrs, int defStyle) {
+        this(context, attrs, defStyle, false /*singleTaskInstance*/);
+    }
+
+    public ActivityView(
+            Context context, AttributeSet attrs, int defStyle, boolean singleTaskInstance) {
         super(context, attrs, defStyle);
+        mSingleTaskInstance = singleTaskInstance;
 
         mActivityTaskManager = ActivityTaskManager.getService();
         mSurfaceView = new SurfaceView(context);
@@ -311,7 +321,7 @@
         public void surfaceCreated(SurfaceHolder surfaceHolder) {
             mTmpSurface = new Surface();
             if (mVirtualDisplay == null) {
-                initVirtualDisplay(new SurfaceSession(surfaceHolder.getSurface()));
+                initVirtualDisplay(new SurfaceSession());
                 if (mVirtualDisplay != null && mActivityViewCallback != null) {
                     mActivityViewCallback.onActivityViewReady(ActivityView.this);
                 }
@@ -345,6 +355,12 @@
         }
     }
 
+    @Override
+    protected void onVisibilityChanged(View changedView, int visibility) {
+        super.onVisibilityChanged(changedView, visibility);
+        mSurfaceView.setVisibility(visibility);
+    }
+
     private void initVirtualDisplay(SurfaceSession surfaceSession) {
         if (mVirtualDisplay != null) {
             throw new IllegalStateException("Trying to initialize for the second time.");
@@ -361,7 +377,8 @@
                 DISPLAY_NAME + "@" + System.identityHashCode(this),
                 width, height, getBaseDisplayDensity(), mTmpSurface,
                 DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
-                        | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
+                        | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+                        | DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL);
         if (mVirtualDisplay == null) {
             Log.e(TAG, "Failed to initialize ActivityView");
             return;
@@ -372,12 +389,16 @@
 
         mRootSurfaceControl = new SurfaceControl.Builder(surfaceSession)
                 .setContainerLayer(true)
+                .setParent(mSurfaceView.getSurfaceControl())
                 .setName(DISPLAY_NAME)
                 .build();
 
         try {
             wm.reparentDisplayContent(displayId, mRootSurfaceControl.getHandle());
             wm.dontOverrideDisplayInfo(displayId);
+            if (mSingleTaskInstance) {
+                mActivityTaskManager.setDisplayToSingleTaskInstance(displayId);
+            }
         } catch (RemoteException e) {
             e.rethrowAsRuntimeException();
         }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 6905cb5..78fe002 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -41,8 +41,10 @@
 import android.provider.Settings;
 import android.util.ArrayMap;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IAppOpsActiveCallback;
 import com.android.internal.app.IAppOpsCallback;
+import com.android.internal.app.IAppOpsNotedCallback;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.util.Preconditions;
 
@@ -86,10 +88,20 @@
      */
 
     final Context mContext;
+
     @UnsupportedAppUsage
     final IAppOpsService mService;
-    final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers = new ArrayMap<>();
-    final ArrayMap<OnOpActiveChangedListener, IAppOpsActiveCallback> mActiveWatchers =
+
+    @GuardedBy("mModeWatchers")
+    private final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers =
+            new ArrayMap<>();
+
+    @GuardedBy("mActiveWatchers")
+    private final ArrayMap<OnOpActiveChangedListener, IAppOpsActiveCallback> mActiveWatchers =
+            new ArrayMap<>();
+
+    @GuardedBy("mNotedWatchers")
+    private final ArrayMap<OnOpNotedListener, IAppOpsNotedCallback> mNotedWatchers =
             new ArrayMap<>();
 
     static IBinder sToken;
@@ -2471,6 +2483,23 @@
     }
 
     /**
+     * Callback for notification of an op being noted.
+     *
+     * @hide
+     */
+    public interface OnOpNotedListener {
+        /**
+         * Called when an op was noted.
+         *
+         * @param code The op code.
+         * @param uid The UID performing the operation.
+         * @param packageName The package performing the operation.
+         * @param result The result of the note.
+         */
+        void onOpNoted(int code, int uid, String packageName, int result);
+    }
+
+    /**
      * Callback for notification of changes to operation state.
      * This allows you to see the raw op codes instead of strings.
      * @hide
@@ -2819,7 +2848,7 @@
      */
     public void stopWatchingMode(OnOpChangedListener callback) {
         synchronized (mModeWatchers) {
-            IAppOpsCallback cb = mModeWatchers.get(callback);
+            IAppOpsCallback cb = mModeWatchers.remove(callback);
             if (cb != null) {
                 try {
                     mService.stopWatchingMode(cb);
@@ -2893,7 +2922,7 @@
     @TestApi
     public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) {
         synchronized (mActiveWatchers) {
-            final IAppOpsActiveCallback cb = mActiveWatchers.get(callback);
+            final IAppOpsActiveCallback cb = mActiveWatchers.remove(callback);
             if (cb != null) {
                 try {
                     mService.stopWatchingActive(cb);
@@ -2904,6 +2933,70 @@
         }
     }
 
+    /**
+     * Start watching for noted app ops. An app op may be immediate or long running.
+     * Immediate ops are noted while long running ones are started and stopped. This
+     * method allows registering a listener to be notified when an app op is noted. If
+     * an op is being noted by any package you will get a callback. To change the
+     * watched ops for a registered callback you need to unregister and register it again.
+     *
+     * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
+     * you can watch changes only for your UID.
+     *
+     * @param ops The ops to watch.
+     * @param callback Where to report changes.
+     *
+     * @see #startWatchingActive(int[], OnOpActiveChangedListener)
+     * @see #stopWatchingNoted(OnOpNotedListener)
+     * @see #noteOp(String, int, String)
+     *
+     * @hide
+     */
+    @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
+    public void startWatchingNoted(@NonNull int[] ops, @NonNull OnOpNotedListener callback) {
+        IAppOpsNotedCallback cb;
+        synchronized (mNotedWatchers) {
+            cb = mNotedWatchers.get(callback);
+            if (cb != null) {
+                return;
+            }
+            cb = new IAppOpsNotedCallback.Stub() {
+                @Override
+                public void opNoted(int op, int uid, String packageName, int mode) {
+                    callback.onOpNoted(op, uid, packageName, mode);
+                }
+            };
+            mNotedWatchers.put(callback, cb);
+        }
+        try {
+            mService.startWatchingNoted(ops, cb);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Stop watching for noted app ops. An app op may be immediate or long running.
+     * Unregistering a non-registered callback has no effect.
+     *
+     * @see #startWatchingNoted(int[], OnOpNotedListener)
+     * @see #noteOp(String, int, String)
+     *
+     * @hide
+     */
+    public void stopWatchingNoted(@NonNull OnOpNotedListener callback) {
+        synchronized (mNotedWatchers) {
+            final IAppOpsNotedCallback cb = mNotedWatchers.get(callback);
+            if (cb != null) {
+                try {
+                    mService.stopWatchingNoted(cb);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+        }
+    }
+
     private String buildSecurityExceptionMsg(int op, int uid, String packageName) {
         return packageName + " from uid " + uid + " not allowed to perform " + sOpNames[op];
     }
@@ -2981,7 +3074,7 @@
      */
     public int unsafeCheckOpRaw(String op, int uid, String packageName) {
         try {
-            return mService.checkOperation(strOpToOp(op), uid, packageName);
+            return mService.checkOperationRaw(strOpToOp(op), uid, packageName);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index 7fe21b2..b556033 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -16,7 +16,6 @@
 
 package android.app;
 
-import android.annotation.NonNull;
 import android.util.SparseIntArray;
 
 import com.android.internal.util.function.QuadFunction;
@@ -37,10 +36,11 @@
          * @param uid The UID for which to check.
          * @param packageName The package for which to check.
          * @param superImpl The super implementation.
+         * @param raw Whether to check the raw op i.e. not interpret the mode based on UID state.
          * @return The app op check result.
          */
-        int checkOperation(int code, int uid, String packageName,
-                TriFunction<Integer, Integer, String, Integer> superImpl);
+        int checkOperation(int code, int uid, String packageName, boolean raw,
+                QuadFunction<Integer, Integer, String, Boolean, Integer> superImpl);
 
         /**
          * Allows overriding check audio operation behavior.
@@ -85,10 +85,7 @@
      *
      * @param code The op code to set.
      * @param uid The UID for which to set.
-     * @param packageName The package for which to set.
      * @param mode The new mode to set.
-     * @param isPrivileged If the package is privileged
      */
-    public abstract void setMode(int code, int uid, @NonNull String packageName, int mode,
-            boolean isPrivileged);
+    public abstract void setUidMode(int code, int uid, int mode);
 }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 2b81c86..94983e1 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -794,12 +794,25 @@
 
     @Override
     public List<ModuleInfo> getInstalledModules(int flags) {
-        return null;
+        try {
+            return mPM.getInstalledModules(flags);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     @Override
     public ModuleInfo getModuleInfo(String packageName, int flags) throws NameNotFoundException {
-        return null;
+        try {
+            ModuleInfo mi = mPM.getModuleInfo(packageName, flags);
+            if (mi != null) {
+                return mi;
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        throw new NameNotFoundException("No module info for package: " + packageName);
     }
 
     @SuppressWarnings("unchecked")
@@ -2286,9 +2299,9 @@
     }
 
     @Override
-    public boolean canSuspendPackage(String packageName) {
+    public String[] getUnsuspendablePackages(String[] packageNames) {
         try {
-            return mPM.canSuspendPackageForUser(packageName, mContext.getUserId());
+            return mPM.getUnsuspendablePackagesForUser(packageNames, mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3002,7 +3015,6 @@
         }
     }
 
-    @Override
     public void sendDeviceCustomizationReadyBroadcast() {
         try {
             mPM.sendDeviceCustomizationReadyBroadcast();
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index e2f2075..fe23e21 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -20,10 +20,14 @@
 
 import android.app.NotificationManager.InterruptionFilter;
 import android.content.ComponentName;
+import android.content.Intent;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.service.notification.ZenPolicy;
+import android.service.notification.Condition;
+
+import com.android.internal.util.Preconditions;
 
 import java.util.Objects;
 
@@ -40,6 +44,7 @@
     private @InterruptionFilter int interruptionFilter;
     private Uri conditionId;
     private ComponentName owner;
+    private ComponentName configurationActivity;
     private long creationTime;
     private ZenPolicy mZenPolicy;
     private boolean mModified = false;
@@ -49,39 +54,51 @@
      *
      * @param name The name of the rule.
      * @param owner The Condition Provider service that owns this rule.
-     * @param conditionId A representation of the state that should cause the Condition Provider
-     *                    service to apply the given interruption filter.
      * @param interruptionFilter The interruption filter defines which notifications are allowed to
      *                           interrupt the user (e.g. via sound &amp; vibration) while this rule
      *                           is active.
      * @param enabled Whether the rule is enabled.
+     * @deprecated use {@link #AutomaticZenRule(String, ComponentName, ComponentName, Uri,
+     * ZenPolicy, int, boolean)}.
      */
+    @Deprecated
     public AutomaticZenRule(String name, ComponentName owner, Uri conditionId,
             int interruptionFilter, boolean enabled) {
-        this.name = name;
-        this.owner = owner;
-        this.conditionId = conditionId;
-        this.interruptionFilter = interruptionFilter;
-        this.enabled = enabled;
+        this(name, owner, null, conditionId, null, interruptionFilter, enabled);
     }
 
     /**
      * Creates an automatic zen rule.
      *
      * @param name The name of the rule.
-     * @param owner The Condition Provider service that owns this rule.
-     * @param conditionId A representation of the state that should cause the Condition Provider
-     *                    service to apply the given interruption filter.
+     * @param owner The Condition Provider service that owns this rule. This can be null if you're
+     *              using {@link NotificationManager#setAutomaticZenRuleState(String, Condition)}
+     *              instead of {@link android.service.notification.ConditionProviderService}.
+     * @param configurationActivity An activity that handles
+     *                              {@link NotificationManager#ACTION_AUTOMATIC_ZEN_RULE} that shows
+     *                              the user
+     *                              more information about this rule and/or allows them to
+     *                              configure it. This is required if you are not using a
+     *                              {@link android.service.notification.ConditionProviderService}.
+     *                              If you are, it overrides the information specified in your
+     *                              manifest.
+     * @param conditionId A representation of the state that should cause your app to apply the
+     *                    given interruption filter.
+     * @param interruptionFilter The interruption filter defines which notifications are allowed to
+     *                           interrupt the user (e.g. via sound &amp; vibration) while this rule
+     *                           is active.
      * @param policy The policy defines which notifications are allowed to interrupt the user
-     *               while this rule is active
+     *               while this rule is active. This overrides the global policy while this rule is
+     *               action ({@link Condition#STATE_TRUE}).
      * @param enabled Whether the rule is enabled.
      */
-    public AutomaticZenRule(String name, ComponentName owner, Uri conditionId, ZenPolicy policy,
-            boolean enabled) {
+    public AutomaticZenRule(String name, ComponentName owner, ComponentName configurationActivity,
+            Uri conditionId, ZenPolicy policy, int interruptionFilter, boolean enabled) {
         this.name = name;
         this.owner = owner;
+        this.configurationActivity = configurationActivity;
         this.conditionId = conditionId;
-        this.interruptionFilter = INTERRUPTION_FILTER_PRIORITY;
+        this.interruptionFilter = interruptionFilter;
         this.enabled = enabled;
         this.mZenPolicy = policy;
     }
@@ -89,18 +106,10 @@
     /**
      * @hide
      */
-    public AutomaticZenRule(String name, ComponentName owner, Uri conditionId,
-            int interruptionFilter, boolean enabled, long creationTime) {
-        this(name, owner, conditionId, interruptionFilter, enabled);
-        this.creationTime = creationTime;
-    }
-
-    /**
-     * @hide
-     */
-    public AutomaticZenRule(String name, ComponentName owner, Uri conditionId, ZenPolicy policy,
-            boolean enabled, long creationTime) {
-        this(name, owner, conditionId, policy, enabled);
+    public AutomaticZenRule(String name, ComponentName owner, ComponentName configurationActivity,
+            Uri conditionId, ZenPolicy policy, int interruptionFilter, boolean enabled,
+            long creationTime) {
+        this(name, owner, configurationActivity, conditionId, policy, interruptionFilter, enabled);
         this.creationTime = creationTime;
     }
 
@@ -112,6 +121,7 @@
         interruptionFilter = source.readInt();
         conditionId = source.readParcelable(null);
         owner = source.readParcelable(null);
+        configurationActivity = source.readParcelable(null);
         creationTime = source.readLong();
         mZenPolicy = source.readParcelable(null);
         mModified = source.readInt() == ENABLED;
@@ -125,6 +135,14 @@
     }
 
     /**
+     * Returns the {@link ComponentName} of the activity that shows configuration options
+     * for this rule.
+     */
+    public ComponentName getConfigurationActivity() {
+        return configurationActivity;
+    }
+
+    /**
      * Returns the representation of the state that causes this rule to become active.
      */
     public Uri getConditionId() {
@@ -218,6 +236,15 @@
         this.mZenPolicy = zenPolicy;
     }
 
+    /**
+     * Sets the configuration activity - an activity that handles
+     * {@link NotificationManager#ACTION_AUTOMATIC_ZEN_RULE} that shows the user more information
+     * about this rule and/or allows them to configure it.
+     */
+    public void setConfigurationActivity(ComponentName componentName) {
+        this.configurationActivity = componentName;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -235,6 +262,7 @@
         dest.writeInt(interruptionFilter);
         dest.writeParcelable(conditionId, 0);
         dest.writeParcelable(owner, 0);
+        dest.writeParcelable(configurationActivity, 0);
         dest.writeLong(creationTime);
         dest.writeParcelable(mZenPolicy, 0);
         dest.writeInt(mModified ? ENABLED : DISABLED);
@@ -248,6 +276,7 @@
                 .append(",interruptionFilter=").append(interruptionFilter)
                 .append(",conditionId=").append(conditionId)
                 .append(",owner=").append(owner)
+                .append(",configActivity=").append(configurationActivity)
                 .append(",creationTime=").append(creationTime)
                 .append(",mZenPolicy=").append(mZenPolicy)
                 .append(']').toString();
@@ -264,14 +293,15 @@
                 && other.interruptionFilter == interruptionFilter
                 && Objects.equals(other.conditionId, conditionId)
                 && Objects.equals(other.owner, owner)
-                && other.creationTime == creationTime
-                && Objects.equals(other.mZenPolicy, mZenPolicy);
+                && Objects.equals(other.mZenPolicy, mZenPolicy)
+                && Objects.equals(other.configurationActivity, configurationActivity)
+                && other.creationTime == creationTime;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(enabled, name, interruptionFilter, conditionId, owner, creationTime,
-                mZenPolicy, mModified);
+        return Objects.hash(enabled, name, interruptionFilter, conditionId, owner,
+                configurationActivity, mZenPolicy, mModified, creationTime);
     }
 
     public static final Parcelable.Creator<AutomaticZenRule> CREATOR
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 6bcfb2e..4bd935c 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -130,7 +130,7 @@
     private boolean mShowing = false;
     private boolean mCanceled = false;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final Handler mHandler = new Handler();
 
     private static final int DISMISS = 0x43;
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index dbc4e34..4f121aa 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -390,7 +390,7 @@
     ViewGroup mContainer;
 
     // The View generated for this fragment.
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     View mView;
 
     // Whether this fragment should defer starting until after other fragments
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 88fb025..fb519b6 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -124,7 +124,7 @@
             int ignoreWindowingMode);
     void moveTaskToFront(int task, int flags, in Bundle options);
     int getTaskForActivity(in IBinder token, in boolean onlyRoot);
-    ContentProviderHolder getContentProvider(in IApplicationThread caller,
+    ContentProviderHolder getContentProvider(in IApplicationThread caller, in String callingPackage,
             in String name, int userId, boolean stable);
     void publishContentProviders(in IApplicationThread caller,
             in List<ContentProviderHolder> providers);
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 777a494..dd87dc3 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -119,6 +119,8 @@
             in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
             int requestCode, int flags, in ProfilerInfo profilerInfo, in Bundle options,
             IBinder permissionToken, boolean ignoreTargetSecurity, int userId);
+    boolean isActivityStartAllowedOnDisplay(int displayId, in Intent intent, in String resolvedType,
+            int userId);
 
     void unhandledBack();
     boolean finishActivity(in IBinder token, int code, in Intent data, int finishTask);
@@ -450,4 +452,10 @@
      * Clears launch params for given packages.
      */
     void clearLaunchParamsForPackages(in List<String> packageNames);
+
+    /**
+     * Makes the display with the given id a single task instance display. I.e the display can only
+     * contain one task.
+     */
+    void setDisplayToSingleTaskInstance(int displayId);
 }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index e508d42..163be8e 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -65,6 +65,10 @@
     boolean areNotificationsEnabled(String pkg);
     int getPackageImportance(String pkg);
 
+    void setAppOverlaysAllowed(String pkg, int uid, boolean allowed);
+    boolean areAppOverlaysAllowed(String pkg);
+    boolean areAppOverlaysAllowedForPackage(String pkg, int uid);
+
     void createNotificationChannelGroups(String pkg, in ParceledListSlice channelGroupList);
     void createNotificationChannels(String pkg, in ParceledListSlice channelsList);
     void createNotificationChannelsForPackage(String pkg, int uid, in ParceledListSlice channelsList);
@@ -164,6 +168,7 @@
     boolean removeAutomaticZenRule(String id);
     boolean removeAutomaticZenRules(String packageName);
     int getRuleInstanceCount(in ComponentName owner);
+    void setAutomaticZenRuleState(String id, in Condition condition);
 
     byte[] getBackupPayload(int user);
     void applyRestore(in byte[] payload, int user);
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 3a2038d..666f721 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -142,19 +142,20 @@
      *
      * @param which either {@link WallpaperManager#FLAG_LOCK}
      * or {@link WallpaperManager#FLAG_SYSTEM}
+     * @param displayId Which display is interested
      * @return colors of chosen wallpaper
      */
-    WallpaperColors getWallpaperColors(int which, int userId);
+    WallpaperColors getWallpaperColors(int which, int userId, int displayId);
 
     /**
-     * Register a callback to receive color updates
+     * Register a callback to receive color updates from a display
      */
-    void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId);
+    void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId, int displayId);
 
     /**
-     * Unregister a callback that was receiving color updates
+     * Unregister a callback that was receiving color updates from a display
      */
-    void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId);
+    void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId, int displayId);
 
     /**
      * Called from SystemUI when it shows the AoD UI.
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 015bc6c..dda5569 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -29,6 +29,7 @@
 import android.content.res.Configuration;
 import android.hardware.input.InputManager;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.IBinder;
@@ -110,6 +111,7 @@
     private PerformanceCollector mPerformanceCollector;
     private Bundle mPerfMetrics = new Bundle();
     private UiAutomation mUiAutomation;
+    private final Object mAnimationCompleteLock = new Object();
 
     public Instrumentation() {
     }
@@ -397,6 +399,31 @@
         idler.waitForIdle();
     }
 
+    private void waitForEnterAnimationComplete(Activity activity) {
+        synchronized (mAnimationCompleteLock) {
+            long timeout = 5000;
+            try {
+                // We need to check that this specified Activity completed the animation, not just
+                // any Activity. If it was another Activity, then decrease the timeout by how long
+                // it's already waited and wait for the thread to wakeup again.
+                while (timeout > 0 && !activity.mEnterAnimationComplete) {
+                    long startTime = System.currentTimeMillis();
+                    mAnimationCompleteLock.wait(timeout);
+                    long totalTime = System.currentTimeMillis() - startTime;
+                    timeout -= totalTime;
+                }
+            } catch (InterruptedException e) {
+            }
+        }
+    }
+
+    /** @hide */
+    public void onEnterAnimationComplete() {
+        synchronized (mAnimationCompleteLock) {
+            mAnimationCompleteLock.notifyAll();
+        }
+    }
+
     /**
      * Execute a call on the application's main thread, blocking until it is
      * complete.  Useful for doing things that are not thread-safe, such as
@@ -499,6 +526,7 @@
                 }
             } while (mWaitingActivities.contains(aw));
 
+            waitForEnterAnimationComplete(aw.activity);
             return aw.activity;
         }
     }
@@ -2002,7 +2030,7 @@
     }
 
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static void checkStartActivityResult(int res, Object intent) {
         if (!ActivityManager.isStartResultFatalError(res)) {
             return;
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index f8309bc..1ae0f52 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -270,14 +270,14 @@
     }
 
     /**
+     * Handle returned by {@link KeyguardManager#newKeyguardLock} that allows
+     * you to disable / reenable the keyguard.
+     *
      * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
      * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
      * instead; this allows you to seamlessly hide the keyguard as your application
      * moves in and out of the foreground and does not require that any special
      * permissions be requested.
-     *
-     * Handle returned by {@link KeyguardManager#newKeyguardLock} that allows
-     * you to disable / reenable the keyguard.
      */
     @Deprecated
     public class KeyguardLock {
@@ -303,7 +303,7 @@
         @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
         public void disableKeyguard() {
             try {
-                mWM.disableKeyguard(mToken, mTag);
+                mWM.disableKeyguard(mToken, mTag, mContext.getUserId());
             } catch (RemoteException ex) {
             }
         }
@@ -322,16 +322,17 @@
         @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
         public void reenableKeyguard() {
             try {
-                mWM.reenableKeyguard(mToken);
+                mWM.reenableKeyguard(mToken, mContext.getUserId());
             } catch (RemoteException ex) {
             }
         }
     }
 
     /**
-     * @deprecated Use {@link KeyguardDismissCallback}
      * Callback passed to {@link KeyguardManager#exitKeyguardSecurely} to notify
      * caller of result.
+     *
+     * @deprecated Use {@link KeyguardDismissCallback}
      */
     @Deprecated
     public interface OnKeyguardExitResult {
@@ -380,12 +381,6 @@
     }
 
     /**
-     * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
-     * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
-     * instead; this allows you to seamlessly hide the keyguard as your application
-     * moves in and out of the foreground and does not require that any special
-     * permissions be requested.
-     *
      * Enables you to lock or unlock the keyguard. Get an instance of this class by
      * calling {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
      * This class is wrapped by {@link android.app.KeyguardManager KeyguardManager}.
@@ -394,6 +389,12 @@
      *
      * @return A {@link KeyguardLock} handle to use to disable and reenable the
      *   keyguard.
+     *
+     * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
+     *   and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
+     *   instead; this allows you to seamlessly hide the keyguard as your application
+     *   moves in and out of the foreground and does not require that any special
+     *   permissions be requested.
      */
     @Deprecated
     public KeyguardLock newKeyguardLock(String tag) {
@@ -430,13 +431,12 @@
     }
 
     /**
-     * @deprecated Use {@link #isKeyguardLocked()} instead.
-     *
      * If keyguard screen is showing or in restricted key input mode (i.e. in
      * keyguard password emergency screen). When in such mode, certain keys,
      * such as the Home key and the right soft keys, don't work.
      *
      * @return true if in keyguard restricted input mode.
+     * @deprecated Use {@link #isKeyguardLocked()} instead.
      */
     public boolean inKeyguardRestrictedInputMode() {
         return isKeyguardLocked();
@@ -588,12 +588,6 @@
     }
 
     /**
-     * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
-     * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
-     * instead; this allows you to seamlessly hide the keyguard as your application
-     * moves in and out of the foreground and does not require that any special
-     * permissions be requested.
-     *
      * Exit the keyguard securely.  The use case for this api is that, after
      * disabling the keyguard, your app, which was granted permission to
      * disable the keyguard and show a limited amount of information deemed
@@ -606,6 +600,12 @@
      * @param callback Lets you know whether the operation was successful and
      *   it is safe to launch anything that would normally be considered safe
      *   once the user has gotten past the keyguard.
+
+     * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
+     *   and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
+     *   instead; this allows you to seamlessly hide the keyguard as your application
+     *   moves in and out of the foreground and does not require that any special
+     *   permissions be requested.
      */
     @Deprecated
     @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index b9d5907..b657a91 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1427,18 +1427,13 @@
          */
         public static final int SEMANTIC_ACTION_CALL = 10;
 
-        /**
-         * {@code SemanticAction}: Contextual action - dependent on the current notification. E.g.
-         * open a Map application with an address shown in the notification.
-         */
-        public static final int SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION = 11;
-
         private final Bundle mExtras;
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         private Icon mIcon;
         private final RemoteInput[] mRemoteInputs;
         private boolean mAllowGeneratedReplies = true;
         private final @SemanticAction int mSemanticAction;
+        private final boolean mIsContextual;
 
         /**
          * Small icon representing the action.
@@ -1474,6 +1469,7 @@
             mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
             mAllowGeneratedReplies = in.readInt() == 1;
             mSemanticAction = in.readInt();
+            mIsContextual = in.readInt() == 1;
         }
 
         /**
@@ -1482,13 +1478,13 @@
         @Deprecated
         public Action(int icon, CharSequence title, PendingIntent intent) {
             this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true,
-                    SEMANTIC_ACTION_NONE);
+                    SEMANTIC_ACTION_NONE, false /* isContextual */);
         }
 
         /** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */
         private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
                 RemoteInput[] remoteInputs, boolean allowGeneratedReplies,
-                       @SemanticAction int semanticAction) {
+                       @SemanticAction int semanticAction, boolean isContextual) {
             this.mIcon = icon;
             if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
                 this.icon = icon.getResId();
@@ -1499,6 +1495,7 @@
             this.mRemoteInputs = remoteInputs;
             this.mAllowGeneratedReplies = allowGeneratedReplies;
             this.mSemanticAction = semanticAction;
+            this.mIsContextual = isContextual;
         }
 
         /**
@@ -1546,6 +1543,15 @@
         }
 
         /**
+         * Returns whether this is a contextual Action, i.e. whether the action is dependent on the
+         * notification message body. An example of a contextual action could be an action opening a
+         * map application with an address shown in the notification.
+         */
+        public boolean isContextual() {
+            return mIsContextual;
+        }
+
+        /**
          * Get the list of inputs to be collected from the user that ONLY accept data when this
          * action is sent. These remote inputs are guaranteed to return true on a call to
          * {@link RemoteInput#isDataOnly}.
@@ -1570,6 +1576,7 @@
             private final Bundle mExtras;
             private ArrayList<RemoteInput> mRemoteInputs;
             private @SemanticAction int mSemanticAction;
+            private boolean mIsContextual;
 
             /**
              * Construct a new builder for {@link Action} object.
@@ -1684,6 +1691,16 @@
             }
 
             /**
+             * Sets whether this {@link Action} is a contextual action, i.e. whether the action is
+             * dependent on the notification message body. An example of a contextual action could
+             * be an action opening a map application with an address shown in the notification.
+             */
+            public Builder setContextual(boolean isContextual) {
+                mIsContextual = isContextual;
+                return this;
+            }
+
+            /**
              * Apply an extender to this action builder. Extenders may be used to add
              * metadata or change options on this builder.
              */
@@ -1697,7 +1714,7 @@
              * necessary to display the action.
              */
             private void checkContextualActionNullFields() {
-                if (mSemanticAction != SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) return;
+                if (!mIsContextual) return;
 
                 if (mIcon == null) {
                     throw new NullPointerException("Contextual Actions must contain a valid icon");
@@ -1743,7 +1760,7 @@
                 RemoteInput[] textInputsArr = textInputs.isEmpty()
                         ? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
                 return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
-                        mAllowGeneratedReplies, mSemanticAction);
+                        mAllowGeneratedReplies, mSemanticAction, mIsContextual);
             }
         }
 
@@ -1756,7 +1773,8 @@
                     mExtras == null ? new Bundle() : new Bundle(mExtras),
                     getRemoteInputs(),
                     getAllowGeneratedReplies(),
-                    getSemanticAction());
+                    getSemanticAction(),
+                    isContextual());
         }
 
         @Override
@@ -1784,6 +1802,7 @@
             out.writeTypedArray(mRemoteInputs, flags);
             out.writeInt(mAllowGeneratedReplies ? 1 : 0);
             out.writeInt(mSemanticAction);
+            out.writeInt(mIsContextual ? 1 : 0);
         }
 
         public static final Parcelable.Creator<Action> CREATOR =
@@ -2073,8 +2092,7 @@
                 SEMANTIC_ACTION_UNMUTE,
                 SEMANTIC_ACTION_THUMBS_UP,
                 SEMANTIC_ACTION_THUMBS_DOWN,
-                SEMANTIC_ACTION_CALL,
-                SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION
+                SEMANTIC_ACTION_CALL
         })
         @Retention(RetentionPolicy.SOURCE)
         public @interface SemanticAction {}
@@ -3130,6 +3148,10 @@
         return mAppOverlayIntent;
     }
 
+    /**
+     * Returns whether the platform is allowed (by the app developer) to generate contextual actions
+     * for this notification.
+     */
     public boolean getAllowSystemGeneratedContextualActions() {
         return mAllowSystemGeneratedContextualActions;
     }
@@ -3226,8 +3248,7 @@
     }
 
     /**
-     * Returns the actions that are contextual (marked as SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) out
-     * of the actions in this notification.
+     * Returns the actions that are contextual out of the actions in this notification.
      *
      * @hide
      */
@@ -3236,8 +3257,7 @@
 
         List<Notification.Action> contextualActions = new ArrayList<>();
         for (Notification.Action action : actions) {
-            if (action.getSemanticAction()
-                    == Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) {
+            if (action.isContextual()) {
                 contextualActions.add(action);
             }
         }
@@ -5058,8 +5078,7 @@
                 List<Notification.Action> actions) {
             List<Notification.Action> nonContextualActions = new ArrayList<>();
             for (Notification.Action action : actions) {
-                if (action.getSemanticAction()
-                        != Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) {
+                if (!action.isContextual()) {
                     nonContextualActions.add(action);
                 }
             }
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index 2322a42..34cd9f0 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -50,20 +50,12 @@
     private static final String ATT_DESC = "desc";
     private static final String ATT_ID = "id";
     private static final String ATT_BLOCKED = "blocked";
-    private static final String ATT_ALLOW_APP_OVERLAY = "app_overlay";
     private static final String ATT_USER_LOCKED = "locked";
 
-    private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true;
-
     /**
      * @hide
      */
     public static final int USER_LOCKED_BLOCKED_STATE = 0x00000001;
-    /**
-     * @hide
-     */
-    @TestApi
-    public static final int USER_LOCKED_ALLOW_APP_OVERLAY = 0x00000002;
 
     /**
      * @see #getId()
@@ -74,7 +66,6 @@
     private String mDescription;
     private boolean mBlocked;
     private List<NotificationChannel> mChannels = new ArrayList<>();
-    private boolean mAllowAppOverlay = DEFAULT_ALLOW_APP_OVERLAY;
     // Bitwise representation of fields that have been changed by the user
     private int mUserLockedFields;
 
@@ -110,7 +101,6 @@
         }
         in.readParcelableList(mChannels, NotificationChannel.class.getClassLoader());
         mBlocked = in.readBoolean();
-        mAllowAppOverlay = in.readBoolean();
         mUserLockedFields = in.readInt();
     }
 
@@ -138,7 +128,6 @@
         }
         dest.writeParcelableList(mChannels, flags);
         dest.writeBoolean(mBlocked);
-        dest.writeBoolean(mAllowAppOverlay);
         dest.writeInt(mUserLockedFields);
     }
 
@@ -181,15 +170,6 @@
     }
 
     /**
-     * Returns whether notifications posted to this channel group can display outside of the
-     * notification shade, in a floating window on top of other apps. These may additionally be
-     * blocked at the notification channel level, see {@link NotificationChannel#canOverlayApps()}.
-     */
-    public boolean canOverlayApps() {
-        return mAllowAppOverlay;
-    }
-
-    /**
      * Sets the user visible description of this group.
      *
      * <p>The recommended maximum length is 300 characters; the value may be truncated if it is too
@@ -200,21 +180,6 @@
     }
 
     /**
-     * Sets whether notifications posted to this channel group can appear outside of the
-     * notification shade, floating over other apps' content.
-     *
-     * <p>This value will be ignored for notifications that are posted to channels that do not
-     * allow app overlays ({@link NotificationChannel#canOverlayApps()}.
-     *
-     * <p>Only modifiable before the channel is submitted to
-     * {@link NotificationManager#createNotificationChannelGroup(NotificationChannelGroup)}.</p>
-     * @see Notification#getAppOverlayIntent()
-     */
-    public void setAllowAppOverlay(boolean allowAppOverlay) {
-        mAllowAppOverlay = allowAppOverlay;
-    }
-
-    /**
      * @hide
      */
     @TestApi
@@ -266,7 +231,6 @@
         // Name, id, and importance are set in the constructor.
         setDescription(parser.getAttributeValue(null, ATT_DESC));
         setBlocked(safeBool(parser, ATT_BLOCKED, false));
-        setAllowAppOverlay(safeBool(parser, ATT_ALLOW_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY));
     }
 
     private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) {
@@ -289,9 +253,6 @@
             out.attribute(null, ATT_DESC, getDescription().toString());
         }
         out.attribute(null, ATT_BLOCKED, Boolean.toString(isBlocked()));
-        if (canOverlayApps() != DEFAULT_ALLOW_APP_OVERLAY) {
-            out.attribute(null, ATT_ALLOW_APP_OVERLAY, Boolean.toString(canOverlayApps()));
-        }
         out.attribute(null, ATT_USER_LOCKED, Integer.toString(mUserLockedFields));
 
         out.endTag(null, TAG_GROUP);
@@ -307,7 +268,6 @@
         record.put(ATT_NAME, getName());
         record.put(ATT_DESC, getDescription());
         record.put(ATT_BLOCKED, isBlocked());
-        record.put(ATT_ALLOW_APP_OVERLAY, canOverlayApps());
         record.put(ATT_USER_LOCKED, mUserLockedFields);
         return record;
     }
@@ -336,7 +296,6 @@
         if (o == null || getClass() != o.getClass()) return false;
         NotificationChannelGroup that = (NotificationChannelGroup) o;
         return isBlocked() == that.isBlocked() &&
-                mAllowAppOverlay == that.mAllowAppOverlay &&
                 mUserLockedFields == that.mUserLockedFields &&
                 Objects.equals(getId(), that.getId()) &&
                 Objects.equals(getName(), that.getName()) &&
@@ -347,7 +306,7 @@
     @Override
     public int hashCode() {
         return Objects.hash(getId(), getName(), getDescription(), isBlocked(), getChannels(),
-                mAllowAppOverlay, mUserLockedFields);
+                mUserLockedFields);
     }
 
     @Override
@@ -356,7 +315,6 @@
         cloned.setDescription(getDescription());
         cloned.setBlocked(isBlocked());
         cloned.setChannels(getChannels());
-        cloned.setAllowAppOverlay(canOverlayApps());
         cloned.lockFields(mUserLockedFields);
         return cloned;
     }
@@ -369,7 +327,6 @@
                 + ", mDescription=" + (!TextUtils.isEmpty(mDescription) ? "hasDescription " : "")
                 + ", mBlocked=" + mBlocked
                 + ", mChannels=" + mChannels
-                + ", mAllowAppOverlay=" + mAllowAppOverlay
                 + ", mUserLockedFields=" + mUserLockedFields
                 + '}';
     }
@@ -385,7 +342,6 @@
         for (NotificationChannel channel : mChannels) {
             channel.writeToProto(proto, NotificationChannelGroupProto.CHANNELS);
         }
-        proto.write(NotificationChannelGroupProto.ALLOW_APP_OVERLAY, mAllowAppOverlay);
         proto.end(token);
     }
 }
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 25fa897..aad3253 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -41,6 +41,7 @@
 import android.os.StrictMode;
 import android.os.UserHandle;
 import android.provider.Settings.Global;
+import android.service.notification.Condition;
 import android.service.notification.StatusBarNotification;
 import android.service.notification.ZenModeConfig;
 import android.util.Log;
@@ -262,6 +263,68 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface Importance {}
 
+    /**
+     * Activity Action: Launch an Automatic Zen Rule configuration screen
+     * <p>
+     * Input: Optionally, {@link #EXTRA_AUTOMATIC_RULE_ID}, if the configuration screen for an
+     * existing rule should be displayed. If the rule id is missing or null, apps should display
+     * a configuration screen where users can create a new instance of the rule.
+     * <p>
+     * Output: Nothing
+     * <p>
+     *     You can have multiple activities handling this intent, if you support multiple
+     *     {@link AutomaticZenRule rules}. In order for the system to properly display all of your
+     *     rule types so that users can create new instances or configure existing ones, you need
+     *     to add some extra metadata ({@link #META_DATA_AUTOMATIC_RULE_TYPE})
+     *     to your activity tag in your manifest. If you'd like to limit the number of rules a user
+     *     can create from this flow, you can additionally optionally include
+     *     {@link #META_DATA_RULE_INSTANCE_LIMIT}.
+     *
+     *     For example,
+     *     &lt;meta-data
+     *         android:name="android.app.zen.automatic.ruleType"
+     *         android:value="@string/my_condition_rule">
+     *     &lt;/meta-data>
+     *     &lt;meta-data
+     *         android:name="android.app.zen.automatic.ruleInstanceLimit"
+     *         android:value="1">
+     *     &lt;/meta-data>
+     * </p>
+     * </p>
+     *
+     * @see {@link #addAutomaticZenRule(AutomaticZenRule)}
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_AUTOMATIC_ZEN_RULE =
+            "android.app.action.AUTOMATIC_ZEN_RULE";
+
+    /**
+     * Used as an optional string extra on {@link #ACTION_AUTOMATIC_ZEN_RULE} intents. If
+     * provided, contains the id of the {@link AutomaticZenRule} (as returned from
+     * {@link NotificationManager#addAutomaticZenRule(AutomaticZenRule)}) for which configuration
+     * settings should be displayed.
+     */
+    public static final String EXTRA_AUTOMATIC_RULE_ID = "android.app.extra.AUTOMATIC_RULE_ID";
+
+    /**
+     * A required {@code meta-data} tag for activities that handle
+     * {@link #ACTION_AUTOMATIC_ZEN_RULE}.
+     *
+     * This tag should contain a localized name of the type of the zen rule provided by the
+     * activity.
+     */
+    public static final String META_DATA_AUTOMATIC_RULE_TYPE = "android.app.automatic.ruleType";
+
+    /**
+     * An optional {@code meta-data} tag for activities that handle
+     * {@link #ACTION_AUTOMATIC_ZEN_RULE}.
+     *
+     * This tag should contain the maximum number of rule instances that
+     * can be created for this rule type. Omit or enter a value <= 0 to allow unlimited instances.
+     */
+    public static final String META_DATA_RULE_INSTANCE_LIMIT =
+            "android.app.zen.automatic.ruleInstanceLimit";
+
     /** Value signifying that the user has not expressed a per-app visibility override value.
      * @hide */
     public static final int VISIBILITY_NO_OVERRIDE = -1000;
@@ -859,14 +922,10 @@
             List<ZenModeConfig.ZenRule> rules = service.getZenRules();
             Map<String, AutomaticZenRule> ruleMap = new HashMap<>();
             for (ZenModeConfig.ZenRule rule : rules) {
-                if (rule.zenPolicy == null) {
-                    ruleMap.put(rule.id, new AutomaticZenRule(rule.name, rule.component,
-                            rule.conditionId, zenModeToInterruptionFilter(rule.zenMode),
-                            rule.enabled, rule.creationTime));
-                } else {
-                    ruleMap.put(rule.id, new AutomaticZenRule(rule.name, rule.component,
-                            rule.conditionId, rule.zenPolicy, rule.enabled, rule.creationTime));
-                }
+                ruleMap.put(rule.id, new AutomaticZenRule(rule.name, rule.component,
+                        rule.configurationActivity, rule.conditionId, rule.zenPolicy,
+                        zenModeToInterruptionFilter(rule.zenMode), rule.enabled,
+                        rule.creationTime));
             }
             return ruleMap;
         } catch (RemoteException e) {
@@ -936,6 +995,26 @@
     }
 
     /**
+     * Informs the notification manager that the state of an {@link AutomaticZenRule} has changed.
+     * Use this method to put the system into Do Not Disturb mode or request that it exits Do Not
+     * Disturb mode. The calling app must own the provided {@link android.app.AutomaticZenRule}.
+     * <p>
+     *     This method can be used in conjunction with or as a replacement to
+     *     {@link android.service.notification.ConditionProviderService#notifyCondition(Condition)}.
+     * </p>
+     * @param id The id of the rule whose state should change
+     * @param condition The new state of this rule
+     */
+    public void setAutomaticZenRuleState(String id, Condition condition) {
+        INotificationManager service = getService();
+        try {
+            service.setAutomaticZenRuleState(id, condition);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Deletes the automatic zen rule with the given id.
      *
      * <p>
@@ -995,6 +1074,25 @@
         }
     }
 
+
+    /**
+     * Sets whether notifications posted by this app can appear outside of the
+     * notification shade, floating over other apps' content.
+     *
+     * <p>This value will be ignored for notifications that are posted to channels that do not
+     * allow app overlays ({@link NotificationChannel#canOverlayApps()}.
+     *
+     * @see Notification#getAppOverlayIntent()
+     */
+    public boolean areAppOverlaysAllowed() {
+        INotificationManager service = getService();
+        try {
+            return service.areAppOverlaysAllowed(mContext.getPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Checks the ability to modify notification do not disturb policy for the calling package.
      *
@@ -1540,46 +1638,6 @@
             return true;
         }
 
-        /**
-         * @hide
-         */
-        public static boolean areAnyScreenOffEffectsSuppressed(int effects) {
-            for (int i = 0; i < SCREEN_OFF_SUPPRESSED_EFFECTS.length; i++) {
-                final int effect = SCREEN_OFF_SUPPRESSED_EFFECTS[i];
-                if ((effects & effect) != 0) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        /**
-         * @hide
-         */
-        public static boolean areAnyScreenOnEffectsSuppressed(int effects) {
-            for (int i = 0; i < SCREEN_ON_SUPPRESSED_EFFECTS.length; i++) {
-                final int effect = SCREEN_ON_SUPPRESSED_EFFECTS[i];
-                if ((effects & effect) != 0) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        /**
-         * @hide
-         */
-        public static int toggleScreenOffEffectsSuppressed(int currentEffects, boolean suppress) {
-            return toggleEffects(currentEffects, SCREEN_OFF_SUPPRESSED_EFFECTS, suppress);
-        }
-
-        /**
-         * @hide
-         */
-        public static int toggleScreenOnEffectsSuppressed(int currentEffects, boolean suppress) {
-            return toggleEffects(currentEffects, SCREEN_ON_SUPPRESSED_EFFECTS, suppress);
-        }
-
         private static int toggleEffects(int currentEffects, int[] effects, boolean suppress) {
             for (int i = 0; i < effects.length; i++) {
                 final int effect = effects[i];
@@ -1739,6 +1797,41 @@
             return priorityMessageSenders;
         }
 
+        /** @hide **/
+        public boolean showFullScreenIntents() {
+            return (suppressedVisualEffects & SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) == 0;
+        }
+
+        /** @hide **/
+        public boolean showLights() {
+            return (suppressedVisualEffects & SUPPRESSED_EFFECT_LIGHTS) == 0;
+        }
+
+        /** @hide **/
+        public boolean showPeeking() {
+            return (suppressedVisualEffects & SUPPRESSED_EFFECT_PEEK) == 0;
+        }
+
+        /** @hide **/
+        public boolean showStatusBarIcons() {
+            return (suppressedVisualEffects & SUPPRESSED_EFFECT_STATUS_BAR) == 0;
+        }
+
+        /** @hide **/
+        public boolean showAmbient() {
+            return (suppressedVisualEffects & SUPPRESSED_EFFECT_AMBIENT) == 0;
+        }
+
+        /** @hide **/
+        public boolean showBadges() {
+            return (suppressedVisualEffects & SUPPRESSED_EFFECT_BADGE) == 0;
+        }
+
+        /** @hide **/
+        public boolean showInNotificationList() {
+            return (suppressedVisualEffects & SUPPRESSED_EFFECT_NOTIFICATION_LIST) == 0;
+        }
+
         /**
          * returns a deep copy of this policy
          * @hide
@@ -1759,12 +1852,17 @@
      * Recover a list of active notifications: ones that have been posted by the calling app that
      * have not yet been dismissed by the user or {@link #cancel(String, int)}ed by the app.
      *
-     * Each notification is embedded in a {@link StatusBarNotification} object, including the
+     * <p><Each notification is embedded in a {@link StatusBarNotification} object, including the
      * original <code>tag</code> and <code>id</code> supplied to
      * {@link #notify(String, int, Notification) notify()}
      * (via {@link StatusBarNotification#getTag() getTag()} and
      * {@link StatusBarNotification#getId() getId()}) as well as a copy of the original
      * {@link Notification} object (via {@link StatusBarNotification#getNotification()}).
+     * </p>
+     * <p>From {@link Build.VERSION_CODES#Q}, will also return notifications you've posted as an
+     * app's notification delegate via
+     * {@link NotificationManager#notifyAsPackage(String, String, int, Notification)}.
+     * </p>
      *
      * @return An array of {@link StatusBarNotification}.
      */
diff --git a/core/java/android/app/ProgressDialog.java b/core/java/android/app/ProgressDialog.java
index 1b10fd7..3193eb8 100644
--- a/core/java/android/app/ProgressDialog.java
+++ b/core/java/android/app/ProgressDialog.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -64,7 +65,7 @@
      */
     public static final int STYLE_HORIZONTAL = 1;
     
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private ProgressBar mProgress;
     @UnsupportedAppUsage
     private TextView mMessageView;
diff --git a/core/java/android/app/ResultInfo.java b/core/java/android/app/ResultInfo.java
index 4335488..d916c94 100644
--- a/core/java/android/app/ResultInfo.java
+++ b/core/java/android/app/ResultInfo.java
@@ -18,6 +18,7 @@
 
 import android.annotation.UnsupportedAppUsage;
 import android.content.Intent;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -65,7 +66,7 @@
         }
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static final Parcelable.Creator<ResultInfo> CREATOR
             = new Parcelable.Creator<ResultInfo>() {
         public ResultInfo createFromParcel(Parcel in) {
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index bf3d885..bbe5b8b 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -69,6 +69,22 @@
             | DISABLE_SYSTEM_INFO | DISABLE_RECENT | DISABLE_HOME | DISABLE_BACK | DISABLE_CLOCK
             | DISABLE_SEARCH;
 
+    @IntDef(flag = true, prefix = {"DISABLE_"}, value = {
+            DISABLE_NONE,
+            DISABLE_EXPAND,
+            DISABLE_NOTIFICATION_ICONS,
+            DISABLE_NOTIFICATION_ALERTS,
+            DISABLE_NOTIFICATION_TICKER,
+            DISABLE_SYSTEM_INFO,
+            DISABLE_HOME,
+            DISABLE_RECENT,
+            DISABLE_BACK,
+            DISABLE_CLOCK,
+            DISABLE_SEARCH
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DisableFlags {}
+
     /**
      * Flag to disable quick settings.
      *
@@ -104,10 +120,25 @@
     public static final int WINDOW_STATUS_BAR = 1;
     public static final int WINDOW_NAVIGATION_BAR = 2;
 
+    @IntDef(flag = true, prefix = { "WINDOW_" }, value = {
+        WINDOW_STATUS_BAR,
+        WINDOW_NAVIGATION_BAR
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface WindowType {}
+
     public static final int WINDOW_STATE_SHOWING = 0;
     public static final int WINDOW_STATE_HIDING = 1;
     public static final int WINDOW_STATE_HIDDEN = 2;
 
+    @IntDef(flag = true, prefix = { "WINDOW_STATE_" }, value = {
+            WINDOW_STATE_SHOWING,
+            WINDOW_STATE_HIDING,
+            WINDOW_STATE_HIDDEN
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface WindowVisibleState {}
+
     public static final int CAMERA_LAUNCH_SOURCE_WIGGLE = 0;
     public static final int CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP = 1;
     public static final int CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER = 2;
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index f4fd5d1..9ddf4bd 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -49,11 +49,14 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ShortcutManager;
 import android.content.res.Resources;
+import android.content.rollback.IRollbackManager;
+import android.content.rollback.RollbackManager;
 import android.debug.AdbManager;
 import android.debug.IAdbManager;
 import android.hardware.ConsumerIrManager;
 import android.hardware.ISerialManager;
 import android.hardware.SensorManager;
+import android.hardware.SensorPrivacyManager;
 import android.hardware.SerialManager;
 import android.hardware.SystemSensorManager;
 import android.hardware.biometrics.BiometricManager;
@@ -97,6 +100,7 @@
 import android.net.IpSecManager;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkScoreManager;
+import android.net.NetworkStack;
 import android.net.NetworkWatchlistManager;
 import android.net.lowpan.ILowpanManager;
 import android.net.lowpan.LowpanManager;
@@ -140,6 +144,7 @@
 import android.os.Vibrator;
 import android.os.health.SystemHealthManager;
 import android.os.storage.StorageManager;
+import android.permission.PermissionControllerManager;
 import android.permission.PermissionManager;
 import android.print.IPrintManager;
 import android.print.PrintManager;
@@ -314,6 +319,13 @@
                 return new ConnectivityManager(context, service);
             }});
 
+        registerService(Context.NETWORK_STACK_SERVICE, NetworkStack.class,
+                new StaticServiceFetcher<NetworkStack>() {
+                @Override
+                public NetworkStack createService() {
+                    return new NetworkStack();
+                }});
+
         registerService(Context.IPSEC_SERVICE, IpSecManager.class,
                 new CachedServiceFetcher<IpSecManager>() {
             @Override
@@ -503,6 +515,13 @@
                   ctx.mMainThread.getHandler().getLooper());
             }});
 
+        registerService(Context.SENSOR_PRIVACY_SERVICE, SensorPrivacyManager.class,
+                new CachedServiceFetcher<SensorPrivacyManager>() {
+                    @Override
+                    public SensorPrivacyManager createService(ContextImpl ctx) {
+                        return SensorPrivacyManager.getInstance(ctx);
+                    }});
+
         registerService(Context.STATS_MANAGER, StatsManager.class,
                 new CachedServiceFetcher<StatsManager>() {
             @Override
@@ -1146,6 +1165,13 @@
                         return new PermissionManager(ctx.getOuterContext());
                     }});
 
+        registerService(Context.PERMISSION_CONTROLLER_SERVICE, PermissionControllerManager.class,
+                new CachedServiceFetcher<PermissionControllerManager>() {
+                    @Override
+                    public PermissionControllerManager createService(ContextImpl ctx) {
+                        return new PermissionControllerManager(ctx.getOuterContext());
+                    }});
+
         registerService(Context.ROLE_SERVICE, RoleManager.class,
                 new CachedServiceFetcher<RoleManager>() {
                     @Override
@@ -1153,6 +1179,16 @@
                             throws ServiceNotFoundException {
                         return new RoleManager(ctx.getOuterContext());
                     }});
+
+        registerService(Context.ROLLBACK_SERVICE, RollbackManager.class,
+                new CachedServiceFetcher<RollbackManager>() {
+                    @Override
+                    public RollbackManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        IBinder b = ServiceManager.getServiceOrThrow(Context.ROLLBACK_SERVICE);
+                        return new RollbackManager(ctx.getOuterContext(),
+                                IRollbackManager.Stub.asInterface(b));
+                    }});
         //CHECKSTYLE:ON IndentationCheck
     }
 
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 27ae0b0..a929fe0 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -307,13 +307,14 @@
          * @param callback Listener
          * @param handler Thread to call it from. Main thread if null.
          * @param userId Owner of the wallpaper or UserHandle.USER_ALL
+         * @param displayId Caller comes from which display
          */
         public void addOnColorsChangedListener(@NonNull OnColorsChangedListener callback,
-                @Nullable Handler handler, int userId) {
+                @Nullable Handler handler, int userId, int displayId) {
             synchronized (this) {
                 if (!mColorCallbackRegistered) {
                     try {
-                        mService.registerWallpaperColorsCallback(this, userId);
+                        mService.registerWallpaperColorsCallback(this, userId, displayId);
                         mColorCallbackRegistered = true;
                     } catch (RemoteException e) {
                         // Failed, service is gone
@@ -329,16 +330,17 @@
          *
          * @param callback listener
          * @param userId Owner of the wallpaper or UserHandle.USER_ALL
+         * @param displayId Which display is interested
          */
         public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback,
-                int userId) {
+                int userId, int displayId) {
             synchronized (this) {
                 mColorListeners.removeIf(pair -> pair.first == callback);
 
                 if (mColorListeners.size() == 0 && mColorCallbackRegistered) {
                     mColorCallbackRegistered = false;
                     try {
-                        mService.unregisterWallpaperColorsCallback(this, userId);
+                        mService.unregisterWallpaperColorsCallback(this, userId, displayId);
                     } catch (RemoteException e) {
                         // Failed, service is gone
                         Log.w(TAG, "Can't unregister color updates", e);
@@ -370,14 +372,14 @@
             }
         }
 
-        WallpaperColors getWallpaperColors(int which, int userId) {
+        WallpaperColors getWallpaperColors(int which, int userId, int displayId) {
             if (which != FLAG_LOCK && which != FLAG_SYSTEM) {
                 throw new IllegalArgumentException(
                         "Must request colors for exactly one kind of wallpaper");
             }
 
             try {
-                return mService.getWallpaperColors(which, userId);
+                return mService.getWallpaperColors(which, userId, displayId);
             } catch (RemoteException e) {
                 // Can't get colors, connection lost.
             }
@@ -894,7 +896,7 @@
     @UnsupportedAppUsage
     public void addOnColorsChangedListener(@NonNull OnColorsChangedListener listener,
             @NonNull Handler handler, int userId) {
-        sGlobals.addOnColorsChangedListener(listener, handler, userId);
+        sGlobals.addOnColorsChangedListener(listener, handler, userId, mContext.getDisplayId());
     }
 
     /**
@@ -913,7 +915,7 @@
      */
     public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback,
             int userId) {
-        sGlobals.removeOnColorsChangedListener(callback, userId);
+        sGlobals.removeOnColorsChangedListener(callback, userId, mContext.getDisplayId());
     }
 
     /**
@@ -947,7 +949,7 @@
      */
     @UnsupportedAppUsage
     public @Nullable WallpaperColors getWallpaperColors(int which, int userId) {
-        return sGlobals.getWallpaperColors(which, userId);
+        return sGlobals.getWallpaperColors(which, userId, mContext.getDisplayId());
     }
 
     /**
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index beb1c78..121161e 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -29,6 +29,7 @@
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
@@ -174,7 +175,7 @@
     /** @hide */
     public static class PolicyInfo {
         public final int ident;
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         public final String tag;
         public final int label;
         public final int description;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 03e5933..7da67d9 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -55,6 +55,7 @@
 import android.net.ProxyInfo;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
@@ -906,9 +907,8 @@
         = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER";
 
     /**
-     * A String extra holding the URL-safe base64 encoded SHA-256 or SHA-1 hash (see notes below) of
-     * the file at download location specified in
-     * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}.
+     * A String extra holding the URL-safe base64 encoded SHA-256 hash of the file at download
+     * location specified in {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}.
      *
      * <p>Either this extra or {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM} must be
      * present. The provided checksum must match the checksum of the file at the download
@@ -921,7 +921,8 @@
      * <p><strong>Note:</strong> for devices running {@link android.os.Build.VERSION_CODES#LOLLIPOP}
      * and {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1} only SHA-1 hash is supported.
      * Starting from {@link android.os.Build.VERSION_CODES#M}, this parameter accepts SHA-256 in
-     * addition to SHA-1. Support for SHA-1 is likely to be removed in future OS releases.
+     * addition to SHA-1. From {@link android.os.Build.VERSION_CODES#Q}, only SHA-256 hash is
+     * supported.
      */
     public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM
         = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM";
@@ -2487,7 +2488,7 @@
     }
 
     /** @hide per-user version */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public int getPasswordQuality(@Nullable ComponentName admin, int userHandle) {
         if (mService != null) {
             try {
@@ -2553,7 +2554,7 @@
     }
 
     /** @hide per-user version */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public int getPasswordMinimumLength(@Nullable ComponentName admin, int userHandle) {
         if (mService != null) {
             try {
@@ -2623,7 +2624,7 @@
     }
 
     /** @hide per-user version */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public int getPasswordMinimumUpperCase(@Nullable ComponentName admin, int userHandle) {
         if (mService != null) {
             try {
@@ -2693,7 +2694,7 @@
     }
 
     /** @hide per-user version */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public int getPasswordMinimumLowerCase(@Nullable ComponentName admin, int userHandle) {
         if (mService != null) {
             try {
@@ -2762,7 +2763,7 @@
     }
 
     /** @hide per-user version */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public int getPasswordMinimumLetters(@Nullable ComponentName admin, int userHandle) {
         if (mService != null) {
             try {
@@ -2831,7 +2832,7 @@
     }
 
     /** @hide per-user version */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public int getPasswordMinimumNumeric(@Nullable ComponentName admin, int userHandle) {
         if (mService != null) {
             try {
@@ -2899,7 +2900,7 @@
     }
 
     /** @hide per-user version */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public int getPasswordMinimumSymbols(@Nullable ComponentName admin, int userHandle) {
         if (mService != null) {
             try {
@@ -2968,7 +2969,7 @@
     }
 
     /** @hide per-user version */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public int getPasswordMinimumNonLetter(@Nullable ComponentName admin, int userHandle) {
         if (mService != null) {
             try {
@@ -3120,7 +3121,7 @@
     }
 
     /** @hide per-user version */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public int getPasswordHistoryLength(@Nullable ComponentName admin, int userHandle) {
         if (mService != null) {
             try {
diff --git a/core/java/android/app/assist/AssistContent.java b/core/java/android/app/assist/AssistContent.java
index bdbce52..ff7e181 100644
--- a/core/java/android/app/assist/AssistContent.java
+++ b/core/java/android/app/assist/AssistContent.java
@@ -4,6 +4,7 @@
 import android.content.ClipData;
 import android.content.Intent;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -14,18 +15,18 @@
  * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}.
  */
 public class AssistContent implements Parcelable {
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private boolean mIsAppProvidedIntent = false;
     private boolean mIsAppProvidedWebUri = false;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private Intent mIntent;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private String mStructuredData;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private ClipData mClipData;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private Uri mUri;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final Bundle mExtras;
 
     public AssistContent() {
@@ -174,7 +175,7 @@
         mIsAppProvidedWebUri = in.readInt() == 1;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     void writeToParcelInternal(Parcel dest, int flags) {
         if (mIntent != null) {
             dest.writeInt(1);
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index c6086f1..a6f6d06 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -335,7 +335,8 @@
         if (sService != null) {
             try {
                 // All packages, current transport
-                IRestoreSession binder = sService.beginRestoreSession(null, null);
+                IRestoreSession binder =
+                        sService.beginRestoreSessionForUser(mContext.getUserId(), null, null);
                 if (binder != null) {
                     session = new RestoreSession(mContext, binder);
                 }
@@ -465,7 +466,7 @@
         checkServiceBinder();
         if (sService != null) {
             try {
-                return sService.getCurrentTransportComponent();
+                return sService.getCurrentTransportComponentForUser(mContext.getUserId());
             } catch (RemoteException e) {
                 Log.e(TAG, "getCurrentTransportComponent() couldn't connect");
             }
@@ -530,7 +531,8 @@
         checkServiceBinder();
         if (sService != null) {
             try {
-                sService.updateTransportAttributes(
+                sService.updateTransportAttributesForUser(
+                        mContext.getUserId(),
                         transportComponent,
                         name,
                         configurationIntent,
@@ -590,7 +592,8 @@
             try {
                 SelectTransportListenerWrapper wrapper = listener == null ?
                         null : new SelectTransportListenerWrapper(mContext, listener);
-                sService.selectBackupTransportAsync(transport, wrapper);
+                sService.selectBackupTransportAsyncForUser(
+                        mContext.getUserId(), transport, wrapper);
             } catch (RemoteException e) {
                 Log.e(TAG, "selectBackupTransportAsync() couldn't connect");
             }
@@ -637,7 +640,7 @@
         checkServiceBinder();
         if (sService != null) {
             try {
-                return sService.getAvailableRestoreToken(packageName);
+                return sService.getAvailableRestoreTokenForUser(mContext.getUserId(), packageName);
             } catch (RemoteException e) {
                 Log.e(TAG, "getAvailableRestoreToken() couldn't connect");
             }
@@ -659,7 +662,7 @@
         checkServiceBinder();
         if (sService != null) {
             try {
-                return sService.isAppEligibleForBackup(packageName);
+                return sService.isAppEligibleForBackupForUser(mContext.getUserId(), packageName);
             } catch (RemoteException e) {
                 Log.e(TAG, "isAppEligibleForBackup(pkg) couldn't connect");
             }
@@ -760,7 +763,7 @@
     public Intent getConfigurationIntent(String transportName) {
         if (sService != null) {
             try {
-                return sService.getConfigurationIntent(transportName);
+                return sService.getConfigurationIntentForUser(mContext.getUserId(), transportName);
             } catch (RemoteException e) {
                 Log.e(TAG, "getConfigurationIntent() couldn't connect");
             }
@@ -781,7 +784,7 @@
     public String getDestinationString(String transportName) {
         if (sService != null) {
             try {
-                return sService.getDestinationString(transportName);
+                return sService.getDestinationStringForUser(mContext.getUserId(), transportName);
             } catch (RemoteException e) {
                 Log.e(TAG, "getDestinationString() couldn't connect");
             }
@@ -802,7 +805,7 @@
     public Intent getDataManagementIntent(String transportName) {
         if (sService != null) {
             try {
-                return sService.getDataManagementIntent(transportName);
+                return sService.getDataManagementIntentForUser(mContext.getUserId(), transportName);
             } catch (RemoteException e) {
                 Log.e(TAG, "getDataManagementIntent() couldn't connect");
             }
@@ -825,7 +828,7 @@
     public String getDataManagementLabel(String transportName) {
         if (sService != null) {
             try {
-                return sService.getDataManagementLabel(transportName);
+                return sService.getDataManagementLabelForUser(mContext.getUserId(), transportName);
             } catch (RemoteException e) {
                 Log.e(TAG, "getDataManagementLabel() couldn't connect");
             }
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index 0afb98f..f8c5a81 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -43,6 +43,15 @@
      * Any application can invoke this method for its own package, but
      * only callers who hold the android.permission.BACKUP permission
      * may invoke it for arbitrary packages.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which the caller has made changes to its data.
+     */
+    void dataChangedForUser(int userId, String packageName);
+
+    /**
+     * {@link android.app.backup.IBackupManager.dataChangedForUser} for the calling user id.
      */
     void dataChanged(String packageName);
 
@@ -53,6 +62,15 @@
      * Any application can invoke this method for its own package, but
      * only callers who hold the android.permission.BACKUP permission
      * may invoke it for arbitrary packages.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which backup data should be erased.
+     */
+    void clearBackupDataForUser(int userId, String transportName, String packageName);
+
+    /**
+     * {@link android.app.backup.IBackupManager.clearBackupDataForUser} for the calling user id.
      */
     void clearBackupData(String transportName, String packageName);
 
@@ -62,24 +80,59 @@
      * operations.
      *
      * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which the given transports should be initialized.
      */
-    void initializeTransports(in String[] transportNames, IBackupObserver observer);
+    void initializeTransportsForUser(int userId, in String[] transportNames,
+        IBackupObserver observer);
 
     /**
      * Notifies the Backup Manager Service that an agent has become available.  This
      * method is only invoked by the Activity Manager.
+     *
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which an agent has become available.
+     */
+    void agentConnectedForUser(int userId, String packageName, IBinder agent);
+
+    /**
+     * {@link android.app.backup.IBackupManager.agentConnected} for the calling user id.
      */
     void agentConnected(String packageName, IBinder agent);
 
     /**
      * Notify the Backup Manager Service that an agent has unexpectedly gone away.
      * This method is only invoked by the Activity Manager.
+     *
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which an agent has unexpectedly gone away.
+     */
+    void agentDisconnectedForUser(int userId, String packageName);
+
+    /**
+     * {@link android.app.backup.IBackupManager.agentDisconnected} for the calling user id.
      */
     void agentDisconnected(String packageName);
 
     /**
      * Notify the Backup Manager Service that an application being installed will
      * need a data-restore pass.  This method is only invoked by the Package Manager.
+     *
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which the application will need a data-restore pass.
+     */
+    void restoreAtInstallForUser(int userId, String packageName, int token);
+
+    /**
+     * {@link android.app.backup.IBackupManager.restoreAtInstallForUser} for the calling user id.
      */
     void restoreAtInstall(String packageName, int token);
 
@@ -112,18 +165,19 @@
      * is made generally available for launch.
      *
      * <p>Callers must hold  the android.permission.BACKUP permission to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
      *
+     * @param userId User id for which automatic restore should be enabled/disabled.
      * @param doAutoRestore When true, enables the automatic app-data restore facility.  When
      *   false, this facility will be disabled.
      */
-    void setAutoRestore(boolean doAutoRestore);
+    void setAutoRestoreForUser(int userId, boolean doAutoRestore);
 
     /**
-     * Indicate that any necessary one-time provisioning has occurred.
-     *
-     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     * {@link android.app.backup.IBackupManager.setAutoRestoreForUser} for the calling user id.
      */
-    void setBackupProvisioned(boolean isProvisioned);
+    void setAutoRestore(boolean doAutoRestore);
 
     /**
      * Report whether the backup mechanism is currently enabled.
@@ -189,8 +243,11 @@
      * completed.
      *
      * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     * If the {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
      *
-     * @param fd The file descriptor to which a 'tar' file stream is to be written
+     * @param userId User id for which backup should be performed.
+     * @param fd The file descriptor to which a 'tar' file stream is to be written.
      * @param includeApks If <code>true</code>, the resulting tar stream will include the
      *     application .apk files themselves as well as their data.
      * @param includeObbs If <code>true</code>, the resulting tar stream will include any
@@ -209,7 +266,7 @@
      * @param packageNames The package names of the apps whose data (and optionally .apk files)
      *     are to be backed up.  The <code>allApps</code> parameter supersedes this.
      */
-    void adbBackup(in ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
+    void adbBackup(int userId, in ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
             boolean includeShared, boolean doWidgets, boolean allApps, boolean allIncludesSystem,
             boolean doCompress, boolean doKeyValue, in String[] packageNames);
 
@@ -217,9 +274,13 @@
      * Perform a full-dataset backup of the given applications via the currently active
      * transport.
      *
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which the full-dataset backup should be performed.
      * @param packageNames The package names of the apps whose data are to be backed up.
      */
-    void fullTransportBackup(in String[] packageNames);
+    void fullTransportBackupForUser(int userId, in String[] packageNames);
 
     /**
      * Restore device content from the data stream passed through the given socket.  The
@@ -227,8 +288,12 @@
      * Currently only used by the 'adb restore' command.
      *
      * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     * If the {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL.
+     *
+     * @param userId User id for which restore should be performed.
      */
-    void adbRestore(in ParcelFileDescriptor fd);
+    void adbRestore(int userId, in ParcelFileDescriptor fd);
 
     /**
      * Confirm that the requested full backup/restore operation can proceed.  The system will
@@ -243,6 +308,18 @@
      * backup dataset being used for restore.
      *
      * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which the requested backup/restore operation can proceed.
+     */
+    void acknowledgeFullBackupOrRestoreForUser(int userId, int token, boolean allow,
+            in String curPassword, in String encryptionPassword,
+            IFullBackupRestoreObserver observer);
+
+    /**
+     * {@link android.app.backup.IBackupManager.acknowledgeFullBackupOrRestoreForUser} for the
+     * calling user id.
      */
     void acknowledgeFullBackupOrRestore(int token, boolean allow,
             in String curPassword, in String encryptionPassword,
@@ -253,7 +330,10 @@
      * specified transport has not been bound at least once (for registration), this call will be
      * ignored. Only the host process of the transport can change its description, otherwise a
      * {@link SecurityException} will be thrown.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
      *
+     * @param userId User id for which the attributes of the transport should be updated.
      * @param transportComponent The identity of the transport being described.
      * @param name A {@link String} with the new name for the transport. This is NOT for
      *     identification. MUST NOT be {@code null}.
@@ -272,13 +352,23 @@
      * @throws SecurityException If the UID of the calling process differs from the package UID of
      *     {@code transportComponent} or if the caller does NOT have BACKUP permission.
      */
-    void updateTransportAttributes(in ComponentName transportComponent, in String name,
+    void updateTransportAttributesForUser(int userId, in ComponentName transportComponent,
+            in String name,
             in Intent configurationIntent, in String currentDestinationString,
             in Intent dataManagementIntent, in String dataManagementLabel);
 
     /**
      * Identify the currently selected transport.  Callers must hold the
      * android.permission.BACKUP permission to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which the currently selected transport should be identified.
+     */
+    String getCurrentTransportForUser(int userId);
+
+    /**
+     * {@link android.app.backup.IBackupManager.getCurrentTransportForUser} for the calling user id.
      */
     String getCurrentTransport();
 
@@ -286,16 +376,35 @@
       * Returns the {@link ComponentName} of the host service of the selected transport or {@code
       * null} if no transport selected or if the transport selected is not registered.  Callers must
       * hold the android.permission.BACKUP permission to use this method.
+      * If {@code userId} is different from the calling user id, then the caller must hold the
+      * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+      *
+      * @param userId User id for which the currently selected transport should be identified.
       */
-    ComponentName getCurrentTransportComponent();
+    ComponentName getCurrentTransportComponentForUser(int userId);
 
     /**
      * Request a list of all available backup transports' names.  Callers must
      * hold the android.permission.BACKUP permission to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which all available backup transports' names should be listed.
+     */
+    String[] listAllTransportsForUser(int userId);
+
+    /**
+     * {@link android.app.backup.IBackupManager.listAllTransportsForUser} for the calling user id.
      */
     String[] listAllTransports();
 
-    ComponentName[] listAllTransportComponents();
+    /**
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which all available backup transports should be listed.
+     */
+    ComponentName[] listAllTransportComponentsForUser(int userId);
 
     /**
      * Retrieve the list of whitelisted transport components.  Callers do </i>not</i> need
@@ -308,13 +417,22 @@
     /**
      * Specify the current backup transport.  Callers must hold the
      * android.permission.BACKUP permission to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
      *
+     * @param userId User id for which the transport should be selected.
      * @param transport The name of the transport to select.  This should be one
      * of {@link BackupManager.TRANSPORT_GOOGLE} or {@link BackupManager.TRANSPORT_ADB}.
      * @return The name of the previously selected transport.  If the given transport
      *   name is not one of the currently available transports, no change is made to
      *   the current transport setting and the method returns null.
      */
+    String selectBackupTransportForUser(int userId, String transport);
+
+    /**
+     * {@link android.app.backup.IBackupManager.selectBackupTransportForUser} for the calling user
+     * id.
+     */
     String selectBackupTransport(String transport);
 
     /**
@@ -323,43 +441,85 @@
      * which is in a separate process.
      *
      * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
      *
+     * @param userId User id for which the transport should be selected.
      * @param transport ComponentName of the service hosting the transport. This is different from
      *                  the transport's name that is returned by {@link BackupTransport#name()}.
      * @param listener A listener object to get a callback on the transport being selected.
      */
-    void selectBackupTransportAsync(in ComponentName transport, ISelectBackupTransportCallback listener);
+    void selectBackupTransportAsyncForUser(int userId, in ComponentName transport,
+        ISelectBackupTransportCallback listener);
 
     /**
      * Get the configuration Intent, if any, from the given transport.  Callers must
      * hold the android.permission.BACKUP permission in order to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
      *
+     * @param userId User id for which the configuration Intent should be reported.
      * @param transport The name of the transport to query.
      * @return An Intent to use with Activity#startActivity() to bring up the configuration
      *   UI supplied by the transport.  If the transport has no configuration UI, it should
      *   return {@code null} here.
      */
+    Intent getConfigurationIntentForUser(int userId, String transport);
+
+    /**
+     * {@link android.app.backup.IBackupManager.getConfigurationIntentForUser} for the calling user
+     * id.
+     */
     Intent getConfigurationIntent(String transport);
 
     /**
      * Get the destination string supplied by the given transport.  Callers must
      * hold the android.permission.BACKUP permission in order to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
      *
+     * @param userId User id for which the transport destination string should be reported.
      * @param transport The name of the transport to query.
      * @return A string describing the current backup destination.  This string is used
      *   verbatim by the Settings UI as the summary text of the "configure..." item.
      */
+    String getDestinationStringForUser(int userId, String transport);
+
+    /**
+     * {@link android.app.backup.IBackupManager.getDestinationStringForUser} for the calling user
+     * id.
+     */
     String getDestinationString(String transport);
 
     /**
      * Get the manage-data UI intent, if any, from the given transport.  Callers must
      * hold the android.permission.BACKUP permission in order to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which the manage-data UI intent should be reported.
+     */
+    Intent getDataManagementIntentForUser(int userId, String transport);
+
+    /**
+     * {@link android.app.backup.IBackupManager.getDataManagementIntentForUser} for the calling user
+     * id.
      */
     Intent getDataManagementIntent(String transport);
 
     /**
      * Get the manage-data menu label, if any, from the given transport.  Callers must
      * hold the android.permission.BACKUP permission in order to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which the manage-data menu label should be reported.
+     */
+    String getDataManagementLabelForUser(int userId, String transport);
+
+    /**
+     * {@link android.app.backup.IBackupManager.getDataManagementLabelForUser} for the calling user
+     * id.
      */
     String getDataManagementLabel(String transport);
 
@@ -374,7 +534,10 @@
      * package.  In that case, the restore session returned is suitable for supporting
      * the BackupManager.requestRestore() functionality via RestoreSession.restorePackage()
      * without requiring the app to hold any special permission.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
      *
+     * @param userId User id for which a restore session should be begun.
      * @param packageName The name of the single package for which a restore will
      *        be requested.  May be null, in which case all packages in the restore
      *        set can be restored.
@@ -382,7 +545,7 @@
      *        May be null, in which case the current active transport is used.
      * @return An interface to the restore session, or null on error.
      */
-    IRestoreSession beginRestoreSession(String packageName, String transportID);
+    IRestoreSession beginRestoreSessionForUser(int userId, String packageName, String transportID);
 
     /**
      * Notify the backup manager that a BackupAgent has completed the operation
@@ -420,13 +583,16 @@
      * restored from if we were to install it right now.
      *
      * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
      *
+     * @param userId User id for which this operation should be performed.
      * @param packageName The name of the package whose most-suitable dataset we
      *     wish to look up
      * @return The dataset token from which a restore should be attempted, or zero if
      *     no suitable data is available.
      */
-    long getAvailableRestoreToken(String packageName);
+    long getAvailableRestoreTokenForUser(int userId, String packageName);
 
     /**
      * Ask the framework whether this app is eligible for backup.
@@ -435,21 +601,27 @@
      * {@link #filterAppsEligibleForBackup(String[])} to save resources.
      *
      * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
      *
+     * @param userId User id for which this operation should be performed.
      * @param packageName The name of the package.
      * @return Whether this app is eligible for backup.
      */
-    boolean isAppEligibleForBackup(String packageName);
+    boolean isAppEligibleForBackupForUser(int userId, String packageName);
 
     /**
      * Filter the packages that are eligible for backup and return the result.
      *
      * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
      *
+     * @param userId User id for which the filter should be performed.
      * @param packages The list of packages to filter.
      * @return The packages eligible for backup.
      */
-    String[] filterAppsEligibleForBackup(in String[] packages);
+    String[] filterAppsEligibleForBackupForUser(int userId, in String[] packages);
 
     /**
      * Request an immediate backup, providing an observer to which results of the backup operation
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index e84517d..e7fe161 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -1545,13 +1545,6 @@
          * @return The job object to hand to the JobScheduler. This object is immutable.
          */
         public JobInfo build() {
-            // Allow jobs with no constraints - What am I, a database?
-            if (!mHasEarlyConstraint && !mHasLateConstraint && mConstraintFlags == 0 &&
-                    mNetworkRequest == null &&
-                    mTriggerContentUris == null) {
-                throw new IllegalArgumentException("You're trying to build a job with no " +
-                        "constraints, this is not allowed.");
-            }
             // Check that network estimates require network type
             if ((mNetworkDownloadBytes > 0 || mNetworkUploadBytes > 0) && mNetworkRequest == null) {
                 throw new IllegalArgumentException(
diff --git a/core/java/android/app/job/JobWorkItem.java b/core/java/android/app/job/JobWorkItem.java
index bfc6df2..f804681 100644
--- a/core/java/android/app/job/JobWorkItem.java
+++ b/core/java/android/app/job/JobWorkItem.java
@@ -21,6 +21,7 @@
 import android.annotation.BytesLong;
 import android.annotation.UnsupportedAppUsage;
 import android.content.Intent;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -30,15 +31,15 @@
  * {@link JobParameters#dequeueWork() JobParameters.dequeueWork} for more details.
  */
 final public class JobWorkItem implements Parcelable {
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     final Intent mIntent;
     final long mNetworkDownloadBytes;
     final long mNetworkUploadBytes;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     int mDeliveryCount;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     int mWorkId;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     Object mGrants;
 
     /**
@@ -224,7 +225,7 @@
         }
     };
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     JobWorkItem(Parcel in) {
         if (in.readInt() != 0) {
             mIntent = Intent.CREATOR.createFromParcel(in);
diff --git a/core/java/android/app/role/IRoleManager.aidl b/core/java/android/app/role/IRoleManager.aidl
index 4ce0f31..0c9b41bf 100644
--- a/core/java/android/app/role/IRoleManager.aidl
+++ b/core/java/android/app/role/IRoleManager.aidl
@@ -48,4 +48,6 @@
     boolean addRoleHolderFromController(in String roleName, in String packageName);
 
     boolean removeRoleHolderFromController(in String roleName, in String packageName);
+
+    List<String> getHeldRolesFromController(in String packageName);
 }
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index 5d101ab..2d630a6 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -542,6 +542,27 @@
         }
     }
 
+
+    /**
+     * Returns the list of all roles that the given package is currently holding
+     *
+     * @param packageName the package name
+     * @return the list of role names
+     *
+     * @hide
+     */
+    @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
+    @SystemApi
+    @NonNull
+    public List<String> getHeldRolesFromController(@NonNull String packageName) {
+        Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
+        try {
+            return mService.getHeldRolesFromController(packageName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     private static class RoleManagerCallbackDelegate extends IRoleManagerCallback.Stub {
 
         @NonNull
diff --git a/core/java/android/app/usage/ConfigurationStats.java b/core/java/android/app/usage/ConfigurationStats.java
index dff9b61..87a737d 100644
--- a/core/java/android/app/usage/ConfigurationStats.java
+++ b/core/java/android/app/usage/ConfigurationStats.java
@@ -17,6 +17,7 @@
 
 import android.annotation.UnsupportedAppUsage;
 import android.content.res.Configuration;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -29,37 +30,37 @@
     /**
      * {@hide}
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public Configuration mConfiguration;
 
     /**
      * {@hide}
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public long mBeginTimeStamp;
 
     /**
      * {@hide}
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public long mEndTimeStamp;
 
     /**
      * {@hide}
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public long mLastTimeActive;
 
     /**
      * {@hide}
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public long mTotalTimeActive;
 
     /**
      * {@hide}
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public int mActivationCount;
 
     /**
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 4d52263..bbae7d3 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -55,4 +55,8 @@
             long sessionThresholdTimeMs, in PendingIntent limitReachedCallbackIntent,
             in PendingIntent sessionEndCallbackIntent, String callingPackage);
     void unregisterUsageSessionObserver(int sessionObserverId, String callingPackage);
+    void reportUsageStart(in IBinder activity, String token, String callingPackage);
+    void reportPastUsageStart(in IBinder activity, String token, long timeAgoMs,
+            String callingPackage);
+    void reportUsageStop(in IBinder activity, String token, String callingPackage);
 }
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index a06213d..d7a5328 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -19,6 +19,7 @@
 import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.content.res.Configuration;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -268,13 +269,13 @@
         /**
          * {@hide}
          */
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         public String mPackage;
 
         /**
          * {@hide}
          */
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         public String mClass;
 
 
@@ -286,20 +287,20 @@
         /**
          * {@hide}
          */
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         public long mTimeStamp;
 
         /**
          * {@hide}
          */
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         public int mEventType;
 
         /**
          * Only present for {@link #CONFIGURATION_CHANGE} event types.
          * {@hide}
          */
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         public Configuration mConfiguration;
 
         /**
@@ -501,30 +502,30 @@
     }
 
     // Only used when creating the resulting events. Not used for reading/unparceling.
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private List<Event> mEventsToWrite = null;
 
     // Only used for reading/unparceling events.
     @UnsupportedAppUsage
     private Parcel mParcel = null;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final int mEventCount;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mIndex = 0;
 
     /*
      * In order to save space, since ComponentNames will be duplicated everywhere,
      * we use a map and index into it.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private String[] mStringPool;
 
     /**
      * Construct the iterator from a parcel.
      * {@hide}
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public UsageEvents(Parcel in) {
         byte[] bytes = in.readBlob();
         Parcel data = Parcel.obtain();
@@ -609,7 +610,7 @@
         }
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int findStringIndex(String str) {
         final int index = Arrays.binarySearch(mStringPool, str);
         if (index < 0) {
@@ -621,7 +622,7 @@
     /**
      * Writes a single event to the parcel. Modify this when updating {@link Event}.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private void writeEventToParcel(Event event, Parcel p, int flags) {
         final int packageIndex;
         if (event.mPackage != null) {
@@ -666,7 +667,7 @@
     /**
      * Reads a single event from the parcel. Modify this when updating {@link Event}.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private void readEventFromParcel(Parcel p, Event eventOut) {
         final int packageIndex = p.readInt();
         if (packageIndex >= 0) {
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index 8fb7f4c..308180b 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -29,6 +29,7 @@
 
 import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -44,19 +45,19 @@
     /**
      * {@hide}
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public String mPackageName;
 
     /**
      * {@hide}
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public long mBeginTimeStamp;
 
     /**
      * {@hide}
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public long mEndTimeStamp;
 
     /**
@@ -64,7 +65,7 @@
      * {@link android.app.usage.UsageEvents.Event#ACTIVITY_RESUMED} event.
      * {@hide}
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public long mLastTimeUsed;
 
     /**
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 3f34803..605deac 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -23,6 +23,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.UnsupportedAppUsage;
+import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
@@ -233,7 +234,7 @@
     @SystemApi
     public static final String EXTRA_TIME_USED = "android.app.usage.extra.TIME_USED";
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static final UsageEvents sEmptyResults = new UsageEvents();
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -579,15 +580,18 @@
     /**
      * @hide
      * Register an app usage limit observer that receives a callback on the provided intent when
-     * the sum of usages of apps in the packages array exceeds the {@code timeLimit} specified. The
-     * observer will automatically be unregistered when the time limit is reached and the intent
-     * is delivered. Registering an {@code observerId} that was already registered will override
-     * the previous one. No more than 1000 unique {@code observerId} may be registered by a single
-     * uid at any one time.
+     * the sum of usages of apps and tokens in the {@code observed} array exceeds the
+     * {@code timeLimit} specified. The structure of a token is a String with the reporting
+     * package's name and a token the reporting app will use, separated by the forward slash
+     * character. Example: com.reporting.package/5OM3*0P4QU3-7OK3N
+     * The observer will automatically be unregistered when the time limit is reached and the
+     * intent is delivered. Registering an {@code observerId} that was already registered will
+     * override the previous one. No more than 1000 unique {@code observerId} may be registered by
+     * a single uid at any one time.
      * @param observerId A unique id associated with the group of apps to be monitored. There can
      *                  be multiple groups with common packages and different time limits.
-     * @param packages The list of packages to observe for foreground activity time. Cannot be null
-     *                 and must include at least one package.
+     * @param observedEntities The list of packages and token to observe for usage time. Cannot be
+     *                         null and must include at least one package or token.
      * @param timeLimit The total time the set of apps can be in the foreground before the
      *                  callbackIntent is delivered. Must be at least one minute.
      * @param timeUnit The unit for time specified in {@code timeLimit}. Cannot be null.
@@ -600,11 +604,11 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE)
-    public void registerAppUsageObserver(int observerId, @NonNull String[] packages, long timeLimit,
-            @NonNull TimeUnit timeUnit, @NonNull PendingIntent callbackIntent) {
+    public void registerAppUsageObserver(int observerId, @NonNull String[] observedEntities,
+            long timeLimit, @NonNull TimeUnit timeUnit, @NonNull PendingIntent callbackIntent) {
         try {
-            mService.registerAppUsageObserver(observerId, packages, timeUnit.toMillis(timeLimit),
-                    callbackIntent, mContext.getOpPackageName());
+            mService.registerAppUsageObserver(observerId, observedEntities,
+                    timeUnit.toMillis(timeLimit), callbackIntent, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -631,18 +635,21 @@
 
     /**
      * Register a usage session observer that receives a callback on the provided {@code
-     * limitReachedCallbackIntent} when the sum of usages of apps in the packages array exceeds
-     * the {@code timeLimit} specified within a usage session. After the {@code timeLimit} has
-     * been reached, the usage session observer will receive a callback on the provided {@code
-     * sessionEndCallbackIntent} when the usage session ends. Registering another session
-     * observer against a {@code sessionObserverId} that has already been registered will
-     * override the previous session observer.
+     * limitReachedCallbackIntent} when the sum of usages of apps and tokens in the {@code
+     * observed} array exceeds the {@code timeLimit} specified within a usage session. The
+     * structure of a token is a String with the reporting packages' name and a token the
+     * reporting app will use, separated by the forward slash character.
+     * Example: com.reporting.package/5OM3*0P4QU3-7OK3N
+     * After the {@code timeLimit} has been reached, the usage session observer will receive a
+     * callback on the provided {@code sessionEndCallbackIntent} when the usage session ends.
+     * Registering another session observer against a {@code sessionObserverId} that has already
+     * been registered will override the previous session observer.
      *
      * @param sessionObserverId A unique id associated with the group of apps to be
      *                          monitored. There can be multiple groups with common
      *                          packages and different time limits.
-     * @param packages The list of packages to observe for foreground activity time. Cannot be null
-     *                 and must include at least one package.
+     * @param observedEntities The list of packages and token to observe for usage time. Cannot be
+     *                         null and must include at least one package or token.
      * @param timeLimit The total time the set of apps can be used continuously before the {@code
      *                  limitReachedCallbackIntent} is delivered. Must be at least one minute.
      * @param timeUnit The unit for time specified in {@code timeLimit}. Cannot be null.
@@ -668,13 +675,13 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE)
-    public void registerUsageSessionObserver(int sessionObserverId, @NonNull String[] packages,
-            long timeLimit, @NonNull TimeUnit timeUnit, long sessionThresholdTime,
-            @NonNull TimeUnit sessionThresholdTimeUnit,
+    public void registerUsageSessionObserver(int sessionObserverId,
+            @NonNull String[] observedEntities, long timeLimit, @NonNull TimeUnit timeUnit,
+            long sessionThresholdTime,  @NonNull TimeUnit sessionThresholdTimeUnit,
             @NonNull PendingIntent limitReachedCallbackIntent,
             @Nullable PendingIntent sessionEndCallbackIntent) {
         try {
-            mService.registerUsageSessionObserver(sessionObserverId, packages,
+            mService.registerUsageSessionObserver(sessionObserverId, observedEntities,
                     timeUnit.toMillis(timeLimit),
                     sessionThresholdTimeUnit.toMillis(sessionThresholdTime),
                     limitReachedCallbackIntent, sessionEndCallbackIntent,
@@ -704,6 +711,71 @@
         }
     }
 
+    /**
+     * Report usage associated with a particular {@code token} has started. Tokens are app defined
+     * strings used to represent usage of in-app features. Apps with the {@link
+     * android.Manifest.permission#OBSERVE_APP_USAGE} permission can register time limit observers
+     * to monitor the usage of a token. In app usage can only associated with an {@code activity}
+     * and usage will be considered stopped if the activity stops or crashes.
+     * @see #registerAppUsageObserver
+     * @see #registerUsageSessionObserver
+     *
+     * @param activity The activity {@code token} is associated with.
+     * @param token The token to report usage against.
+     * @hide
+     */
+    @SystemApi
+    public void reportUsageStart(@NonNull Activity activity, @NonNull String token) {
+        try {
+            mService.reportUsageStart(activity.getActivityToken(), token,
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Report usage associated with a particular {@code token} had started some amount of time in
+     * the past. Tokens are app defined strings used to represent usage of in-app features. Apps
+     * with the {@link android.Manifest.permission#OBSERVE_APP_USAGE} permission can register time
+     * limit observers to monitor the usage of a token. In app usage can only associated with an
+     * {@code activity} and usage will be considered stopped if the activity stops or crashes.
+     * @see #registerAppUsageObserver
+     * @see #registerUsageSessionObserver
+     *
+     * @param activity The activity {@code token} is associated with.
+     * @param token The token to report usage against.
+     * @param timeAgoMs How long ago the start of usage took place
+     * @hide
+     */
+    @SystemApi
+    public void reportUsageStart(@NonNull Activity activity, @NonNull String token,
+                                 long timeAgoMs) {
+        try {
+            mService.reportPastUsageStart(activity.getActivityToken(), token, timeAgoMs,
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Report the usage associated with a particular {@code token} has stopped.
+     *
+     * @param activity The activity {@code token} is associated with.
+     * @param token The token to report usage against.
+     * @hide
+     */
+    @SystemApi
+    public void reportUsageStop(@NonNull Activity activity, @NonNull String token) {
+        try {
+            mService.reportUsageStop(activity.getActivityToken(), token,
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /** @hide */
     public static String reasonToString(int standbyReason) {
         StringBuilder sb = new StringBuilder();
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 466b9ce..171c2f5 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -878,7 +878,7 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static String stateToString(int state) {
         switch (state) {
             case STATE_DISCONNECTED:
diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java
index 3a78cbd..1edbacb 100755
--- a/core/java/android/bluetooth/BluetoothClass.java
+++ b/core/java/android/bluetooth/BluetoothClass.java
@@ -18,6 +18,7 @@
 
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -65,7 +66,7 @@
     private final int mClass;
 
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public BluetoothClass(int classInt) {
         mClass = classInt;
     }
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index e08d405..adedff3 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -67,7 +67,9 @@
         }
         // Legacy api - getDefaultAdapter does not take in the context
         mAdapter = BluetoothAdapter.getDefaultAdapter();
-        mAdapter.setContext(context);
+        if (mAdapter != null) {
+            mAdapter.setContext(context);
+        }
     }
 
     /**
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index fdbfec0..bc3c9a9 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -17,6 +17,7 @@
 package android.bluetooth;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.ParcelUuid;
 
 import java.nio.ByteBuffer;
@@ -38,20 +39,20 @@
      * The following 128 bit values are calculated as:
      *  uuid * 2^96 + BASE_UUID
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static final ParcelUuid AudioSink =
             ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
     public static final ParcelUuid AudioSource =
             ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static final ParcelUuid AdvAudioDist =
             ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB");
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static final ParcelUuid HSP =
             ParcelUuid.fromString("00001108-0000-1000-8000-00805F9B34FB");
     public static final ParcelUuid HSP_AG =
             ParcelUuid.fromString("00001112-0000-1000-8000-00805F9B34FB");
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static final ParcelUuid Handsfree =
             ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB");
     public static final ParcelUuid Handsfree_AG =
diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java
index 54e6342..e6ffe8b4 100644
--- a/core/java/android/content/ComponentName.java
+++ b/core/java/android/content/ComponentName.java
@@ -192,6 +192,17 @@
     }
 
     /**
+     * Helper to get {@link #flattenToShortString()} in a {@link ComponentName} reference that can
+     * be {@code null}.
+     *
+     * @hide
+     */
+    @Nullable
+    public static String flattenToShortString(@Nullable ComponentName componentName) {
+        return componentName == null ? null : componentName.flattenToShortString();
+    }
+
+    /**
      * Return a String that unambiguously describes both the package and
      * class names contained in the ComponentName.  You can later recover
      * the ComponentName from this string through
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b39010d..eb7be6f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -49,6 +49,7 @@
 import android.database.sqlite.SQLiteDatabase.CursorFactory;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
+import android.net.NetworkStack;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
@@ -691,7 +692,8 @@
      *
      * @see android.content.res.Resources.Theme#obtainStyledAttributes(int[])
      */
-    public final TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) {
+    @NonNull
+    public final TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[] attrs) {
         return getTheme().obtainStyledAttributes(attrs);
     }
 
@@ -702,8 +704,9 @@
      *
      * @see android.content.res.Resources.Theme#obtainStyledAttributes(int, int[])
      */
-    public final TypedArray obtainStyledAttributes(
-            @StyleRes int resid, @StyleableRes int[] attrs) throws Resources.NotFoundException {
+    @NonNull
+    public final TypedArray obtainStyledAttributes(@StyleRes int resid,
+            @NonNull @StyleableRes int[] attrs) throws Resources.NotFoundException {
         return getTheme().obtainStyledAttributes(resid, attrs);
     }
 
@@ -714,8 +717,9 @@
      *
      * @see android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
      */
+    @NonNull
     public final TypedArray obtainStyledAttributes(
-            AttributeSet set, @StyleableRes int[] attrs) {
+            @Nullable AttributeSet set, @NonNull @StyleableRes int[] attrs) {
         return getTheme().obtainStyledAttributes(set, attrs, 0, 0);
     }
 
@@ -726,8 +730,9 @@
      *
      * @see android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
      */
-    public final TypedArray obtainStyledAttributes(
-            AttributeSet set, @StyleableRes int[] attrs, @AttrRes int defStyleAttr,
+    @NonNull
+    public final TypedArray obtainStyledAttributes(@Nullable AttributeSet set,
+            @NonNull @StyleableRes int[] attrs, @AttrRes int defStyleAttr,
             @StyleRes int defStyleRes) {
         return getTheme().obtainStyledAttributes(
             set, attrs, defStyleAttr, defStyleRes);
@@ -3080,6 +3085,7 @@
             //@hide: COUNTRY_DETECTOR,
             SEARCH_SERVICE,
             SENSOR_SERVICE,
+            SENSOR_PRIVACY_SERVICE,
             STORAGE_SERVICE,
             STORAGE_STATS_SERVICE,
             WALLPAPER_SERVICE,
@@ -3117,6 +3123,7 @@
             APPWIDGET_SERVICE,
             //@hide: VOICE_INTERACTION_MANAGER_SERVICE,
             //@hide: BACKUP_SERVICE,
+            ROLLBACK_SERVICE,
             DROPBOX_SERVICE,
             //@hide: DEVICE_IDLE_CONTROLLER,
             DEVICE_POLICY_SERVICE,
@@ -3544,6 +3551,18 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.hardware.SensorPrivacyManager} for accessing sensor privacy
+     * functions.
+     *
+     * @see #getSystemService(String)
+     * @see android.hardware.SensorPrivacyManager
+     *
+     * @hide
+     */
+    public static final String SENSOR_PRIVACY_SERVICE = "sensor_privacy";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
      * android.os.storage.StorageManager} for accessing system storage
      * functions.
      *
@@ -3602,6 +3621,15 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link NetworkStack} for communicating with the network stack
+     * @hide
+     * @see #getSystemService(String)
+     * @see NetworkStack
+     */
+    public static final String NETWORK_STACK_SERVICE = "network_stack";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.net.IpSecManager} for encrypting Sockets or Networks with
      * IPSec.
      *
@@ -3965,6 +3993,14 @@
     public static final String PERMISSION_SERVICE = "permission";
 
     /**
+     * Official published name of the (internal) permission controller service.
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    public static final String PERMISSION_CONTROLLER_SERVICE = "permission_controller";
+
+    /**
      * Use with {@link #getSystemService(String)} to retrieve an
      * {@link android.app.backup.IBackupManager IBackupManager} for communicating
      * with the backup mechanism.
@@ -3976,6 +4012,17 @@
     public static final String BACKUP_SERVICE = "backup";
 
     /**
+     * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link android.content.rollback.RollbackManager} for communicating
+     * with the rollback manager
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    @SystemApi
+    public static final String ROLLBACK_SERVICE = "rollback";
+
+    /**
      * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.os.DropBoxManager} instance for recording
      * diagnostic logs.
diff --git a/core/java/android/content/Entity.java b/core/java/android/content/Entity.java
index b9473e0..ff4f150 100644
--- a/core/java/android/content/Entity.java
+++ b/core/java/android/content/Entity.java
@@ -18,6 +18,7 @@
 
 import android.annotation.UnsupportedAppUsage;
 import android.net.Uri;
+import android.os.Build;
 
 import java.util.ArrayList;
 
@@ -29,9 +30,9 @@
  * corresponds to that RawContact. The uri refers to the Data table uri for each row.
  */
 public final class Entity {
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     final private ContentValues mValues;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     final private ArrayList<NamedContentValues> mSubValues;
 
     public Entity(ContentValues values) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 2f0618c..ab60b84 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -816,6 +816,28 @@
             = "android.intent.action.SHOW_APP_INFO";
 
     /**
+     * Activity Action: Start an activity to show the app's detailed usage information for
+     * permission protected data.
+     *
+     * The Intent contains an extra {@link #EXTRA_PERMISSION_USAGE_PERMISSIONS} that is of
+     * type {@code String[]} and contains the specific permissions to show information for.
+     *
+     * Apps should handle this intent if they want to provide more information about permission
+     * usage to users beyond the information provided in the manifest.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_PERMISSION_USAGE_DETAILS =
+            "android.intent.action.PERMISSION_USAGE_DETAILS";
+
+    /**
+     * The name of the extra used to contain the permissions in
+     * {@link #ACTION_PERMISSION_USAGE_DETAILS}.
+     * @see #ACTION_PERMISSION_USAGE_DETAILS
+     */
+    public static final String EXTRA_PERMISSION_USAGE_PERMISSIONS =
+            "android.intent.extra.PERMISSION_USAGE_PERMISSIONS";
+
+    /**
      * Represents a shortcut/live folder icon resource.
      *
      * @see Intent#ACTION_CREATE_SHORTCUT
@@ -2248,6 +2270,32 @@
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED";
     /**
+     * Broadcast Action: Sent to the system rollback manager when a package
+     * needs to have rollback enabled.
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     * </p>
+     *
+     * @hide This broadcast is used internally by the system.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PACKAGE_ENABLE_ROLLBACK =
+            "android.intent.action.PACKAGE_ENABLE_ROLLBACK";
+    /**
+     * Broadcast Action: An existing version of an application package has been
+     * rolled back to a previous version.
+     * The data contains the name of the package.
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PACKAGE_ROLLBACK_EXECUTED =
+            "android.intent.action.PACKAGE_ROLLBACK_EXECUTED";
+    /**
      * @hide
      * Broadcast Action: Ask system services if there is any reason to
      * restart the given package.  The data contains the name of the
@@ -4222,6 +4270,11 @@
     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
     public static final String CATEGORY_HOME_MAIN = "android.intent.category.HOME_MAIN";
     /**
+     * The home activity shown on secondary displays that support showing home activities.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_SECONDARY_HOME = "android.intent.category.SECONDARY_HOME";
+    /**
      * This is the setup wizard activity, that is the first activity that is displayed
      * when the user sets up the device for the first time.
      * @hide
@@ -9137,7 +9190,7 @@
      * @param extras The new set of extras in the Intent, or null to erase
      * all extras.
      */
-    public @NonNull Intent replaceExtras(@NonNull Bundle extras) {
+    public @NonNull Intent replaceExtras(@Nullable Bundle extras) {
         mExtras = extras != null ? new Bundle(extras) : null;
         return this;
     }
@@ -10338,6 +10391,7 @@
                 case ACTION_MEDIA_SCANNER_SCAN_FILE:
                 case ACTION_PACKAGE_NEEDS_VERIFICATION:
                 case ACTION_PACKAGE_VERIFIED:
+                case ACTION_PACKAGE_ENABLE_ROLLBACK:
                     // Ignore legacy actions
                     break;
                 default:
diff --git a/core/java/android/content/SyncAdapterType.java b/core/java/android/content/SyncAdapterType.java
index ff77676..6cd4841 100644
--- a/core/java/android/content/SyncAdapterType.java
+++ b/core/java/android/content/SyncAdapterType.java
@@ -18,6 +18,7 @@
 
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.text.TextUtils;
 import android.os.Parcelable;
 import android.os.Parcel;
@@ -30,9 +31,9 @@
     public final String authority;
     public final String accountType;
     public final boolean isKey;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final boolean userVisible;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final boolean supportsUploading;
     @UnsupportedAppUsage
     private final boolean isAlwaysSyncable;
@@ -85,7 +86,7 @@
         this.packageName = packageName;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private SyncAdapterType(String authority, String accountType) {
         if (TextUtils.isEmpty(authority)) {
             throw new IllegalArgumentException("the authority must not be empty: " + authority);
diff --git a/core/java/android/content/SyncInfo.java b/core/java/android/content/SyncInfo.java
index 7ebf922..403cbcb 100644
--- a/core/java/android/content/SyncInfo.java
+++ b/core/java/android/content/SyncInfo.java
@@ -18,6 +18,7 @@
 
 import android.accounts.Account;
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -95,7 +96,7 @@
     }
 
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     SyncInfo(Parcel parcel) {
         authorityId = parcel.readInt();
         account = parcel.readParcelable(Account.class.getClassLoader());
diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java
index 07b23d1..dd55003 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -114,7 +115,7 @@
     /**
      * Package name of the target package
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public final String targetPackageName;
 
     /**
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 0a4f4eb..0edb36c 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -236,6 +236,15 @@
     public float maxAspectRatio;
 
     /**
+     * Value indicating the minimum aspect ratio the activity supports.
+     * <p>
+     * 0 means unset.
+     * @See {@link android.R.attr#minAspectRatio}.
+     * @hide
+     */
+    public float minAspectRatio;
+
+    /**
      * Name of the VrListenerService component to run for this activity.
      * @see android.R.attr#enableVrMode
      * @hide
@@ -979,6 +988,7 @@
         rotationAnimation = orig.rotationAnimation;
         colorMode = orig.colorMode;
         maxAspectRatio = orig.maxAspectRatio;
+        minAspectRatio = orig.minAspectRatio;
     }
 
     /**
@@ -1142,6 +1152,9 @@
         if (maxAspectRatio != 0) {
             pw.println(prefix + "maxAspectRatio=" + maxAspectRatio);
         }
+        if (minAspectRatio != 0) {
+            pw.println(prefix + "minAspectRatio=" + minAspectRatio);
+        }
         super.dumpBack(pw, prefix, dumpFlags);
     }
 
@@ -1190,6 +1203,7 @@
         dest.writeInt(rotationAnimation);
         dest.writeInt(colorMode);
         dest.writeFloat(maxAspectRatio);
+        dest.writeFloat(minAspectRatio);
     }
 
     /**
@@ -1309,6 +1323,7 @@
         rotationAnimation = source.readInt();
         colorMode = source.readInt();
         maxAspectRatio = source.readFloat();
+        minAspectRatio = source.readFloat();
     }
 
     /**
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index c361ac1..2978058 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -27,6 +27,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Environment;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -638,6 +639,21 @@
      */
     public static final int PRIVATE_FLAG_HAS_FRAGILE_USER_DATA = 1 << 24;
 
+    /**
+     * Indicate whether this application prefers code integrity, that is, run only code that is
+     * signed. This requires android:extractNativeLibs to be "false", as well as .dex and .so (if
+     * any) stored uncompressed inside the APK, which is signed. At run time, the implications
+     * include:
+     *
+     * <ul>
+     * <li>ART will JIT the dex code directly from the APK. There may be performance characteristic
+     * changes depend on the actual workload.
+     * </ul>
+     *
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_PREFER_CODE_INTEGRITY = 1 << 25;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
             PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
@@ -653,6 +669,7 @@
             PRIVATE_FLAG_ISOLATED_SPLIT_LOADING,
             PRIVATE_FLAG_OEM,
             PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE,
+            PRIVATE_FLAG_PREFER_CODE_INTEGRITY,
             PRIVATE_FLAG_PRIVILEGED,
             PRIVATE_FLAG_PRODUCT,
             PRIVATE_FLAG_PRODUCT_SERVICES,
@@ -712,6 +729,15 @@
      */
     public float maxAspectRatio;
 
+    /**
+     * Value indicating the minimum aspect ratio the application supports.
+     * <p>
+     * 0 means unset.
+     * @see {@link android.R.attr#minAspectRatio}.
+     * @hide
+     */
+    public float minAspectRatio;
+
     /** @removed */
     @Deprecated
     public String volumeUuid;
@@ -1440,9 +1466,9 @@
             return sCollator.compare(sa.toString(), sb.toString());
         }
 
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         private final Collator   sCollator = Collator.getInstance();
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         private PackageManager   mPM;
     }
 
@@ -1684,7 +1710,7 @@
      * 
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void disableCompatibilityMode() {
         flags |= (FLAG_SUPPORTS_LARGE_SCREENS | FLAG_SUPPORTS_NORMAL_SCREENS |
                 FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS |
@@ -1820,7 +1846,7 @@
         return pm.getDefaultActivityIcon();
     }
     
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private boolean isPackageUnavailable(PackageManager pm) {
         try {
             return pm.getPackageInfo(packageName, 0) == null;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index eea2b88..64a4479b 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -36,6 +36,7 @@
 import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.KeySet;
+import android.content.pm.ModuleInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ProviderInfo;
@@ -276,7 +277,7 @@
             in PersistableBundle appExtras, in PersistableBundle launcherExtras,
             in SuspendDialogInfo dialogInfo, String callingPackage, int userId);
 
-    boolean canSuspendPackageForUser(String packageName, int userId);
+    String[] getUnsuspendablePackagesForUser(in String[] packageNames, int userId);
 
     boolean isPackageSuspendedForUser(String packageName, int userId);
 
@@ -680,4 +681,8 @@
     boolean isPackageStateProtected(String packageName, int userId);
 
     void sendDeviceCustomizationReadyBroadcast();
+
+    List<ModuleInfo> getInstalledModules(int flags);
+
+    ModuleInfo getModuleInfo(String packageName, int flags);
 }
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/core/java/android/content/pm/ModuleInfo.aidl
similarity index 83%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to core/java/android/content/pm/ModuleInfo.aidl
index b7a7920..cc13bf1 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/core/java/android/content/pm/ModuleInfo.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ * Copyright 2018, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.content.pm;
 
-parcelable UsbPort;
+parcelable ModuleInfo;
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 099d15a..b8d7889 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.apex.ApexInfo;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -514,7 +515,7 @@
         }
     };
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private PackageInfo(Parcel source) {
         packageName = source.readString();
         splitNames = source.createStringArray();
diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java
index e0e67b9..b878a95 100644
--- a/core/java/android/content/pm/PackageInfoLite.java
+++ b/core/java/android/content/pm/PackageInfoLite.java
@@ -17,6 +17,7 @@
 package android.content.pm;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -116,7 +117,7 @@
         }
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static final Parcelable.Creator<PackageInfoLite> CREATOR
             = new Parcelable.Creator<PackageInfoLite>() {
         public PackageInfoLite createFromParcel(Parcel source) {
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index f81eb76..94b7c45 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -17,6 +17,7 @@
 package android.content.pm;
 
 import android.Manifest;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -54,6 +55,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.security.MessageDigest;
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -142,6 +145,15 @@
     public static final String ACTION_SESSION_COMMITTED =
             "android.content.pm.action.SESSION_COMMITTED";
 
+    /**
+     * Broadcast Action: Send information about a staged install session when its state is updated.
+     * <p>
+     * The associated session information is defined in {@link #EXTRA_SESSION}.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SESSION_UPDATED =
+            "android.content.pm.action.SESSION_UPDATED";
+
     /** {@hide} */
     public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL";
 
@@ -1413,6 +1425,15 @@
             this.grantedRuntimePermissions = permissions;
         }
 
+        /**
+         * Request that rollbacks be enabled for the given upgrade.
+         * @hide
+         */
+        @SystemApi
+        public void setEnableRollback() {
+            installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK;
+        }
+
         /** {@hide} */
         @SystemApi
         public void setAllowDowngrade(boolean allowDowngrade) {
@@ -1594,6 +1615,29 @@
         public static final int INVALID_ID = -1;
         /** {@hide} */
         private static final int[] NO_SESSIONS = {};
+
+        /** @hide */
+        @IntDef(value = {NO_ERROR, VERIFICATION_FAILED, ACTIVATION_FAILED})
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface StagedSessionErrorCode{}
+        /**
+         * Constant indicating that no error occurred during the preparation or the activation of
+         * this staged session.
+         */
+        public static final int NO_ERROR = 0;
+
+        /**
+         * Constant indicating that an error occurred during the verification phase (pre-reboot) of
+         * this staged session.
+         */
+        public static final int VERIFICATION_FAILED = 1;
+
+        /**
+         * Constant indicating that an error occurred during the activation phase (post-reboot) of
+         * this staged session.
+         */
+        public static final int ACTIVATION_FAILED = 2;
+
         /** {@hide} */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         public int sessionId;
@@ -1653,6 +1697,14 @@
         public int[] childSessionIds = NO_SESSIONS;
 
         /** {@hide} */
+        public boolean isSessionApplied;
+        /** {@hide} */
+        public boolean isSessionReady;
+        /** {@hide} */
+        public boolean isSessionFailed;
+        private int mStagedSessionErrorCode;
+
+        /** {@hide} */
         @UnsupportedAppUsage
         public SessionInfo() {
         }
@@ -1686,6 +1738,10 @@
             if (childSessionIds == null) {
                 childSessionIds = NO_SESSIONS;
             }
+            isSessionApplied = source.readBoolean();
+            isSessionReady = source.readBoolean();
+            isSessionFailed = source.readBoolean();
+            mStagedSessionErrorCode = source.readInt();
         }
 
         /**
@@ -1970,6 +2026,44 @@
             return childSessionIds;
         }
 
+        /**
+         * Whether the staged session has been applied successfully, meaning that all of its
+         * packages have been activated and no further action is required.
+         * Only meaningful if {@code isStaged} is true.
+         */
+        public boolean isSessionApplied() {
+            return isSessionApplied;
+        }
+
+        /**
+         * Whether the staged session is ready to be applied at next reboot. Only meaningful if
+         * {@code isStaged} is true.
+         */
+        public boolean isSessionReady() {
+            return isSessionReady;
+        }
+
+        /**
+         * Whether something went wrong and the staged session is declared as failed, meaning that
+         * it will be ignored at next reboot. Only meaningful if {@code isStaged} is true.
+         */
+        public boolean isSessionFailed() {
+            return isSessionFailed;
+        }
+
+        /**
+         * If something went wrong with a staged session, clients can check this error code to
+         * understand which kind of failure happened. Only meaningful if {@code isStaged} is true.
+         */
+        public int getStagedSessionErrorCode() {
+            return mStagedSessionErrorCode;
+        }
+
+        /** {@hide} */
+        public void setStagedSessionErrorCode(@StagedSessionErrorCode int errorCode) {
+            mStagedSessionErrorCode = errorCode;
+        }
+
         @Override
         public int describeContents() {
             return 0;
@@ -2001,6 +2095,10 @@
             dest.writeBoolean(isStaged);
             dest.writeInt(parentSessionId);
             dest.writeIntArray(childSessionIds);
+            dest.writeBoolean(isSessionApplied);
+            dest.writeBoolean(isSessionReady);
+            dest.writeBoolean(isSessionFailed);
+            dest.writeInt(mStagedSessionErrorCode);
         }
 
         public static final Parcelable.Creator<SessionInfo>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 566017b..2aeb68d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -720,6 +720,9 @@
             INSTALL_FORCE_SDK,
             INSTALL_FULL_APP,
             INSTALL_ALLOCATE_AGGRESSIVE,
+            INSTALL_VIRTUAL_PRELOAD,
+            INSTALL_APEX,
+            INSTALL_ENABLE_ROLLBACK,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface InstallFlags {}
@@ -857,6 +860,14 @@
      */
     public static final int INSTALL_APEX = 0x00020000;
 
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that rollback
+     * should be enabled for this install.
+     *
+     * @hide
+     */
+    public static final int INSTALL_ENABLE_ROLLBACK = 0x00040000;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "DONT_KILL_APP" }, value = {
             DONT_KILL_APP
@@ -1357,6 +1368,14 @@
      */
     public static final int INSTALL_FAILED_BAD_DEX_METADATA = -117;
 
+    /**
+     * Installation parse return code: this is passed in the
+     * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if there is any signature problem.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FAILED_BAD_SIGNATURE = -118;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "DELETE_" }, value = {
             DELETE_KEEP_DATA,
@@ -1920,6 +1939,30 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports uicc-
+     * based NFC card emulation.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC =
+                                                                       "android.hardware.nfc.uicc";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports eSE-
+     * based NFC card emulation.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE = "android.hardware.nfc.ese";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The Beam API is enabled on the device.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_NFC_BEAM = "android.sofware.nfc.beam";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device supports any
      * one of the {@link #FEATURE_NFC}, {@link #FEATURE_NFC_HOST_CARD_EMULATION},
      * or {@link #FEATURE_NFC_HOST_CARD_EMULATION_NFCF} features.
@@ -2288,21 +2331,28 @@
      * {@link #hasSystemFeature}: The device has biometric hardware to detect a fingerprint.
      */
     @SdkConstant(SdkConstantType.FEATURE)
-    public static final String FEATURE_FINGERPRINT = "android.hardware.fingerprint";
+    public static final String FEATURE_FINGERPRINT_PRE_29 = "android.hardware.fingerprint";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device has biometric hardware to detect a fingerprint.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_FINGERPRINT = "android.hardware.biometrics.fingerprint";
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device has biometric hardware to perform face authentication.
      */
     @SdkConstant(SdkConstantType.FEATURE)
-    public static final String FEATURE_FACE = "android.hardware.face";
+    public static final String FEATURE_FACE = "android.hardware.biometrics.face";
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device has biometric hardware to perform iris authentication.
      */
     @SdkConstant(SdkConstantType.FEATURE)
-    public static final String FEATURE_IRIS = "android.hardware.iris";
+    public static final String FEATURE_IRIS = "android.hardware.biometrics.iris";
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
@@ -5951,27 +6001,28 @@
     }
 
     /**
-     * Returns whether or not a given package can be suspended via a call to {@link
+     * Returns any packages in a given set of packages that cannot be suspended via a call to {@link
      * #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
      * SuspendDialogInfo) setPackagesSuspended}. The platform prevents suspending certain critical
      * packages to keep the device in a functioning state, e.g. the default dialer.
      * Apps need to hold {@link Manifest.permission#SUSPEND_APPS SUSPEND_APPS} to call this api.
      *
      * <p>
-     * Note that this set of critical packages can change with time, so <em>a value of {@code true}
-     * returned by this api does not guarantee that a following call to {@link
-     * #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
-     * SuspendDialogInfo) setPackagesSuspended} for the same package will succeed</em>, especially
-     * if considerable time elapsed between the two calls.
+     * Note that this set of critical packages can change with time, so even though a package name
+     * was not returned by this call, it does not guarantee that a subsequent call to
+     * {@link #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
+     * SuspendDialogInfo) setPackagesSuspended} for that package will succeed, especially if
+     * significant time elapsed between the two calls.
      *
-     * @param packageName The package to check.
-     * @return {@code true} if the given package can be suspended, {@code false} otherwise.
+     * @param packageNames The packages to check.
+     * @return A list of packages that can not be currently suspended by the system.
      * @hide
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.SUSPEND_APPS)
-    public boolean canSuspendPackage(@NonNull String packageName) {
-        throw new UnsupportedOperationException("canSuspendPackage not implemented");
+    @NonNull
+    public String[] getUnsuspendablePackages(@NonNull String[] packageNames) {
+        throw new UnsupportedOperationException("canSuspendPackages not implemented");
     }
 
     /**
@@ -6224,6 +6275,7 @@
             case INSTALL_FAILED_ABORTED: return "INSTALL_FAILED_ABORTED";
             case INSTALL_FAILED_BAD_DEX_METADATA: return "INSTALL_FAILED_BAD_DEX_METADATA";
             case INSTALL_FAILED_MISSING_SPLIT: return "INSTALL_FAILED_MISSING_SPLIT";
+            case INSTALL_FAILED_BAD_SIGNATURE: return "INSTALL_FAILED_BAD_SIGNATURE";
             default: return Integer.toString(status);
         }
     }
@@ -6269,6 +6321,7 @@
             case INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: return PackageInstaller.STATUS_FAILURE_INVALID;
             case INSTALL_PARSE_FAILED_MANIFEST_EMPTY: return PackageInstaller.STATUS_FAILURE_INVALID;
             case INSTALL_FAILED_BAD_DEX_METADATA: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_FAILED_BAD_SIGNATURE: return PackageInstaller.STATUS_FAILURE_INVALID;
             case INSTALL_FAILED_INTERNAL_ERROR: return PackageInstaller.STATUS_FAILURE;
             case INSTALL_FAILED_USER_RESTRICTED: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
             case INSTALL_FAILED_DUPLICATE_PERMISSION: return PackageInstaller.STATUS_FAILURE_CONFLICT;
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 5db9f50..83979e9 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -759,4 +759,48 @@
     /** Returns whether the given package is enabled for the given user */
     public abstract @PackageManager.EnabledState int getApplicationEnabledState(
             String packageName, int userId);
+
+    /**
+     * Extra field name for the token of a request to enable rollback for a
+     * package.
+     */
+    public static final String EXTRA_ENABLE_ROLLBACK_TOKEN =
+            "android.content.pm.extra.ENABLE_ROLLBACK_TOKEN";
+
+    /**
+     * Extra field name for the installFlags of a request to enable rollback
+     * for a package.
+     */
+    public static final String EXTRA_ENABLE_ROLLBACK_INSTALL_FLAGS =
+            "android.content.pm.extra.ENABLE_ROLLBACK_INSTALL_FLAGS";
+
+    /**
+     * Used as the {@code enableRollbackCode} argument for
+     * {@link PackageManagerInternal#setEnableRollbackCode} to indicate that
+     * enabling rollback succeeded.
+     */
+    public static final int ENABLE_ROLLBACK_SUCCEEDED = 1;
+
+    /**
+     * Used as the {@code enableRollbackCode} argument for
+     * {@link PackageManagerInternal#setEnableRollbackCode} to indicate that
+     * enabling rollback failed.
+     */
+    public static final int ENABLE_ROLLBACK_FAILED = -1;
+
+    /**
+     * Allows the rollback manager listening to the
+     * {@link Intent#ACTION_PACKAGE_ENABLE_ROLLBACK enable rollback broadcast}
+     * to respond to the package manager. The response must include the
+     * {@code enableRollbackCode} which is one of
+     * {@link PackageManager#ENABLE_ROLLBACK_SUCCEEDED} or
+     * {@link PackageManager#ENABLE_ROLLBACK_FAILED}.
+     *
+     * @param token pending package identifier as passed via the
+     *            {@link PackageManager#EXTRA_ENABLE_ROLLBACK_TOKEN} Intent extra.
+     * @param enableRollbackCode the status code result of enabling rollback
+     * @throws SecurityException if the caller does not have the
+     *            PACKAGE_ROLLBACK_AGENT permission.
+     */
+    public abstract void setEnableRollbackCode(int token, int enableRollbackCode);
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 61a74de..2b266b7 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -30,6 +30,7 @@
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.PackageManager.FEATURE_WATCH;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
@@ -162,6 +163,8 @@
             SystemProperties.getBoolean(PROPERTY_CHILD_PACKAGES_ENABLED, false);
 
     private static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f;
+    private static final float DEFAULT_PRE_Q_MIN_ASPECT_RATIO = 1.333f;
+    private static final float DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH = 1f;
 
     // TODO: switch outError users to PackageParserException
     // TODO: refactor "codePath" to "apkPath"
@@ -472,6 +475,7 @@
         public final boolean extractNativeLibs;
         public final boolean isolatedSplits;
         public final boolean isSplitRequired;
+        public final boolean preferCodeIntegrity;
 
         public ApkLite(String codePath, String packageName, String splitName,
                 boolean isFeatureSplit,
@@ -480,7 +484,7 @@
                 int revisionCode, int installLocation, List<VerifierInfo> verifiers,
                 SigningDetails signingDetails, boolean coreApp,
                 boolean debuggable, boolean multiArch, boolean use32bitAbi,
-                boolean extractNativeLibs, boolean isolatedSplits) {
+                boolean preferCodeIntegrity, boolean extractNativeLibs, boolean isolatedSplits) {
             this.codePath = codePath;
             this.packageName = packageName;
             this.splitName = splitName;
@@ -497,6 +501,7 @@
             this.debuggable = debuggable;
             this.multiArch = multiArch;
             this.use32bitAbi = use32bitAbi;
+            this.preferCodeIntegrity = preferCodeIntegrity;
             this.extractNativeLibs = extractNativeLibs;
             this.isolatedSplits = isolatedSplits;
             this.isSplitRequired = isSplitRequired;
@@ -1719,6 +1724,7 @@
         boolean isolatedSplits = false;
         boolean isFeatureSplit = false;
         boolean isSplitRequired = false;
+        boolean preferCodeIntegrity = false;
         String configForSplit = null;
         String usesSplitName = null;
 
@@ -1781,6 +1787,9 @@
                     if ("extractNativeLibs".equals(attr)) {
                         extractNativeLibs = attrs.getAttributeBooleanValue(i, true);
                     }
+                    if ("preferCodeIntegrity".equals(attr)) {
+                        preferCodeIntegrity = attrs.getAttributeBooleanValue(i, false);
+                    }
                 }
             } else if (TAG_USES_SPLIT.equals(parser.getName())) {
                 if (usesSplitName != null) {
@@ -1797,10 +1806,16 @@
             }
         }
 
+        if (preferCodeIntegrity && extractNativeLibs) {
+            throw new PackageParserException(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "Can't request both preferCodeIntegrity and extractNativeLibs");
+        }
+
         return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
                 configForSplit, usesSplitName, isSplitRequired, versionCode, versionCodeMajor,
                 revisionCode, installLocation, verifiers, signingDetails, coreApp, debuggable,
-                multiArch, use32bitAbi, extractNativeLibs, isolatedSplits);
+                multiArch, use32bitAbi, preferCodeIntegrity, extractNativeLibs, isolatedSplits);
     }
 
     /**
@@ -3652,6 +3667,12 @@
         }
 
         if (sa.getBoolean(
+                R.styleable.AndroidManifestApplication_preferCodeIntegrity,
+                false)) {
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PREFER_CODE_INTEGRITY;
+        }
+
+        if (sa.getBoolean(
                 R.styleable.AndroidManifestApplication_defaultToDeviceProtectedStorage,
                 false)) {
             ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE;
@@ -3673,6 +3694,7 @@
         }
 
         ai.maxAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0);
+        ai.minAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_minAspectRatio, 0);
 
         ai.networkSecurityConfigRes = sa.getResourceId(
                 com.android.internal.R.styleable.AndroidManifestApplication_networkSecurityConfig,
@@ -3995,6 +4017,7 @@
         // Must be ran after the entire {@link ApplicationInfo} has been fully processed and after
         // every activity info has had a chance to set it from its attributes.
         setMaxAspectRatio(owner);
+        setMinAspectRatio(owner);
 
         PackageBackwardCompatibility.modifySharedLibraries(owner);
 
@@ -4492,6 +4515,13 @@
                         0 /*default*/));
             }
 
+            if (sa.hasValue(R.styleable.AndroidManifestActivity_minAspectRatio)
+                    && sa.getType(R.styleable.AndroidManifestActivity_minAspectRatio)
+                    == TypedValue.TYPE_FLOAT) {
+                a.setMinAspectRatio(sa.getFloat(R.styleable.AndroidManifestActivity_minAspectRatio,
+                        0 /*default*/));
+            }
+
             a.info.lockTaskLaunchMode =
                     sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0);
 
@@ -4751,6 +4781,34 @@
     }
 
     /**
+     * Sets every the max aspect ratio of every child activity that doesn't already have an aspect
+     * ratio set.
+     */
+    private void setMinAspectRatio(Package owner) {
+        final float minAspectRatio;
+        if (owner.applicationInfo.minAspectRatio != 0) {
+            // Use the application max aspect ration as default if set.
+            minAspectRatio = owner.applicationInfo.minAspectRatio;
+        } else {
+            // Default to (1.33) 4:3 aspect ratio for pre-Q apps and unset for Q and greater.
+            // NOTE: 4:3 was the min aspect ratio Android devices can support pre-Q per the CDD,
+            // except for watches which always supported 1:1.
+            minAspectRatio = owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q
+                    ? 0
+                    : mCallback.hasFeature(FEATURE_WATCH)
+                            ? DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH
+                            : DEFAULT_PRE_Q_MIN_ASPECT_RATIO;
+        }
+
+        for (Activity activity : owner.activities) {
+            if (activity.hasMinAspectRatio()) {
+                continue;
+            }
+            activity.setMinAspectRatio(minAspectRatio);
+        }
+    }
+
+    /**
      * @param configChanges The bit mask of configChanges fetched from AndroidManifest.xml.
      * @param recreateOnConfigChanges The bit mask recreateOnConfigChanges fetched from
      *                                AndroidManifest.xml.
@@ -4888,6 +4946,7 @@
         info.windowLayout = target.info.windowLayout;
         info.resizeMode = target.info.resizeMode;
         info.maxAspectRatio = target.info.maxAspectRatio;
+        info.minAspectRatio = target.info.minAspectRatio;
         info.requestedVrComponent = target.info.requestedVrComponent;
 
         info.encryptionAware = info.directBootAware = target.info.directBootAware;
@@ -7773,11 +7832,16 @@
         @UnsupportedAppUsage
         public final ActivityInfo info;
         private boolean mHasMaxAspectRatio;
+        private boolean mHasMinAspectRatio;
 
         private boolean hasMaxAspectRatio() {
             return mHasMaxAspectRatio;
         }
 
+        private boolean hasMinAspectRatio() {
+            return mHasMinAspectRatio;
+        }
+
         // To construct custom activity which does not exist in manifest
         Activity(final Package owner, final String className, final ActivityInfo info) {
             super(owner, new ArrayList<>(0), className);
@@ -7813,6 +7877,22 @@
             mHasMaxAspectRatio = true;
         }
 
+        private void setMinAspectRatio(float minAspectRatio) {
+            if (info.resizeMode == RESIZE_MODE_RESIZEABLE
+                    || info.resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
+                // Resizeable activities can be put in any aspect ratio.
+                return;
+            }
+
+            if (minAspectRatio < 1.0f && minAspectRatio != 0) {
+                // Ignore any value lesser than 1.0.
+                return;
+            }
+
+            info.minAspectRatio = minAspectRatio;
+            mHasMinAspectRatio = true;
+        }
+
         public String toString() {
             StringBuilder sb = new StringBuilder(128);
             sb.append("Activity{");
@@ -7833,12 +7913,14 @@
             super.writeToParcel(dest, flags);
             dest.writeParcelable(info, flags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
             dest.writeBoolean(mHasMaxAspectRatio);
+            dest.writeBoolean(mHasMinAspectRatio);
         }
 
         private Activity(Parcel in) {
             super(in);
             info = in.readParcelable(Object.class.getClassLoader());
             mHasMaxAspectRatio = in.readBoolean();
+            mHasMinAspectRatio = in.readBoolean();
 
             for (ActivityIntentInfo aii : intents) {
                 aii.activity = this;
diff --git a/core/java/android/content/pm/PackageStats.java b/core/java/android/content/pm/PackageStats.java
index f70ec39..d3e86a3 100644
--- a/core/java/android/content/pm/PackageStats.java
+++ b/core/java/android/content/pm/PackageStats.java
@@ -18,6 +18,7 @@
 
 import android.annotation.UnsupportedAppUsage;
 import android.app.usage.StorageStatsManager;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
@@ -38,7 +39,7 @@
     public String packageName;
 
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public int userHandle;
 
     /** Size of the code (e.g., APK) */
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index ad82626d..35609c9 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -33,11 +33,29 @@
  * This class provides information for a shared library. There are
  * three types of shared libraries: builtin - non-updatable part of
  * the OS; dynamic - updatable backwards-compatible dynamically linked;
- * static - updatable non backwards-compatible emulating static linking.
+ * static - non backwards-compatible emulating static linking.
  */
 public final class SharedLibraryInfo implements Parcelable {
 
     /** @hide */
+    public static SharedLibraryInfo createForStatic(PackageParser.Package pkg) {
+        return new SharedLibraryInfo(null, pkg.packageName, pkg.getAllCodePaths(),
+                pkg.staticSharedLibName,
+                pkg.staticSharedLibVersion,
+                TYPE_STATIC,
+                new VersionedPackage(pkg.manifestPackageName, pkg.getLongVersionCode()),
+                null, null);
+    }
+
+    /** @hide */
+    public static SharedLibraryInfo createForDynamic(PackageParser.Package pkg, String name) {
+        return new SharedLibraryInfo(null, pkg.packageName, pkg.getAllCodePaths(), name,
+                (long) VERSION_UNDEFINED,
+                TYPE_DYNAMIC, new VersionedPackage(pkg.packageName, pkg.getLongVersionCode()),
+                null, null);
+    }
+
+    /** @hide */
     @IntDef(flag = true, prefix = { "TYPE_" }, value = {
             TYPE_BUILTIN,
             TYPE_DYNAMIC,
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index 1ca7b13..7d101b8 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -612,7 +612,7 @@
         dest.writeFloat(applicationInvertedScale);
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static final Parcelable.Creator<CompatibilityInfo> CREATOR
             = new Parcelable.Creator<CompatibilityInfo>() {
         @Override
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 88b1c88..365ceac 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1468,7 +1468,8 @@
          * @see #obtainStyledAttributes(int, int[])
          * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
          */
-        public TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) {
+        @NonNull
+        public TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[] attrs) {
             return mThemeImpl.obtainStyledAttributes(this, null, attrs, 0, 0);
         }
 
@@ -1493,7 +1494,9 @@
          * @see #obtainStyledAttributes(int[])
          * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
          */
-        public TypedArray obtainStyledAttributes(@StyleRes int resId, @StyleableRes int[] attrs)
+        @NonNull
+        public TypedArray obtainStyledAttributes(@StyleRes int resId,
+                @NonNull @StyleableRes int[] attrs)
                 throws NotFoundException {
             return mThemeImpl.obtainStyledAttributes(this, null, attrs, 0, resId);
         }
@@ -1547,8 +1550,10 @@
          * @see #obtainStyledAttributes(int[])
          * @see #obtainStyledAttributes(int, int[])
          */
-        public TypedArray obtainStyledAttributes(AttributeSet set,
-                @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
+        @NonNull
+        public TypedArray obtainStyledAttributes(@Nullable AttributeSet set,
+                @NonNull @StyleableRes int[] attrs, @AttrRes int defStyleAttr,
+                @StyleRes int defStyleRes) {
             return mThemeImpl.obtainStyledAttributes(this, set, attrs, defStyleAttr, defStyleRes);
         }
 
diff --git a/core/java/android/content/rollback/IRollbackManager.aidl b/core/java/android/content/rollback/IRollbackManager.aidl
new file mode 100644
index 0000000..7f557cd
--- /dev/null
+++ b/core/java/android/content/rollback/IRollbackManager.aidl
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.rollback;
+
+import android.content.pm.ParceledListSlice;
+import android.content.pm.StringParceledListSlice;
+import android.content.rollback.RollbackInfo;
+import android.content.IntentSender;
+
+/** {@hide} */
+interface IRollbackManager {
+
+    RollbackInfo getAvailableRollback(String packageName);
+
+    StringParceledListSlice getPackagesWithAvailableRollbacks();
+
+    ParceledListSlice getRecentlyExecutedRollbacks();
+
+    void executeRollback(in RollbackInfo rollback, String callerPackageName,
+            in IntentSender statusReceiver);
+
+    // Exposed for test purposes only.
+    void reloadPersistedData();
+
+    // Exposed for test purposes only.
+    void expireRollbackForPackage(String packageName);
+}
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/core/java/android/content/rollback/PackageRollbackInfo.aidl
similarity index 74%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to core/java/android/content/rollback/PackageRollbackInfo.aidl
index b7a7920..9cb52c9 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/core/java/android/content/rollback/PackageRollbackInfo.aidl
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package android.content.rollback;
 
-package android.hardware.usb;
-
-parcelable UsbPort;
+parcelable PackageRollbackInfo;
diff --git a/core/java/android/content/rollback/PackageRollbackInfo.java b/core/java/android/content/rollback/PackageRollbackInfo.java
new file mode 100644
index 0000000..2040024
--- /dev/null
+++ b/core/java/android/content/rollback/PackageRollbackInfo.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.rollback;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Information about a rollback available for a particular package.
+ *
+ * @hide
+ */
+@SystemApi
+public final class PackageRollbackInfo implements Parcelable {
+    /**
+     * The name of a package being rolled back.
+     */
+    public final String packageName;
+
+    /**
+     * The version the package was rolled back from.
+     */
+    public final PackageVersion higherVersion;
+
+    /**
+     * The version the package was rolled back to.
+     */
+    public final PackageVersion lowerVersion;
+
+    /**
+     * Represents a version of a package.
+     */
+    public static class PackageVersion {
+        public final long versionCode;
+
+        // TODO(b/120200473): Include apk sha or some other way to distinguish
+        // between two different apks with the same version code.
+        public PackageVersion(long versionCode) {
+            this.versionCode = versionCode;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof PackageVersion)  {
+                PackageVersion otherVersion = (PackageVersion) other;
+                return versionCode == otherVersion.versionCode;
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(versionCode);
+        }
+    }
+
+    public PackageRollbackInfo(String packageName,
+            PackageVersion higherVersion, PackageVersion lowerVersion) {
+        this.packageName = packageName;
+        this.higherVersion = higherVersion;
+        this.lowerVersion = lowerVersion;
+    }
+
+    private PackageRollbackInfo(Parcel in) {
+        this.packageName = in.readString();
+        this.higherVersion = new PackageVersion(in.readLong());
+        this.lowerVersion = new PackageVersion(in.readLong());
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(packageName);
+        out.writeLong(higherVersion.versionCode);
+        out.writeLong(lowerVersion.versionCode);
+    }
+
+    public static final Parcelable.Creator<PackageRollbackInfo> CREATOR =
+            new Parcelable.Creator<PackageRollbackInfo>() {
+        public PackageRollbackInfo createFromParcel(Parcel in) {
+            return new PackageRollbackInfo(in);
+        }
+
+        public PackageRollbackInfo[] newArray(int size) {
+            return new PackageRollbackInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/core/java/android/content/rollback/RollbackInfo.aidl
similarity index 75%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to core/java/android/content/rollback/RollbackInfo.aidl
index b7a7920..a9dc5cd 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/core/java/android/content/rollback/RollbackInfo.aidl
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package android.content.rollback;
 
-package android.hardware.usb;
-
-parcelable UsbPort;
+parcelable RollbackInfo;
diff --git a/core/java/android/content/rollback/RollbackInfo.java b/core/java/android/content/rollback/RollbackInfo.java
new file mode 100644
index 0000000..66df4fe
--- /dev/null
+++ b/core/java/android/content/rollback/RollbackInfo.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.rollback;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Information about a set of packages that can be, or already have been
+ * rolled back together.
+ *
+ * @hide
+ */
+@SystemApi
+public final class RollbackInfo implements Parcelable {
+
+    /**
+     * The package that needs to be rolled back.
+     */
+    public final PackageRollbackInfo targetPackage;
+
+    // TODO: Add a list of additional packages rolled back due to atomic
+    // install dependencies when rollback of atomic installs is supported.
+    // TODO: Add a flag to indicate if reboot is required, when rollback of
+    // staged installs is supported.
+
+    /** @hide */
+    public RollbackInfo(PackageRollbackInfo targetPackage) {
+        this.targetPackage = targetPackage;
+    }
+
+    private RollbackInfo(Parcel in) {
+        this.targetPackage = PackageRollbackInfo.CREATOR.createFromParcel(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        targetPackage.writeToParcel(out, flags);
+    }
+
+    public static final Parcelable.Creator<RollbackInfo> CREATOR =
+            new Parcelable.Creator<RollbackInfo>() {
+        public RollbackInfo createFromParcel(Parcel in) {
+            return new RollbackInfo(in);
+        }
+
+        public RollbackInfo[] newArray(int size) {
+            return new RollbackInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/content/rollback/RollbackManager.java b/core/java/android/content/rollback/RollbackManager.java
new file mode 100644
index 0000000..c1c0bc1
--- /dev/null
+++ b/core/java/android/content/rollback/RollbackManager.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.rollback;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.content.IntentSender;
+import android.os.RemoteException;
+
+import java.util.List;
+
+/**
+ * Offers the ability to rollback packages after upgrade.
+ * <p>
+ * For packages installed with rollbacks enabled, the RollbackManager can be
+ * used to initiate rollback of those packages for a limited time period after
+ * upgrade.
+ *
+ * @see PackageInstaller.SessionParams#setEnableRollback()
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.ROLLBACK_SERVICE)
+public final class RollbackManager {
+    private final String mCallerPackageName;
+    private final IRollbackManager mBinder;
+
+    /** {@hide} */
+    public RollbackManager(Context context, IRollbackManager binder) {
+        mCallerPackageName = context.getPackageName();
+        mBinder = binder;
+    }
+
+    /**
+     * Returns the rollback currently available to be executed for the given
+     * package.
+     * <p>
+     * The returned RollbackInfo describes what packages would be rolled back,
+     * including package version codes before and after rollback. The rollback
+     * can be initiated using {@link #executeRollback(RollbackInfo,IntentSender)}.
+     * <p>
+     * TODO: What if there is no package installed on device for packageName?
+     *
+     * @param packageName name of the package to get the availble RollbackInfo for.
+     * @return the rollback available for the package, or null if no rollback
+     *         is available for the package.
+     * @throws SecurityException if the caller does not have the
+     *            MANAGE_ROLLBACKS permission.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
+    public @Nullable RollbackInfo getAvailableRollback(@NonNull String packageName) {
+        try {
+            return mBinder.getAvailableRollback(packageName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the names of packages that are available for rollback.
+     * Call {@link #getAvailableRollback(String)} to get more information
+     * about the rollback available for a particular package.
+     *
+     * @return the names of packages that are available for rollback.
+     * @throws SecurityException if the caller does not have the
+     *            MANAGE_ROLLBACKS permission.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
+    public @NonNull List<String> getPackagesWithAvailableRollbacks() {
+        try {
+            return mBinder.getPackagesWithAvailableRollbacks().getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
+    /**
+     * Gets the list of all recently executed rollbacks.
+     * This is for the purposes of preventing re-install of a bad version of a
+     * package.
+     * <p>
+     * Returns an empty list if there are no recently executed rollbacks.
+     * <p>
+     * To avoid having to keep around complete rollback history forever on a
+     * device, the returned list of rollbacks is only guaranteed to include
+     * rollbacks that are still relevant. A rollback is no longer considered
+     * relevant if the package is subsequently uninstalled or upgraded
+     * (without the possibility of rollback) to a higher version code than was
+     * rolled back from.
+     *
+     * @return the recently executed rollbacks
+     * @throws SecurityException if the caller does not have the
+     *            MANAGE_ROLLBACKS permission.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
+    public @NonNull List<RollbackInfo> getRecentlyExecutedRollbacks() {
+        try {
+            return mBinder.getRecentlyExecutedRollbacks().getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Execute the given rollback, rolling back all versions of the packages
+     * to the last good versions previously installed on the device as
+     * specified in the given rollback object. The rollback will fail if any
+     * of the installed packages or available rollbacks are inconsistent with
+     * the versions specified in the given rollback object, which can happen
+     * if a package has been updated or a rollback expired since the rollback
+     * object was retrieved from {@link #getAvailableRollback(String)}.
+     * <p>
+     * TODO: Specify the returns status codes.
+     * TODO: What happens in case reboot is required for the rollback to take
+     * effect for staged installs?
+     *
+     * @param rollback to execute
+     * @param statusReceiver where to deliver the results
+     * @throws SecurityException if the caller does not have the
+     *            MANAGE_ROLLBACKS permission.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
+    public void executeRollback(@NonNull RollbackInfo rollback,
+            @NonNull IntentSender statusReceiver) {
+        try {
+            mBinder.executeRollback(rollback, mCallerPackageName, statusReceiver);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Reload all persisted rollback data from device storage.
+     * This API is meant to test that rollback state is properly preserved
+     * across device reboot, by simulating what happens on reboot without
+     * actually rebooting the device.
+     *
+     * @throws SecurityException if the caller does not have the
+     *            MANAGE_ROLLBACKS permission.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
+    public void reloadPersistedData() {
+        try {
+            mBinder.reloadPersistedData();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Expire the rollback data for a given package.
+     * This API is meant to facilitate testing of rollback logic for
+     * expiring rollback data.
+     *
+     * @param packageName the name of the package to expire data for.
+     * @throws SecurityException if the caller does not have the
+     *            MANAGE_ROLLBACKS permission.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
+    public void expireRollbackForPackage(@NonNull String packageName) {
+        try {
+            mBinder.expireRollbackForPackage(packageName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index f78b484..992da31 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -30,6 +30,7 @@
 import android.database.sqlite.SQLiteFullException;
 import android.database.sqlite.SQLiteProgram;
 import android.database.sqlite.SQLiteStatement;
+import android.os.Build;
 import android.os.OperationCanceledException;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
@@ -322,7 +323,7 @@
      * @return object value type
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static int getTypeOfObject(Object obj) {
         if (obj == null) {
             return Cursor.FIELD_TYPE_NULL;
diff --git a/core/java/android/database/MatrixCursor.java b/core/java/android/database/MatrixCursor.java
index 5033296..a52e96e 100644
--- a/core/java/android/database/MatrixCursor.java
+++ b/core/java/android/database/MatrixCursor.java
@@ -17,6 +17,7 @@
 package android.database;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import java.util.ArrayList;
 
 /**
@@ -27,9 +28,9 @@
 public class MatrixCursor extends AbstractCursor {
 
     private final String[] columnNames;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private Object[] data;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int rowCount = 0;
     private final int columnCount;
 
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index 4fce2d7..5722e7b 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -53,11 +53,11 @@
     private Map<String, String> mProjectionMap = null;
     private List<Pattern> mProjectionGreylist = null;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private String mTables = "";
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private StringBuilder mWhereClause = null;  // lazily created
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private boolean mDistinct;
     private SQLiteDatabase.CursorFactory mFactory;
     private boolean mStrict;
@@ -800,7 +800,7 @@
         return query.toString();
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private String[] computeProjection(String[] projectionIn) {
         if (projectionIn != null && projectionIn.length > 0) {
             if (mProjectionMap != null) {
diff --git a/core/java/android/hardware/ISensorPrivacyListener.aidl b/core/java/android/hardware/ISensorPrivacyListener.aidl
new file mode 100644
index 0000000..5d40265
--- /dev/null
+++ b/core/java/android/hardware/ISensorPrivacyListener.aidl
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+/**
+ * @hide
+ */
+oneway interface ISensorPrivacyListener {
+    // Since these transactions are also called from native code, these must be kept in sync with
+    // the ones in
+    //   frameworks/native/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl
+    // =============== Beginning of transactions used on native side as well ======================
+    void onSensorPrivacyChanged(boolean enabled);
+    // =============== End of transactions used on native side as well ============================
+}
diff --git a/core/java/android/hardware/ISensorPrivacyManager.aidl b/core/java/android/hardware/ISensorPrivacyManager.aidl
new file mode 100644
index 0000000..1ba7b98
--- /dev/null
+++ b/core/java/android/hardware/ISensorPrivacyManager.aidl
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+import android.hardware.ISensorPrivacyListener;
+
+/** @hide */
+interface ISensorPrivacyManager {
+    // Since these transactions are also called from native code, these must be kept in sync with
+    // the ones in
+    //   frameworks/native/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
+    // =============== Beginning of transactions used on native side as well ======================
+    void addSensorPrivacyListener(in ISensorPrivacyListener listener);
+
+    void removeSensorPrivacyListener(in ISensorPrivacyListener listener);
+
+    boolean isSensorPrivacyEnabled();
+
+    void setSensorPrivacy(boolean enable);
+    // =============== End of transactions used on native side as well ============================
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
new file mode 100644
index 0000000..274202f
--- /dev/null
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * This class provides access to the sensor privacy services; sensor privacy allows the
+ * user to disable access to all sensors on the device. This class provides methods to query the
+ * current state of sensor privacy as well as to register / unregister for notification when
+ * the sensor privacy state changes.
+ *
+ * @hide
+ */
+@SystemService(Context.SENSOR_PRIVACY_SERVICE)
+public final class SensorPrivacyManager {
+
+    /**
+     * A class implementing this interface can register with the {@link
+     * android.hardware.SensorPrivacyManager} to receive notification when the sensor privacy
+     * state changes.
+     */
+    public interface OnSensorPrivacyChangedListener {
+        /**
+         * Callback invoked when the sensor privacy state changes.
+         *
+         * @param enabled true if sensor privacy is enabled, false otherwise.
+         */
+        void onSensorPrivacyChanged(boolean enabled);
+    }
+
+    private static final Object sInstanceLock = new Object();
+
+    @GuardedBy("sInstanceLock")
+    private static SensorPrivacyManager sInstance;
+
+    @NonNull
+    private final Context mContext;
+
+    @NonNull
+    private final ISensorPrivacyManager mService;
+
+    @NonNull
+    private final ArrayMap<OnSensorPrivacyChangedListener, ISensorPrivacyListener> mListeners;
+
+    /**
+     * Private constructor to ensure only a single instance is created.
+     */
+    private SensorPrivacyManager(Context context, ISensorPrivacyManager service) {
+        mContext = context;
+        mService = service;
+        mListeners = new ArrayMap<>();
+    }
+
+    /**
+     * Returns the single instance of the SensorPrivacyManager.
+     */
+    public static SensorPrivacyManager getInstance(Context context) {
+        synchronized (sInstanceLock) {
+            if (sInstance == null) {
+                try {
+                    IBinder b = ServiceManager.getServiceOrThrow(Context.SENSOR_PRIVACY_SERVICE);
+                    ISensorPrivacyManager service = ISensorPrivacyManager.Stub.asInterface(b);
+                    sInstance = new SensorPrivacyManager(context, service);
+                } catch (ServiceManager.ServiceNotFoundException e) {
+                    throw new IllegalStateException(e);
+                }
+            }
+            return sInstance;
+        }
+    }
+
+    /**
+     * Sets sensor privacy to the specified state.
+     *
+     * @param enable the state to which sensor privacy should be set.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
+    public void setSensorPrivacy(boolean enable) {
+        try {
+            mService.setSensorPrivacy(enable);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Registers a new listener to receive notification when the state of sensor privacy
+     * changes.
+     *
+     * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
+     *                 privacy changes.
+     */
+    public void addSensorPrivacyListener(final OnSensorPrivacyChangedListener listener) {
+        synchronized (mListeners) {
+            ISensorPrivacyListener iListener = mListeners.get(listener);
+            if (iListener == null) {
+                iListener = new ISensorPrivacyListener.Stub() {
+                    @Override
+                    public void onSensorPrivacyChanged(boolean enabled) {
+                        listener.onSensorPrivacyChanged(enabled);
+                    }
+                };
+                mListeners.put(listener, iListener);
+            }
+
+            try {
+                mService.addSensorPrivacyListener(iListener);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Unregisters the specified listener from receiving notifications when the state of sensor
+     * privacy changes.
+     *
+     * @param listener the OnSensorPrivacyChangedListener to be unregistered from notifications when
+     *                 sensor privacy changes.
+     */
+    public void removeSensorPrivacyListener(OnSensorPrivacyChangedListener listener) {
+        synchronized (mListeners) {
+            ISensorPrivacyListener iListener = mListeners.get(listener);
+            if (iListener != null) {
+                mListeners.remove(iListener);
+                try {
+                    mService.removeSensorPrivacyListener(iListener);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns whether sensor privacy is currently enabled.
+     *
+     * @return true if sensor privacy is currently enabled, false otherwise.
+     */
+    public boolean isSensorPrivacyEnabled() {
+        try {
+            return mService.isSensorPrivacyEnabled();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/hardware/display/ColorDisplayManager.java b/core/java/android/hardware/display/ColorDisplayManager.java
index a4c1332..dd782ec 100644
--- a/core/java/android/hardware/display/ColorDisplayManager.java
+++ b/core/java/android/hardware/display/ColorDisplayManager.java
@@ -60,6 +60,13 @@
         return context.getResources().getBoolean(R.bool.config_nightDisplayAvailable);
     }
 
+    /**
+     * Returns {@code true} if display white balance is supported by the device.
+     */
+    public static boolean isDisplayWhiteBalanceAvailable(Context context) {
+        return context.getResources().getBoolean(R.bool.config_displayWhiteBalanceAvailable);
+    }
+
     private static class ColorDisplayManagerInternal {
 
         private static ColorDisplayManagerInternal sInstance;
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index bac23b3..1630b06 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -975,14 +975,11 @@
             mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
         }
         final String msg = getAcquiredString(mContext, acquireInfo, vendorCode);
-        if (msg == null) {
-            return;
-        }
         final int clientInfo = acquireInfo == FACE_ACQUIRED_VENDOR
                 ? (vendorCode + FACE_ACQUIRED_VENDOR_BASE) : acquireInfo;
         if (mEnrollmentCallback != null) {
             mEnrollmentCallback.onEnrollmentHelp(clientInfo, msg);
-        } else if (mAuthenticationCallback != null) {
+        } else if (mAuthenticationCallback != null && msg != null) {
             mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg);
         }
     }
diff --git a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
index 9bebbd2..d382eb9 100644
--- a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
+++ b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
@@ -17,6 +17,7 @@
 
 import android.annotation.Nullable;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.RemoteException;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -52,7 +53,7 @@
     @VisibleForTesting(visibility = Visibility.PACKAGE)
     public HdmiAudioSystemClient(IHdmiControlService service, @Nullable Handler handler) {
         super(service);
-        mHandler = handler == null ? new Handler() : handler;
+        mHandler = handler == null ? new Handler(Looper.getMainLooper()) : handler;
     }
 
     /** @hide */
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 72a6ffe..b520d2c 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -388,6 +388,19 @@
     }
 
     /**
+     * Gets whether the system is in system audio mode.
+     *
+     * @hide
+     */
+    public boolean getSystemAudioMode() {
+        try {
+            return mService.getSystemAudioMode();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Listener used to get hotplug event from HDMI port.
      */
     public interface HotplugEventListener {
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index f4e776c..edc3f94 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -20,7 +20,7 @@
 import android.content.ComponentName;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbPort;
+import android.hardware.usb.ParcelableUsbPort;
 import android.hardware.usb.UsbPortStatus;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
@@ -112,7 +112,7 @@
     ParcelFileDescriptor getControlFd(long function);
 
     /* Gets the list of USB ports. */
-    UsbPort[] getPorts();
+    List<ParcelableUsbPort> getPorts();
 
     /* Gets the status of the specified USB port. */
     UsbPortStatus getPortStatus(in String portId);
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/core/java/android/hardware/usb/ParcelableUsbPort.aidl
similarity index 87%
rename from core/java/android/hardware/usb/UsbPort.aidl
rename to core/java/android/hardware/usb/ParcelableUsbPort.aidl
index b7a7920..4431551 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/core/java/android/hardware/usb/ParcelableUsbPort.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ * Copyright (C) 2018, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,4 +16,4 @@
 
 package android.hardware.usb;
 
-parcelable UsbPort;
+parcelable ParcelableUsbPort;
diff --git a/core/java/android/hardware/usb/ParcelableUsbPort.java b/core/java/android/hardware/usb/ParcelableUsbPort.java
new file mode 100644
index 0000000..7f7ba96
--- /dev/null
+++ b/core/java/android/hardware/usb/ParcelableUsbPort.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.Immutable;
+
+/**
+ * A parcelable wrapper to send UsbPorts over binders.
+ *
+ * @hide
+ */
+@Immutable
+public final class ParcelableUsbPort implements Parcelable {
+    private final @NonNull String mId;
+    private final int mSupportedModes;
+
+    private ParcelableUsbPort(@NonNull String id, int supportedModes) {
+        mId = id;
+        mSupportedModes = supportedModes;
+    }
+
+    /**
+     * Create the parcelable version of a {@link UsbPort}.
+     *
+     * @param port The port to create a parcealable version of
+     *
+     * @return The parcelable version of the port
+     */
+    public static @NonNull ParcelableUsbPort of(@NonNull UsbPort port) {
+        return new ParcelableUsbPort(port.getId(), port.getSupportedModes());
+    }
+
+    /**
+     * Create a {@link UsbPort} from this object.
+     *
+     * @param usbManager A link to the usbManager in the current context
+     *
+     * @return The UsbPort for this object
+     */
+    public @NonNull UsbPort getUsbPort(@NonNull UsbManager usbManager) {
+        return new UsbPort(usbManager, mId, mSupportedModes);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mId);
+        dest.writeInt(mSupportedModes);
+    }
+
+    public static final Creator<ParcelableUsbPort> CREATOR =
+            new Creator<ParcelableUsbPort>() {
+                @Override
+                public ParcelableUsbPort createFromParcel(Parcel in) {
+                    String id = in.readString();
+                    int supportedModes = in.readInt();
+                    return new ParcelableUsbPort(id, supportedModes);
+                }
+
+                @Override
+                public ParcelableUsbPort[] newArray(int size) {
+                    return new ParcelableUsbPort[size];
+                }
+            };
+}
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 4111941..6014478 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -18,6 +18,7 @@
 package android.hardware.usb;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
@@ -39,9 +40,10 @@
 import android.os.RemoteException;
 import android.util.Log;
 
-import com.android.internal.util.Preconditions;
-
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.StringJoiner;
 
@@ -97,15 +99,11 @@
      * Broadcast Action: A broadcast for USB port changes.
      *
      * This intent is sent when a USB port is added, removed, or changes state.
-     * <ul>
-     * <li> {@link #EXTRA_PORT} containing the {@link android.hardware.usb.UsbPort}
-     * for the port.
-     * <li> {@link #EXTRA_PORT_STATUS} containing the {@link android.hardware.usb.UsbPortStatus}
-     * for the port, or null if the port has been removed
-     * </ul>
      *
      * @hide
      */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
     public static final String ACTION_USB_PORT_CHANGED =
             "android.hardware.usb.action.USB_PORT_CHANGED";
 
@@ -796,34 +794,44 @@
      * device class (which supports all types of ports despite its name).
      * </p>
      *
-     * @return The list of USB ports, or null if none.
+     * @return The list of USB ports
      *
      * @hide
      */
-    @UnsupportedAppUsage
-    public UsbPort[] getPorts() {
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
+    public @NonNull List<UsbPort> getPorts() {
         if (mService == null) {
-            return null;
+            return Collections.emptyList();
         }
+
+        List<ParcelableUsbPort> parcelablePorts;
         try {
-            return mService.getPorts();
+            parcelablePorts = mService.getPorts();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
+
+        if (parcelablePorts == null) {
+            return Collections.emptyList();
+        } else {
+            int numPorts = parcelablePorts.size();
+
+            ArrayList<UsbPort> ports = new ArrayList<>(numPorts);
+            for (int i = 0; i < numPorts; i++) {
+                ports.add(parcelablePorts.get(i).getUsbPort(this));
+            }
+
+            return ports;
+        }
     }
 
     /**
-     * Gets the status of the specified USB port.
-     *
-     * @param port The port to query.
-     * @return The status of the specified USB port, or null if unknown.
+     * Should only be called by {@link UsbPort#getStatus}.
      *
      * @hide
      */
-    @UnsupportedAppUsage
-    public UsbPortStatus getPortStatus(UsbPort port) {
-        Preconditions.checkNotNull(port, "port must not be null");
-
+    UsbPortStatus getPortStatus(UsbPort port) {
         try {
             return mService.getPortStatus(port.getId());
         } catch (RemoteException e) {
@@ -832,29 +840,11 @@
     }
 
     /**
-     * Sets the desired role combination of the port.
-     * <p>
-     * The supported role combinations depend on what is connected to the port and may be
-     * determined by consulting
-     * {@link UsbPortStatus#isRoleCombinationSupported UsbPortStatus.isRoleCombinationSupported}.
-     * </p><p>
-     * Note: This function is asynchronous and may fail silently without applying
-     * the requested changes.  If this function does cause a status change to occur then
-     * a {@link #ACTION_USB_PORT_CHANGED} broadcast will be sent.
-     * </p>
-     *
-     * @param powerRole The desired power role: {@link UsbPort#POWER_ROLE_SOURCE}
-     * or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
-     * @param dataRole The desired data role: {@link UsbPort#DATA_ROLE_HOST}
-     * or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
+     * Should only be called by {@link UsbPort#setRoles}.
      *
      * @hide
      */
-    @UnsupportedAppUsage
-    public void setPortRoles(UsbPort port, int powerRole, int dataRole) {
-        Preconditions.checkNotNull(port, "port must not be null");
-        UsbPort.checkRoles(powerRole, dataRole);
-
+    void setPortRoles(UsbPort port, int powerRole, int dataRole) {
         Log.d(TAG, "setPortRoles Package:" + mContext.getPackageName());
         try {
             mService.setPortRoles(port.getId(), powerRole, dataRole);
diff --git a/core/java/android/hardware/usb/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java
index afdb202..37154e4 100644
--- a/core/java/android/hardware/usb/UsbPort.java
+++ b/core/java/android/hardware/usb/UsbPort.java
@@ -16,104 +16,53 @@
 
 package android.hardware.usb;
 
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_NONE;
+import static android.hardware.usb.UsbPortStatus.MODE_AUDIO_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.MODE_DEBUG_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_NONE;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_NONE;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.hardware.usb.V1_0.Constants;
-import android.os.Parcel;
-import android.os.Parcelable;
 
 import com.android.internal.util.Preconditions;
 
 /**
  * Represents a physical USB port and describes its characteristics.
- * <p>
- * This object is immutable.
- * </p>
  *
  * @hide
  */
-public final class UsbPort implements Parcelable {
+@SystemApi
+public final class UsbPort {
     private final String mId;
     private final int mSupportedModes;
-
-    public static final int MODE_NONE = Constants.PortMode.NONE;
-    /**
-     * Mode bit: This USB port can act as a downstream facing port (host).
-     * <p>
-     * Implies that the port supports the {@link #POWER_ROLE_SOURCE} and {@link #DATA_ROLE_HOST}
-     * combination of roles (and possibly others as well).
-     * </p>
-     */
-    public static final int MODE_DFP = Constants.PortMode.DFP;
-
-    /**
-     * Mode bit: This USB port can act as an upstream facing port (device).
-     * <p>
-     * Implies that the port supports the {@link #POWER_ROLE_SINK} and {@link #DATA_ROLE_DEVICE}
-     * combination of roles (and possibly others as well).
-     * </p>
-     */
-    public static final int MODE_UFP = Constants.PortMode.UFP;
-
-    /**
-     * Mode bit: This USB port can act either as an downstream facing port (host) or as
-     * an upstream facing port (device).
-     * <p>
-     * Implies that the port supports the {@link #POWER_ROLE_SOURCE} and {@link #DATA_ROLE_HOST}
-     * combination of roles and the {@link #POWER_ROLE_SINK} and {@link #DATA_ROLE_DEVICE}
-     * combination of roles (and possibly others as well).
-     * </p>
-     */
-    public static final int MODE_DUAL = Constants.PortMode.DRP;
-
-    /**
-     * Mode bit: This USB port can support USB Type-C Audio accessory.
-     */
-    public static final int MODE_AUDIO_ACCESSORY =
-            android.hardware.usb.V1_1.Constants.PortMode_1_1.AUDIO_ACCESSORY;
-
-    /**
-     * Mode bit: This USB port can support USB Type-C debug accessory.
-     */
-    public static final int MODE_DEBUG_ACCESSORY =
-            android.hardware.usb.V1_1.Constants.PortMode_1_1.DEBUG_ACCESSORY;
-
-    /**
-     * Power role: This USB port does not have a power role.
-     */
-    public static final int POWER_ROLE_NONE = Constants.PortPowerRole.NONE;
-
-    /**
-     * Power role: This USB port can act as a source (provide power).
-     */
-    public static final int POWER_ROLE_SOURCE = Constants.PortPowerRole.SOURCE;
-
-    /**
-     * Power role: This USB port can act as a sink (receive power).
-     */
-    public static final int POWER_ROLE_SINK = Constants.PortPowerRole.SINK;
-
-    /**
-     * Power role: This USB port does not have a data role.
-     */
-    public static final int DATA_ROLE_NONE = Constants.PortDataRole.NONE;
-
-    /**
-     * Data role: This USB port can act as a host (access data services).
-     */
-    public static final int DATA_ROLE_HOST = Constants.PortDataRole.HOST;
-
-    /**
-     * Data role: This USB port can act as a device (offer data services).
-     */
-    public static final int DATA_ROLE_DEVICE = Constants.PortDataRole.DEVICE;
+    private final UsbManager mUsbManager;
 
     private static final int NUM_DATA_ROLES = Constants.PortDataRole.NUM_DATA_ROLES;
+
     /**
      * Points to the first power role in the IUsb HAL.
      */
     private static final int POWER_ROLE_OFFSET = Constants.PortPowerRole.NONE;
 
     /** @hide */
-    public UsbPort(String id, int supportedModes) {
+    public UsbPort(@NonNull UsbManager usbManager, @NonNull String id, int supportedModes) {
+        Preconditions.checkNotNull(id);
+        Preconditions.checkFlagsArgument(supportedModes,
+                MODE_DFP | MODE_UFP | MODE_AUDIO_ACCESSORY | MODE_DEBUG_ACCESSORY);
+
+        mUsbManager = usbManager;
         mId = id;
         mSupportedModes = supportedModes;
     }
@@ -122,6 +71,8 @@
      * Gets the unique id of the port.
      *
      * @return The unique id of the port; not intended for display.
+     *
+     * @hide
      */
     public String getId() {
         return mId;
@@ -133,23 +84,62 @@
      * The actual mode of the port may vary depending on what is plugged into it.
      * </p>
      *
-     * @return The supported modes: one of {@link #MODE_DFP}, {@link #MODE_UFP}, or
-     * {@link #MODE_DUAL}.
+     * @return The supported modes: one of {@link UsbPortStatus#MODE_DFP},
+     * {@link UsbPortStatus#MODE_UFP}, or {@link UsbPortStatus#MODE_DUAL}.
+     *
+     * @hide
      */
     public int getSupportedModes() {
         return mSupportedModes;
     }
 
     /**
+     * Gets the status of this USB port.
+     *
+     * @return The status of the this port, or {@code null} if port is unknown.
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
+    public @Nullable UsbPortStatus getStatus() {
+        return mUsbManager.getPortStatus(this);
+    }
+
+    /**
+     * Sets the desired role combination of the port.
+     * <p>
+     * The supported role combinations depend on what is connected to the port and may be
+     * determined by consulting
+     * {@link UsbPortStatus#isRoleCombinationSupported UsbPortStatus.isRoleCombinationSupported}.
+     * </p><p>
+     * Note: This function is asynchronous and may fail silently without applying
+     * the requested changes.  If this function does cause a status change to occur then
+     * a {@link UsbManager#ACTION_USB_PORT_CHANGED} broadcast will be sent.
+     * </p>
+     *
+     * @param powerRole The desired power role: {@link UsbPortStatus#POWER_ROLE_SOURCE} or
+     *                  {@link UsbPortStatus#POWER_ROLE_SINK}, or
+     *                  {@link UsbPortStatus#POWER_ROLE_NONE} if no power role.
+     * @param dataRole The desired data role: {@link UsbPortStatus#DATA_ROLE_HOST} or
+     *                 {@link UsbPortStatus#DATA_ROLE_DEVICE}, or
+     *                 {@link UsbPortStatus#DATA_ROLE_NONE} if no data role.
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
+    public void setRoles(@UsbPortStatus.UsbPowerRole int powerRole,
+            @UsbPortStatus.UsbDataRole int dataRole) {
+        UsbPort.checkRoles(powerRole, dataRole);
+
+        mUsbManager.setPortRoles(this, powerRole, dataRole);
+    }
+
+    /**
      * Combines one power and one data role together into a unique value with
      * exactly one bit set.  This can be used to efficiently determine whether
      * a combination of roles is supported by testing whether that bit is present
      * in a bit-field.
      *
-     * @param powerRole The desired power role: {@link UsbPort#POWER_ROLE_SOURCE}
-     *                  or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
-     * @param dataRole  The desired data role: {@link UsbPort#DATA_ROLE_HOST}
-     *                  or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
+     * @param powerRole The desired power role: {@link UsbPortStatus#POWER_ROLE_SOURCE}
+     *                  or {@link UsbPortStatus#POWER_ROLE_SINK}, or 0 if no power role.
+     * @param dataRole  The desired data role: {@link UsbPortStatus#DATA_ROLE_HOST}
+     *                  or {@link UsbPortStatus#DATA_ROLE_DEVICE}, or 0 if no data role.
      * @hide
      */
     public static int combineRolesAsBit(int powerRole, int dataRole) {
@@ -276,30 +266,4 @@
     public String toString() {
         return "UsbPort{id=" + mId + ", supportedModes=" + modeToString(mSupportedModes) + "}";
     }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mId);
-        dest.writeInt(mSupportedModes);
-    }
-
-    public static final Parcelable.Creator<UsbPort> CREATOR =
-            new Parcelable.Creator<UsbPort>() {
-                @Override
-                public UsbPort createFromParcel(Parcel in) {
-                    String id = in.readString();
-                    int supportedModes = in.readInt();
-                    return new UsbPort(id, supportedModes);
-                }
-
-                @Override
-                public UsbPort[] newArray(int size) {
-                    return new UsbPort[size];
-                }
-            };
 }
diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java
index 2cd8209..d30201a 100644
--- a/core/java/android/hardware/usb/UsbPortStatus.java
+++ b/core/java/android/hardware/usb/UsbPortStatus.java
@@ -16,27 +16,134 @@
 
 package android.hardware.usb;
 
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.hardware.usb.V1_0.Constants;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.annotations.Immutable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Describes the status of a USB port.
- * <p>
- * This object is immutable.
- * </p>
  *
  * @hide
  */
+@Immutable
+@SystemApi
 public final class UsbPortStatus implements Parcelable {
     private final int mCurrentMode;
-    private final int mCurrentPowerRole;
-    private final int mCurrentDataRole;
+    private final @UsbPowerRole int mCurrentPowerRole;
+    private final @UsbDataRole int mCurrentDataRole;
     private final int mSupportedRoleCombinations;
 
+    /**
+     * Power role: This USB port does not have a power role.
+     */
+    public static final int POWER_ROLE_NONE = Constants.PortPowerRole.NONE;
+
+    /**
+     * Power role: This USB port can act as a source (provide power).
+     */
+    public static final int POWER_ROLE_SOURCE = Constants.PortPowerRole.SOURCE;
+
+    /**
+     * Power role: This USB port can act as a sink (receive power).
+     */
+    public static final int POWER_ROLE_SINK = Constants.PortPowerRole.SINK;
+
+    @IntDef(prefix = { "POWER_ROLE_" }, value = {
+            POWER_ROLE_NONE,
+            POWER_ROLE_SOURCE,
+            POWER_ROLE_SINK
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface UsbPowerRole{}
+
+    /**
+     * Power role: This USB port does not have a data role.
+     */
+    public static final int DATA_ROLE_NONE = Constants.PortDataRole.NONE;
+
+    /**
+     * Data role: This USB port can act as a host (access data services).
+     */
+    public static final int DATA_ROLE_HOST = Constants.PortDataRole.HOST;
+
+    /**
+     * Data role: This USB port can act as a device (offer data services).
+     */
+    public static final int DATA_ROLE_DEVICE = Constants.PortDataRole.DEVICE;
+
+    @IntDef(prefix = { "DATA_ROLE_" }, value = {
+            DATA_ROLE_NONE,
+            DATA_ROLE_HOST,
+            DATA_ROLE_DEVICE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface UsbDataRole{}
+
+    /**
+     * There is currently nothing connected to this USB port.
+     */
+    public static final int MODE_NONE = Constants.PortMode.NONE;
+
+    /**
+     * This USB port can act as a downstream facing port (host).
+     *
+     * <p> Implies that the port supports the {@link #POWER_ROLE_SOURCE} and
+     * {@link #DATA_ROLE_HOST} combination of roles (and possibly others as well).
+     */
+    public static final int MODE_DFP = Constants.PortMode.DFP;
+
+    /**
+     * This USB port can act as an upstream facing port (device).
+     *
+     * <p> Implies that the port supports the {@link #POWER_ROLE_SINK} and
+     * {@link #DATA_ROLE_DEVICE} combination of roles (and possibly others as well).
+     */
+    public static final int MODE_UFP = Constants.PortMode.UFP;
+
+    /**
+     * This USB port can act either as an downstream facing port (host) or as
+     * an upstream facing port (device).
+     *
+     * <p> Implies that the port supports the {@link #POWER_ROLE_SOURCE} and
+     * {@link #DATA_ROLE_HOST} combination of roles and the {@link #POWER_ROLE_SINK} and
+     * {@link #DATA_ROLE_DEVICE} combination of roles (and possibly others as well).
+     *
+     * @hide
+     */
+    public static final int MODE_DUAL = Constants.PortMode.DRP;
+
+    /**
+     * This USB port can support USB Type-C Audio accessory.
+     */
+    public static final int MODE_AUDIO_ACCESSORY =
+            android.hardware.usb.V1_1.Constants.PortMode_1_1.AUDIO_ACCESSORY;
+
+    /**
+     * This USB port can support USB Type-C debug accessory.
+     */
+    public static final int MODE_DEBUG_ACCESSORY =
+            android.hardware.usb.V1_1.Constants.PortMode_1_1.DEBUG_ACCESSORY;
+
+    @IntDef(prefix = { "MODE_" }, flag = true, value = {
+            MODE_NONE,
+            MODE_DFP,
+            MODE_UFP,
+            MODE_AUDIO_ACCESSORY,
+            MODE_DEBUG_ACCESSORY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface UsbPortMode{}
+
     /** @hide */
-    public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole,
-            int supportedRoleCombinations) {
+    public UsbPortStatus(int currentMode, @UsbPowerRole int currentPowerRole,
+            @UsbDataRole int currentDataRole, int supportedRoleCombinations) {
         mCurrentMode = currentMode;
         mCurrentPowerRole = currentPowerRole;
         mCurrentDataRole = currentDataRole;
@@ -46,9 +153,8 @@
     /**
      * Returns true if there is anything connected to the port.
      *
-     * @return True if there is anything connected to the port.
+     * @return {@code true} iff there is anything connected to the port.
      */
-    @UnsupportedAppUsage
     public boolean isConnected() {
         return mCurrentMode != 0;
     }
@@ -56,33 +162,31 @@
     /**
      * Gets the current mode of the port.
      *
-     * @return The current mode: {@link UsbPort#MODE_DFP}, {@link UsbPort#MODE_UFP},
-     * or 0 if nothing is connected.
+     * @return The current mode: {@link #MODE_DFP}, {@link #MODE_UFP},
+     * {@link #MODE_AUDIO_ACCESSORY}, {@link #MODE_DEBUG_ACCESSORY}, or {@link {@link #MODE_NONE} if
+     * nothing is connected.
      */
-    @UnsupportedAppUsage
-    public int getCurrentMode() {
+    public @UsbPortMode int getCurrentMode() {
         return mCurrentMode;
     }
 
     /**
      * Gets the current power role of the port.
      *
-     * @return The current power role: {@link UsbPort#POWER_ROLE_SOURCE},
-     * {@link UsbPort#POWER_ROLE_SINK}, or 0 if nothing is connected.
+     * @return The current power role: {@link #POWER_ROLE_SOURCE}, {@link #POWER_ROLE_SINK}, or
+     * {@link #POWER_ROLE_NONE} if nothing is connected.
      */
-    @UnsupportedAppUsage
-    public int getCurrentPowerRole() {
+    public @UsbPowerRole int getCurrentPowerRole() {
         return mCurrentPowerRole;
     }
 
     /**
      * Gets the current data role of the port.
      *
-     * @return The current data role: {@link UsbPort#DATA_ROLE_HOST},
-     * {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if nothing is connected.
+     * @return The current data role: {@link #DATA_ROLE_HOST}, {@link #DATA_ROLE_DEVICE}, or
+     * {@link #DATA_ROLE_NONE} if nothing is connected.
      */
-    @UnsupportedAppUsage
-    public int getCurrentDataRole() {
+    public @UsbDataRole int getCurrentDataRole() {
         return mCurrentDataRole;
     }
 
@@ -90,19 +194,20 @@
      * Returns true if the specified power and data role combination is supported
      * given what is currently connected to the port.
      *
-     * @param powerRole The power role to check: {@link UsbPort#POWER_ROLE_SOURCE}
-     * or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
-     * @param dataRole The data role to check: either {@link UsbPort#DATA_ROLE_HOST}
-     * or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
+     * @param powerRole The power role to check: {@link #POWER_ROLE_SOURCE}  or
+     *                  {@link #POWER_ROLE_SINK}, or {@link #POWER_ROLE_NONE} if no power role.
+     * @param dataRole  The data role to check: either {@link #DATA_ROLE_HOST} or
+     *                  {@link #DATA_ROLE_DEVICE}, or {@link #DATA_ROLE_NONE} if no data role.
      */
-    @UnsupportedAppUsage
-    public boolean isRoleCombinationSupported(int powerRole, int dataRole) {
+    public boolean isRoleCombinationSupported(@UsbPowerRole int powerRole,
+            @UsbDataRole int dataRole) {
         return (mSupportedRoleCombinations &
                 UsbPort.combineRolesAsBit(powerRole, dataRole)) != 0;
     }
 
-    /** @hide */
-    @UnsupportedAppUsage
+    /**
+     * Get the supported role combinations.
+     */
     public int getSupportedRoleCombinations() {
         return mSupportedRoleCombinations;
     }
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 097a3e3..7d40227 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -41,6 +41,7 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -352,7 +353,7 @@
     InputMethodManager mImm;
     private InputMethodPrivilegedOperations mPrivOps = new InputMethodPrivilegedOperations();
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     int mTheme = 0;
     
     LayoutInflater mInflater;
@@ -410,7 +411,7 @@
     @GuardedBy("mLock")
     private boolean mNotifyUserActionSent;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     final Insets mTmpInsets = new Insets();
     final int[] mTmpLocation = new int[2];
 
diff --git a/core/java/android/inputmethodservice/Keyboard.java b/core/java/android/inputmethodservice/Keyboard.java
index 51d33b2..3f25113 100644
--- a/core/java/android/inputmethodservice/Keyboard.java
+++ b/core/java/android/inputmethodservice/Keyboard.java
@@ -16,8 +16,6 @@
 
 package android.inputmethodservice;
 
-import org.xmlpull.v1.XmlPullParserException;
-
 import android.annotation.UnsupportedAppUsage;
 import android.annotation.XmlRes;
 import android.content.Context;
@@ -27,10 +25,12 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.text.TextUtils;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.TypedValue;
 import android.util.Xml;
-import android.util.DisplayMetrics;
+
+import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -59,7 +59,12 @@
  * @attr ref android.R.styleable#Keyboard_keyHeight
  * @attr ref android.R.styleable#Keyboard_horizontalGap
  * @attr ref android.R.styleable#Keyboard_verticalGap
+ * @deprecated This class is deprecated because this is just a convenient UI widget class that
+ *             application developers can re-implement on top of existing public APIs.  If you have
+ *             already depended on this class, consider copying the implementation from AOSP into
+ *             your project or re-implementing a similar widget by yourselves
  */
+@Deprecated
 public class Keyboard {
 
     static final String TAG = "Keyboard";
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 9ca8049..45f067b 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -65,7 +65,13 @@
  * @attr ref android.R.styleable#KeyboardView_keyTextColor
  * @attr ref android.R.styleable#KeyboardView_verticalCorrection
  * @attr ref android.R.styleable#KeyboardView_popupLayout
+ *
+ * @deprecated This class is deprecated because this is just a convenient UI widget class that
+ *             application developers can re-implement on top of existing public APIs.  If you have
+ *             already depended on this class, consider copying the implementation from AOSP into
+ *             your project or re-implementing a similar widget by yourselves
  */
+@Deprecated
 public class KeyboardView extends View implements View.OnClickListener {
 
     /**
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 4714587..23e4ec0 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -16,6 +16,7 @@
 package android.net;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
@@ -27,6 +28,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
 import android.os.Handler;
@@ -187,13 +189,19 @@
      * is for a network to which the connectivity manager was failing over
      * following a disconnect on another network.
      * Retrieve it with {@link android.content.Intent#getBooleanExtra(String,boolean)}.
+     *
+     * @deprecated See {@link NetworkInfo}.
      */
+    @Deprecated
     public static final String EXTRA_IS_FAILOVER = "isFailover";
     /**
      * The lookup key for a {@link NetworkInfo} object. This is supplied when
      * there is another network that it may be possible to connect to. Retrieve with
      * {@link android.content.Intent#getParcelableExtra(String)}.
+     *
+     * @deprecated See {@link NetworkInfo}.
      */
+    @Deprecated
     public static final String EXTRA_OTHER_NETWORK_INFO = "otherNetwork";
     /**
      * The lookup key for a boolean that indicates whether there is a
@@ -214,7 +222,10 @@
      * may be passed up from the lower networking layers, and its
      * meaning may be specific to a particular network type. Retrieve
      * it with {@link android.content.Intent#getStringExtra(String)}.
+     *
+     * @deprecated See {@link NetworkInfo#getExtraInfo()}.
      */
+    @Deprecated
     public static final String EXTRA_EXTRA_INFO = "extraInfo";
     /**
      * The lookup key for an int that provides information about
@@ -895,8 +906,11 @@
      *
      * @return a {@link NetworkInfo} object for the current default network
      *        or {@code null} if no default network is currently active
+     * @deprecated See {@link NetworkInfo}.
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @Nullable
     public NetworkInfo getActiveNetworkInfo() {
         try {
             return mService.getActiveNetworkInfo();
@@ -916,6 +930,7 @@
      *        {@code null} if no default network is currently active
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @Nullable
     public Network getActiveNetwork() {
         try {
             return mService.getActiveNetwork();
@@ -937,6 +952,7 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+    @Nullable
     public Network getActiveNetworkForUid(int uid) {
         return getActiveNetworkForUid(uid, false);
     }
@@ -1062,6 +1078,7 @@
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @Nullable
     public NetworkInfo getNetworkInfo(int networkType) {
         try {
             return mService.getNetworkInfo(networkType);
@@ -1079,9 +1096,12 @@
      * @return a {@link NetworkInfo} object for the requested
      *        network or {@code null} if the {@code Network}
      *        is not valid.
+     * @deprecated See {@link NetworkInfo}.
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public NetworkInfo getNetworkInfo(Network network) {
+    @Nullable
+    public NetworkInfo getNetworkInfo(@Nullable Network network) {
         return getNetworkInfoForUid(network, Process.myUid(), false);
     }
 
@@ -1107,6 +1127,7 @@
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @NonNull
     public NetworkInfo[] getAllNetworkInfo() {
         try {
             return mService.getAllNetworkInfo();
@@ -1142,6 +1163,7 @@
      * @return an array of {@link Network} objects.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @NonNull
     public Network[] getAllNetworks() {
         try {
             return mService.getAllNetworks();
@@ -1216,7 +1238,8 @@
      * @return The {@link LinkProperties} for the network, or {@code null}.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public LinkProperties getLinkProperties(Network network) {
+    @Nullable
+    public LinkProperties getLinkProperties(@Nullable Network network) {
         try {
             return mService.getLinkProperties(network);
         } catch (RemoteException e) {
@@ -1232,7 +1255,8 @@
      * @return The {@link android.net.NetworkCapabilities} for the network, or {@code null}.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public NetworkCapabilities getNetworkCapabilities(Network network) {
+    @Nullable
+    public NetworkCapabilities getNetworkCapabilities(@Nullable Network network) {
         try {
             return mService.getNetworkCapabilities(network);
         } catch (RemoteException e) {
@@ -1986,7 +2010,7 @@
      *
      * @param l Previously registered listener.
      */
-    public void removeDefaultNetworkActiveListener(OnNetworkActiveListener l) {
+    public void removeDefaultNetworkActiveListener(@NonNull OnNetworkActiveListener l) {
         INetworkActivityListener rl = mNetworkActivityListeners.get(l);
         Preconditions.checkArgument(rl != null, "Listener was not registered.");
         try {
@@ -2514,7 +2538,7 @@
      *             working and non-working connectivity.
      */
     @Deprecated
-    public void reportBadNetwork(Network network) {
+    public void reportBadNetwork(@Nullable Network network) {
         printStackTrace();
         try {
             // One of these will be ignored because it matches system's current state.
@@ -2537,7 +2561,7 @@
      * @param hasConnectivity {@code true} if the application was able to successfully access the
      *                        Internet using {@code network} or {@code false} if not.
      */
-    public void reportNetworkConnectivity(Network network, boolean hasConnectivity) {
+    public void reportNetworkConnectivity(@Nullable Network network, boolean hasConnectivity) {
         printStackTrace();
         try {
             mService.reportNetworkConnectivity(network, hasConnectivity);
@@ -2611,6 +2635,7 @@
      * @return the {@link ProxyInfo} for the current HTTP proxy, or {@code null} if no
      *        HTTP proxy is active.
      */
+    @Nullable
     public ProxyInfo getDefaultProxy() {
         return getProxyForNetwork(getBoundNetworkForProcess());
     }
@@ -2755,7 +2780,7 @@
     }
 
     /** {@hide} */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void unregisterNetworkFactory(Messenger messenger) {
         try {
             mService.unregisterNetworkFactory(messenger);
@@ -3142,8 +3167,9 @@
      *
      * @hide
      */
-    public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
-            int timeoutMs, int legacyType, Handler handler) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, int timeoutMs, int legacyType,
+            @NonNull Handler handler) {
         CallbackHandler cbHandler = new CallbackHandler(handler);
         NetworkCapabilities nc = request.networkCapabilities;
         sendRequestForNetwork(nc, networkCallback, timeoutMs, REQUEST, legacyType, cbHandler);
@@ -3180,7 +3206,8 @@
      * @throws IllegalArgumentException if {@code request} specifies any mutable
      *         {@code NetworkCapabilities}.
      */
-    public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback) {
         requestNetwork(request, networkCallback, getDefaultHandler());
     }
 
@@ -3215,8 +3242,8 @@
      * @throws IllegalArgumentException if {@code request} specifies any mutable
      *         {@code NetworkCapabilities}.
      */
-    public void requestNetwork(
-            NetworkRequest request, NetworkCallback networkCallback, Handler handler) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
         int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
         CallbackHandler cbHandler = new CallbackHandler(handler);
         requestNetwork(request, networkCallback, 0, legacyType, cbHandler);
@@ -3250,8 +3277,8 @@
      *                  before {@link NetworkCallback#onUnavailable()} is called. The timeout must
      *                  be a positive value (i.e. >0).
      */
-    public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
-            int timeoutMs) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, int timeoutMs) {
         checkTimeout(timeoutMs);
         int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
         requestNetwork(request, networkCallback, timeoutMs, legacyType, getDefaultHandler());
@@ -3284,8 +3311,8 @@
      * @param timeoutMs The time in milliseconds to attempt looking for a suitable network
      *                  before {@link NetworkCallback#onUnavailable} is called.
      */
-    public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
-            Handler handler, int timeoutMs) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, @NonNull Handler handler, int timeoutMs) {
         checkTimeout(timeoutMs);
         int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
         CallbackHandler cbHandler = new CallbackHandler(handler);
@@ -3357,7 +3384,8 @@
      *         {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or
      *         {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL}.
      */
-    public void requestNetwork(NetworkRequest request, PendingIntent operation) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull PendingIntent operation) {
         printStackTrace();
         checkPendingIntentNotNull(operation);
         try {
@@ -3381,7 +3409,7 @@
      *                  {@link #requestNetwork(NetworkRequest, android.app.PendingIntent)} with the
      *                  corresponding NetworkRequest you'd like to remove. Cannot be null.
      */
-    public void releaseNetworkRequest(PendingIntent operation) {
+    public void releaseNetworkRequest(@NonNull PendingIntent operation) {
         printStackTrace();
         checkPendingIntentNotNull(operation);
         try {
@@ -3414,7 +3442,8 @@
      *                        The callback is invoked on the default internal Handler.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public void registerNetworkCallback(NetworkRequest request, NetworkCallback networkCallback) {
+    public void registerNetworkCallback(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback) {
         registerNetworkCallback(request, networkCallback, getDefaultHandler());
     }
 
@@ -3429,8 +3458,8 @@
      * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public void registerNetworkCallback(
-            NetworkRequest request, NetworkCallback networkCallback, Handler handler) {
+    public void registerNetworkCallback(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
         CallbackHandler cbHandler = new CallbackHandler(handler);
         NetworkCapabilities nc = request.networkCapabilities;
         sendRequestForNetwork(nc, networkCallback, 0, LISTEN, TYPE_NONE, cbHandler);
@@ -3466,7 +3495,8 @@
      *                  comes from {@link PendingIntent#getBroadcast}. Cannot be null.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public void registerNetworkCallback(NetworkRequest request, PendingIntent operation) {
+    public void registerNetworkCallback(@NonNull NetworkRequest request,
+            @NonNull PendingIntent operation) {
         printStackTrace();
         checkPendingIntentNotNull(operation);
         try {
@@ -3488,7 +3518,7 @@
      *                        The callback is invoked on the default internal Handler.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public void registerDefaultNetworkCallback(NetworkCallback networkCallback) {
+    public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback) {
         registerDefaultNetworkCallback(networkCallback, getDefaultHandler());
     }
 
@@ -3502,7 +3532,8 @@
      * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public void registerDefaultNetworkCallback(NetworkCallback networkCallback, Handler handler) {
+    public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback,
+            @NonNull Handler handler) {
         // This works because if the NetworkCapabilities are null,
         // ConnectivityService takes them from the default request.
         //
@@ -3527,7 +3558,7 @@
      * @param network {@link Network} specifying which network you're interested.
      * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
      */
-    public boolean requestBandwidthUpdate(Network network) {
+    public boolean requestBandwidthUpdate(@NonNull Network network) {
         try {
             return mService.requestBandwidthUpdate(network);
         } catch (RemoteException e) {
@@ -3548,7 +3579,7 @@
      *
      * @param networkCallback The {@link NetworkCallback} used when making the request.
      */
-    public void unregisterNetworkCallback(NetworkCallback networkCallback) {
+    public void unregisterNetworkCallback(@NonNull NetworkCallback networkCallback) {
         printStackTrace();
         checkCallbackNotNull(networkCallback);
         final List<NetworkRequest> reqs = new ArrayList<>();
@@ -3587,7 +3618,7 @@
      *                  {@link #registerNetworkCallback(NetworkRequest, android.app.PendingIntent)}.
      *                  Cannot be null.
      */
-    public void unregisterNetworkCallback(PendingIntent operation) {
+    public void unregisterNetworkCallback(@NonNull PendingIntent operation) {
         checkPendingIntentNotNull(operation);
         releaseNetworkRequest(operation);
     }
@@ -3709,7 +3740,7 @@
      * @return a bitwise OR of zero or more of the  {@code MULTIPATH_PREFERENCE_*} constants.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public @MultipathPreference int getMultipathPreference(Network network) {
+    public @MultipathPreference int getMultipathPreference(@Nullable Network network) {
         try {
             return mService.getMultipathPreference(network);
         } catch (RemoteException e) {
@@ -3747,7 +3778,7 @@
      *                the current binding.
      * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
      */
-    public boolean bindProcessToNetwork(Network network) {
+    public boolean bindProcessToNetwork(@Nullable Network network) {
         // Forcing callers to call through non-static function ensures ConnectivityManager
         // instantiated.
         return setProcessDefaultNetwork(network);
@@ -3775,7 +3806,7 @@
      *             is a direct replacement.
      */
     @Deprecated
-    public static boolean setProcessDefaultNetwork(Network network) {
+    public static boolean setProcessDefaultNetwork(@Nullable Network network) {
         int netId = (network == null) ? NETID_UNSET : network.netId;
         if (netId == NetworkUtils.getBoundNetworkForProcess()) {
             return true;
@@ -3806,6 +3837,7 @@
      *
      * @return {@code Network} to which this process is bound, or {@code null}.
      */
+    @Nullable
     public Network getBoundNetworkForProcess() {
         // Forcing callers to call thru non-static function ensures ConnectivityManager
         // instantiated.
@@ -3822,6 +3854,7 @@
      *             {@code getBoundNetworkForProcess} is a direct replacement.
      */
     @Deprecated
+    @Nullable
     public static Network getProcessDefaultNetwork() {
         int netId = NetworkUtils.getBoundNetworkForProcess();
         if (netId == NETID_UNSET) return null;
@@ -3948,6 +3981,7 @@
      *
      * @return Hash of network watchlist config file. Null if config does not exist.
      */
+    @Nullable
     public byte[] getNetworkWatchlistConfigHash() {
         try {
             return mService.getNetworkWatchlistConfigHash();
@@ -3969,8 +4003,8 @@
      * (e.g., if it is associated with the calling VPN app's tunnel) or
      * {@link android.os.Process#INVALID_UID} if the connection is not found.
      */
-    public int getConnectionOwnerUid(int protocol, InetSocketAddress local,
-                                     InetSocketAddress remote) {
+    public int getConnectionOwnerUid(int protocol, @NonNull InetSocketAddress local,
+            @NonNull InetSocketAddress remote) {
         ConnectionInfo connectionInfo = new ConnectionInfo(protocol, local, remote);
         try {
             return mService.getConnectionOwnerUid(connectionInfo);
diff --git a/core/java/android/net/INetdEventCallback.aidl b/core/java/android/net/INetdEventCallback.aidl
index 4b1a08d..0877a1a4 100644
--- a/core/java/android/net/INetdEventCallback.aidl
+++ b/core/java/android/net/INetdEventCallback.aidl
@@ -45,6 +45,20 @@
             in String[] ipAddresses, int ipAddressesCount, long timestamp, int uid);
 
     /**
+     * Represents adding or removing a NAT64 prefix.
+     * This method must not block or perform long-running operations.
+     *
+     * @param netId the ID of the network the prefix was performed on.
+     * @param added true if the NAT64 prefix was added, or false if the NAT64 prefix was removed.
+     *        There is only one prefix at a time for each netId. If a prefix is added, it replaces
+     *        the previous-added prefix.
+     * @param prefixString the detected NAT64 prefix as a string literal.
+     * @param prefixLength the prefix length associated with this NAT64 prefix.
+     */
+    void onNat64PrefixEvent(int netId, boolean added, @utf8InCpp String prefixString,
+            int prefixLength);
+
+    /**
      * Represents a private DNS validation success or failure.
      * This method must not block or perform long-running operations.
      *
diff --git a/core/java/android/service/contentcapture/InteractionContext.aidl b/core/java/android/net/INetworkStackConnector.aidl
similarity index 72%
copy from core/java/android/service/contentcapture/InteractionContext.aidl
copy to core/java/android/net/INetworkStackConnector.aidl
index 982e095..29f8828 100644
--- a/core/java/android/service/contentcapture/InteractionContext.aidl
+++ b/core/java/android/net/INetworkStackConnector.aidl
@@ -10,10 +10,12 @@
  * 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
+ * See the License for the specific language governing perNmissions and
  * limitations under the License.
  */
+package android.net;
 
-package android.service.contentcapture;
-
-parcelable InteractionContext;
+/** @hide */
+oneway interface INetworkStackConnector {
+    // TODO: requestDhcpServer(), etc. will go here
+}
\ No newline at end of file
diff --git a/core/java/android/net/InetAddresses.java b/core/java/android/net/InetAddresses.java
new file mode 100644
index 0000000..8e6c69a
--- /dev/null
+++ b/core/java/android/net/InetAddresses.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import libcore.net.InetAddressUtils;
+
+import java.net.InetAddress;
+
+/**
+ * Utility methods for {@link InetAddress} implementations.
+ */
+public class InetAddresses {
+
+    private InetAddresses() {}
+
+    /**
+     * Checks to see if the {@code address} is a numeric address (such as {@code "192.0.2.1"} or
+     * {@code "2001:db8::1:2"}).
+     *
+     * <p>A numeric address is either an IPv4 address containing exactly 4 decimal numbers or an
+     * IPv6 numeric address. IPv4 addresses that consist of either hexadecimal or octal digits or
+     * do not have exactly 4 numbers are not treated as numeric.
+     *
+     * <p>This method will never do a DNS lookup.
+     *
+     * @param address the address to parse.
+     * @return true if the supplied address is numeric, false otherwise.
+     */
+    public static boolean isNumericAddress(String address) {
+        return InetAddressUtils.isNumericAddress(address);
+    }
+
+    /**
+     * Returns an InetAddress corresponding to the given numeric address (such
+     * as {@code "192.168.0.1"} or {@code "2001:4860:800d::68"}).
+     *
+     * <p>See {@link #isNumericAddress(String)} (String)} for a definition as to what constitutes a
+     * numeric address.
+     *
+     * <p>This method will never do a DNS lookup.
+     *
+     * @param address the address to parse, must be numeric.
+     * @return an {@link InetAddress} instance corresponding to the address.
+     * @throws IllegalArgumentException if {@code address} is not a numeric address.
+     */
+    public static InetAddress parseNumericAddress(String address) {
+        return InetAddressUtils.parseNumericAddress(address);
+    }
+}
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index 77562db..b40f15a 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -27,6 +27,7 @@
 
 import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Pair;
@@ -56,13 +57,13 @@
     /**
      * IPv4 or IPv6 address.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private InetAddress address;
 
     /**
      * Prefix length.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int prefixLength;
 
     /**
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 1b9a66c..3a79206 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -18,7 +18,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -49,7 +51,7 @@
  */
 public final class LinkProperties implements Parcelable {
     // The interface described by the network link.
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private String mIfaceName;
     private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<>();
     private ArrayList<InetAddress> mDnses = new ArrayList<>();
@@ -161,7 +163,7 @@
     /**
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public LinkProperties() {
     }
 
@@ -195,7 +197,7 @@
      * @param iface The name of the network interface used for this link.
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public void setInterfaceName(String iface) {
         mIfaceName = iface;
         ArrayList<RouteInfo> newRoutes = new ArrayList<>(mRoutes.size());
@@ -346,7 +348,7 @@
      *                  object.
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public void setLinkAddresses(Collection<LinkAddress> addresses) {
         mLinkAddresses.clear();
         for (LinkAddress address: addresses) {
@@ -392,7 +394,7 @@
      * @param dnsServers The {@link Collection} of DNS servers to set in this object.
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public void setDnsServers(Collection<InetAddress> dnsServers) {
         mDnses.clear();
         for (InetAddress dnsServer: dnsServers) {
@@ -529,7 +531,7 @@
      *                domains to search when resolving host names on this link.
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public void setDomains(String domains) {
         mDomains = domains;
     }
@@ -552,7 +554,7 @@
      * @param mtu The MTU to use for this link.
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public void setMtu(int mtu) {
         mMtu = mtu;
     }
@@ -562,9 +564,7 @@
      * this will return 0.
      *
      * @return The mtu value set for this link.
-     * @hide
      */
-    @UnsupportedAppUsage
     public int getMtu() {
         return mMtu;
     }
@@ -613,7 +613,7 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public boolean addRoute(RouteInfo route) {
         if (route != null) {
             String routeIface = route.getInterface();
@@ -688,7 +688,7 @@
      * @param proxy A {@link ProxyInfo} defining the HTTP Proxy to use on this link.
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public void setHttpProxy(ProxyInfo proxy) {
         mHttpProxy = proxy;
     }
@@ -760,7 +760,7 @@
      * Clears this object to its initial state.
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public void clear() {
         mIfaceName = null;
         mLinkAddresses.clear();
@@ -1137,7 +1137,7 @@
      * @return {@code true} if both are identical, {@code false} otherwise.
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public boolean isIdenticalHttpProxy(LinkProperties target) {
         return getHttpProxy() == null ? target.getHttpProxy() == null :
                 getHttpProxy().equals(target.getHttpProxy());
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 114b423..99bfc14 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -19,6 +19,7 @@
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.net.ConnectivityManager.PacketKeepalive;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -352,7 +353,7 @@
     /**
      * Called by the bearer code when it has new NetworkInfo data.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void sendNetworkInfo(NetworkInfo networkInfo) {
         queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, new NetworkInfo(networkInfo));
     }
diff --git a/core/java/android/net/NetworkBadging.java b/core/java/android/net/NetworkBadging.java
index c119b63..cd9ea88 100644
--- a/core/java/android/net/NetworkBadging.java
+++ b/core/java/android/net/NetworkBadging.java
@@ -21,13 +21,10 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
 import android.net.wifi.WifiManager;
-import android.view.View;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -96,34 +93,4 @@
                 throw new IllegalArgumentException("Invalid signal level: " + signalLevel);
         }
     }
-
-    /**
-     * Returns the badged wifi signal resource id for the given signal level.
-     *
-     * <p>This badged wifi signal resource should be displayed with the quality badge retrieved
-     * from {@link #getWifiBadgeResource(int)}. If there is no badge,
-     * {@link #getWifiBadgeResource(int)} should be used instead of this method.
-     *
-     * @param signalLevel The level returned by {@link WifiManager#calculateSignalLevel(int, int)}
-     *                    for a network. Must be between 0 and {@link WifiManager#RSSI_LEVELS}-1.
-     * @return the @DrawableRes for the icon
-     * @throws IllegalArgumentException for an invalid signal level
-     * @hide
-     */
-    @DrawableRes private static int getBadgedWifiSignalResource(int signalLevel) {
-        switch (signalLevel) {
-            case 0:
-                return com.android.internal.R.drawable.ic_signal_wifi_badged_0_bars;
-            case 1:
-                return com.android.internal.R.drawable.ic_signal_wifi_badged_1_bar;
-            case 2:
-                return com.android.internal.R.drawable.ic_signal_wifi_badged_2_bars;
-            case 3:
-                return com.android.internal.R.drawable.ic_signal_wifi_badged_3_bars;
-            case 4:
-                return com.android.internal.R.drawable.ic_signal_wifi_badged_4_bars;
-            default:
-                throw new IllegalArgumentException("Invalid signal level: " + signalLevel);
-        }
-    }
 }
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 8a5f43d..1b44c92 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -1017,7 +1017,7 @@
      * @return The bearer-specific signal strength.
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public int getSignalStrength() {
         return mSignalStrength;
     }
@@ -1467,7 +1467,7 @@
             appendStringRepresentationOfBitMaskToStringBuilder(sb, mNetworkCapabilities,
                     NetworkCapabilities::capabilityNameOf, "&");
         }
-        if (0 != mNetworkCapabilities) {
+        if (0 != mUnwantedNetworkCapabilities) {
             sb.append(" Unwanted: ");
             appendStringRepresentationOfBitMaskToStringBuilder(sb, mUnwantedNetworkCapabilities,
                     NetworkCapabilities::capabilityNameOf, "&");
diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java
index 9c836c3..181cab4 100644
--- a/core/java/android/net/NetworkFactory.java
+++ b/core/java/android/net/NetworkFactory.java
@@ -18,6 +18,7 @@
 
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -289,7 +290,7 @@
         sendMessage(obtainMessage(CMD_CANCEL_REQUEST, networkRequest));
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void setScoreFilter(int score) {
         sendMessage(obtainMessage(CMD_SET_SCORE, score, 0));
     }
@@ -307,7 +308,7 @@
         Log.d(LOG_TAG, s);
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
         pw.println(toString());
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 1a1d2d334..89d9961 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -28,7 +28,20 @@
  * Describes the status of a network interface.
  * <p>Use {@link ConnectivityManager#getActiveNetworkInfo()} to get an instance that represents
  * the current network connection.
+ *
+ * @deprecated Callers should instead use the {@link ConnectivityManager.NetworkCallback} API to
+ *             learn about connectivity changes, or switch to use
+ *             {@link ConnectivityManager#getNetworkCapabilities} or
+ *             {@link ConnectivityManager#getLinkProperties} to get information synchronously. Keep
+ *             in mind that while callbacks are guaranteed to be called for every event in order,
+ *             synchronous calls have no such constraints, and as such it is unadvisable to use the
+ *             synchronous methods inside the callbacks as they will often not offer a view of
+ *             networking that is consistent (that is: they may return a past or a future state with
+ *             respect to the event being processed by the callback). Instead, callers are advised
+ *             to only use the arguments of the callbacks, possibly memorizing the specific bits of
+ *             information they need to keep from one callback to another.
  */
+@Deprecated
 public class NetworkInfo implements Parcelable {
 
     /**
@@ -52,7 +65,10 @@
      * <tr><td><code>FAILED</code></td><td><code>DISCONNECTED</code></td></tr>
      * <tr><td><code>BLOCKED</code></td><td><code>DISCONNECTED</code></td></tr>
      * </table>
+     *
+     * @deprecated See {@link NetworkInfo}.
      */
+    @Deprecated
     public enum State {
         CONNECTING, CONNECTED, SUSPENDED, DISCONNECTING, DISCONNECTED, UNKNOWN
     }
@@ -61,7 +77,10 @@
      * The fine-grained state of a network connection. This level of detail
      * is probably of interest to few applications. Most should use
      * {@link android.net.NetworkInfo.State State} instead.
+     *
+     * @deprecated See {@link NetworkInfo}.
      */
+    @Deprecated
     public enum DetailedState {
         /** Ready to start data connection setup. */
         IDLE,
@@ -463,8 +482,10 @@
      * Set the extraInfo field.
      * @param extraInfo an optional {@code String} providing addditional network state
      * information passed up from the lower networking layers.
+     * @deprecated See {@link NetworkInfo#getExtraInfo}.
      * @hide
      */
+    @Deprecated
     public void setExtraInfo(String extraInfo) {
         synchronized (this) {
             this.mExtraInfo = extraInfo;
@@ -488,7 +509,10 @@
      * Report the extra information about the network state, if any was
      * provided by the lower networking layers.
      * @return the extra information, or null if not available
+     * @deprecated Use other services e.g. WifiManager to get additional information passed up from
+     *             the lower networking layers.
      */
+    @Deprecated
     public String getExtraInfo() {
         synchronized (this) {
             return mExtraInfo;
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 299b232..5ab34e9 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -28,6 +28,7 @@
 import android.content.pm.Signature;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
+import android.os.Build;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.DebugUtils;
@@ -203,7 +204,7 @@
         }
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void registerListener(INetworkPolicyListener listener) {
         try {
             mService.registerListener(listener);
@@ -212,7 +213,7 @@
         }
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void unregisterListener(INetworkPolicyListener listener) {
         try {
             mService.unregisterListener(listener);
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 04b6b44..9508217 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -17,9 +17,11 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.net.NetworkCapabilities.NetCapability;
 import android.net.NetworkCapabilities.Transport;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.Process;
@@ -56,7 +58,7 @@
      * Causes CONNECTIVITY_ACTION broadcasts to be sent.
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public final int legacyType;
 
     /**
@@ -344,7 +346,7 @@
          * @param signalStrength the bearer-specific signal strength.
          * @hide
          */
-        @UnsupportedAppUsage
+        @SystemApi
         public Builder setSignalStrength(int signalStrength) {
             mNetworkCapabilities.setSignalStrength(signalStrength);
             return this;
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
new file mode 100644
index 0000000..82a4e31
--- /dev/null
+++ b/core/java/android/net/NetworkStack.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net;
+
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+
+/**
+ * Service used to communicate with the network stack, which is running in a separate module.
+ * @hide
+ */
+@SystemService(Context.NETWORK_STACK_SERVICE)
+public class NetworkStack {
+    private static final String TAG = NetworkStack.class.getSimpleName();
+
+    @NonNull
+    @GuardedBy("mPendingNetStackRequests")
+    private final ArrayList<NetworkStackRequest> mPendingNetStackRequests = new ArrayList<>();
+    @Nullable
+    @GuardedBy("mPendingNetStackRequests")
+    private INetworkStackConnector mConnector;
+
+    private interface NetworkStackRequest {
+        void onNetworkStackConnected(INetworkStackConnector connector);
+    }
+
+    public NetworkStack() { }
+
+    private class NetworkStackConnection implements ServiceConnection {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            registerNetworkStackService(service);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            // TODO: crash/reboot the system ?
+            Slog.wtf(TAG, "Lost network stack connector");
+        }
+    };
+
+    private void registerNetworkStackService(@NonNull IBinder service) {
+        final INetworkStackConnector connector = INetworkStackConnector.Stub.asInterface(service);
+
+        ServiceManager.addService(Context.NETWORK_STACK_SERVICE, service, false /* allowIsolated */,
+                DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
+
+        final ArrayList<NetworkStackRequest> requests;
+        synchronized (mPendingNetStackRequests) {
+            requests = new ArrayList<>(mPendingNetStackRequests);
+            mPendingNetStackRequests.clear();
+            mConnector = connector;
+        }
+
+        for (NetworkStackRequest r : requests) {
+            r.onNetworkStackConnected(connector);
+        }
+    }
+
+    /**
+     * Start the network stack. Should be called only once on device startup.
+     *
+     * <p>This method will start the network stack either in the network stack process, or inside
+     * the system server on devices that do not support the network stack module. The network stack
+     * connector will then be delivered asynchronously to clients that requested it before it was
+     * started.
+     */
+    public void start(Context context) {
+        // Try to bind in-process if the library is available
+        IBinder connector = null;
+        try {
+            final Class service = Class.forName(
+                    "com.android.server.NetworkStackService",
+                    true /* initialize */,
+                    context.getClassLoader());
+            connector = (IBinder) service.getMethod("makeConnector").invoke(null);
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            Slog.wtf(TAG, "Could not create network stack connector from NetworkStackService");
+            // TODO: crash/reboot system here ?
+            return;
+        } catch (ClassNotFoundException e) {
+            // Normal behavior if stack is provided by the app: fall through
+        }
+
+        // In-process network stack. Add the service to the service manager here.
+        if (connector != null) {
+            registerNetworkStackService(connector);
+            return;
+        }
+        // Start the network stack process. The service will be added to the service manager in
+        // NetworkStackConnection.onServiceConnected().
+        final Intent intent = new Intent(INetworkStackConnector.class.getName());
+        final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
+        intent.setComponent(comp);
+
+        if (comp == null || !context.bindServiceAsUser(intent, new NetworkStackConnection(),
+                Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
+            Slog.wtf(TAG,
+                    "Could not bind to network stack in-process, or in app with " + intent);
+            // TODO: crash/reboot system server if no network stack after a timeout ?
+        }
+    }
+
+    // TODO: use this method to obtain the connector when implementing network stack operations
+    private void requestConnector(@NonNull NetworkStackRequest request) {
+        // TODO: PID check.
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            // Don't even attempt to obtain the connector and give a nice error message
+            throw new SecurityException(
+                    "Only the system server should try to bind to the network stack.");
+        }
+
+        final INetworkStackConnector connector;
+        synchronized (mPendingNetStackRequests) {
+            connector = mConnector;
+            if (connector == null) {
+                mPendingNetStackRequests.add(request);
+                return;
+            }
+        }
+
+        request.onNetworkStackConnected(connector);
+    }
+}
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index a00b9a3..9cf582b 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -16,6 +16,8 @@
 
 package android.net;
 
+import static android.os.Process.CLAT_UID;
+
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -828,13 +830,15 @@
      *
      * <p>For 464xlat traffic, xt_qtaguid sees every IPv4 packet twice, once as a native IPv4
      * packet on the stacked interface, and once as translated to an IPv6 packet on the
-     * base interface. For correct stats accounting on the base interface, every 464xlat
-     * packet needs to be subtracted from the root UID on the base interface both for tx
-     * and rx traffic (http://b/12249687, http:/b/33681750).
+     * base interface. For correct stats accounting on the base interface, if using xt_qtaguid,
+     * every rx 464xlat packet needs to be subtracted from the root UID on the base interface
+     * (http://b/12249687, http:/b/33681750), and every tx 464xlat packet which was counted onto
+     * clat uid should be ignored.
      *
      * As for eBPF, the per uid stats is collected by different hook, the rx packets on base
-     * interface will not be counted. Thus, the adjustment on root uid is only needed in tx
-     * direction.
+     * interface will not be counted. Thus, the adjustment on root uid is not needed. However, the
+     * tx traffic counted in the same way xt_qtaguid does, so the traffic on clat uid still
+     * needs to be ignored.
      *
      * <p>This method will behave fine if {@code stackedIfaces} is an non-synchronized but add-only
      * {@code ConcurrentHashMap}
@@ -862,17 +866,14 @@
             if (baseIface == null) {
                 continue;
             }
-            // Subtract any 464lat traffic seen for the root UID on the current base interface.
-            // However, for eBPF, the per uid stats is collected by different hook, the rx packets
-            // on base interface will not be counted. Thus, the adjustment on root uid is only
-            // needed in tx direction.
+            // Subtract xt_qtaguid 464lat rx traffic seen for the root UID on the current base
+            // interface. As for eBPF, the per uid stats is collected by different hook, the rx
+            // packets on base interface will not be counted.
             adjust.iface = baseIface;
             if (!useBpfStats) {
                 adjust.rxBytes = -(entry.rxBytes + entry.rxPackets * IPV4V6_HEADER_DELTA);
                 adjust.rxPackets = -entry.rxPackets;
             }
-            adjust.txBytes = -(entry.txBytes + entry.txPackets * IPV4V6_HEADER_DELTA);
-            adjust.txPackets = -entry.txPackets;
             adjustments.combineValues(adjust);
 
             // For 464xlat traffic, per uid stats only counts the bytes of the native IPv4 packet
@@ -884,6 +885,9 @@
             stackedTraffic.setValues(i, entry);
         }
 
+        // Traffic on clat uid is v6 tx traffic that is already counted with app uid on the stacked
+        // v4 interface, so it needs to be removed to avoid double-counting.
+        baseTraffic.removeUids(new int[] {CLAT_UID});
         baseTraffic.combineAllValues(adjustments);
     }
 
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index c431e40e..4eab49c 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -21,6 +21,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.system.Os;
 import android.util.Log;
@@ -299,8 +300,10 @@
      * @param addrString
      * @return the InetAddress
      * @hide
+     * @deprecated Use {@link InetAddresses#parseNumericAddress(String)}, if possible.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @Deprecated
     public static InetAddress numericToInetAddress(String addrString)
             throws IllegalArgumentException {
         return InetAddress.parseNumericAddress(addrString);
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 3e73d3d..37ab9ff 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -81,7 +82,7 @@
 
     // Derived data members.
     // TODO: remove these.
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final boolean mIsHost;
     private final boolean mHasGateway;
 
@@ -265,7 +266,7 @@
         }
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private boolean isHost() {
         return (mDestination.getAddress() instanceof Inet4Address &&
                 mDestination.getPrefixLength() == 32) ||
diff --git a/core/java/android/net/WebAddress.java b/core/java/android/net/WebAddress.java
index 8026d76..fbc281f 100644
--- a/core/java/android/net/WebAddress.java
+++ b/core/java/android/net/WebAddress.java
@@ -20,6 +20,7 @@
 
 import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 
 import java.util.Locale;
 import java.util.regex.Matcher;
@@ -50,7 +51,7 @@
     private String mScheme;
     @UnsupportedAppUsage
     private String mHost;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mPort;
     @UnsupportedAppUsage
     private String mPath;
diff --git a/core/java/android/service/contentcapture/InteractionContext.aidl b/core/java/android/net/dhcp/DhcpServingParamsParcel.aidl
similarity index 63%
copy from core/java/android/service/contentcapture/InteractionContext.aidl
copy to core/java/android/net/dhcp/DhcpServingParamsParcel.aidl
index 982e095..7b8b9ee 100644
--- a/core/java/android/service/contentcapture/InteractionContext.aidl
+++ b/core/java/android/net/dhcp/DhcpServingParamsParcel.aidl
@@ -1,5 +1,6 @@
 /**
- * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +15,16 @@
  * limitations under the License.
  */
 
-package android.service.contentcapture;
+package android.net.dhcp;
 
-parcelable InteractionContext;
+parcelable DhcpServingParamsParcel {
+    int serverAddr;
+    int serverAddrPrefixLength;
+    int[] defaultRouters;
+    int[] dnsServers;
+    int[] excludedAddrs;
+    long dhcpLeaseTimeSecs;
+    int linkMtu;
+    boolean metered;
+}
+
diff --git a/core/java/android/net/http/SslError.java b/core/java/android/net/http/SslError.java
index fad6689..b3f2fb7 100644
--- a/core/java/android/net/http/SslError.java
+++ b/core/java/android/net/http/SslError.java
@@ -17,6 +17,7 @@
 package android.net.http;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import java.security.cert.X509Certificate;
 
 /**
@@ -68,7 +69,7 @@
      * The SSL error set bitfield (each individual error is a bit index;
      * multiple individual errors can be OR-ed)
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     int mErrors;
 
     /**
@@ -80,7 +81,7 @@
     /**
      * The URL associated with the error set.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     final String mUrl;
 
     /**
diff --git a/core/java/android/nfc/INfcCardEmulation.aidl b/core/java/android/nfc/INfcCardEmulation.aidl
index d1b132c..dd2c0d4 100644
--- a/core/java/android/nfc/INfcCardEmulation.aidl
+++ b/core/java/android/nfc/INfcCardEmulation.aidl
@@ -31,6 +31,8 @@
     boolean setDefaultServiceForCategory(int userHandle, in ComponentName service, String category);
     boolean setDefaultForNextTap(int userHandle, in ComponentName service);
     boolean registerAidGroupForService(int userHandle, in ComponentName service, in AidGroup aidGroup);
+    boolean setOffHostForService(int userHandle, in ComponentName service, in String offHostSecureElement);
+    boolean unsetOffHostForService(int userHandle, in ComponentName service);
     AidGroup getAidGroupForService(int userHandle, in ComponentName service, String category);
     boolean removeAidGroupForService(int userHandle, in ComponentName service, String category);
     List<ApduServiceInfo> getServices(int userHandle, in String category);
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 21fed48..e55e036 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -16,6 +16,7 @@
 
 package android.nfc;
 
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -42,7 +43,9 @@
 import android.util.Log;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 
 /**
  * Represents the local NFC adapter.
@@ -322,6 +325,7 @@
     // Guarded by NfcAdapter.class
     static boolean sIsInitialized = false;
     static boolean sHasNfcFeature;
+    static boolean sHasBeamFeature;
 
     // Final after first constructor, except for
     // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
@@ -369,7 +373,9 @@
      * A callback to be invoked when the system successfully delivers your {@link NdefMessage}
      * to another device.
      * @see #setOnNdefPushCompleteCallback
+     * @deprecated this feature is deprecated.
      */
+    @java.lang.Deprecated
     public interface OnNdefPushCompleteCallback {
         /**
          * Called on successful NDEF push.
@@ -392,7 +398,9 @@
      * content currently visible to the user. Alternatively, you can call {@link
      * #setNdefPushMessage setNdefPushMessage()} if the {@link NdefMessage} always contains the
      * same data.
+     * @deprecated this feature is deprecated.
      */
+    @java.lang.Deprecated
     public interface CreateNdefMessageCallback {
         /**
          * Called to provide a {@link NdefMessage} to push.
@@ -418,7 +426,10 @@
     }
 
 
-    // TODO javadoc
+     /**
+     * @deprecated this feature is deprecated.
+     */
+    @java.lang.Deprecated
     public interface CreateBeamUrisCallback {
         public Uri[] createBeamUris(NfcEvent event);
     }
@@ -446,6 +457,25 @@
         public boolean onUnlockAttempted(Tag tag);
     }
 
+    /**
+     * Helper to check if this device has FEATURE_NFC_BEAM, but without using
+     * a context.
+     * Equivalent to
+     * context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_BEAM)
+     */
+    private static boolean hasBeamFeature() {
+        IPackageManager pm = ActivityThread.getPackageManager();
+        if (pm == null) {
+            Log.e(TAG, "Cannot get package manager, assuming no Android Beam feature");
+            return false;
+        }
+        try {
+            return pm.hasSystemFeature(PackageManager.FEATURE_NFC_BEAM, 0);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Package manager query failed, assuming no Android Beam feature", e);
+            return false;
+        }
+    }
 
     /**
      * Helper to check if this device has FEATURE_NFC, but without using
@@ -488,6 +518,35 @@
     }
 
     /**
+     * Return list of Secure Elements which support off host card emulation.
+     *
+     * @return List<String> containing secure elements on the device which supports
+     *                      off host card emulation. eSE for Embedded secure element,
+     *                      SIM for UICC and so on.
+     */
+    public @NonNull List<String> getSupportedOffHostSecureElements() {
+        List<String> offHostSE = new ArrayList<String>();
+        IPackageManager pm = ActivityThread.getPackageManager();
+        if (pm == null) {
+            Log.e(TAG, "Cannot get package manager, assuming no off-host CE feature");
+            return offHostSE;
+        }
+        try {
+            if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC, 0)) {
+                offHostSE.add("SIM");
+            }
+            if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE, 0)) {
+                offHostSE.add("eSE");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Package manager query failed, assuming no off-host CE feature", e);
+            offHostSE.clear();
+            return offHostSE;
+        }
+        return offHostSE;
+    }
+
+    /**
      * Returns the NfcAdapter for application context,
      * or throws if NFC is not available.
      * @hide
@@ -496,6 +555,7 @@
     public static synchronized NfcAdapter getNfcAdapter(Context context) {
         if (!sIsInitialized) {
             sHasNfcFeature = hasNfcFeature();
+            sHasBeamFeature = hasBeamFeature();
             boolean hasHceFeature = hasNfcHceFeature();
             /* is this device meant to have NFC */
             if (!sHasNfcFeature && !hasHceFeature) {
@@ -921,12 +981,17 @@
      * @param uris an array of Uri(s) to push over Android Beam
      * @param activity activity for which the Uri(s) will be pushed
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @deprecated this feature is deprecated.
      */
+    @java.lang.Deprecated
     public void setBeamPushUris(Uri[] uris, Activity activity) {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return;
+            }
         }
         if (activity == null) {
             throw new NullPointerException("activity cannot be null");
@@ -1003,12 +1068,17 @@
      * @param callback callback, or null to disable
      * @param activity activity for which the Uri(s) will be pushed
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @deprecated this feature is deprecated.
      */
+    @java.lang.Deprecated
     public void setBeamPushUrisCallback(CreateBeamUrisCallback callback, Activity activity) {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return;
+            }
         }
         if (activity == null) {
             throw new NullPointerException("activity cannot be null");
@@ -1087,13 +1157,18 @@
      *        to only register one at a time, and to do so in that activity's
      *        {@link Activity#onCreate}
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @deprecated this feature is deprecated.
      */
+    @java.lang.Deprecated
     public void setNdefPushMessage(NdefMessage message, Activity activity,
             Activity ... activities) {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return;
+            }
         }
         int targetSdkVersion = getSdkVersion();
         try {
@@ -1200,13 +1275,18 @@
      *        to only register one at a time, and to do so in that activity's
      *        {@link Activity#onCreate}
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @deprecated this feature is deprecated.
      */
+    @java.lang.Deprecated
     public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity,
             Activity ... activities) {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return;
+            }
         }
         int targetSdkVersion = getSdkVersion();
         try {
@@ -1281,13 +1361,18 @@
      *        to only register one at a time, and to do so in that activity's
      *        {@link Activity#onCreate}
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @deprecated this feature is deprecated.
      */
+    @java.lang.Deprecated
     public void setOnNdefPushCompleteCallback(OnNdefPushCompleteCallback callback,
             Activity activity, Activity ... activities) {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return;
+            }
         }
         int targetSdkVersion = getSdkVersion();
         try {
@@ -1492,12 +1577,17 @@
      * @param activity the current foreground Activity that has registered data to share
      * @return whether the Beam animation was successfully invoked
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @deprecated this feature is deprecated.
      */
+    @java.lang.Deprecated
     public boolean invokeBeam(Activity activity) {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return false;
+            }
         }
         if (activity == null) {
             throw new NullPointerException("activity may not be null.");
@@ -1561,6 +1651,9 @@
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return;
+            }
         }
         if (activity == null || message == null) {
             throw new NullPointerException();
@@ -1595,6 +1688,9 @@
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return;
+            }
         }
         if (activity == null) {
             throw new NullPointerException();
@@ -1668,12 +1764,18 @@
      * @see android.provider.Settings#ACTION_NFCSHARING_SETTINGS
      * @return true if NDEF Push feature is enabled
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @deprecated this feature is deprecated.
      */
+    @java.lang.Deprecated
+
     public boolean isNdefPushEnabled() {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return false;
+            }
         }
         try {
             return sService.isNdefPushEnabled();
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index e8d801c..911ec84 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -18,11 +18,10 @@
 
 import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
 import android.content.res.TypedArray;
@@ -30,7 +29,6 @@
 import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.ResultReceiver;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Xml;
@@ -69,6 +67,18 @@
     final boolean mOnHost;
 
     /**
+     * Offhost reader name.
+     * eg: SIM, eSE etc
+     */
+    String mOffHostName;
+
+    /**
+     * Offhost reader name from manifest file.
+     * Used for unsetOffHostSecureElement()
+     */
+    final String mStaticOffHostName;
+
+    /**
      * Mapping from category to static AID group
      */
     @UnsupportedAppUsage
@@ -104,15 +114,17 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
+    public ApduServiceInfo(ResolveInfo info, String description,
             ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups,
             boolean requiresUnlock, int bannerResource, int uid,
-            String settingsActivityName) {
+            String settingsActivityName, String offHost, String staticOffHost) {
         this.mService = info;
         this.mDescription = description;
         this.mStaticAidGroups = new HashMap<String, AidGroup>();
         this.mDynamicAidGroups = new HashMap<String, AidGroup>();
-        this.mOnHost = onHost;
+        this.mOffHostName = offHost;
+        this.mStaticOffHostName = staticOffHost;
+        this.mOnHost = (offHost == null);
         this.mRequiresDeviceUnlock = requiresUnlock;
         for (AidGroup aidGroup : staticAidGroups) {
             this.mStaticAidGroups.put(aidGroup.category, aidGroup);
@@ -174,6 +186,8 @@
                         com.android.internal.R.styleable.HostApduService_apduServiceBanner, -1);
                 mSettingsActivityName = sa.getString(
                         com.android.internal.R.styleable.HostApduService_settingsActivity);
+                mOffHostName = null;
+                mStaticOffHostName = mOffHostName;
                 sa.recycle();
             } else {
                 TypedArray sa = res.obtainAttributes(attrs,
@@ -186,6 +200,16 @@
                         com.android.internal.R.styleable.OffHostApduService_apduServiceBanner, -1);
                 mSettingsActivityName = sa.getString(
                         com.android.internal.R.styleable.HostApduService_settingsActivity);
+                mOffHostName = sa.getString(
+                        com.android.internal.R.styleable.OffHostApduService_secureElementName);
+                if (mOffHostName != null) {
+                    if (mOffHostName.equals("eSE")) {
+                        mOffHostName = "eSE1";
+                    } else if (mOffHostName.equals("SIM")) {
+                        mOffHostName = "SIM1";
+                    }
+                }
+                mStaticOffHostName = mOffHostName;
                 sa.recycle();
             }
 
@@ -289,6 +313,10 @@
                 mService.serviceInfo.name);
     }
 
+    public String getOffHostSecureElement() {
+        return mOffHostName;
+    }
+
     /**
      * Returns a consolidated list of AIDs from the AID groups
      * registered by this service. Note that if a service has both
@@ -404,6 +432,20 @@
         mDynamicAidGroups.put(aidGroup.getCategory(), aidGroup);
     }
 
+    @UnsupportedAppUsage
+    public void setOffHostSecureElement(String offHost) {
+        mOffHostName = offHost;
+    }
+
+    /**
+     * Resets the off host Secure Element to statically defined
+     * by the service in the manifest file.
+     */
+    @UnsupportedAppUsage
+    public void unsetOffHostSecureElement() {
+        mOffHostName = mStaticOffHostName;
+    }
+
     public CharSequence loadLabel(PackageManager pm) {
         return mService.loadLabel(pm);
     }
@@ -481,6 +523,8 @@
         mService.writeToParcel(dest, flags);
         dest.writeString(mDescription);
         dest.writeInt(mOnHost ? 1 : 0);
+        dest.writeString(mOffHostName);
+        dest.writeString(mStaticOffHostName);
         dest.writeInt(mStaticAidGroups.size());
         if (mStaticAidGroups.size() > 0) {
             dest.writeTypedList(new ArrayList<AidGroup>(mStaticAidGroups.values()));
@@ -503,6 +547,8 @@
             ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source);
             String description = source.readString();
             boolean onHost = source.readInt() != 0;
+            String offHostName = source.readString();
+            String staticOffHostName = source.readString();
             ArrayList<AidGroup> staticAidGroups = new ArrayList<AidGroup>();
             int numStaticGroups = source.readInt();
             if (numStaticGroups > 0) {
@@ -517,9 +563,9 @@
             int bannerResource = source.readInt();
             int uid = source.readInt();
             String settingsActivityName = source.readString();
-            return new ApduServiceInfo(info, onHost, description, staticAidGroups,
+            return new ApduServiceInfo(info, description, staticAidGroups,
                     dynamicAidGroups, requiresUnlock, bannerResource, uid,
-                    settingsActivityName);
+                    settingsActivityName, offHostName, staticOffHostName);
         }
 
         @Override
@@ -531,6 +577,14 @@
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("    " + getComponent() +
                 " (Description: " + getDescription() + ")");
+        if (mOnHost) {
+            pw.println("    On Host Service");
+        } else {
+            pw.println("    Off-host Service");
+            pw.println("        " + "Current off-host SE" + mOffHostName
+                    + " static off-host: " + mOffHostName);
+        }
+        pw.println("    Static off-host Secure Element:");
         pw.println("    Static AID groups:");
         for (AidGroup group : mStaticAidGroups.values()) {
             pw.println("        Category: " + group.category);
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index 15d02f2..01932ab 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -27,7 +27,6 @@
 import android.nfc.INfcCardEmulation;
 import android.nfc.NfcAdapter;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.util.Log;
@@ -345,6 +344,108 @@
     }
 
     /**
+     * Unsets the off-host Secure Element for the given service.
+     *
+     * <p>Note that this will only remove Secure Element that was dynamically
+     * set using the {@link #setOffHostForService(ComponentName, String)}
+     * and resets it to a value that was statically assigned using manifest.
+     *
+     * <p>Note that you can only unset off-host SE for a service that
+     * is running under the same UID as the caller of this API. Typically
+     * this means you need to call this from the same
+     * package as the service itself, though UIDs can also
+     * be shared between packages using shared UIDs.
+     *
+     * @param service The component name of the service
+     * @return whether the registration was successful.
+     */
+    public boolean unsetOffHostForService(ComponentName service) {
+        NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+        if (adapter == null) {
+            return false;
+        }
+
+        try {
+            return sService.unsetOffHostForService(mContext.getUserId(), service);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.unsetOffHostForService(mContext.getUserId(), service);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Sets the off-host Secure Element for the given service.
+     *
+     * <p>If off-host SE was initially set (either statically
+     * through the manifest, or dynamically by using this API),
+     * it will be replaced with this one. All AIDs registered by
+     * this service will be re-routed to this Secure Element if
+     * successful.
+     *
+     * <p>Note that you can only set off-host SE for a service that
+     * is running under the same UID as the caller of this API. Typically
+     * this means you need to call this from the same
+     * package as the service itself, though UIDs can also
+     * be shared between packages using shared UIDs.
+     *
+     * <p>Registeration will be successful only if the Secure Element
+     * exists on the device.
+     *
+     * @param service The component name of the service
+     * @param offHostSecureElement Secure Element to register the AID to
+     * @return whether the registration was successful.
+     */
+    public boolean setOffHostForService(ComponentName service, String offHostSecureElement) {
+        boolean validSecureElement = false;
+
+        NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+        if (adapter == null || offHostSecureElement == null) {
+            return false;
+        }
+
+        List<String> validSE = adapter.getSupportedOffHostSecureElements();
+        if ((offHostSecureElement.startsWith("eSE") && !validSE.contains("eSE"))
+                || (offHostSecureElement.startsWith("SIM") && !validSE.contains("SIM"))) {
+            return false;
+        }
+
+        if (offHostSecureElement.equals("eSE")) {
+            offHostSecureElement = "eSE1";
+        } else if (offHostSecureElement.equals("SIM")) {
+            offHostSecureElement = "SIM1";
+        }
+
+        try {
+            return sService.setOffHostForService(mContext.getUserId(), service,
+                offHostSecureElement);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.setOffHostForService(mContext.getUserId(), service,
+                        offHostSecureElement);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+    /**
      * Retrieves the currently registered AIDs for the specified
      * category for a service.
      *
diff --git a/core/java/android/os/AppZygote.java b/core/java/android/os/AppZygote.java
new file mode 100644
index 0000000..40cbaf7
--- /dev/null
+++ b/core/java/android/os/AppZygote.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.content.pm.ApplicationInfo;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * AppZygote is responsible for interfacing with an application-specific zygote.
+ *
+ * Application zygotes can pre-load app-specific code and data, and this interface can
+ * be used to spawn isolated services from such an application zygote.
+ *
+ * Note that we'll have only one instance of this per application / uid combination.
+ *
+ * @hide
+ */
+public class AppZygote {
+    private static final String LOG_TAG = "AppZygote";
+
+    private final int mZygoteUid;
+
+    private final Object mLock = new Object();
+
+    /**
+     * Instance that maintains the socket connection to the zygote. This is {@code null} if the
+     * zygote is not running or is not connected.
+     */
+    @GuardedBy("mLock")
+    private ChildZygoteProcess mZygote;
+
+    private final ApplicationInfo mAppInfo;
+
+    public AppZygote(ApplicationInfo appInfo, int zygoteUid) {
+        mAppInfo = appInfo;
+        mZygoteUid = zygoteUid;
+    }
+
+    /**
+     * Returns the zygote process associated with this app zygote.
+     * Creates the process if it's not already running.
+     */
+    public ChildZygoteProcess getProcess() {
+        synchronized (mLock) {
+            if (mZygote != null) return mZygote;
+
+            connectToZygoteIfNeededLocked();
+            return mZygote;
+        }
+    }
+
+    /**
+     * Stops the Zygote and kills the zygote process.
+     */
+    public void stopZygote() {
+        synchronized (mLock) {
+            stopZygoteLocked();
+        }
+    }
+
+    public ApplicationInfo getAppInfo() {
+        return mAppInfo;
+    }
+
+    @GuardedBy("mLock")
+    private void stopZygoteLocked() {
+        if (mZygote != null) {
+            // Close the connection and kill the zygote process. This will not cause
+            // child processes to be killed by itself.
+            mZygote.close();
+            Process.killProcess(mZygote.getPid());
+            mZygote = null;
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void connectToZygoteIfNeededLocked() {
+        String abi = mAppInfo.primaryCpuAbi != null ? mAppInfo.primaryCpuAbi :
+                Build.SUPPORTED_ABIS[0];
+        try {
+            mZygote = Process.zygoteProcess.startChildZygote(
+                    "com.android.internal.os.AppZygoteInit",
+                    mAppInfo.processName + "_zygote",
+                    mZygoteUid,
+                    mZygoteUid,
+                    null,  // gids
+                    0,  // runtimeFlags
+                    "app_zygote",  // seInfo
+                    abi,  // abi
+                    abi, // acceptedAbiList
+                    null);  // instructionSet
+
+            ZygoteProcess.waitForConnectionToZygote(mZygote.getPrimarySocketAddress());
+            // preload application code in the zygote
+            Log.i(LOG_TAG, "Starting application preload.");
+            mZygote.preloadApp(mAppInfo, abi);
+            Log.i(LOG_TAG, "Application preload done.");
+        } catch (Exception e) {
+            Log.e(LOG_TAG, "Error connecting to app zygote", e);
+            stopZygoteLocked();
+        }
+    }
+}
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 8904ee6..a236300 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -382,6 +382,11 @@
     }
 
     /** {@hide} */
+    public static File getDataStagingDirectory(String volumeUuid) {
+        return new File(getDataDirectory(volumeUuid), "staging");
+    }
+
+    /** {@hide} */
     public static File getDataUserCeDirectory(String volumeUuid) {
         return new File(getDataDirectory(volumeUuid), "user");
     }
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 567efa7..e84a518 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -23,6 +23,7 @@
 import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
 import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
 import static android.system.OsConstants.F_OK;
+import static android.system.OsConstants.O_ACCMODE;
 import static android.system.OsConstants.O_APPEND;
 import static android.system.OsConstants.O_CREAT;
 import static android.system.OsConstants.O_RDONLY;
@@ -49,6 +50,7 @@
 import android.webkit.MimeTypeMap;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.SizedInputStream;
 
 import libcore.io.IoUtils;
@@ -110,8 +112,6 @@
         public static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+");
     }
 
-    private static final File[] EMPTY = new File[0];
-
     // non-final so it can be toggled by Robolectric's ShadowFileUtils
     private static boolean sEnableCopyOptimizations = true;
 
@@ -1164,35 +1164,20 @@
 
     /** {@hide} */
     public static @NonNull String[] listOrEmpty(@Nullable File dir) {
-        if (dir == null) return EmptyArray.STRING;
-        final String[] res = dir.list();
-        if (res != null) {
-            return res;
-        } else {
-            return EmptyArray.STRING;
-        }
+        return (dir != null) ? ArrayUtils.defeatNullable(dir.list())
+                : EmptyArray.STRING;
     }
 
     /** {@hide} */
     public static @NonNull File[] listFilesOrEmpty(@Nullable File dir) {
-        if (dir == null) return EMPTY;
-        final File[] res = dir.listFiles();
-        if (res != null) {
-            return res;
-        } else {
-            return EMPTY;
-        }
+        return (dir != null) ? ArrayUtils.defeatNullable(dir.listFiles())
+                : ArrayUtils.EMPTY_FILE;
     }
 
     /** {@hide} */
     public static @NonNull File[] listFilesOrEmpty(@Nullable File dir, FilenameFilter filter) {
-        if (dir == null) return EMPTY;
-        final File[] res = dir.listFiles(filter);
-        if (res != null) {
-            return res;
-        } else {
-            return EMPTY;
-        }
+        return (dir != null) ? ArrayUtils.defeatNullable(dir.listFiles(filter))
+                : ArrayUtils.EMPTY_FILE;
     }
 
     /** {@hide} */
@@ -1275,11 +1260,11 @@
 
         int res = 0;
         if (mode.startsWith("rw")) {
-            res |= O_RDWR | O_CREAT;
+            res = O_RDWR | O_CREAT;
         } else if (mode.startsWith("w")) {
-            res |= O_WRONLY | O_CREAT;
+            res = O_WRONLY | O_CREAT;
         } else if (mode.startsWith("r")) {
-            res |= O_RDONLY;
+            res = O_RDONLY;
         } else {
             throw new IllegalArgumentException("Bad mode: " + mode);
         }
@@ -1295,12 +1280,12 @@
     /** {@hide} */
     public static String translateModePosixToString(int mode) {
         String res = "";
-        if ((mode & O_RDWR) == O_RDWR) {
-            res += "rw";
-        } else if ((mode & O_WRONLY) == O_WRONLY) {
-            res += "w";
-        } else if ((mode & O_RDONLY) == O_RDONLY) {
-            res += "r";
+        if ((mode & O_ACCMODE) == O_RDWR) {
+            res = "rw";
+        } else if ((mode & O_ACCMODE) == O_WRONLY) {
+            res = "w";
+        } else if ((mode & O_ACCMODE) == O_RDONLY) {
+            res = "r";
         } else {
             throw new IllegalArgumentException("Bad mode: " + mode);
         }
@@ -1316,12 +1301,12 @@
     /** {@hide} */
     public static int translateModePosixToPfd(int mode) {
         int res = 0;
-        if ((mode & O_RDWR) == O_RDWR) {
-            res |= MODE_READ_WRITE;
-        } else if ((mode & O_WRONLY) == O_WRONLY) {
-            res |= MODE_WRITE_ONLY;
-        } else if ((mode & O_RDONLY) == O_RDONLY) {
-            res |= MODE_READ_ONLY;
+        if ((mode & O_ACCMODE) == O_RDWR) {
+            res = MODE_READ_WRITE;
+        } else if ((mode & O_ACCMODE) == O_WRONLY) {
+            res = MODE_WRITE_ONLY;
+        } else if ((mode & O_ACCMODE) == O_RDONLY) {
+            res = MODE_READ_ONLY;
         } else {
             throw new IllegalArgumentException("Bad mode: " + mode);
         }
@@ -1341,11 +1326,11 @@
     public static int translateModePfdToPosix(int mode) {
         int res = 0;
         if ((mode & MODE_READ_WRITE) == MODE_READ_WRITE) {
-            res |= O_RDWR;
+            res = O_RDWR;
         } else if ((mode & MODE_WRITE_ONLY) == MODE_WRITE_ONLY) {
-            res |= O_WRONLY;
+            res = O_WRONLY;
         } else if ((mode & MODE_READ_ONLY) == MODE_READ_ONLY) {
-            res |= O_RDONLY;
+            res = O_RDONLY;
         } else {
             throw new IllegalArgumentException("Bad mode: " + mode);
         }
@@ -1444,4 +1429,3 @@
         }
     }
 }
-
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 124d7b1..4c0ee6f 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -17,8 +17,10 @@
 package android.os;
 
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.content.res.AssetFileDescriptor;
 import android.content.res.AssetManager;
 import android.opengl.EGL14;
@@ -56,10 +58,10 @@
     private static final boolean DEBUG = false;
     private static final String TAG = "GraphicsEnvironment";
     private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
-    private static final String PROPERTY_GFX_DRIVER_WHITELIST = "ro.gfx.driver.whitelist.0";
-    private static final String ANGLE_PACKAGE_NAME = "com.google.android.angle";
+    private static final String GUP_WHITELIST_FILENAME = "whitelist.txt";
     private static final String ANGLE_RULES_FILE = "a4a_rules.json";
     private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
+    private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID";
 
     private ClassLoader mClassLoader;
     private String mLayerPath;
@@ -255,8 +257,7 @@
             return sDriverMap.get(OpenGlDriverChoice.DEFAULT);
         }
         // Make sure we have good settings to use
-        if (globalSettingsDriverPkgs.isEmpty() || globalSettingsDriverValues.isEmpty()
-                || (globalSettingsDriverPkgs.size() != globalSettingsDriverValues.size())) {
+        if (globalSettingsDriverPkgs.size() != globalSettingsDriverValues.size()) {
             Log.w(TAG,
                     "Global.Settings values are invalid: "
                         + "globalSettingsDriverPkgs.size = "
@@ -276,9 +277,121 @@
     }
 
     /**
+     * Get the ANGLE package name.
+     */
+    private String getAnglePackageName(Context context) {
+        Intent intent = new Intent(ACTION_ANGLE_FOR_ANDROID);
+
+        List<ResolveInfo> resolveInfos = context.getPackageManager()
+                .queryIntentActivities(intent, PackageManager.MATCH_SYSTEM_ONLY);
+        if (resolveInfos.size() != 1) {
+            Log.e(TAG, "Invalid number of ANGLE packages. Required: 1, Found: "
+                    + resolveInfos.size());
+            for (ResolveInfo resolveInfo : resolveInfos) {
+                Log.e(TAG, "Found ANGLE package: " + resolveInfo.activityInfo.packageName);
+            }
+            return "";
+        }
+
+        // Must be exactly 1 ANGLE PKG found to get here.
+        return resolveInfos.get(0).activityInfo.packageName;
+    }
+
+    /**
+     * Attempt to setup ANGLE with a temporary rules file.
+     * True: Temporary rules file was loaded.
+     * False: Temporary rules file was *not* loaded.
+     */
+    private boolean setupAngleWithTempRulesFile(Context context,
+                                                String packageName,
+                                                String paths,
+                                                String devOptIn) {
+        // Check for temporary rules if debuggable or root
+        if (!isDebuggable(context) && !(getCanLoadSystemLibraries() == 1)) {
+            Log.v(TAG, "Skipping loading temporary rules file");
+            return false;
+        }
+
+        String angleTempRules = SystemProperties.get(ANGLE_TEMP_RULES);
+
+        if ((angleTempRules == null) || angleTempRules.isEmpty()) {
+            Log.v(TAG, "System property '" + ANGLE_TEMP_RULES + "' is not set or is empty");
+            return false;
+        }
+
+        Log.i(TAG, "Detected system property " + ANGLE_TEMP_RULES + ": " + angleTempRules);
+
+        File tempRulesFile = new File(angleTempRules);
+        if (tempRulesFile.exists()) {
+            Log.i(TAG, angleTempRules + " exists, loading file.");
+            try {
+                FileInputStream stream = new FileInputStream(angleTempRules);
+
+                try {
+                    FileDescriptor rulesFd = stream.getFD();
+                    long rulesOffset = 0;
+                    long rulesLength = stream.getChannel().size();
+                    Log.i(TAG, "Loaded temporary ANGLE rules from " + angleTempRules);
+
+                    setAngleInfo(paths, packageName, devOptIn, rulesFd, rulesOffset, rulesLength);
+
+                    stream.close();
+
+                    // We successfully setup ANGLE, so return with good status
+                    return true;
+                } catch (IOException e) {
+                    Log.w(TAG, "Hit IOException thrown by FileInputStream: " + e);
+                }
+            } catch (FileNotFoundException e) {
+                Log.w(TAG, "Temp ANGLE rules file not found: " + e);
+            } catch (SecurityException e) {
+                Log.w(TAG, "Temp ANGLE rules file not accessible: " + e);
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Attempt to setup ANGLE with a rules file loaded from the ANGLE APK.
+     * True: APK rules file was loaded.
+     * False: APK rules file was *not* loaded.
+     */
+    private boolean setupAngleRulesApk(String anglePkgName,
+            ApplicationInfo angleInfo,
+            Context context,
+            String packageName,
+            String paths,
+            String devOptIn) {
+        // Pass the rules file to loader for ANGLE decisions
+        try {
+            AssetManager angleAssets =
+                    context.getPackageManager().getResourcesForApplication(angleInfo).getAssets();
+
+            try {
+                AssetFileDescriptor assetsFd = angleAssets.openFd(ANGLE_RULES_FILE);
+
+                setAngleInfo(paths, packageName, devOptIn, assetsFd.getFileDescriptor(),
+                        assetsFd.getStartOffset(), assetsFd.getLength());
+
+                assetsFd.close();
+
+                return true;
+            } catch (IOException e) {
+                Log.w(TAG, "Failed to get AssetFileDescriptor for " + ANGLE_RULES_FILE
+                        + " from '" + anglePkgName + "': " + e);
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.w(TAG, "Failed to get AssetManager for '" + anglePkgName + "': " + e);
+        }
+
+        return false;
+    }
+
+    /**
      * Pass ANGLE details down to trigger enable logic
      */
-    private void setupAngle(Context context, Bundle bundle, String packageName) {
+    public void setupAngle(Context context, Bundle bundle, String packageName) {
         String devOptIn = getDriverForPkg(bundle, packageName);
 
         if (DEBUG) {
@@ -286,98 +399,41 @@
                     + "set to: '" + devOptIn + "'");
         }
 
+        String anglePkgName = getAnglePackageName(context);
+        if (anglePkgName.isEmpty()) {
+            Log.e(TAG, "Failed to find ANGLE package.");
+            return;
+        }
+
         ApplicationInfo angleInfo;
         try {
-            angleInfo = context.getPackageManager().getApplicationInfo(ANGLE_PACKAGE_NAME,
+            angleInfo = context.getPackageManager().getApplicationInfo(anglePkgName,
                 PackageManager.MATCH_SYSTEM_ONLY);
         } catch (PackageManager.NameNotFoundException e) {
-            Log.w(TAG, "ANGLE package '" + ANGLE_PACKAGE_NAME + "' not installed");
+            Log.w(TAG, "ANGLE package '" + anglePkgName + "' not installed");
             return;
         }
 
         String abi = chooseAbi(angleInfo);
 
         // Build a path that includes installed native libs and APK
-        StringBuilder sb = new StringBuilder();
-        sb.append(angleInfo.nativeLibraryDir)
-            .append(File.pathSeparator)
-            .append(angleInfo.sourceDir)
-            .append("!/lib/")
-            .append(abi);
-        String paths = sb.toString();
+        String paths = angleInfo.nativeLibraryDir
+                + File.pathSeparator
+                + angleInfo.sourceDir
+                + "!/lib/"
+                + abi;
 
         if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths);
 
-        // Look up rules file to pass to ANGLE
-        FileDescriptor rulesFd = null;
-        long rulesOffset = 0;
-        long rulesLength = 0;
-
-        // Check for temporary rules if debuggable or root
-        if (isDebuggable(context) || (getCanLoadSystemLibraries() == 1)) {
-            String angleTempRules = SystemProperties.get(ANGLE_TEMP_RULES);
-            if (angleTempRules != null && !angleTempRules.isEmpty()) {
-                Log.i(TAG, "Detected system property " + ANGLE_TEMP_RULES + ": " + angleTempRules);
-                File tempRulesFile = new File(angleTempRules);
-                if (tempRulesFile.exists()) {
-                    Log.i(TAG, angleTempRules + " exists, loading file.");
-                    FileInputStream stream = null;
-                    try {
-                        stream = new FileInputStream(angleTempRules);
-                    } catch (FileNotFoundException e) {
-                        Log.w(TAG, "Unable to create stream for temp ANGLE rules");
-                    }
-
-                    if (stream != null) {
-                        try {
-                            rulesFd = stream.getFD();
-                            rulesOffset = 0;
-                            rulesLength = stream.getChannel().size();
-                            Log.i(TAG, "Loaded temporary ANGLE rules from " + angleTempRules);
-                        } catch (IOException e) {
-                            Log.w(TAG, "Failed to get input stream for " + angleTempRules);
-                        }
-                    }
-                }
-            }
+        if (setupAngleWithTempRulesFile(context, packageName, paths, devOptIn)) {
+            // We setup ANGLE with a temp rules file, so we're done here.
+            return;
         }
 
-        // If no temp rules, load the real ones from the APK
-        if (DEBUG && (rulesFd == null)) {
-
-            // Pass the rules file to loader for ANGLE decisions
-            AssetManager angleAssets = null;
-            try {
-                angleAssets =
-                    context.getPackageManager().getResourcesForApplication(angleInfo).getAssets();
-            } catch (PackageManager.NameNotFoundException e) {
-                Log.w(TAG, "Failed to get AssetManager for '" + ANGLE_PACKAGE_NAME + "'");
-                return;
-            }
-
-            AssetFileDescriptor assetsFd = null;
-            try {
-                assetsFd = angleAssets.openFd(ANGLE_RULES_FILE);
-            } catch (IOException e) {
-                Log.w(TAG, "Failed to get AssetFileDescriptor for " + ANGLE_RULES_FILE + " from "
-                           + "'" + ANGLE_PACKAGE_NAME + "'");
-                return;
-            }
-
-            if (assetsFd != null) {
-                rulesFd = assetsFd.getFileDescriptor();
-                rulesOffset = assetsFd.getStartOffset();
-                rulesLength = assetsFd.getLength();
-            } else {
-                Log.w(TAG, "Failed to get file descriptor for " + ANGLE_RULES_FILE);
-                return;
-            }
+        if (setupAngleRulesApk(anglePkgName, angleInfo, context, packageName, paths, devOptIn)) {
+            // We setup ANGLE with rules from the APK, so we're done here.
+            return;
         }
-
-        // Further opt-in logic is handled in native, so pass relevant info down
-        // TODO: Move the ANGLE selection logic earlier so we don't need to keep these
-        //       file descriptors open.
-        setAngleInfo(paths, packageName, devOptIn, rulesFd, rulesOffset, rulesLength);
     }
 
     /**
@@ -400,7 +456,7 @@
 
         String applicationPackageName = context.getPackageName();
         String devOptInApplicationName = coreSettings.getString(
-                Settings.Global.UPDATED_GFX_DRIVER_DEV_OPT_IN_APP);
+                Settings.Global.GUP_DEV_OPT_IN_APPS);
         boolean devOptIn = applicationPackageName.equals(devOptInApplicationName);
         boolean whitelisted = onWhitelist(context, driverPackageName, ai.packageName);
         if (!devOptIn && !whitelisted) {
@@ -485,22 +541,11 @@
 
     private static boolean onWhitelist(Context context, String driverPackageName,
             String applicationPackageName) {
-        String whitelistName = SystemProperties.get(PROPERTY_GFX_DRIVER_WHITELIST);
-
-        // Empty whitelist implies no updatable graphics driver. Typically, the pre-installed
-        // updatable graphics driver is supposed to be a place holder and contains no graphics
-        // driver and whitelist.
-        if (whitelistName == null || whitelistName.isEmpty()) {
-            if (DEBUG) {
-                Log.w(TAG, "No whitelist found.");
-            }
-            return false;
-        }
         try {
             Context driverContext = context.createPackageContext(driverPackageName,
                                                                  Context.CONTEXT_RESTRICTED);
             AssetManager assets = driverContext.getAssets();
-            InputStream stream = assets.open(whitelistName);
+            InputStream stream = assets.open(GUP_WHITELIST_FILENAME);
             BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
             for (String packageName; (packageName = reader.readLine()) != null; ) {
                 if (packageName.equals(applicationPackageName)) {
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index be8cf0e..fdd7488 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -390,7 +390,7 @@
     /**
      * Setup a new VPN.
      */
-    void createVirtualNetwork(int netId, boolean hasDNS, boolean secure);
+    void createVirtualNetwork(int netId, boolean secure);
 
     /**
      * Remove a network.
diff --git a/core/java/android/os/NativeHandle.java b/core/java/android/os/NativeHandle.java
index fbecc8e..f7ffc37 100644
--- a/core/java/android/os/NativeHandle.java
+++ b/core/java/android/os/NativeHandle.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import static android.system.OsConstants.F_DUPFD_CLOEXEC;
+
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.system.ErrnoException;
@@ -108,7 +110,10 @@
         FileDescriptor[] fds = new FileDescriptor[mFds.length];
         try {
             for (int i = 0; i < mFds.length; i++) {
-                fds[i] = Os.dup(mFds[i]);
+                FileDescriptor newFd = new FileDescriptor();
+                int fdint = Os.fcntlInt(mFds[i], F_DUPFD_CLOEXEC, 0);
+                newFd.setInt$(fdint);
+                fds[i] = newFd;
             }
         } catch (ErrnoException e) {
             e.rethrowAsIOException();
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 6de1ff4..63912ec 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -17,7 +17,11 @@
 package android.os;
 
 import static android.system.OsConstants.AF_UNIX;
+import static android.system.OsConstants.F_DUPFD;
+import static android.system.OsConstants.F_DUPFD_CLOEXEC;
+import static android.system.OsConstants.O_CLOEXEC;
 import static android.system.OsConstants.SEEK_SET;
+import static android.system.OsConstants.SOCK_CLOEXEC;
 import static android.system.OsConstants.SOCK_SEQPACKET;
 import static android.system.OsConstants.SOCK_STREAM;
 import static android.system.OsConstants.S_IROTH;
@@ -37,6 +41,7 @@
 import android.util.Log;
 
 import dalvik.system.CloseGuard;
+import dalvik.system.VMRuntime;
 
 import libcore.io.IoUtils;
 import libcore.io.Memory;
@@ -293,7 +298,7 @@
     }
 
     private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException {
-        final int flags = FileUtils.translateModePfdToPosix(mode);
+        final int flags = FileUtils.translateModePfdToPosix(mode) | ifAtLeastQ(O_CLOEXEC);
 
         int realMode = S_IRWXU | S_IRWXG;
         if ((mode & MODE_WORLD_READABLE) != 0) realMode |= S_IROTH;
@@ -315,7 +320,9 @@
      */
     public static ParcelFileDescriptor dup(FileDescriptor orig) throws IOException {
         try {
-            final FileDescriptor fd = Os.dup(orig);
+            final FileDescriptor fd = new FileDescriptor();
+            int intfd = Os.fcntlInt(orig, (isAtLeastQ() ? F_DUPFD_CLOEXEC : F_DUPFD), 0);
+            fd.setInt$(intfd);
             return new ParcelFileDescriptor(fd);
         } catch (ErrnoException e) {
             throw e.rethrowAsIOException();
@@ -351,7 +358,9 @@
         original.setInt$(fd);
 
         try {
-            final FileDescriptor dup = Os.dup(original);
+            final FileDescriptor dup = new FileDescriptor();
+            int intfd = Os.fcntlInt(original, (isAtLeastQ() ? F_DUPFD_CLOEXEC : F_DUPFD), 0);
+            dup.setInt$(intfd);
             return new ParcelFileDescriptor(dup);
         } catch (ErrnoException e) {
             throw e.rethrowAsIOException();
@@ -413,7 +422,7 @@
      */
     public static ParcelFileDescriptor[] createPipe() throws IOException {
         try {
-            final FileDescriptor[] fds = Os.pipe();
+            final FileDescriptor[] fds = Os.pipe2(ifAtLeastQ(O_CLOEXEC));
             return new ParcelFileDescriptor[] {
                     new ParcelFileDescriptor(fds[0]),
                     new ParcelFileDescriptor(fds[1]) };
@@ -435,7 +444,7 @@
     public static ParcelFileDescriptor[] createReliablePipe() throws IOException {
         try {
             final FileDescriptor[] comm = createCommSocketPair();
-            final FileDescriptor[] fds = Os.pipe();
+            final FileDescriptor[] fds = Os.pipe2(ifAtLeastQ(O_CLOEXEC));
             return new ParcelFileDescriptor[] {
                     new ParcelFileDescriptor(fds[0], comm[0]),
                     new ParcelFileDescriptor(fds[1], comm[1]) };
@@ -459,7 +468,7 @@
         try {
             final FileDescriptor fd0 = new FileDescriptor();
             final FileDescriptor fd1 = new FileDescriptor();
-            Os.socketpair(AF_UNIX, type, 0, fd0, fd1);
+            Os.socketpair(AF_UNIX, type | ifAtLeastQ(SOCK_CLOEXEC), 0, fd0, fd1);
             return new ParcelFileDescriptor[] {
                     new ParcelFileDescriptor(fd0),
                     new ParcelFileDescriptor(fd1) };
@@ -489,7 +498,7 @@
             final FileDescriptor[] comm = createCommSocketPair();
             final FileDescriptor fd0 = new FileDescriptor();
             final FileDescriptor fd1 = new FileDescriptor();
-            Os.socketpair(AF_UNIX, type, 0, fd0, fd1);
+            Os.socketpair(AF_UNIX, type | ifAtLeastQ(SOCK_CLOEXEC), 0, fd0, fd1);
             return new ParcelFileDescriptor[] {
                     new ParcelFileDescriptor(fd0, comm[0]),
                     new ParcelFileDescriptor(fd1, comm[1]) };
@@ -505,7 +514,7 @@
             // across multiple IO operations.
             final FileDescriptor comm1 = new FileDescriptor();
             final FileDescriptor comm2 = new FileDescriptor();
-            Os.socketpair(AF_UNIX, SOCK_SEQPACKET, 0, comm1, comm2);
+            Os.socketpair(AF_UNIX, SOCK_SEQPACKET | ifAtLeastQ(SOCK_CLOEXEC), 0, comm1, comm2);
             IoUtils.setBlocking(comm1, false);
             IoUtils.setBlocking(comm2, false);
             return new FileDescriptor[] { comm1, comm2 };
@@ -1111,4 +1120,12 @@
             return "{" + status + ": " + msg + "}";
         }
     }
+
+    private static boolean isAtLeastQ() {
+        return (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q);
+    }
+
+    private static int ifAtLeastQ(int value) {
+        return isAtLeastQ() ? value : 0;
+    }
 }
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 2abcb4c..ee56e3d 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -111,6 +111,12 @@
     public static final int NFC_UID = 1027;
 
     /**
+     * Defines the UID/GID for the clatd process.
+     * @hide
+     * */
+    public static final int CLAT_UID = 1029;
+
+    /**
      * Defines the UID/GID for the Bluetooth service process.
      * @hide
      */
@@ -176,6 +182,12 @@
      */
     public static final int SE_UID = 1068;
 
+    /**
+     * Defines the UID/GID for the NetworkStack app.
+     * @hide
+     */
+    public static final int NETWORK_STACK_UID = 1073;
+
     /** {@hide} */
     public static final int NOBODY_UID = 9999;
 
@@ -193,6 +205,24 @@
     public static final int LAST_APPLICATION_UID = 19999;
 
     /**
+     * First uid used for fully isolated sandboxed processes spawned from an app zygote
+     * @hide
+     */
+    public static final int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000;
+
+    /**
+     * Number of UIDs we allocate per application zygote
+     * @hide
+     */
+    public static final int NUM_UIDS_PER_APP_ZYGOTE = 100;
+
+    /**
+     * Last uid used for fully isolated sandboxed processes spawned from an app zygote
+     * @hide
+     */
+    public static final int LAST_APP_ZYGOTE_ISOLATED_UID = 98999;
+
+    /**
      * First uid used for fully isolated sandboxed processes (with no permissions of their own)
      * @hide
      */
@@ -638,7 +668,8 @@
     /** {@hide} */
     public static final boolean isIsolated(int uid) {
         uid = UserHandle.getAppId(uid);
-        return uid >= FIRST_ISOLATED_UID && uid <= LAST_ISOLATED_UID;
+        return (uid >= FIRST_ISOLATED_UID && uid <= LAST_ISOLATED_UID)
+                || (uid >= FIRST_APP_ZYGOTE_ISOLATED_UID && uid <= LAST_APP_ZYGOTE_ISOLATED_UID);
     }
 
     /**
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index bdc776d..cbcf600 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -25,9 +25,14 @@
 
 import com.android.internal.annotations.GuardedBy;
 
-import java.util.ArrayList;
-import java.util.HashMap;
+import libcore.util.HexEncoding;
 
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
 
 /**
  * Gives access to the system properties store.  The system properties
@@ -232,6 +237,27 @@
         native_report_sysprop_change();
     }
 
+    /**
+     * Return a {@code SHA-1} digest of the given keys and their values as a
+     * hex-encoded string. The ordering of the incoming keys doesn't change the
+     * digest result.
+     *
+     * @hide
+     */
+    public static @NonNull String digestOf(@NonNull String... keys) {
+        Arrays.sort(keys);
+        try {
+            final MessageDigest digest = MessageDigest.getInstance("SHA-1");
+            for (String key : keys) {
+                final String item = key + "=" + get(key) + "\n";
+                digest.update(item.getBytes(StandardCharsets.UTF_8));
+            }
+            return HexEncoding.encodeToString(digest.digest()).toLowerCase();
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     private SystemProperties() {
     }
 }
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index 8f2826c..1df3dad 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -66,6 +66,7 @@
         public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10;
         public static final int PAYLOAD_SIZE_MISMATCH_ERROR = 11;
         public static final int DOWNLOAD_PAYLOAD_VERIFICATION_ERROR = 12;
+        public static final int PAYLOAD_TIMESTAMP_ERROR = 51;
         public static final int UPDATED_BUT_NOT_ACTIVE = 52;
     }
 
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index f8feb7b..ad8a4d5 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -138,8 +138,7 @@
      */
     public static boolean isIsolated(int uid) {
         if (uid > 0) {
-            final int appId = getAppId(uid);
-            return appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID;
+            return Process.isIsolated(uid);
         } else {
             return false;
         }
@@ -294,9 +293,14 @@
             sb.append('u');
             sb.append(getUserId(uid));
             final int appId = getAppId(uid);
-            if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) {
-                sb.append('i');
-                sb.append(appId - Process.FIRST_ISOLATED_UID);
+            if (isIsolated(appId)) {
+                if (appId > Process.FIRST_ISOLATED_UID) {
+                    sb.append('i');
+                    sb.append(appId - Process.FIRST_ISOLATED_UID);
+                } else {
+                    sb.append("ai");
+                    sb.append(appId - Process.FIRST_APP_ZYGOTE_ISOLATED_UID);
+                }
             } else if (appId >= Process.FIRST_APPLICATION_UID) {
                 sb.append('a');
                 sb.append(appId - Process.FIRST_APPLICATION_UID);
@@ -330,9 +334,14 @@
             pw.print('u');
             pw.print(getUserId(uid));
             final int appId = getAppId(uid);
-            if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) {
-                pw.print('i');
-                pw.print(appId - Process.FIRST_ISOLATED_UID);
+            if (isIsolated(appId)) {
+                if (appId > Process.FIRST_ISOLATED_UID) {
+                    pw.print('i');
+                    pw.print(appId - Process.FIRST_ISOLATED_UID);
+                } else {
+                    pw.print("ai");
+                    pw.print(appId - Process.FIRST_APP_ZYGOTE_ISOLATED_UID);
+                }
             } else if (appId >= Process.FIRST_APPLICATION_UID) {
                 pw.print('a');
                 pw.print(appId - Process.FIRST_APPLICATION_UID);
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index f136cd6..251c5ee 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
 import android.util.Log;
@@ -34,6 +35,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Base64;
 import java.util.Collections;
 import java.util.List;
 import java.util.UUID;
@@ -673,6 +675,36 @@
     }
 
     /**
+     * Instructs the zygote to pre-load the application code for the given Application.
+     * Only the app zygote supports this function.
+     * TODO preloadPackageForAbi() can probably be removed and the callers an use this instead.
+     */
+    public boolean preloadApp(ApplicationInfo appInfo, String abi) throws ZygoteStartFailedEx,
+                                                                          IOException {
+        synchronized (mLock) {
+            ZygoteState state = openZygoteSocketIfNeeded(abi);
+            state.writer.write("2");
+            state.writer.newLine();
+
+            state.writer.write("--preload-app");
+            state.writer.newLine();
+
+            // Zygote args needs to be strings, so in order to pass ApplicationInfo,
+            // write it to a Parcel, and base64 the raw Parcel bytes to the other side.
+            Parcel parcel = Parcel.obtain();
+            appInfo.writeToParcel(parcel, 0 /* flags */);
+            String encodedParcelData = Base64.getEncoder().encodeToString(parcel.marshall());
+            parcel.recycle();
+            state.writer.write(encodedParcelData);
+            state.writer.newLine();
+
+            state.writer.flush();
+
+            return (state.inputStream.readInt() == 0);
+        }
+    }
+
+    /**
      * Instructs the zygote to pre-load the classes and native libraries at the given paths
      * for the specified abi. Not all zygotes support this function.
      */
@@ -763,6 +795,20 @@
      * secondary zygotes that inherit data from the zygote that this object
      * communicates with. This returns a new ZygoteProcess representing a connection
      * to the newly created zygote. Throws an exception if the zygote cannot be started.
+     *
+     * @param processClass The class to use as the child zygote's main entry
+     *                     point.
+     * @param niceName A more readable name to use for the process.
+     * @param uid The user-id under which the child zygote will run.
+     * @param gid The group-id under which the child zygote will run.
+     * @param gids Additional group-ids associated with the child zygote process.
+     * @param runtimeFlags Additional flags.
+     * @param seInfo null-ok SELinux information for the child zygote process.
+     * @param abi non-null the ABI of the child zygote
+     * @param acceptedAbiList ABIs this child zygote will accept connections for; this
+     *                        may be different from <code>abi</code> in case the children
+     *                        spawned from this Zygote only communicate using ABI-safe methods.
+     * @param instructionSet null-ok the instruction set to use.
      */
     public ChildZygoteProcess startChildZygote(final String processClass,
                                                final String niceName,
@@ -770,12 +816,14 @@
                                                int runtimeFlags,
                                                String seInfo,
                                                String abi,
+                                               String acceptedAbiList,
                                                String instructionSet) {
         // Create an unguessable address in the global abstract namespace.
         final LocalSocketAddress serverAddress = new LocalSocketAddress(
                 processClass + "/" + UUID.randomUUID().toString());
 
-        final String[] extraArgs = {Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + serverAddress.getName()};
+        final String[] extraArgs = {Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + serverAddress.getName(),
+                                    Zygote.CHILD_ZYGOTE_ABI_LIST_ARG + acceptedAbiList};
 
         Process.ProcessStartResult result;
         try {
diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java
index 7b6c971..828f1c3 100644
--- a/core/java/android/os/storage/DiskInfo.java
+++ b/core/java/android/os/storage/DiskInfo.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -66,7 +67,7 @@
         this.flags = flags;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public DiskInfo(Parcel parcel) {
         id = parcel.readString();
         flags = parcel.readInt();
@@ -196,7 +197,7 @@
         return id.hashCode();
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static final Creator<DiskInfo> CREATOR = new Creator<DiskInfo>() {
         @Override
         public DiskInfo createFromParcel(Parcel in) {
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index d315383..735f4f2 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -132,6 +132,8 @@
     public static final String PROP_VIRTUAL_DISK = "persist.sys.virtual_disk";
     /** {@hide} */
     public static final String PROP_ISOLATED_STORAGE = "persist.sys.isolated_storage";
+    /** {@hide} */
+    public static final String PROP_ISOLATED_STORAGE_SNAPSHOT = "sys.isolated_storage_snapshot";
 
     /** {@hide} */
     public static final String PROP_FORCE_AUDIO = "persist.fw.force_audio";
@@ -226,7 +228,9 @@
     /** {@hide} */
     public static final int DEBUG_VIRTUAL_DISK = 1 << 5;
     /** {@hide} */
-    public static final int DEBUG_ISOLATED_STORAGE = 1 << 6;
+    public static final int DEBUG_ISOLATED_STORAGE_FORCE_ON = 1 << 6;
+    /** {@hide} */
+    public static final int DEBUG_ISOLATED_STORAGE_FORCE_OFF = 1 << 7;
 
     /** {@hide} */
     public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE;
@@ -1536,9 +1540,12 @@
     }
 
     /** {@hide} */
+    @SystemApi
     @TestApi
     public static boolean hasIsolatedStorage() {
-        return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false);
+        // Prefer to use snapshot for current boot when available
+        return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE_SNAPSHOT,
+                SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false));
     }
 
     /**
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 8a03e9e..df1a713 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -16,6 +16,7 @@
 
 package android.os.storage;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
@@ -348,6 +349,32 @@
         return intent;
     }
 
+    /**
+     * Builds an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} to allow the user to grant access to any
+     * directory subtree (or entire volume) from the {@link android.provider.DocumentsProvider}s
+     * available on the device. The initial location of the document navigation will be the root of
+     * this {@link StorageVolume}.
+     *
+     * Note that the returned {@link Intent} simply suggests that the user picks this {@link
+     * StorageVolume} by default, but the user may select a different location. Callers must respect
+     * the user's chosen location, even if it is different from the originally requested location.
+     *
+     * @return intent to {@link Intent#ACTION_OPEN_DOCUMENT_TREE} initially showing the contents
+     *         of this {@link StorageVolume}
+     * @see Intent#ACTION_OPEN_DOCUMENT_TREE
+     */
+    @NonNull public Intent createOpenDocumentTreeIntent() {
+        final String rootId = isEmulated()
+                ? DocumentsContract.EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID
+                : mFsUuid;
+        final Uri rootUri = DocumentsContract.buildRootUri(
+                DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY, rootId);
+        final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
+                .putExtra(DocumentsContract.EXTRA_INITIAL_URI, rootUri)
+                .putExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, true);
+        return intent;
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (obj instanceof StorageVolume && mPath != null) {
diff --git a/core/java/android/permission/IRuntimePermissionPresenter.aidl b/core/java/android/permission/IPermissionController.aidl
similarity index 89%
rename from core/java/android/permission/IRuntimePermissionPresenter.aidl
rename to core/java/android/permission/IPermissionController.aidl
index e95428a..38951d5 100644
--- a/core/java/android/permission/IRuntimePermissionPresenter.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -19,11 +19,11 @@
 import android.os.RemoteCallback;
 
 /**
- * Interface for communication with the permission presenter service.
+ * Interface for system apps to communication with the permission controller.
  *
  * @hide
  */
-oneway interface IRuntimePermissionPresenter {
+oneway interface IPermissionController {
     void getAppPermissions(String packageName, in RemoteCallback callback);
     void revokeRuntimePermission(String packageName, String permissionName);
     void countPermissionApps(in List<String> permissionNames, boolean countOnlyGranted,
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
new file mode 100644
index 0000000..66e8666
--- /dev/null
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -0,0 +1,334 @@
+/*
+ * 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.permission;
+
+import static android.permission.PermissionControllerService.SERVICE_INTERFACE;
+
+import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
+import com.android.internal.infra.AbstractRemoteService;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Interface for communicating with the permission controller from system apps. All UI operations
+ * regarding permissions and any changes to the permission state should flow through this
+ * interface.
+ *
+ * @hide
+ */
+@SystemService(Context.PERMISSION_CONTROLLER_SERVICE)
+public final class PermissionControllerManager {
+    private static final String TAG = PermissionControllerManager.class.getSimpleName();
+
+    /**
+     * The key for retrieving the result from the returned bundle.
+     */
+    public static final String KEY_RESULT =
+            "android.permission.PermissionControllerManager.key.result";
+
+    /**
+     * Callback for delivering the result of {@link #getAppPermissions}.
+     */
+    public interface OnGetAppPermissionResultCallback {
+        /**
+         * The result for {@link #getAppPermissions(String, OnGetAppPermissionResultCallback,
+         * Handler)}.
+         *
+         * @param permissions The permissions list.
+         */
+        void onGetAppPermissions(@NonNull List<RuntimePermissionPresentationInfo> permissions);
+    }
+
+    /**
+     * Callback for delivering the result of {@link #countPermissionApps}.
+     */
+    public interface OnCountPermissionAppsResultCallback {
+        /**
+         * The result for {@link #countPermissionApps(List, boolean, boolean,
+         * OnCountPermissionAppsResultCallback, Handler)}.
+         *
+         * @param numApps The number of apps that have one of the permissions
+         */
+        void onCountPermissionApps(int numApps);
+    }
+
+    private final RemoteService mRemoteService;
+
+    public PermissionControllerManager(@NonNull Context context) {
+        Intent intent = new Intent(SERVICE_INTERFACE);
+        intent.setPackage(context.getPackageManager().getPermissionControllerPackageName());
+        ResolveInfo serviceInfo = context.getPackageManager().resolveService(intent, 0);
+
+        mRemoteService = new RemoteService(context,
+                serviceInfo.getComponentInfo().getComponentName());
+    }
+
+    /**
+     * Gets the runtime permissions for an app.
+     *
+     * @param packageName The package for which to query.
+     * @param callback Callback to receive the result.
+     * @param handler Handler on which to invoke the callback.
+     */
+    @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
+    public void getAppPermissions(@NonNull String packageName,
+            @NonNull OnGetAppPermissionResultCallback callback, @Nullable Handler handler) {
+        checkNotNull(packageName);
+        checkNotNull(callback);
+
+        mRemoteService.scheduleRequest(new PendingGetAppPermissionRequest(mRemoteService,
+                packageName, callback, handler == null ? mRemoteService.getHandler() : handler));
+    }
+
+    /**
+     * Revoke the permission {@code permissionName} for app {@code packageName}
+     *
+     * @param packageName The package for which to revoke
+     * @param permissionName The permission to revoke
+     */
+    @RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
+    public void revokeRuntimePermission(@NonNull String packageName,
+            @NonNull String permissionName) {
+        checkNotNull(packageName);
+        checkNotNull(permissionName);
+
+        mRemoteService.scheduleAsyncRequest(new PendingRevokeAppPermissionRequest(packageName,
+                permissionName));
+    }
+
+    /**
+     * Count how many apps have one of a set of permissions.
+     *
+     * @param permissionNames The permissions the app might have
+     * @param countOnlyGranted Count an app only if the permission is granted to the app
+     * @param countSystem Also count system apps
+     * @param callback Callback to receive the result
+     * @param handler Handler on which to invoke the callback
+     */
+    @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
+    public void countPermissionApps(@NonNull List<String> permissionNames,
+            boolean countOnlyGranted, boolean countSystem,
+            @NonNull OnCountPermissionAppsResultCallback callback, @Nullable Handler handler) {
+        checkCollectionElementsNotNull(permissionNames, "permissionNames");
+        checkNotNull(callback);
+
+        mRemoteService.scheduleRequest(new PendingCountPermissionAppsRequest(mRemoteService,
+                permissionNames, countOnlyGranted, countSystem, callback,
+                handler == null ? mRemoteService.getHandler() : handler));
+    }
+
+    /**
+     * A connection to the remote service
+     */
+    static final class RemoteService extends
+            AbstractMultiplePendingRequestsRemoteService<RemoteService, IPermissionController> {
+        private static final long UNBIND_TIMEOUT_MILLIS = 10000;
+        private static final long MESSAGE_TIMEOUT_MILLIS = 30000;
+
+        /**
+         * Create a connection to the remote service
+         *
+         * @param context A context to use
+         * @param componentName The component of the service to connect to
+         */
+        RemoteService(@NonNull Context context, @NonNull ComponentName componentName) {
+            super(context, SERVICE_INTERFACE, componentName, UserHandle.myUserId(),
+                    service -> Log.e(TAG, "RuntimePermPresenterService " + service + " died"),
+                    false, false, 1);
+        }
+
+        /**
+         * @return The default handler used by this service.
+         */
+        Handler getHandler() {
+            return mHandler;
+        }
+
+        @Override
+        protected @NonNull IPermissionController getServiceInterface(@NonNull IBinder binder) {
+            return IPermissionController.Stub.asInterface(binder);
+        }
+
+        @Override
+        protected long getTimeoutIdleBindMillis() {
+            return UNBIND_TIMEOUT_MILLIS;
+        }
+
+        @Override
+        protected long getRemoteRequestMillis() {
+            return MESSAGE_TIMEOUT_MILLIS;
+        }
+
+        @Override
+        public void scheduleRequest(@NonNull PendingRequest<RemoteService,
+                IPermissionController> pendingRequest) {
+            super.scheduleRequest(pendingRequest);
+        }
+
+        @Override
+        public void scheduleAsyncRequest(@NonNull AsyncRequest<IPermissionController> request) {
+            super.scheduleAsyncRequest(request);
+        }
+    }
+
+    /**
+     * Request for {@link #getAppPermissions}
+     */
+    private static final class PendingGetAppPermissionRequest extends
+            AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> {
+        private final @NonNull String mPackageName;
+        private final @NonNull OnGetAppPermissionResultCallback mCallback;
+
+        private final @NonNull RemoteCallback mRemoteCallback;
+
+        private PendingGetAppPermissionRequest(@NonNull RemoteService service,
+                @NonNull String packageName, @NonNull OnGetAppPermissionResultCallback callback,
+                @NonNull Handler handler) {
+            super(service);
+
+            mPackageName = packageName;
+            mCallback = callback;
+
+            mRemoteCallback = new RemoteCallback(result -> {
+                final List<RuntimePermissionPresentationInfo> reportedPermissions;
+                List<RuntimePermissionPresentationInfo> permissions = null;
+                if (result != null) {
+                    permissions = result.getParcelableArrayList(KEY_RESULT);
+                }
+                if (permissions == null) {
+                    permissions = Collections.emptyList();
+                }
+                reportedPermissions = permissions;
+
+                callback.onGetAppPermissions(reportedPermissions);
+
+                finish();
+            }, handler);
+        }
+
+        @Override
+        protected void onTimeout(RemoteService remoteService) {
+            mCallback.onGetAppPermissions(Collections.emptyList());
+        }
+
+        @Override
+        public void run() {
+            try {
+                getService().getServiceInterface().getAppPermissions(mPackageName, mRemoteCallback);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error getting app permission", e);
+            }
+        }
+    }
+
+    /**
+     * Request for {@link #revokeRuntimePermission}
+     */
+    private static final class PendingRevokeAppPermissionRequest
+            implements AbstractRemoteService.AsyncRequest<IPermissionController> {
+        private final @NonNull String mPackageName;
+        private final @NonNull String mPermissionName;
+
+        private PendingRevokeAppPermissionRequest(@NonNull String packageName,
+                @NonNull String permissionName) {
+            mPackageName = packageName;
+            mPermissionName = permissionName;
+        }
+
+        @Override
+        public void run(IPermissionController remoteInterface) {
+            try {
+                remoteInterface.revokeRuntimePermission(mPackageName, mPermissionName);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error revoking app permission", e);
+            }
+        }
+    }
+
+    /**
+     * Request for {@link #countPermissionApps}
+     */
+    private static final class PendingCountPermissionAppsRequest extends
+            AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> {
+        private final @NonNull List<String> mPermissionNames;
+        private final @NonNull OnCountPermissionAppsResultCallback mCallback;
+        private final boolean mCountOnlyGranted;
+        private final boolean mCountSystem;
+
+        private final @NonNull RemoteCallback mRemoteCallback;
+
+        private PendingCountPermissionAppsRequest(@NonNull RemoteService service,
+                @NonNull List<String> permissionNames, boolean countOnlyGranted,
+                boolean countSystem, @NonNull OnCountPermissionAppsResultCallback callback,
+                @NonNull Handler handler) {
+            super(service);
+
+            mPermissionNames = permissionNames;
+            mCountOnlyGranted = countOnlyGranted;
+            mCountSystem = countSystem;
+            mCallback = callback;
+
+            mRemoteCallback = new RemoteCallback(result -> {
+                final int numApps;
+                if (result != null) {
+                    numApps = result.getInt(KEY_RESULT);
+                } else {
+                    numApps = 0;
+                }
+
+                callback.onCountPermissionApps(numApps);
+
+                finish();
+            }, handler);
+        }
+
+        @Override
+        protected void onTimeout(RemoteService remoteService) {
+            mCallback.onCountPermissionApps(0);
+        }
+
+        @Override
+        public void run() {
+            try {
+                getService().getServiceInterface().countPermissionApps(mPermissionNames,
+                        mCountOnlyGranted, mCountSystem, mRemoteCallback);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error counting permission apps", e);
+            }
+        }
+    }
+}
diff --git a/core/java/android/permission/RuntimePermissionPresenterService.java b/core/java/android/permission/PermissionControllerService.java
similarity index 76%
rename from core/java/android/permission/RuntimePermissionPresenterService.java
rename to core/java/android/permission/PermissionControllerService.java
index 81ec7be..5dad071 100644
--- a/core/java/android/permission/RuntimePermissionPresenterService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -20,6 +20,7 @@
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.app.Service;
@@ -33,26 +34,21 @@
 import java.util.List;
 
 /**
- * This service presents information regarding runtime permissions that is
- * used for presenting them in the UI. Runtime permissions are presented as
- * a single permission in the UI but may be composed of several individual
- * permissions.
+ * This service is meant to be implemented by the app controlling permissions.
  *
- * @see RuntimePermissionPresenter
- * @see RuntimePermissionPresentationInfo
+ * @see PermissionController
  *
  * @hide
  */
 @SystemApi
-public abstract class RuntimePermissionPresenterService extends Service {
+public abstract class PermissionControllerService extends Service {
 
     /**
      * The {@link Intent} action that must be declared as handled by a service
      * in its manifest for the system to recognize it as a runtime permission
      * presenter service.
      */
-    public static final String SERVICE_INTERFACE =
-            "android.permission.RuntimePermissionPresenterService";
+    public static final String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
 
     // No need for locking - always set first and never modified
     private Handler mHandler;
@@ -96,16 +92,17 @@
 
     @Override
     public final IBinder onBind(Intent intent) {
-        return new IRuntimePermissionPresenter.Stub() {
+        return new IPermissionController.Stub() {
             @Override
             public void getAppPermissions(String packageName, RemoteCallback callback) {
                 checkNotNull(packageName, "packageName");
                 checkNotNull(callback, "callback");
 
+                enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
+
                 mHandler.sendMessage(
-                        obtainMessage(
-                                RuntimePermissionPresenterService::getAppPermissions,
-                                RuntimePermissionPresenterService.this, packageName, callback));
+                        obtainMessage(PermissionControllerService::getAppPermissions,
+                                PermissionControllerService.this, packageName, callback));
             }
 
             @Override
@@ -113,11 +110,11 @@
                 checkNotNull(packageName, "packageName");
                 checkNotNull(permissionName, "permissionName");
 
+                enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, null);
+
                 mHandler.sendMessage(
-                        obtainMessage(
-                                RuntimePermissionPresenterService::onRevokeRuntimePermission,
-                                RuntimePermissionPresenterService.this, packageName,
-                                permissionName));
+                        obtainMessage(PermissionControllerService::onRevokeRuntimePermission,
+                                PermissionControllerService.this, packageName, permissionName));
             }
 
             @Override
@@ -126,11 +123,12 @@
                 checkCollectionElementsNotNull(permissionNames, "permissionNames");
                 checkNotNull(callback, "callback");
 
+                enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
+
                 mHandler.sendMessage(
-                        obtainMessage(
-                                RuntimePermissionPresenterService::countPermissionApps,
-                                RuntimePermissionPresenterService.this, permissionNames,
-                                countOnlyGranted, countSystem, callback));
+                        obtainMessage(PermissionControllerService::countPermissionApps,
+                                PermissionControllerService.this, permissionNames, countOnlyGranted,
+                                countSystem, callback));
             }
         };
     }
@@ -139,7 +137,7 @@
         List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName);
         if (permissions != null && !permissions.isEmpty()) {
             Bundle result = new Bundle();
-            result.putParcelableList(RuntimePermissionPresenter.KEY_RESULT, permissions);
+            result.putParcelableList(PermissionControllerManager.KEY_RESULT, permissions);
             callback.sendResult(result);
         } else {
             callback.sendResult(null);
@@ -151,7 +149,7 @@
         int numApps = onCountPermissionApps(permissionNames, countOnlyGranted, countSystem);
 
         Bundle result = new Bundle();
-        result.putInt(RuntimePermissionPresenter.KEY_RESULT, numApps);
+        result.putInt(PermissionControllerManager.KEY_RESULT, numApps);
         callback.sendResult(result);
     }
 }
diff --git a/core/java/android/permission/RuntimePermissionPresenter.java b/core/java/android/permission/RuntimePermissionPresenter.java
deleted file mode 100644
index 3ce3c9d..0000000
--- a/core/java/android/permission/RuntimePermissionPresenter.java
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.permission;
-
-import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
-import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteCallback;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.function.pooled.PooledLambda;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * This class provides information about runtime permissions for a specific
- * app or all apps. This information is dedicated for presentation purposes
- * and does not necessarily reflect the individual permissions requested/
- * granted to an app as the platform may be grouping permissions to improve
- * presentation and help the user make an informed choice. For example, all
- * runtime permissions in the same permission group may be presented as a
- * single permission in the UI.
- *
- * @hide
- */
-public final class RuntimePermissionPresenter {
-    private static final String TAG = "RuntimePermPresenter";
-
-    /**
-     * The key for retrieving the result from the returned bundle.
-     *
-     * @hide
-     */
-    public static final String KEY_RESULT =
-            "android.permission.RuntimePermissionPresenter.key.result";
-
-    /**
-     * Listener for delivering the result of {@link #getAppPermissions}.
-     */
-    public interface OnGetAppPermissionResultCallback {
-        /**
-         * The result for {@link #getAppPermissions(String, OnGetAppPermissionResultCallback,
-         * Handler)}.
-         *
-         * @param permissions The permissions list.
-         */
-        void onGetAppPermissions(@NonNull List<RuntimePermissionPresentationInfo> permissions);
-    }
-
-    /**
-     * Listener for delivering the result of {@link #countPermissionApps}.
-     */
-    public interface OnCountPermissionAppsResultCallback {
-        /**
-         * The result for {@link #countPermissionApps(List, boolean,
-         * OnCountPermissionAppsResultCallback, Handler)}.
-         *
-         * @param numApps The number of apps that have one of the permissions
-         */
-        void onCountPermissionApps(int numApps);
-    }
-
-    private static final Object sLock = new Object();
-
-    @GuardedBy("sLock")
-    private static RuntimePermissionPresenter sInstance;
-
-    private final RemoteService mRemoteService;
-
-    /**
-     * Gets the singleton runtime permission presenter.
-     *
-     * @param context Context for accessing resources.
-     * @return The singleton instance.
-     */
-    public static RuntimePermissionPresenter getInstance(@NonNull Context context) {
-        synchronized (sLock) {
-            if (sInstance == null) {
-                sInstance = new RuntimePermissionPresenter(context.getApplicationContext());
-            }
-            return sInstance;
-        }
-    }
-
-    private RuntimePermissionPresenter(Context context) {
-        mRemoteService = new RemoteService(context);
-    }
-
-    /**
-     * Gets the runtime permissions for an app.
-     *
-     * @param packageName The package for which to query.
-     * @param callback Callback to receive the result.
-     * @param handler Handler on which to invoke the callback.
-     */
-    public void getAppPermissions(@NonNull String packageName,
-            @NonNull OnGetAppPermissionResultCallback callback, @Nullable Handler handler) {
-        checkNotNull(packageName);
-        checkNotNull(callback);
-
-        mRemoteService.processMessage(obtainMessage(RemoteService::getAppPermissions,
-                mRemoteService, packageName, callback, handler));
-    }
-
-    /**
-     * Revoke the permission {@code permissionName} for app {@code packageName}
-     *
-     * @param packageName The package for which to revoke
-     * @param permissionName The permission to revoke
-     */
-    public void revokeRuntimePermission(@NonNull String packageName,
-            @NonNull String permissionName) {
-        checkNotNull(packageName);
-        checkNotNull(permissionName);
-
-        mRemoteService.processMessage(obtainMessage(RemoteService::revokeAppPermissions,
-                mRemoteService, packageName, permissionName));
-    }
-
-    /**
-     * Count how many apps have one of a set of permissions.
-     *
-     * @param permissionNames The permissions the app might have
-     * @param countOnlyGranted Count an app only if the permission is granted to the app
-     * @param countSystem Also count system apps
-     * @param callback Callback to receive the result
-     * @param handler Handler on which to invoke the callback
-     */
-    public void countPermissionApps(@NonNull List<String> permissionNames,
-            boolean countOnlyGranted, boolean countSystem,
-            @NonNull OnCountPermissionAppsResultCallback callback, @Nullable Handler handler) {
-        checkCollectionElementsNotNull(permissionNames, "permissionNames");
-        checkNotNull(callback);
-
-        mRemoteService.processMessage(obtainMessage(RemoteService::countPermissionApps,
-                mRemoteService, permissionNames, countOnlyGranted, countSystem, callback, handler));
-    }
-
-    private static final class RemoteService
-            extends Handler implements ServiceConnection {
-        private static final long UNBIND_TIMEOUT_MILLIS = 10000;
-
-        public static final int MSG_UNBIND = 0;
-
-        private final Object mLock = new Object();
-
-        private final Context mContext;
-
-        @GuardedBy("mLock")
-        private final List<Message> mPendingWork = new ArrayList<>();
-
-        @GuardedBy("mLock")
-        private IRuntimePermissionPresenter mRemoteInstance;
-
-        @GuardedBy("mLock")
-        private boolean mBound;
-
-        RemoteService(Context context) {
-            super(context.getMainLooper(), null, false);
-            mContext = context;
-        }
-
-        public void processMessage(Message message) {
-            synchronized (mLock) {
-                if (!mBound) {
-                    Intent intent = new Intent(
-                            RuntimePermissionPresenterService.SERVICE_INTERFACE);
-                    intent.setPackage(mContext.getPackageManager()
-                            .getPermissionControllerPackageName());
-                    mBound = mContext.bindService(intent, this,
-                            Context.BIND_AUTO_CREATE);
-                }
-                mPendingWork.add(message);
-                scheduleNextMessageIfNeededLocked();
-            }
-        }
-
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            synchronized (mLock) {
-                mRemoteInstance = IRuntimePermissionPresenter.Stub.asInterface(service);
-                scheduleNextMessageIfNeededLocked();
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            synchronized (mLock) {
-                mRemoteInstance = null;
-            }
-        }
-
-        private void getAppPermissions(@NonNull String packageName,
-                @NonNull OnGetAppPermissionResultCallback callback, @Nullable Handler handler) {
-            final IRuntimePermissionPresenter remoteInstance;
-            synchronized (mLock) {
-                remoteInstance = mRemoteInstance;
-            }
-            if (remoteInstance == null) {
-                return;
-            }
-            try {
-                remoteInstance.getAppPermissions(packageName,
-                        new RemoteCallback(result -> {
-                            final List<RuntimePermissionPresentationInfo> reportedPermissions;
-                            List<RuntimePermissionPresentationInfo> permissions = null;
-                            if (result != null) {
-                                permissions = result.getParcelableArrayList(KEY_RESULT);
-                            }
-                            if (permissions == null) {
-                                permissions = Collections.emptyList();
-                            }
-                            reportedPermissions = permissions;
-                            if (handler != null) {
-                                handler.post(
-                                        () -> callback.onGetAppPermissions(reportedPermissions));
-                            } else {
-                                callback.onGetAppPermissions(reportedPermissions);
-                            }
-                        }, this));
-            } catch (RemoteException re) {
-                Log.e(TAG, "Error getting app permissions", re);
-            }
-            scheduleUnbind();
-
-            synchronized (mLock) {
-                scheduleNextMessageIfNeededLocked();
-            }
-        }
-
-        private void revokeAppPermissions(@NonNull String packageName,
-                @NonNull String permissionName) {
-            final IRuntimePermissionPresenter remoteInstance;
-            synchronized (mLock) {
-                remoteInstance = mRemoteInstance;
-            }
-            if (remoteInstance == null) {
-                return;
-            }
-            try {
-                remoteInstance.revokeRuntimePermission(packageName, permissionName);
-            } catch (RemoteException re) {
-                Log.e(TAG, "Error getting app permissions", re);
-            }
-
-            synchronized (mLock) {
-                scheduleNextMessageIfNeededLocked();
-            }
-        }
-
-        private void countPermissionApps(@NonNull List<String> permissionNames,
-                boolean countOnlyGranted, boolean countSystem,
-                @NonNull OnCountPermissionAppsResultCallback callback, @Nullable Handler handler) {
-            final IRuntimePermissionPresenter remoteInstance;
-
-            synchronized (mLock) {
-                remoteInstance = mRemoteInstance;
-            }
-            if (remoteInstance == null) {
-                return;
-            }
-
-            try {
-                remoteInstance.countPermissionApps(permissionNames, countOnlyGranted, countSystem,
-                        new RemoteCallback(result -> {
-                            final int numApps;
-                            if (result != null) {
-                                numApps = result.getInt(KEY_RESULT);
-                            } else {
-                                numApps = 0;
-                            }
-
-                            if (handler != null) {
-                                handler.post(() -> callback.onCountPermissionApps(numApps));
-                            } else {
-                                callback.onCountPermissionApps(numApps);
-                            }
-                        }, this));
-            } catch (RemoteException re) {
-                Log.e(TAG, "Error counting permission apps", re);
-            }
-
-            scheduleUnbind();
-
-            synchronized (mLock) {
-                scheduleNextMessageIfNeededLocked();
-            }
-        }
-
-        private void unbind() {
-            synchronized (mLock) {
-                if (mBound) {
-                    mContext.unbindService(this);
-                    mBound = false;
-                }
-                mRemoteInstance = null;
-            }
-        }
-
-        @GuardedBy("mLock")
-        private void scheduleNextMessageIfNeededLocked() {
-            if (mBound && mRemoteInstance != null && !mPendingWork.isEmpty()) {
-                Message nextMessage = mPendingWork.remove(0);
-                sendMessage(nextMessage);
-            }
-        }
-
-        private void scheduleUnbind() {
-            removeMessages(MSG_UNBIND);
-            sendMessageDelayed(PooledLambda.obtainMessage(RemoteService::unbind, this)
-                    .setWhat(MSG_UNBIND), UNBIND_TIMEOUT_MILLIS);
-        }
-    }
-}
diff --git a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
index 2b3f0f5..8d568c8 100644
--- a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
+++ b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
@@ -30,6 +30,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteCallback;
+import android.permission.PermissionControllerService;
 
 import java.util.List;
 
@@ -43,7 +44,7 @@
  *
  * @hide
  *
- * @deprecated use {@link android.permission.RuntimePermissionPresenterService} instead
+ * @deprecated use {@link PermissionControllerService} instead
  */
 @Deprecated
 @SystemApi
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index f01d5b1..015146466 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -123,7 +123,7 @@
         mContext = context;
         mAudioManager = context.getSystemService(AudioManager.class);
         mNotificationManager = context.getSystemService(NotificationManager.class);
-        mNotificationPolicy = mNotificationManager.getNotificationPolicy();
+        mNotificationPolicy = mNotificationManager.getConsolidatedNotificationPolicy();
         mAllowAlarms = (mNotificationPolicy.priorityCategories & NotificationManager.Policy
                 .PRIORITY_CATEGORY_ALARMS) != 0;
         mAllowMedia = (mNotificationPolicy.priorityCategories & NotificationManager.Policy
@@ -485,7 +485,7 @@
                 mZenMode = mNotificationManager.getZenMode();
                 updateSlider();
             } else if (NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED.equals(action)) {
-                mNotificationPolicy = mNotificationManager.getNotificationPolicy();
+                mNotificationPolicy = mNotificationManager.getConsolidatedNotificationPolicy();
                 mAllowAlarms = (mNotificationPolicy.priorityCategories & NotificationManager.Policy
                         .PRIORITY_CATEGORY_ALARMS) != 0;
                 mAllowMedia = (mNotificationPolicy.priorityCategories
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 3d93afd..de54a8aa 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -28,12 +28,14 @@
 import android.location.Country;
 import android.location.CountryDetector;
 import android.net.Uri;
+import android.os.Build;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.ContactsContract.CommonDataKinds.Callable;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.DataUsageFeedback;
+import android.telecom.CallIdentification;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
@@ -603,6 +605,69 @@
         public static final String BLOCK_REASON = "block_reason";
 
         /**
+         * The package name of the {@link android.telecom.CallScreeningService} which provided
+         * {@link android.telecom.CallIdentification} for this call.
+         * <P>Type: TEXT</P>
+         */
+        public static final String CALL_ID_PACKAGE_NAME = "call_id_package_name";
+
+        /**
+         * The app name of the {@link android.telecom.CallScreeningService} which provided
+         * {@link android.telecom.CallIdentification} for this call.
+         * <P>Type: TEXT</P>
+         */
+        public static final String CALL_ID_APP_NAME = "call_id_app_name";
+
+        /**
+         * The {@link CallIdentification#getName() name} of a call, as provided by the
+         * {@link android.telecom.CallScreeningService}.
+         * <p>
+         * The name is provided by the app identified by {@link #CALL_ID_PACKAGE_NAME} and
+         * {@link #CALL_ID_APP_NAME}.
+         * <P>Type: TEXT</P>
+         */
+        public static final String CALL_ID_NAME = "call_id_name";
+
+        /**
+         * The {@link CallIdentification#getDescription() description} of a call, as provided by the
+         * {@link android.telecom.CallScreeningService}.
+         * <p>
+         * The description is provided by the app identified by {@link #CALL_ID_PACKAGE_NAME} and
+         * {@link #CALL_ID_APP_NAME}.
+         * <P>Type: TEXT</P>
+         */
+        public static final String CALL_ID_DESCRIPTION = "call_id_description";
+
+        /**
+         * The {@link CallIdentification#getDetails() details} of a call, as provided by the
+         * {@link android.telecom.CallScreeningService}.
+         * <p>
+         * The details field is provided by the app identified by {@link #CALL_ID_PACKAGE_NAME} and
+         * {@link #CALL_ID_APP_NAME}.
+         * <P>Type: TEXT</P>
+         */
+        public static final String CALL_ID_DETAILS = "call_id_details";
+
+        /**
+         * The {@link CallIdentification#getNuisanceConfidence() nuisance confidence} of a call, as
+         * provided by the {@link android.telecom.CallScreeningService}.
+         * <p>
+         * Valid values are defined in {@link CallIdentification}, and include:
+         * <ul>
+         *     <li>{@link CallIdentification#CONFIDENCE_NOT_NUISANCE}</li>
+         *     <li>{@link CallIdentification#CONFIDENCE_LIKELY_NOT_NUISANCE}</li>
+         *     <li>{@link CallIdentification#CONFIDENCE_UNKNOWN}</li>
+         *     <li>{@link CallIdentification#CONFIDENCE_LIKELY_NUISANCE}</li>
+         *     <li>{@link CallIdentification#CONFIDENCE_NUISANCE}</li>
+         * </ul>
+         * <p>
+         * The nuisance confidence is provided by the app identified by
+         * {@link #CALL_ID_PACKAGE_NAME} and {@link #CALL_ID_APP_NAME}.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String CALL_ID_NUISANCE_CONFIDENCE = "call_id_nuisance_confidence";
+
+        /**
          * Adds a call to the call log.
          *
          * @param ci the CallerInfo object to get the target contact from.  Can be null
@@ -631,7 +696,8 @@
                 presentation, callType, features, accountHandle, start, duration,
                 dataUsage, false /* addForAllUsers */, null /* userToBeInsertedTo */,
                 false /* isRead */, Calls.BLOCK_REASON_NOT_BLOCKED /* callBlockReason */,
-                null /* callScreeningAppName */, null /* callScreeningComponentName */);
+                null /* callScreeningAppName */, null /* callScreeningComponentName */,
+                null /* callIdentification */);
         }
 
 
@@ -671,7 +737,8 @@
                 features, accountHandle, start, duration, dataUsage, addForAllUsers,
                 userToBeInsertedTo, false /* isRead */ , Calls.BLOCK_REASON_NOT_BLOCKED
                 /* callBlockReason */, null /* callScreeningAppName */,
-                null /* callScreeningComponentName */);
+                null /* callScreeningComponentName */,
+                null /* callIdentification */);
         }
 
         /**
@@ -705,19 +772,32 @@
          * @param callBlockReason The reason why the call is blocked.
          * @param callScreeningAppName The call screening application name which block the call.
          * @param callScreeningComponentName The call screening component name which block the call.
+         * @param callIdPackageName The package name of the
+         *      {@link android.telecom.CallScreeningService} which provided
+         *      {@link CallIdentification}.
+         * @param callIdAppName The app name of the {@link android.telecom.CallScreeningService}
+         *                      which provided {@link CallIdentification}.
+         * @param callIdName The caller name provided by the
+         *      {@link android.telecom.CallScreeningService}.
+         * @param callIdDescription The caller description provided by the
+         *      {@link android.telecom.CallScreeningService}.
+         * @param callIdDetails The caller details provided by the
+         *      {@link android.telecom.CallScreeningService}.
+         * @param callIdCallType The caller type provided by the
+         *      {@link android.telecom.CallScreeningService}.
          *
          * @result The URI of the call log entry belonging to the user that made or received this
          *        call.  This could be of the shadow provider.  Do not return it to non-system apps,
          *        as they don't have permissions.
          * {@hide}
          */
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         public static Uri addCall(CallerInfo ci, Context context, String number,
                 String postDialDigits, String viaNumber, int presentation, int callType,
                 int features, PhoneAccountHandle accountHandle, long start, int duration,
                 Long dataUsage, boolean addForAllUsers, UserHandle userToBeInsertedTo,
                 boolean isRead, int callBlockReason, String callScreeningAppName,
-                String callScreeningComponentName) {
+                String callScreeningComponentName, CallIdentification callIdentification) {
             if (VERBOSE_LOG) {
                 Log.v(LOG_TAG, String.format("Add call: number=%s, user=%s, for all=%s",
                         number, userToBeInsertedTo, addForAllUsers));
@@ -799,6 +879,22 @@
             values.put(CALL_SCREENING_APP_NAME, callScreeningAppName);
             values.put(CALL_SCREENING_COMPONENT_NAME, callScreeningComponentName);
 
+            if (callIdentification != null) {
+                values.put(CALL_ID_PACKAGE_NAME, callIdentification.getCallScreeningPackageName());
+                values.put(CALL_ID_APP_NAME, callIdentification.getCallScreeningAppName());
+                values.put(CALL_ID_NAME, callIdentification.getName());
+                values.put(CALL_ID_DESCRIPTION, callIdentification.getDescription());
+                values.put(CALL_ID_DETAILS, callIdentification.getDetails());
+                values.put(CALL_ID_NUISANCE_CONFIDENCE, callIdentification.getNuisanceConfidence());
+            } else {
+                values.putNull(CALL_ID_PACKAGE_NAME);
+                values.putNull(CALL_ID_APP_NAME);
+                values.putNull(CALL_ID_NAME);
+                values.putNull(CALL_ID_DESCRIPTION);
+                values.putNull(CALL_ID_DETAILS);
+                values.putNull(CALL_ID_NUISANCE_CONFIDENCE);
+            }
+
             if ((ci != null) && (ci.contactIdOrZero > 0)) {
                 // Update usage information for the number associated with the contact ID.
                 // We need to use both the number and the ID for obtaining a data ID since other
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
new file mode 100644
index 0000000..4e207ed
--- /dev/null
+++ b/core/java/android/provider/DeviceConfig.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.ActivityThread;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.provider.Settings.ResetMode;
+import android.util.Pair;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * Device level configuration parameters which can be tuned by a separate configuration service.
+ *
+ * @hide
+ */
+@SystemApi
+public final class DeviceConfig {
+    /**
+     * The content:// style URL for the config table.
+     *
+     * @hide
+     */
+    public static final Uri CONTENT_URI = Uri.parse("content://" + Settings.AUTHORITY + "/config");
+
+    private static final Object sLock = new Object();
+    @GuardedBy("sLock")
+    private static Map<OnPropertyChangedListener, Pair<String, Executor>> sListeners =
+            new HashMap<>();
+    @GuardedBy("sLock")
+    private static Map<String, Pair<ContentObserver, Integer>> sNamespaces = new HashMap<>();
+
+    // Should never be invoked
+    private DeviceConfig() {
+    }
+
+    /**
+     * Look up the value of a property for a particular namespace.
+     *
+     * @param namespace The namespace containing the property to look up.
+     * @param name The name of the property to look up.
+     * @return the corresponding value, or null if not present.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static String getProperty(String namespace, String name) {
+        ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
+        String compositeName = createCompositeName(namespace, name);
+        return Settings.Config.getString(contentResolver, compositeName);
+    }
+
+    /**
+     * Create a new property with the the provided name and value in the provided namespace, or
+     * update the value of such a property if it already exists. The same name can exist in multiple
+     * namespaces and might have different values in any or all namespaces.
+     * <p>
+     * The method takes an argument indicating whether to make the value the default for this
+     * property.
+     * <p>
+     * All properties stored for a particular scope can be reverted to their default values
+     * by passing the namespace to {@link #resetToDefaults(int, String)}.
+     *
+     * @param namespace The namespace containing the property to create or update.
+     * @param name The name of the property to create or update.
+     * @param value The value to store for the property.
+     * @param makeDefault Whether to make the new value the default one.
+     * @return True if the value was set, false if the storage implementation throws errors.
+     * @see #resetToDefaults(int, String).
+     *
+     * @hide
+     */
+    @SystemApi
+    public static boolean setProperty(
+            String namespace, String name, String value, boolean makeDefault) {
+        ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
+        String compositeName = createCompositeName(namespace, name);
+        return Settings.Config.putString(contentResolver, compositeName, value, makeDefault);
+    }
+
+    /**
+     * Reset properties to their default values.
+     * <p>
+     * The method accepts an optional namespace parameter. If provided, only properties set within
+     * that namespace will be reset. Otherwise, all properties will be reset.
+     *
+     * @param resetMode The reset mode to use.
+     * @param namespace Optionally, the specific namespace which resets will be limited to.
+     * @see #setProperty(String, String, String, boolean)
+     *
+     * @hide
+     */
+    @SystemApi
+    public static void resetToDefaults(@ResetMode int resetMode, @Nullable String namespace) {
+        ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
+        Settings.Config.resetToDefaults(contentResolver, resetMode, namespace);
+    }
+
+    /**
+     * Add a listener for property changes.
+     * <p>
+     * This listener will be called whenever properties in the specified namespace change. Callbacks
+     * will be made on the specified executor. Future calls to this method with the same listener
+     * will replace the old namespace and executor. Remove the listener entirely by calling
+     * {@link #removeOnPropertyChangedListener(OnPropertyChangedListener)}.
+     *
+     * @param namespace The namespace containing properties to monitor.
+     * @param executor The executor which will be used to run callbacks.
+     * @param onPropertyChangedListener The listener to add.
+     * @see #removeOnPropertyChangedListener(OnPropertyChangedListener)
+     *
+     * @hide
+     */
+    @SystemApi
+    public static void addOnPropertyChangedListener(
+            @NonNull String namespace,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnPropertyChangedListener onPropertyChangedListener) {
+        synchronized (sLock) {
+            Pair<String, Executor> oldNamespace = sListeners.get(onPropertyChangedListener);
+            if (oldNamespace == null) {
+                // Brand new listener, add it to the list.
+                sListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
+                incrementNamespace(namespace);
+            } else if (namespace.equals(oldNamespace.first)) {
+                // Listener is already registered for this namespace, update executor just in case.
+                sListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
+            } else {
+                // Update this listener from an old namespace to the new one.
+                decrementNamespace(sListeners.get(onPropertyChangedListener).first);
+                sListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
+                incrementNamespace(namespace);
+            }
+        }
+    }
+
+    /**
+     * Remove a listener for property changes. The listener will receive no further notification of
+     * property changes.
+     *
+     * @param onPropertyChangedListener The listener to remove.
+     * @see #addOnPropertyChangedListener(String, Executor, OnPropertyChangedListener)
+     *
+     * @hide
+     */
+    @SystemApi
+    public static void removeOnPropertyChangedListener(
+            OnPropertyChangedListener onPropertyChangedListener) {
+        synchronized (sLock) {
+            if (sListeners.containsKey(onPropertyChangedListener)) {
+                decrementNamespace(sListeners.get(onPropertyChangedListener).first);
+                sListeners.remove(onPropertyChangedListener);
+            }
+        }
+    }
+
+    private static String createCompositeName(String namespace, String name) {
+        return namespace + "/" + name;
+    }
+
+    private static Uri createNamespaceUri(String namespace) {
+        return CONTENT_URI.buildUpon().appendPath(namespace).build();
+    }
+
+    /**
+     * Increment the count used to represent the number of listeners subscribed to the given
+     * namespace. If this is the first (i.e. incrementing from 0 to 1) for the given namespace, a
+     * ContentObserver is registered.
+     *
+     * @param namespace The namespace to increment the count for.
+     */
+    @GuardedBy("sLock")
+    private static void incrementNamespace(String namespace) {
+        Pair<ContentObserver, Integer> namespaceCount = sNamespaces.get(namespace);
+        if (namespaceCount != null) {
+            sNamespaces.put(namespace, new Pair<>(namespaceCount.first, namespaceCount.second + 1));
+        } else {
+            // This is a new namespace, register a ContentObserver for it.
+            ContentObserver contentObserver = new ContentObserver(null) {
+                @Override
+                public void onChange(boolean selfChange, Uri uri) {
+                    handleChange(uri);
+                }
+            };
+            ActivityThread.currentApplication().getContentResolver()
+                    .registerContentObserver(createNamespaceUri(namespace), true, contentObserver);
+            sNamespaces.put(namespace, new Pair<>(contentObserver, 1));
+        }
+    }
+
+    /**
+     * Decrement the count used to represent th enumber of listeners subscribed to the given
+     * namespace. If this is the final decrement call (i.e. decrementing from 1 to 0) for the given
+     * namespace, the ContentObserver that had been tracking it will be removed.
+     *
+     * @param namespace The namespace to decrement the count for.
+     */
+    @GuardedBy("sLock")
+    private static void decrementNamespace(String namespace) {
+        Pair<ContentObserver, Integer> namespaceCount = sNamespaces.get(namespace);
+        if (namespaceCount == null) {
+            // This namespace is not registered and does not need to be decremented
+            return;
+        } else if (namespaceCount.second > 1) {
+            sNamespaces.put(namespace, new Pair<>(namespaceCount.first, namespaceCount.second - 1));
+        } else {
+            // Decrementing a namespace to zero means we no longer need its ContentObserver.
+            ActivityThread.currentApplication().getContentResolver()
+                    .unregisterContentObserver(namespaceCount.first);
+            sNamespaces.remove(namespace);
+        }
+    }
+
+    private static void handleChange(Uri uri) {
+        List<String> pathSegments = uri.getPathSegments();
+        // pathSegments(0) is "config"
+        String namespace = pathSegments.get(1);
+        String name = pathSegments.get(2);
+        String value = getProperty(namespace, name);
+        synchronized (sLock) {
+            for (OnPropertyChangedListener listener : sListeners.keySet()) {
+                if (namespace.equals(sListeners.get(listener).first)) {
+                    sListeners.get(listener).second.execute(new Runnable() {
+                        @Override
+                        public void run() {
+                            listener.onPropertyChanged(namespace, name, value);
+                        }
+
+                    });
+                }
+            }
+        }
+    }
+
+    /**
+     * Interface for monitoring to properties.
+     * <p>
+     * Override {@link #onPropertyChanged(String, String, String)} to handle callbacks for changes.
+     */
+    public interface OnPropertyChangedListener {
+        /**
+         * Called when a property has changed.
+         *
+         * @param namespace The namespace containing the property which has changed.
+         * @param name The name of the property which has changed.
+         * @param value The new value of the property which has changed.
+         */
+        void onPropertyChanged(String namespace, String name, String value);
+    }
+}
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index cd991cc..a323ed1 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -238,6 +238,9 @@
             "com.android.externalstorage.documents";
 
     /** {@hide} */
+    public static final String EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID = "primary";
+
+    /** {@hide} */
     public static final String PACKAGE_DOCUMENTS_UI = "com.android.documentsui";
 
     /**
@@ -857,16 +860,6 @@
     }
 
     /**
-     * Builds URI for user home directory on external (local) storage.
-     * {@hide}
-     */
-    public static Uri buildHomeUri() {
-        // TODO: Avoid this type of interpackage copying. Added here to avoid
-        // direct coupling, but not ideal.
-        return DocumentsContract.buildRootUri(EXTERNAL_STORAGE_PROVIDER_AUTHORITY, "home");
-    }
-
-    /**
      * Build URI representing the recently modified documents of a specific root
      * in a document provider. When queried, a provider will return zero or more
      * rows with columns defined by {@link Document}.
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index e5fd29c..b348da4 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -409,6 +409,15 @@
         public static final String COLUMN_MEDIAPROVIDER_URI = "mediaprovider_uri";
 
         /**
+         * Similar to {@link #COLUMN_MEDIAPROVIDER_URI}, except this cannot be updated/queried
+         * by apps and will be the source of truth when updating/deleting download entries in
+         * MediaProvider database.
+         *
+         * <P>Type: TEXT</P>
+         */
+        public static final String COLUMN_MEDIASTORE_URI = "mediastore_uri";
+
+        /**
          * The column that is used to remember whether the media scanner was invoked.
          * It can take the values: null or 0(not scanned), 1(scanned), 2 (not scannable).
          * <P>Type: TEXT</P>
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index f38f740..cdbc979 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -17,6 +17,7 @@
 package android.provider;
 
 import android.annotation.BytesLong;
+import android.annotation.DurationMillisLong;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -55,6 +56,7 @@
 import android.os.storage.VolumeInfo;
 import android.service.media.CameraPrewarmService;
 import android.text.TextUtils;
+import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
@@ -131,6 +133,8 @@
     /** {@hide} */
     public static final String PARAM_INCLUDE_PENDING = "includePending";
     /** {@hide} */
+    public static final String PARAM_INCLUDE_TRASHED = "includeTrashed";
+    /** {@hide} */
     public static final String PARAM_PROGRESS = "progress";
     /** {@hide} */
     public static final String PARAM_REQUIRE_ORIGINAL = "requireOriginal";
@@ -485,12 +489,29 @@
      * By default no pending items are returned.
      *
      * @see MediaColumns#IS_PENDING
+     * @see MediaStore#setIncludePending(Uri)
+     * @see MediaStore#createPending(Context, PendingParams)
      */
     public static @NonNull Uri setIncludePending(@NonNull Uri uri) {
         return uri.buildUpon().appendQueryParameter(PARAM_INCLUDE_PENDING, "1").build();
     }
 
     /**
+     * Update the given {@link Uri} to also include any trashed media items from
+     * calls such as
+     * {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}.
+     * By default no trashed items are returned.
+     *
+     * @see MediaColumns#IS_TRASHED
+     * @see MediaStore#setIncludeTrashed(Uri)
+     * @see MediaStore#trash(Context, Uri)
+     * @see MediaStore#untrash(Context, Uri)
+     */
+    public static @NonNull Uri setIncludeTrashed(@NonNull Uri uri) {
+        return uri.buildUpon().appendQueryParameter(PARAM_INCLUDE_TRASHED, "1").build();
+    }
+
+    /**
      * Update the given {@link Uri} to indicate that the caller requires the
      * original file contents when calling
      * {@link ContentResolver#openFileDescriptor(Uri, String)}.
@@ -516,6 +537,9 @@
      *
      * @return token which can be passed to {@link #openPending(Context, Uri)}
      *         to work with this pending item.
+     * @see MediaColumns#IS_PENDING
+     * @see MediaStore#setIncludePending(Uri)
+     * @see MediaStore#createPending(Context, PendingParams)
      */
     public static @NonNull Uri createPending(@NonNull Context context,
             @NonNull PendingParams params) {
@@ -572,6 +596,8 @@
             this.insertValues.put(MediaColumns.DATE_ADDED, now);
             this.insertValues.put(MediaColumns.DATE_MODIFIED, now);
             this.insertValues.put(MediaColumns.IS_PENDING, 1);
+            this.insertValues.put(MediaColumns.DATE_EXPIRES,
+                    (System.currentTimeMillis() + DateUtils.DAY_IN_MILLIS) / 1000);
         }
 
         /**
@@ -696,6 +722,7 @@
         public @NonNull Uri publish() {
             final ContentValues values = new ContentValues();
             values.put(MediaColumns.IS_PENDING, 0);
+            values.putNull(MediaColumns.DATE_EXPIRES);
             mContext.getContentResolver().update(mUri, values, null, null);
             return mUri;
         }
@@ -717,6 +744,67 @@
     }
 
     /**
+     * Mark the given item as being "trashed", meaning it should be deleted at
+     * some point in the future. This is a more gentle operation than simply
+     * calling {@link ContentResolver#delete(Uri, String, String[])}, which
+     * would take effect immediately.
+     * <p>
+     * This method preserves trashed items for at least 48 hours before erasing
+     * them, giving the user a chance to untrash the item.
+     *
+     * @see MediaColumns#IS_TRASHED
+     * @see MediaStore#setIncludeTrashed(Uri)
+     * @see MediaStore#trash(Context, Uri)
+     * @see MediaStore#untrash(Context, Uri)
+     */
+    public static void trash(@NonNull Context context, @NonNull Uri uri) {
+        trash(context, uri, 48 * DateUtils.HOUR_IN_MILLIS);
+    }
+
+    /**
+     * Mark the given item as being "trashed", meaning it should be deleted at
+     * some point in the future. This is a more gentle operation than simply
+     * calling {@link ContentResolver#delete(Uri, String, String[])}, which
+     * would take effect immediately.
+     * <p>
+     * This method preserves trashed items for at least the given timeout before
+     * erasing them, giving the user a chance to untrash the item.
+     *
+     * @see MediaColumns#IS_TRASHED
+     * @see MediaStore#setIncludeTrashed(Uri)
+     * @see MediaStore#trash(Context, Uri)
+     * @see MediaStore#untrash(Context, Uri)
+     */
+    public static void trash(@NonNull Context context, @NonNull Uri uri,
+            @DurationMillisLong long timeoutMillis) {
+        if (timeoutMillis < 0) {
+            throw new IllegalArgumentException();
+        }
+
+        final ContentValues values = new ContentValues();
+        values.put(MediaColumns.IS_TRASHED, 1);
+        values.put(MediaColumns.DATE_EXPIRES,
+                (System.currentTimeMillis() + timeoutMillis) / 1000);
+        context.getContentResolver().update(uri, values, null, null);
+    }
+
+    /**
+     * Mark the given item as being "untrashed", meaning it should no longer be
+     * deleted as previously requested through {@link #trash(Context, Uri)}.
+     *
+     * @see MediaColumns#IS_TRASHED
+     * @see MediaStore#setIncludeTrashed(Uri)
+     * @see MediaStore#trash(Context, Uri)
+     * @see MediaStore#untrash(Context, Uri)
+     */
+    public static void untrash(@NonNull Context context, @NonNull Uri uri) {
+        final ContentValues values = new ContentValues();
+        values.put(MediaColumns.IS_TRASHED, 0);
+        values.putNull(MediaColumns.DATE_EXPIRES);
+        context.getContentResolver().update(uri, values, null, null);
+    }
+
+    /**
      * Common fields for most MediaProvider tables
      */
     public interface MediaColumns extends BaseColumns {
@@ -821,12 +909,34 @@
          * <p>
          * Type: BOOLEAN
          *
+         * @see MediaColumns#IS_PENDING
+         * @see MediaStore#setIncludePending(Uri)
          * @see MediaStore#createPending(Context, PendingParams)
-         * @see MediaStore#PARAM_INCLUDE_PENDING
          */
         public static final String IS_PENDING = "is_pending";
 
         /**
+         * Flag indicating if a media item is trashed.
+         * <p>
+         * Type: BOOLEAN
+         *
+         * @see MediaColumns#IS_TRASHED
+         * @see MediaStore#setIncludeTrashed(Uri)
+         * @see MediaStore#trash(Context, Uri)
+         * @see MediaStore#untrash(Context, Uri)
+         */
+        public static final String IS_TRASHED = "is_trashed";
+
+        /**
+         * The time the file should be considered expired. Units are seconds
+         * since 1970. Typically only meaningful in the context of
+         * {@link #IS_PENDING} or {@link #IS_TRASHED}.
+         * <p>
+         * Type: INTEGER
+         */
+        public static final String DATE_EXPIRES = "date_expires";
+
+        /**
          * The width of the image/video in pixels.
          */
         public static final String WIDTH = "width";
@@ -915,6 +1025,11 @@
             return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("dir").build();
         }
 
+        /** @hide */
+        public static final Uri getContentUriForPath(String path) {
+            return getContentUri(getVolumeNameForPath(path));
+        }
+
         /**
          * Fields for master table for all media files.
          * Table also contains MediaColumns._ID, DATA, SIZE and DATE_MODIFIED.
@@ -1002,6 +1117,7 @@
         public static final int MICRO_KIND = 3;
 
         public static final Point MINI_SIZE = new Point(512, 384);
+        public static final Point FULL_SCREEN_SIZE = new Point(1024, 786);
         public static final Point MICRO_SIZE = new Point(96, 96);
     }
 
@@ -1020,6 +1136,13 @@
          * Type: TEXT
          */
         String REFERER_URI = "referer_uri";
+
+        /**
+         * The description of the download.
+         * <p>
+         * Type: Text
+         */
+        String DESCRIPTION = "description";
     }
 
     /**
@@ -1127,6 +1250,8 @@
             final Point size;
             if (kind == ThumbnailConstants.MICRO_KIND) {
                 size = ThumbnailConstants.MICRO_SIZE;
+            } else if (kind == ThumbnailConstants.FULL_SCREEN_KIND) {
+                size = ThumbnailConstants.FULL_SCREEN_SIZE;
             } else if (kind == ThumbnailConstants.MINI_KIND) {
                 size = ThumbnailConstants.MINI_SIZE;
             } else {
@@ -1248,18 +1373,32 @@
             public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
 
             /**
-             * The bucket id of the image. This is a read-only property that
-             * is automatically computed from the DATA column.
-             * <P>Type: TEXT</P>
+             * The primary bucket ID of this media item. This can be useful to
+             * present the user a first-level clustering of related media items.
+             * This is a read-only column that is automatically computed.
+             * <p>
+             * Type: INTEGER
              */
             public static final String BUCKET_ID = "bucket_id";
 
             /**
-             * The bucket display name of the image. This is a read-only property that
-             * is automatically computed from the DATA column.
-             * <P>Type: TEXT</P>
+             * The primary bucket display name of this media item. This can be
+             * useful to present the user a first-level clustering of related
+             * media items. This is a read-only column that is automatically
+             * computed.
+             * <p>
+             * Type: TEXT
              */
             public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
+
+            /**
+             * The secondary bucket ID of this media item. This can be useful to
+             * present the user a second-level clustering of related media
+             * items. This is a read-only column that is automatically computed.
+             * <p>
+             * Type: INTEGER
+             */
+            public static final String SECONDARY_BUCKET_ID = "secondary_bucket_id";
         }
 
         public static final class Media implements ImageColumns {
@@ -1335,7 +1474,7 @@
             public static final String insertImage(ContentResolver cr, Bitmap source,
                                                    String title, String description) {
                 ContentValues values = new ContentValues();
-                values.put(Images.Media.TITLE, title);
+                values.put(Images.Media.DISPLAY_NAME, title);
                 values.put(Images.Media.DESCRIPTION, description);
                 values.put(Images.Media.MIME_TYPE, "image/jpeg");
 
@@ -2497,20 +2636,34 @@
             public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
 
             /**
-             * The bucket id of the video. This is a read-only property that
-             * is automatically computed from the DATA column.
-             * <P>Type: TEXT</P>
+             * The primary bucket ID of this media item. This can be useful to
+             * present the user a first-level clustering of related media items.
+             * This is a read-only column that is automatically computed.
+             * <p>
+             * Type: INTEGER
              */
             public static final String BUCKET_ID = "bucket_id";
 
             /**
-             * The bucket display name of the video. This is a read-only property that
-             * is automatically computed from the DATA column.
-             * <P>Type: TEXT</P>
+             * The primary bucket display name of this media item. This can be
+             * useful to present the user a first-level clustering of related
+             * media items. This is a read-only column that is automatically
+             * computed.
+             * <p>
+             * Type: TEXT
              */
             public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
 
             /**
+             * The secondary bucket ID of this media item. This can be useful to
+             * present the user a second-level clustering of related media
+             * items. This is a read-only column that is automatically computed.
+             * <p>
+             * Type: INTEGER
+             */
+            public static final String SECONDARY_BUCKET_ID = "secondary_bucket_id";
+
+            /**
              * The bookmark for the video. Time in ms. Represents the location in the video that the
              * video should start playing at the next time it is opened. If the value is null or
              * out of the range 0..DURATION-1 then the video should start playing from the
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index cbcc492..940736a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -150,6 +150,23 @@
             "android.settings.LOCATION_SOURCE_SETTINGS";
 
     /**
+     * Activity Action: Show settings to allow configuration of location controller extra package.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS =
+            "android.settings.LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS";
+
+    /**
      * Activity Action: Show scanning settings to allow configuration of Wi-Fi
      * and Bluetooth scanning settings.
      * <p>
@@ -7940,6 +7957,15 @@
                 "night_display_last_activated_time";
 
         /**
+         * Control whether display white balance is currently enabled.
+         * @hide
+         */
+        public static final String DISPLAY_WHITE_BALANCE_ENABLED = "display_white_balance_enabled";
+
+        private static final Validator DISPLAY_WHITE_BALANCE_ENABLED_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
+        /**
          * Names of the service components that the current user has explicitly allowed to
          * be a VR mode listener, separated by ':'.
          *
@@ -8262,6 +8288,38 @@
                 "packages_to_clear_data_before_full_restore";
 
         /**
+         * Indicates the location state should be maintained after sensor privacy is disabled.
+         * @hide
+         */
+        public static final String MAINTAIN_LOCATION_AFTER_SP_DISABLED = "0";
+
+        /**
+         * Indicates location should be reenabled after sensor privacy is disabled.
+         * @hide
+         */
+        public static final String REENABLE_LOCATION_AFTER_SP_DISABLED = "1";
+
+        /**
+         * Indicates the state of airplane mode should be maintained after sensor privacy is
+         * disabled.
+         * @hide
+         */
+        public static final String MAINTAIN_AIRPLANE_MODE_AFTER_SP_DISABLED = "0";
+
+        /**
+         * Indicates airplane mode should be disabled after sensor privacy is disabled.
+         * @hide
+         */
+        public static final String DISABLE_AIRPLANE_MODE_AFTER_SP_DISABLED = "1";
+
+        /**
+         * The state of all sensors managed by SensorPrivacyService when sensor privacy is enabled.
+         * @hide
+         */
+        public static final String SENSOR_PRIVACY_SENSOR_STATE =
+                "sensor_privacy_sensor_state";
+
+        /**
          * Setting to determine whether to use the new notification priority handling features.
          * @hide
          */
@@ -8356,6 +8414,7 @@
             NIGHT_DISPLAY_CUSTOM_END_TIME,
             NIGHT_DISPLAY_COLOR_TEMPERATURE,
             NIGHT_DISPLAY_AUTO_MODE,
+            DISPLAY_WHITE_BALANCE_ENABLED,
             SYNC_PARENT_SOUNDS,
             CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED,
             SWIPE_UP_TO_SWITCH_APPS_ENABLED,
@@ -8503,6 +8562,7 @@
             VALIDATORS.put(NIGHT_DISPLAY_COLOR_TEMPERATURE,
                     NIGHT_DISPLAY_COLOR_TEMPERATURE_VALIDATOR);
             VALIDATORS.put(NIGHT_DISPLAY_AUTO_MODE, NIGHT_DISPLAY_AUTO_MODE_VALIDATOR);
+            VALIDATORS.put(DISPLAY_WHITE_BALANCE_ENABLED, DISPLAY_WHITE_BALANCE_ENABLED_VALIDATOR);
             VALIDATORS.put(SYNC_PARENT_SOUNDS, SYNC_PARENT_SOUNDS_VALIDATOR);
             VALIDATORS.put(CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED,
                     CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED_VALIDATOR);
@@ -9355,7 +9415,8 @@
                 "hdmi_control_auto_wakeup_enabled";
 
         /**
-         * Whether TV will also turn off other CEC devices when it goes to standby mode.
+         * Whether TV or Audio System will also turn off other CEC devices when it goes to standby
+         * mode.
          * (0 = false, 1 = true)
          *
          * @hide
@@ -9364,6 +9425,15 @@
                 "hdmi_control_auto_device_off_enabled";
 
         /**
+         * Whether Audio System will also turn off TV when it goes to standby mode.
+         * (0 = false, 1 = true)
+         *
+         * @hide
+         */
+        public static final String HDMI_CONTROL_AUTO_TV_OFF_ENABLED =
+                "hdmi_control_auto_tv_off_enabled";
+
+        /**
          * If <b>true</b>, enables out-of-the-box execution for priv apps.
          * Default: false
          * Values: 0 = false, 1 = true
@@ -11922,11 +11992,16 @@
                 "angle_gl_driver_selection_values";
 
         /**
-         * App that is selected to use updated graphics driver.
+         * Apps that are selected to use Game Update Package.
          * @hide
          */
-        public static final String UPDATED_GFX_DRIVER_DEV_OPT_IN_APP =
-                "updated_gfx_driver_dev_opt_in_app";
+        public static final String GUP_DEV_OPT_IN_APPS = "gup_dev_opt_in_apps";
+
+        /**
+         * Apps on the black list that are forbidden to useGame Update Package.
+         * @hide
+         */
+        public static final String GUP_BLACK_LIST = "gup_black_list";
 
         /**
          * Ordered GPU debug layer list for Vulkan
@@ -12771,6 +12846,17 @@
                 "privileged_device_identifier_3p_check_relaxed";
 
         /**
+         * If set to 1, the device identifier check will be relaxed to the previous READ_PHONE_STATE
+         * permission check for preloaded non-privileged apps.
+         *
+         * STOPSHIP: Remove this once we ship with the new device identifier check enabled.
+         *
+         * @hide
+         */
+        public static final String PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED =
+                "privileged_device_identifier_non_priv_check_relaxed";
+
+        /**
          * If set to 1, SettingsProvider's restoreAnyVersion="true" attribute will be ignored
          * and restoring to lower version of platform API will be skipped.
          *
@@ -12900,14 +12986,20 @@
 
         /**
          * Property used by {@code com.android.server.SystemServer} on start to decide whether
-         * the Content Capture service should be created or not
+         * the Content Capture service should be created or not.
          *
-         * <p>By default it should *NOT* be set (in which case the decision is based on whether
-         * the OEM provides an implementation for the service), but it can be overridden to:
+         * <p>Possible values are:
          *
          * <ul>
-         *   <li>Provide a "kill switch" so OEMs can disable it remotely in case of emergency.
-         *   <li>Enable the CTS tests to be run on AOSP builds
+         *   <li>If set to {@code default}, it will only be set if the OEM provides and defines the
+         *   service name by overlaying {@code config_defaultContentCaptureService} (this is the
+         *   "default" mode)
+         *   <li>If set to {@code always}, it will always be enabled, even when the resource is not
+         *   overlaid (this is useful during development and to run the CTS tests on AOSP builds).
+         *   <li>Otherwise, it's explicitly disabled (this could work as a "kill switch" so OEMs
+         *   can disable it remotely in case of emergency by setting to something else (like
+         *   {@code "false"}); notice that it's also disabled if the OEM doesn't explicitly set one
+         *   of the values above).
          * </ul>
          *
          * @hide
@@ -12915,6 +13007,11 @@
         public static final String CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED =
                 "content_capture_service_explicitly_enabled";
 
+        /** {@hide} */
+        public static final String ISOLATED_STORAGE_LOCAL = "isolated_storage_local";
+        /** {@hide} */
+        public static final String ISOLATED_STORAGE_REMOTE = "isolated_storage_remote";
+
         /**
          * Settings to backup. This is here so that it's in the same place as the settings
          * keys and easy to update.
@@ -13901,7 +13998,6 @@
          */
         public static final String LAST_ACTIVE_USER_ID = "last_active_persistent_user_id";
 
-
         /**
          * Whether we've enabled native flags health check on this device. Takes effect on
          * reboot. The value "1" enables native flags health check; otherwise it's disabled.
@@ -13909,7 +14005,6 @@
          */
         public static final String NATIVE_FLAGS_HEALTH_CHECK_ENABLED =
                 "native_flags_health_check_enabled";
-
     }
 
     /**
@@ -13920,21 +14015,12 @@
      * @hide
      */
     public static final class Config extends NameValueTable {
-        /**
-         * The content:// style URL for the config table.
-         *
-         * TODO(b/113100523): Move this to DeviceConfig.java when it is added, and expose it as a
-         *     System API.
-         */
-        private static final Uri CONTENT_URI =
-                Uri.parse("content://" + AUTHORITY + "/config");
-
         private static final ContentProviderHolder sProviderHolder =
-                new ContentProviderHolder(CONTENT_URI);
+                new ContentProviderHolder(DeviceConfig.CONTENT_URI);
 
         // Populated lazily, guarded by class object:
         private static final NameValueCache sNameValueCache = new NameValueCache(
-                CONTENT_URI,
+                DeviceConfig.CONTENT_URI,
                 CALL_METHOD_GET_CONFIG,
                 CALL_METHOD_PUT_CONFIG,
                 sProviderHolder);
@@ -13999,16 +14085,15 @@
             try {
                 Bundle arg = new Bundle();
                 arg.putInt(CALL_METHOD_USER_KEY, resolver.getUserId());
-                arg.putInt(Settings.CALL_METHOD_RESET_MODE_KEY, resetMode);
+                arg.putInt(CALL_METHOD_RESET_MODE_KEY, resetMode);
                 if (prefix != null) {
                     arg.putString(Settings.CALL_METHOD_PREFIX_KEY, prefix);
                 }
-                arg.putInt(CALL_METHOD_RESET_MODE_KEY, resetMode);
                 IContentProvider cp = sProviderHolder.getProvider(resolver);
                 cp.call(resolver.getPackageName(), sProviderHolder.mUri.getAuthority(),
                         CALL_METHOD_RESET_CONFIG, null, arg);
             } catch (RemoteException e) {
-                Log.w(TAG, "Can't reset to defaults for " + CONTENT_URI, e);
+                Log.w(TAG, "Can't reset to defaults for " + DeviceConfig.CONTENT_URI, e);
             }
         }
     }
@@ -14217,6 +14302,44 @@
         }
     }
 
+    /**
+     * <p>
+     *     A Settings panel is floating UI that contains a fixed subset of settings to address a
+     *     particular user problem. For example, the
+     *     {@link #ACTION_INTERNET_CONNECTIVITY Internet Panel} surfaces settings related to
+     *     connecting to the internet.
+     * <p>
+     *     Settings panels appear above the calling app to address the problem without
+     *     the user needing to open Settings and thus leave their current screen.
+     */
+    public static final class Panel {
+        private Panel() {
+        }
+
+        /**
+         * Activity Action: Show a settings dialog containing settings to enable internet
+         * connection.
+         * <p>
+         * Input: Nothing.
+         * <p>
+         * Output: Nothing.
+         */
+        @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+        public static final String ACTION_INTERNET_CONNECTIVITY =
+                "android.settings.panel.action.INTERNET_CONNECTIVITY";
+
+        /**
+         * Activity Action: Show a settings dialog containing all volume streams.
+         * <p>
+         * Input: Nothing.
+         * <p>
+         * Output: Nothing.
+         */
+        @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+        public static final String ACTION_VOLUME =
+                "android.settings.panel.action.VOLUME";
+    }
+
     private static final String[] PM_WRITE_SETTINGS = {
         android.Manifest.permission.WRITE_SETTINGS
     };
diff --git a/core/java/android/service/autofill/CompositeUserData.java b/core/java/android/service/autofill/CompositeUserData.java
new file mode 100644
index 0000000..2df4ddf
--- /dev/null
+++ b/core/java/android/service/autofill/CompositeUserData.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.autofill;
+
+import static android.view.autofill.Helper.sDebug;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Holds both a generic and package-specific userData used for
+ * <a href="AutofillService.html#FieldClassification">field classification</a>.
+ *
+ * @hide
+ */
+@TestApi
+public final class CompositeUserData implements FieldClassificationUserData, Parcelable {
+
+    private final UserData mGenericUserData;
+    private final UserData mPackageUserData;
+
+    private final String[] mCategories;
+    private final String[] mValues;
+
+    public CompositeUserData(@Nullable UserData genericUserData,
+            @NonNull UserData packageUserData) {
+        mGenericUserData = genericUserData;
+        mPackageUserData = packageUserData;
+
+        final String[] packageCategoryIds = mPackageUserData.getCategoryIds();
+        final String[] packageValues = mPackageUserData.getValues();
+
+        final ArrayList<String> categoryIds = new ArrayList<>(packageCategoryIds.length);
+        final ArrayList<String> values = new ArrayList<>(packageValues.length);
+
+        Collections.addAll(categoryIds, packageCategoryIds);
+        Collections.addAll(values, packageValues);
+
+        if (mGenericUserData != null) {
+            final String[] genericCategoryIds = mGenericUserData.getCategoryIds();
+            final String[] genericValues = mGenericUserData.getValues();
+            final int size = mGenericUserData.getCategoryIds().length;
+            for (int i = 0; i < size; i++) {
+                if (!categoryIds.contains(genericCategoryIds[i])) {
+                    categoryIds.add(genericCategoryIds[i]);
+                    values.add(genericValues[i]);
+                }
+            }
+        }
+
+        mCategories = new String[categoryIds.size()];
+        categoryIds.toArray(mCategories);
+        mValues = new String[values.size()];
+        values.toArray(mValues);
+    }
+
+    @Nullable
+    @Override
+    public String getFieldClassificationAlgorithm() {
+        final String packageDefaultAlgo = mPackageUserData.getFieldClassificationAlgorithm();
+        if (packageDefaultAlgo != null) {
+            return packageDefaultAlgo;
+        } else {
+            return mGenericUserData == null ? null :
+                    mGenericUserData.getFieldClassificationAlgorithm();
+        }
+    }
+
+    @Override
+    public Bundle getDefaultFieldClassificationArgs() {
+        final Bundle packageDefaultArgs = mPackageUserData.getDefaultFieldClassificationArgs();
+        if (packageDefaultArgs != null) {
+            return packageDefaultArgs;
+        } else {
+            return mGenericUserData == null ? null :
+                    mGenericUserData.getDefaultFieldClassificationArgs();
+        }
+    }
+
+    @Nullable
+    @Override
+    public String getFieldClassificationAlgorithmForCategory(@NonNull String categoryId) {
+        Preconditions.checkNotNull(categoryId);
+        final ArrayMap<String, String> categoryAlgorithms = getFieldClassificationAlgorithms();
+        if (categoryAlgorithms == null || !categoryAlgorithms.containsKey(categoryId)) {
+            return null;
+        }
+        return categoryAlgorithms.get(categoryId);
+    }
+
+    @Override
+    public ArrayMap<String, String> getFieldClassificationAlgorithms() {
+        final ArrayMap<String, String> packageAlgos = mPackageUserData
+                .getFieldClassificationAlgorithms();
+        final ArrayMap<String, String> genericAlgos = mGenericUserData == null ? null :
+                mGenericUserData.getFieldClassificationAlgorithms();
+
+        ArrayMap<String, String> categoryAlgorithms = null;
+        if (packageAlgos != null || genericAlgos != null) {
+            categoryAlgorithms = new ArrayMap<>();
+            if (genericAlgos != null) {
+                categoryAlgorithms.putAll(genericAlgos);
+            }
+            if (packageAlgos != null) {
+                categoryAlgorithms.putAll(packageAlgos);
+            }
+        }
+
+        return categoryAlgorithms;
+    }
+
+    @Override
+    public ArrayMap<String, Bundle> getFieldClassificationArgs() {
+        final ArrayMap<String, Bundle> packageArgs = mPackageUserData.getFieldClassificationArgs();
+        final ArrayMap<String, Bundle> genericArgs = mGenericUserData == null ? null :
+                mGenericUserData.getFieldClassificationArgs();
+
+        ArrayMap<String, Bundle> categoryArgs = null;
+        if (packageArgs != null || genericArgs != null) {
+            categoryArgs = new ArrayMap<>();
+            if (genericArgs != null) {
+                categoryArgs.putAll(genericArgs);
+            }
+            if (packageArgs != null) {
+                categoryArgs.putAll(packageArgs);
+            }
+        }
+
+        return categoryArgs;
+    }
+
+    @Override
+    public String[] getCategoryIds() {
+        return mCategories;
+    }
+
+    @Override
+    public String[] getValues() {
+        return mValues;
+    }
+
+    /////////////////////////////////////
+    // Object "contract" methods. //
+    /////////////////////////////////////
+    @Override
+    public String toString() {
+        if (!sDebug) return super.toString();
+
+        // OK to print UserData because UserData.toString() is PII-aware
+        final StringBuilder builder = new StringBuilder("genericUserData=")
+                .append(mGenericUserData)
+                .append(", packageUserData=").append(mPackageUserData);
+        return builder.toString();
+    }
+
+    /////////////////////////////////////
+    // Parcelable "contract" methods. //
+    /////////////////////////////////////
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeParcelable(mGenericUserData, 0);
+        parcel.writeParcelable(mPackageUserData, 0);
+    }
+
+    public static final Parcelable.Creator<CompositeUserData> CREATOR =
+            new Parcelable.Creator<CompositeUserData>() {
+                @Override
+                public CompositeUserData createFromParcel(Parcel parcel) {
+                    // Always go through the builder to ensure the data ingested by
+                    // the system obeys the contract of the builder to avoid attacks
+                    // using specially crafted parcels.
+                    final UserData genericUserData = parcel.readParcelable(null);
+                    final UserData packageUserData = parcel.readParcelable(null);
+                    return new CompositeUserData(genericUserData, packageUserData);
+                }
+
+                @Override
+                public CompositeUserData[] newArray(int size) {
+                    return new CompositeUserData[size];
+                }
+            };
+}
diff --git a/core/java/android/service/autofill/FieldClassificationUserData.java b/core/java/android/service/autofill/FieldClassificationUserData.java
new file mode 100644
index 0000000..3d6cac4
--- /dev/null
+++ b/core/java/android/service/autofill/FieldClassificationUserData.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.autofill;
+
+import android.os.Bundle;
+import android.util.ArrayMap;
+
+/**
+ * Class used to define a generic UserData for field classification
+ *
+ * @hide
+ */
+public interface FieldClassificationUserData {
+    /**
+     * Gets the name of the default algorithm that is used to calculate
+     * {@link FieldClassification.Match#getScore()} match scores}.
+     */
+    String getFieldClassificationAlgorithm();
+
+    /**
+     * Gets the default field classification args.
+     */
+    Bundle getDefaultFieldClassificationArgs();
+
+    /**
+     * Gets the name of the field classification algorithm for a specific category.
+     *
+     * @param categoryId id of the specific category.
+     */
+    String getFieldClassificationAlgorithmForCategory(String categoryId);
+
+    /**
+     * Gets all field classification algorithms for specific categories.
+     */
+    ArrayMap<String, String> getFieldClassificationAlgorithms();
+
+    /**
+     * Gets all field classification args for specific categories.
+     */
+    ArrayMap<String, Bundle> getFieldClassificationArgs();
+
+    /**
+     * Gets all category ids
+     */
+    String[] getCategoryIds();
+
+    /**
+     * Gets all string values for field classification
+     */
+    String[] getValues();
+}
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index d408e9a..93ee8c3 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -516,6 +516,9 @@
         /**
          * Sets a specific {@link UserData} for field classification for this request only.
          *
+         * <p>Any fields in this UserData will override corresponding fields in the generic
+         * UserData object
+         *
          * @return this builder
          * @throws IllegalStateException if the FillResponse
          * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index 37f1923..a793e09 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -48,7 +48,7 @@
  * Defines the user data used for
  * <a href="AutofillService.html#FieldClassification">field classification</a>.
  */
-public final class UserData implements Parcelable {
+public final class UserData implements FieldClassificationUserData, Parcelable {
 
     private static final String TAG = "UserData";
 
@@ -86,11 +86,13 @@
      * {@link Match#getScore()} match scores}.
      */
     @Nullable
+    @Override
     public String getFieldClassificationAlgorithm() {
         return mDefaultAlgorithm;
     }
 
     /** @hide */
+    @Override
     public Bundle getDefaultFieldClassificationArgs() {
         return mDefaultArgs;
     }
@@ -104,6 +106,7 @@
      * @return String name of algorithm, null if none found.
      */
     @Nullable
+    @Override
     public String getFieldClassificationAlgorithmForCategory(@NonNull String categoryId) {
         Preconditions.checkNotNull(categoryId);
         if (mCategoryAlgorithms == null || !mCategoryAlgorithms.containsKey(categoryId)) {
@@ -120,22 +123,26 @@
     }
 
     /** @hide */
+    @Override
     public String[] getCategoryIds() {
         return mCategoryIds;
     }
 
     /** @hide */
+    @Override
     public String[] getValues() {
         return mValues;
     }
 
     /** @hide */
     @TestApi
+    @Override
     public ArrayMap<String, String> getFieldClassificationAlgorithms() {
         return mCategoryAlgorithms;
     }
 
     /** @hide */
+    @Override
     public ArrayMap<String, Bundle> getFieldClassificationArgs() {
         return mCategoryArgs;
     }
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 9aff281..7683b8a 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -254,6 +254,15 @@
         @GuardedBy("mLock")
         private AutofillValue mFocusedValue;
 
+        /**
+         * Id of the last field that cause the Autofill UI to be shown.
+         *
+         * <p>Used to make sure the SmartSuggestionsParams is updated when a new fields is focused.
+         */
+        // TODO(b/111330312): might not be needed when using IME
+        @GuardedBy("mLock")
+        private AutofillId mLastShownId;
+
         // Objects used to log metrics
         private final long mRequestTime;
         private long mOnSuccessTime;
@@ -284,7 +293,7 @@
         @NonNull
         public SystemPopupPresentationParams getSmartSuggestionParams() {
             synchronized (mLock) {
-                if (mSmartSuggestion != null) {
+                if (mSmartSuggestion != null && mFocusedId.equals(mLastShownId)) {
                     return mSmartSuggestion;
                 }
                 Rect rect;
@@ -299,6 +308,7 @@
                     return null;
                 }
                 mSmartSuggestion = new SystemPopupPresentationParams(this, rect);
+                mLastShownId = mFocusedId;
                 return mSmartSuggestion;
             }
         }
@@ -401,6 +411,9 @@
             if (mFocusedValue != null) {
                 pw.print(prefix); pw.print("focusedValue: "); pw.println(mFocusedValue);
             }
+            if (mLastShownId != null) {
+                pw.print(prefix); pw.print("lastShownId: "); pw.println(mLastShownId);
+            }
             pw.print(prefix); pw.print("client: "); pw.println(mClient);
             final String prefix2 = prefix + "  ";
             if (mFillWindow != null) {
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index 27df845..bad7ddd 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -163,14 +163,15 @@
             final int height = rect.bottom - rect.top;
             final int width = rect.right - rect.left;
             final WindowManager.LayoutParams windowParams = window.getAttributes();
-            windowParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
-            windowParams.y = rect.top - height;
+            windowParams.gravity = Gravity.TOP | Gravity.LEFT;
+            windowParams.y = rect.top + height;
             windowParams.height = height;
             windowParams.x = rect.left;
             windowParams.width = width;
 
             window.setAttributes(windowParams);
             window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+            window.setBackgroundDrawableResource(android.R.color.transparent);
 
             mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
             final ViewGroup.LayoutParams diagParams = new ViewGroup.LayoutParams(width, height);
diff --git a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
index df58f52..784e719 100644
--- a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
+++ b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
@@ -21,21 +21,26 @@
 import android.os.Parcelable;
 import android.view.contentcapture.ContentCaptureEvent;
 
+import java.util.Arrays;
 import java.util.List;
 
 /**
- * Batch of content capture events.
+ * Not needed anymore...
+ *
+ * @deprecated
  *
  * @hide
  */
 @SystemApi
+@Deprecated
 public final class ContentCaptureEventsRequest implements Parcelable {
+// TODO(b/121033016): remove .java and .aidl once service implementation doesn't use it anymore
 
-    private final List<ContentCaptureEvent> mEvents;
+    private final ContentCaptureEvent mEvent;
 
     /** @hide */
-    public ContentCaptureEventsRequest(@NonNull List<ContentCaptureEvent> events) {
-        mEvents = events;
+    public ContentCaptureEventsRequest(@NonNull ContentCaptureEvent event) {
+        mEvent = event;
     }
 
     /**
@@ -43,7 +48,7 @@
      */
     @NonNull
     public List<ContentCaptureEvent> getEvents() {
-        return mEvents;
+        return Arrays.asList(mEvent);
     }
 
     @Override
@@ -53,7 +58,7 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeTypedList(mEvents, flags);
+        parcel.writeParcelable(mEvent, flags);
     }
 
     public static final Parcelable.Creator<ContentCaptureEventsRequest> CREATOR =
@@ -61,8 +66,7 @@
 
         @Override
         public ContentCaptureEventsRequest createFromParcel(Parcel parcel) {
-            return new ContentCaptureEventsRequest(parcel
-                    .createTypedArrayList(ContentCaptureEvent.CREATOR));
+            return new ContentCaptureEventsRequest(parcel.readParcelable(null));
         }
 
         @Override
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 3dfeede..c9d46dd 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -24,13 +24,29 @@
 import android.app.Service;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.pm.ParceledListSlice;
+import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.service.autofill.AutofillService;
+import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Slog;
+import android.view.contentcapture.ContentCaptureContext;
 import android.view.contentcapture.ContentCaptureEvent;
+import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.ContentCaptureSession;
+import android.view.contentcapture.ContentCaptureSessionId;
+import android.view.contentcapture.IContentCaptureDirectManager;
+import android.view.contentcapture.MainContentCaptureSession;
 
+import com.android.internal.os.IResultReceiver;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.List;
 import java.util.Set;
 
@@ -45,7 +61,7 @@
 
     private static final String TAG = ContentCaptureService.class.getSimpleName();
 
-    // TODO(b/111330312): STOPSHIP use dynamic value, or change to false
+    // TODO(b/121044306): STOPSHIP use dynamic value, or change to false
     static final boolean DEBUG = true;
     static final boolean VERBOSE = false;
 
@@ -61,29 +77,22 @@
 
     private Handler mHandler;
 
-    private final IContentCaptureService mInterface = new IContentCaptureService.Stub() {
+    /**
+     * Binder that receives calls from the system server.
+     */
+    private final IContentCaptureService mServerInterface = new IContentCaptureService.Stub() {
 
         @Override
-        public void onSessionLifecycle(InteractionContext context, String sessionId)
-                throws RemoteException {
-            if (context != null) {
-                mHandler.sendMessage(
-                        obtainMessage(ContentCaptureService::handleOnCreateInteractionSession,
-                                ContentCaptureService.this, context, sessionId));
-            } else {
-                mHandler.sendMessage(
-                        obtainMessage(ContentCaptureService::handleOnDestroyInteractionSession,
-                                ContentCaptureService.this, sessionId));
-            }
+        public void onConnectedStateChanged(boolean state) {
+            mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnConnectedStateChanged,
+                    ContentCaptureService.this, state));
         }
 
         @Override
-        public void onContentCaptureEventsRequest(String sessionId,
-                ContentCaptureEventsRequest request) {
-            mHandler.sendMessage(
-                    obtainMessage(ContentCaptureService::handleOnContentCaptureEventsRequest,
-                            ContentCaptureService.this, sessionId, request));
-
+        public void onSessionStarted(ContentCaptureContext context, String sessionId, int uid,
+                IResultReceiver clientReceiver) {
+            mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnCreateSession,
+                    ContentCaptureService.this, context, sessionId, uid, clientReceiver));
         }
 
         @Override
@@ -92,8 +101,35 @@
                     obtainMessage(ContentCaptureService::handleOnActivitySnapshot,
                             ContentCaptureService.this, sessionId, snapshotData));
         }
+
+        @Override
+        public void onSessionFinished(String sessionId) {
+            mHandler.sendMessage(obtainMessage(ContentCaptureService::handleFinishSession,
+                    ContentCaptureService.this, sessionId));
+        }
     };
 
+    /**
+     * Binder that receives calls from the app.
+     */
+    private final IContentCaptureDirectManager mClientInterface =
+            new IContentCaptureDirectManager.Stub() {
+
+        @Override
+        public void sendEvents(@SuppressWarnings("rawtypes") ParceledListSlice events) {
+            mHandler.sendMessage(obtainMessage(ContentCaptureService::handleSendEvents,
+                            ContentCaptureService.this, Binder.getCallingUid(), events));
+        }
+    };
+
+    /**
+     * List of sessions per UID.
+     *
+     * <p>This map is populated when an session is started, which is called by the system server
+     * and can be trusted. Then subsequent calls made by the app are verified against this map.
+     */
+    private final ArrayMap<String, Integer> mSessionsByUid = new ArrayMap<>();
+
     @CallSuper
     @Override
     public void onCreate() {
@@ -105,7 +141,7 @@
     @Override
     public final IBinder onBind(Intent intent) {
         if (SERVICE_INTERFACE.equals(intent.getAction())) {
-            return mInterface.asBinder();
+            return mServerInterface.asBinder();
         }
         Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
         return null;
@@ -175,67 +211,204 @@
     }
 
     /**
-     * Creates a new interaction session.
+     * Called when the Android system connects to service.
      *
-     * @param context interaction context
+     * <p>You should generally do initialization here rather than in {@link #onCreate}.
+     */
+    public void onConnected() {
+        Slog.i(TAG, "bound to " + getClass().getName());
+    }
+
+    /**
+     * Creates a new content capture session.
+     *
+     * @param context content capture context
      * @param sessionId the session's Id
      */
-    public void onCreateInteractionSession(@NonNull InteractionContext context,
-            @NonNull InteractionSessionId sessionId) {
+    public void onCreateContentCaptureSession(@NonNull ContentCaptureContext context,
+            @NonNull ContentCaptureSessionId sessionId) {
         if (VERBOSE) {
-            Log.v(TAG, "onCreateInteractionSession(id=" + sessionId + ", ctx=" + context + ")");
+            Log.v(TAG, "onCreateContentCaptureSession(id=" + sessionId + ", ctx=" + context + ")");
         }
     }
 
     /**
+     *
+     * @deprecated use {@link #onContentCaptureEvent(ContentCaptureSessionId, ContentCaptureEvent)}
+     * instead.
+     */
+    @Deprecated
+    public void onContentCaptureEventsRequest(@NonNull ContentCaptureSessionId sessionId,
+            @NonNull ContentCaptureEventsRequest request) {
+        if (VERBOSE) Log.v(TAG, "onContentCaptureEventsRequest(id=" + sessionId + ")");
+    }
+
+    /**
      * Notifies the service of {@link ContentCaptureEvent events} associated with a content capture
      * session.
      *
      * @param sessionId the session's Id
-     * @param request the events
+     * @param event the event
      */
-    public abstract void onContentCaptureEventsRequest(@NonNull InteractionSessionId sessionId,
-            @NonNull ContentCaptureEventsRequest request);
-
+    public void onContentCaptureEvent(@NonNull ContentCaptureSessionId sessionId,
+            @NonNull ContentCaptureEvent event) {
+        if (VERBOSE) Log.v(TAG, "onContentCaptureEventsRequest(id=" + sessionId + ")");
+        onContentCaptureEventsRequest(sessionId, new ContentCaptureEventsRequest(event));
+    }
     /**
      * Notifies the service of {@link SnapshotData snapshot data} associated with a session.
      *
      * @param sessionId the session's Id
      * @param snapshotData the data
      */
-    public void onActivitySnapshot(@NonNull InteractionSessionId sessionId,
+    public void onActivitySnapshot(@NonNull ContentCaptureSessionId sessionId,
             @NonNull SnapshotData snapshotData) {}
 
     /**
-     * Destroys the interaction session.
+     * Destroys the content capture session.
      *
      * @param sessionId the id of the session to destroy
+     * */
+    public void onDestroyContentCaptureSession(@NonNull ContentCaptureSessionId sessionId) {
+        if (VERBOSE) Log.v(TAG, "onDestroyContentCaptureSession(id=" + sessionId + ")");
+    }
+
+    /**
+     * Called when the Android system disconnects from the service.
+     *
+     * <p> At this point this service may no longer be an active {@link AutofillService}.
      */
-    public void onDestroyInteractionSession(@NonNull InteractionSessionId sessionId) {
-        if (VERBOSE) {
-            Log.v(TAG, "onDestroyInteractionSession(id=" + sessionId + ")");
+    public void onDisconnected() {
+        Slog.i(TAG, "unbinding from " + getClass().getName());
+    }
+
+    @Override
+    @CallSuper
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        final int size = mSessionsByUid.size();
+        pw.print("Number sessions: "); pw.println(size);
+        if (size > 0) {
+            final String prefix = "  ";
+            for (int i = 0; i < size; i++) {
+                pw.print(prefix); pw.print(mSessionsByUid.keyAt(i));
+                pw.print(": uid="); pw.println(mSessionsByUid.valueAt(i));
+            }
+        }
+    }
+
+    private void handleOnConnectedStateChanged(boolean state) {
+        if (state) {
+            onConnected();
+        } else {
+            onDisconnected();
         }
     }
 
     //TODO(b/111276913): consider caching the InteractionSessionId for the lifetime of the session,
     // so we don't need to create a temporary InteractionSessionId for each event.
 
-    private void handleOnCreateInteractionSession(@NonNull InteractionContext context,
-            @NonNull String sessionId) {
-        onCreateInteractionSession(context, new InteractionSessionId(sessionId));
+    private void handleOnCreateSession(@NonNull ContentCaptureContext context,
+            @NonNull String sessionId, int uid, IResultReceiver clientReceiver) {
+        mSessionsByUid.put(sessionId, uid);
+        onCreateContentCaptureSession(context, new ContentCaptureSessionId(sessionId));
+        setClientState(clientReceiver, ContentCaptureSession.STATE_ACTIVE,
+                mClientInterface.asBinder());
     }
 
-    private void handleOnContentCaptureEventsRequest(@NonNull String sessionId,
-            @NonNull ContentCaptureEventsRequest request) {
-        onContentCaptureEventsRequest(new InteractionSessionId(sessionId), request);
+    private void handleSendEvents(int uid,
+            @NonNull ParceledListSlice<ContentCaptureEvent> parceledEvents) {
+
+        // Most events belong to the same session, so we can keep a reference to the last one
+        // to avoid creating too many ContentCaptureSessionId objects
+        String lastSessionId = null;
+        ContentCaptureSessionId sessionId = null;
+
+        final List<ContentCaptureEvent> events = parceledEvents.getList();
+        for (int i = 0; i < events.size(); i++) {
+            final ContentCaptureEvent event = events.get(i);
+            if (!handleIsRightCallerFor(event, uid)) continue;
+            String sessionIdString = event.getSessionId();
+            if (!sessionIdString.equals(lastSessionId)) {
+                sessionId = new ContentCaptureSessionId(sessionIdString);
+                lastSessionId = sessionIdString;
+            }
+            switch (event.getType()) {
+                case ContentCaptureEvent.TYPE_SESSION_STARTED:
+                    final ContentCaptureContext clientContext = event.getClientContext();
+                    clientContext.setParentSessionId(event.getParentSessionId());
+                    mSessionsByUid.put(sessionIdString, uid);
+                    onCreateContentCaptureSession(clientContext, sessionId);
+                    break;
+                case ContentCaptureEvent.TYPE_SESSION_FINISHED:
+                    mSessionsByUid.remove(sessionIdString);
+                    onDestroyContentCaptureSession(sessionId);
+                    break;
+                default:
+                    onContentCaptureEvent(sessionId, event);
+            }
+        }
     }
 
     private void handleOnActivitySnapshot(@NonNull String sessionId,
             @NonNull SnapshotData snapshotData) {
-        onActivitySnapshot(new InteractionSessionId(sessionId), snapshotData);
+        onActivitySnapshot(new ContentCaptureSessionId(sessionId), snapshotData);
     }
 
-    private void handleOnDestroyInteractionSession(@NonNull String sessionId) {
-        onDestroyInteractionSession(new InteractionSessionId(sessionId));
+    private void handleFinishSession(@NonNull String sessionId) {
+        mSessionsByUid.remove(sessionId);
+        onDestroyContentCaptureSession(new ContentCaptureSessionId(sessionId));
+    }
+
+    /**
+     * Checks if the given {@code uid} owns the session associated with the event.
+     */
+    private boolean handleIsRightCallerFor(@NonNull ContentCaptureEvent event, int uid) {
+        final String sessionId;
+        switch (event.getType()) {
+            case ContentCaptureEvent.TYPE_SESSION_STARTED:
+            case ContentCaptureEvent.TYPE_SESSION_FINISHED:
+                sessionId = event.getParentSessionId();
+                break;
+            default:
+                sessionId = event.getSessionId();
+        }
+        final Integer rightUid = mSessionsByUid.get(sessionId);
+        if (rightUid == null) {
+            if (VERBOSE) Log.v(TAG, "No session for " + sessionId);
+            // Just ignore, as the session could have finished
+            return false;
+        }
+        if (rightUid != uid) {
+            Log.e(TAG, "invalid call from UID " + uid + ": session " + sessionId + " belongs to "
+                    + rightUid);
+            //TODO(b/111276913): log metrics as this could be a malicious app forging a sessionId
+            return false;
+        }
+        return true;
+
+    }
+
+    /**
+     * Sends the state of the {@link ContentCaptureManager} in the cleint app.
+     *
+     * @param clientReceiver receiver in the client app.
+     * @param binder handle to the {@code IContentCaptureDirectManager} object that resides in the
+     * service.
+     * @hide
+     */
+    public static void setClientState(@NonNull IResultReceiver clientReceiver,
+            int sessionStatus, @Nullable IBinder binder) {
+        try {
+            final Bundle extras;
+            if (binder != null) {
+                extras = new Bundle();
+                extras.putBinder(MainContentCaptureSession.EXTRA_BINDER, binder);
+            } else {
+                extras = null;
+            }
+            clientReceiver.send(sessionStatus, extras);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Error async reporting result to client: " + e);
+        }
     }
 }
diff --git a/core/java/android/service/contentcapture/IContentCaptureService.aidl b/core/java/android/service/contentcapture/IContentCaptureService.aidl
index 29e9abb..1b4cccf 100644
--- a/core/java/android/service/contentcapture/IContentCaptureService.aidl
+++ b/core/java/android/service/contentcapture/IContentCaptureService.aidl
@@ -16,9 +16,10 @@
 
 package android.service.contentcapture;
 
-import android.service.contentcapture.ContentCaptureEventsRequest;
-import android.service.contentcapture.InteractionContext;
 import android.service.contentcapture.SnapshotData;
+import android.view.contentcapture.ContentCaptureContext;
+
+import com.android.internal.os.IResultReceiver;
 
 import java.util.List;
 
@@ -28,11 +29,9 @@
  * @hide
  */
 oneway interface IContentCaptureService {
-
-    // Called when session is created (context not null) or destroyed (context null)
-    void onSessionLifecycle(in InteractionContext context, String sessionId);
-
-    void onContentCaptureEventsRequest(String sessionId, in ContentCaptureEventsRequest request);
-
+    void onConnectedStateChanged(boolean state);
+    void onSessionStarted(in ContentCaptureContext context, String sessionId, int uid,
+                          in IResultReceiver clientReceiver);
+    void onSessionFinished(String sessionId);
     void onActivitySnapshot(String sessionId, in SnapshotData snapshotData);
 }
diff --git a/core/java/android/service/contentcapture/InteractionContext.java b/core/java/android/service/contentcapture/InteractionContext.java
deleted file mode 100644
index f1281ff..0000000
--- a/core/java/android/service/contentcapture/InteractionContext.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.service.contentcapture;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.app.TaskInfo;
-import android.content.ComponentName;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.util.Preconditions;
-
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-// TODO(b/111276913): add javadocs / implement Parcelable / implement equals/hashcode/toString
-/** @hide */
-@SystemApi
-public final class InteractionContext implements Parcelable {
-
-    /**
-     * Flag used to indicate that the app explicitly disabled content capture for the activity
-     * (using
-     * {@link android.view.contentcapture.ContentCaptureManager#setContentCaptureEnabled(boolean)}),
-     * in which case the service will just receive activity-level events.
-     */
-    public static final int FLAG_DISABLED_BY_APP = 0x1;
-
-    /**
-     * Flag used to indicate that the activity's window is tagged with
-     * {@link android.view.Display#FLAG_SECURE}, in which case the service will just receive
-     * activity-level events.
-     */
-    public static final int FLAG_DISABLED_BY_FLAG_SECURE = 0x2;
-
-    /** @hide */
-    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
-            FLAG_DISABLED_BY_APP,
-            FLAG_DISABLED_BY_FLAG_SECURE
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    @interface ContextCreationFlags{}
-
-    // TODO(b/111276913): create new object for taskId + componentName / reuse on other places
-    private final @NonNull ComponentName mComponentName;
-    private final int mTaskId;
-    private final int mDisplayId;
-    private final int mFlags;
-
-
-    /** @hide */
-    public InteractionContext(@NonNull ComponentName componentName, int taskId, int displayId,
-            int flags) {
-        mComponentName = Preconditions.checkNotNull(componentName);
-        mTaskId = taskId;
-        mDisplayId = displayId;
-        mFlags = flags;
-    }
-
-    /**
-     * Gets the id of the {@link TaskInfo task} associated with this context.
-     */
-    public int getTaskId() {
-        return mTaskId;
-    }
-
-    /**
-     * Gets the activity associated with this context.
-     */
-    public @NonNull ComponentName getActivityComponent() {
-        return mComponentName;
-    }
-
-    /**
-     * Gets the ID of the display associated with this context, as defined by
-     * {G android.hardware.display.DisplayManager#getDisplay(int) DisplayManager.getDisplay()}.
-     */
-    public int getDisplayId() {
-        return mDisplayId;
-    }
-
-    /**
-     * Gets the flags associated with this context.
-     *
-     * @return any combination of {@link #FLAG_DISABLED_BY_FLAG_SECURE} and
-     * {@link #FLAG_DISABLED_BY_APP}.
-     */
-    public @ContextCreationFlags int getFlags() {
-        return mFlags;
-    }
-
-    /**
-     * @hide
-     */
-    // TODO(b/111276913): dump to proto as well
-    public void dump(PrintWriter pw) {
-        pw.print("comp="); pw.print(mComponentName.flattenToShortString());
-        pw.print(", taskId="); pw.print(mTaskId);
-        pw.print(", displayId="); pw.print(mDisplayId);
-        if (mFlags > 0) {
-            pw.print(", flags="); pw.print(mFlags);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "Context[act=" + mComponentName.flattenToShortString() + ", taskId=" + mTaskId
-                + ", displayId=" + mDisplayId + ", flags=" + mFlags + "]";
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeParcelable(mComponentName, flags);
-        parcel.writeInt(mTaskId);
-        parcel.writeInt(mDisplayId);
-        parcel.writeInt(mFlags);
-    }
-
-    public static final Parcelable.Creator<InteractionContext> CREATOR =
-            new Parcelable.Creator<InteractionContext>() {
-
-        @Override
-        public InteractionContext createFromParcel(Parcel parcel) {
-            final ComponentName componentName = parcel.readParcelable(null);
-            final int taskId = parcel.readInt();
-            final int displayId = parcel.readInt();
-            final int flags = parcel.readInt();
-            return new InteractionContext(componentName, taskId, displayId, flags);
-        }
-
-        @Override
-        public InteractionContext[] newArray(int size) {
-            return new InteractionContext[size];
-        }
-    };
-}
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/core/java/android/service/euicc/DownloadSubscriptionResult.aidl
similarity index 74%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to core/java/android/service/euicc/DownloadSubscriptionResult.aidl
index b7a7920..b625fd6 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/core/java/android/service/euicc/DownloadSubscriptionResult.aidl
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.service.euicc;
 
-parcelable UsbPort;
+parcelable DownloadSubscriptionResult;
diff --git a/core/java/android/service/euicc/DownloadSubscriptionResult.java b/core/java/android/service/euicc/DownloadSubscriptionResult.java
new file mode 100644
index 0000000..b410e35
--- /dev/null
+++ b/core/java/android/service/euicc/DownloadSubscriptionResult.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.euicc;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.service.euicc.EuiccService.ResolvableError;
+import android.service.euicc.EuiccService.Result;
+
+/**
+ * Result of a {@link EuiccService#onDownloadSubscription} operation.
+ * @hide
+ */
+@SystemApi
+public final class DownloadSubscriptionResult implements Parcelable {
+
+    public static final Creator<DownloadSubscriptionResult> CREATOR =
+            new Creator<DownloadSubscriptionResult>() {
+        @Override
+        public DownloadSubscriptionResult createFromParcel(Parcel in) {
+            return new DownloadSubscriptionResult(in);
+        }
+
+        @Override
+        public DownloadSubscriptionResult[] newArray(int size) {
+            return new DownloadSubscriptionResult[size];
+        }
+    };
+
+    private final @Result int mResult;
+    private final @ResolvableError int mResolvableErrors;
+    private final int mCardId;
+
+    public DownloadSubscriptionResult(@Result int result, @ResolvableError int resolvableErrors,
+            int cardId) {
+        this.mResult = result;
+        this.mResolvableErrors = resolvableErrors;
+        this.mCardId = cardId;
+    }
+
+    /** Gets the result of the operation. */
+    public @Result int getResult() {
+        return mResult;
+    }
+
+    /**
+     * Gets the bit map of resolvable errors.
+     *
+     * <p>The value is passed from EuiccService. The values can be
+     *
+     * <ul>
+     * <li>{@link EuiccService#RESOLVABLE_ERROR_CONFIRMATION_CODE}
+     * <li>{@link EuiccService#RESOLVABLE_ERROR_POLICY_RULES}
+     * </ul>
+     */
+    public @ResolvableError int getResolvableErrors() {
+        return mResolvableErrors;
+    }
+
+    /**
+     * Gets the card Id. This is used when resolving resolvable errors. The value is passed from
+     * EuiccService.
+     */
+    public int getCardId() {
+        return mCardId;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mResult);
+        dest.writeInt(mResolvableErrors);
+        dest.writeInt(mCardId);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    private DownloadSubscriptionResult(Parcel in) {
+        this.mResult = in.readInt();
+        this.mResolvableErrors = in.readInt();
+        this.mCardId = in.readInt();
+    }
+}
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index 49a7320..4be1f9c 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -16,17 +16,24 @@
 package android.service.euicc;
 
 import android.annotation.CallSuper;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Intent;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.telephony.TelephonyManager;
 import android.telephony.euicc.DownloadableSubscription;
 import android.telephony.euicc.EuiccInfo;
 import android.telephony.euicc.EuiccManager.OtaStatus;
 import android.util.ArraySet;
+import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.ThreadPoolExecutor;
@@ -73,6 +80,8 @@
  */
 @SystemApi
 public abstract class EuiccService extends Service {
+    private static final String TAG = "EuiccService";
+
     /** Action which must be included in this service's intent filter. */
     public static final String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService";
 
@@ -115,30 +124,91 @@
     public static final String ACTION_RESOLVE_NO_PRIVILEGES =
             "android.service.euicc.action.RESOLVE_NO_PRIVILEGES";
 
-    /** Ask the user to input carrier confirmation code. */
+    /**
+     * Ask the user to input carrier confirmation code.
+     *
+     * @deprecated From Q, the resolvable errors happened in the download step are presented as
+     * bit map in {@link #EXTRA_RESOLVABLE_ERRORS}. The corresponding action would be
+     * {@link #ACTION_RESOLVE_RESOLVABLE_ERRORS}.
+     */
+    @Deprecated
     public static final String ACTION_RESOLVE_CONFIRMATION_CODE =
             "android.service.euicc.action.RESOLVE_CONFIRMATION_CODE";
 
+    /** Ask the user to resolve all the resolvable errors. */
+    public static final String ACTION_RESOLVE_RESOLVABLE_ERRORS =
+            "android.service.euicc.action.RESOLVE_RESOLVABLE_ERRORS";
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "RESOLVABLE_ERROR_" }, value = {
+            RESOLVABLE_ERROR_CONFIRMATION_CODE,
+            RESOLVABLE_ERROR_POLICY_RULES,
+    })
+    public @interface ResolvableError {}
+
+    /**
+     * Possible value for the bit map of resolvable errors indicating the download process needs
+     * the user to input confirmation code.
+     */
+    public static final int RESOLVABLE_ERROR_CONFIRMATION_CODE = 1 << 0;
+    /**
+     * Possible value for the bit map of resolvable errors indicating the download process needs
+     * the user's consent to allow profile policy rules.
+     */
+    public static final int RESOLVABLE_ERROR_POLICY_RULES = 1 << 1;
+
     /**
      * Intent extra set for resolution requests containing the package name of the calling app.
      * This is used by the above actions including ACTION_RESOLVE_DEACTIVATE_SIM,
-     * ACTION_RESOLVE_NO_PRIVILEGES and ACTION_RESOLVE_CONFIRMATION_CODE.
+     * ACTION_RESOLVE_NO_PRIVILEGES and ACTION_RESOLVE_RESOLVABLE_ERRORS.
      */
     public static final String EXTRA_RESOLUTION_CALLING_PACKAGE =
             "android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE";
 
     /**
+     * Intent extra set for resolution requests containing the list of resolvable errors to be
+     * resolved. Each resolvable error is an integer. Its possible values include:
+     * <UL>
+     * <LI>{@link #RESOLVABLE_ERROR_CONFIRMATION_CODE}
+     * <LI>{@link #RESOLVABLE_ERROR_POLICY_RULES}
+     * </UL>
+     */
+    public static final String EXTRA_RESOLVABLE_ERRORS =
+            "android.service.euicc.extra.RESOLVABLE_ERRORS";
+
+    /**
      * Intent extra set for resolution requests containing a boolean indicating whether to ask the
      * user to retry another confirmation code.
      */
     public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED =
             "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED";
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "RESULT_" }, value = {
+            RESULT_OK,
+            RESULT_MUST_DEACTIVATE_SIM,
+            RESULT_RESOLVABLE_ERRORS,
+            RESULT_NEED_CONFIRMATION_CODE,
+            RESULT_FIRST_USER,
+    })
+    public @interface Result {}
+
     /** Result code for a successful operation. */
     public static final int RESULT_OK = 0;
     /** Result code indicating that an active SIM must be deactivated to perform the operation. */
     public static final int RESULT_MUST_DEACTIVATE_SIM = -1;
-    /** Result code indicating that the user must input a carrier confirmation code. */
+    /** Result code indicating that the user must resolve resolvable errors. */
+    public static final int RESULT_RESOLVABLE_ERRORS = -2;
+    /**
+     * Result code indicating that the user must input a carrier confirmation code.
+     *
+     * @deprecated From Q, the resolvable errors happened in the download step are presented as
+     * bit map in {@link #EXTRA_RESOLVABLE_ERRORS}. The corresponding result would be
+     * {@link #RESULT_RESOLVABLE_ERRORS}.
+     */
+    @Deprecated
     public static final int RESULT_NEED_CONFIRMATION_CODE = -2;
     // New predefined codes should have negative values.
 
@@ -154,7 +224,7 @@
         RESOLUTION_ACTIONS = new ArraySet<>();
         RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM);
         RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_NO_PRIVILEGES);
-        RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_CONFIRMATION_CODE);
+        RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_RESOLVABLE_ERRORS);
     }
 
     /**
@@ -169,6 +239,12 @@
      */
     public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE =
             "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE";
+    /**
+     * String extra for resolution actions indicating whether the user allows policy rules.
+     * This is used and set by the implementation and used in {@code EuiccOperation}.
+     */
+    public static final String EXTRA_RESOLUTION_ALLOW_POLICY_RULES =
+            "android.service.euicc.extra.RESOLUTION_ALLOW_POLICY_RULES";
 
     private final IEuiccService.Stub mStubWrapper;
 
@@ -236,8 +312,7 @@
     /**
      * Return the EID of the eUICC.
      *
-     * @param slotId ID of the SIM slot being queried. This is currently not populated but is here
-     *     to future-proof the APIs.
+     * @param slotId ID of the SIM slot being queried.
      * @return the EID.
      * @see android.telephony.euicc.EuiccManager#getEid
      */
@@ -247,8 +322,7 @@
     /**
      * Return the status of OTA update.
      *
-     * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
-     *     but is here to future-proof the APIs.
+     * @param slotId ID of the SIM slot to use for the operation.
      * @return The status of Euicc OTA update.
      * @see android.telephony.euicc.EuiccManager#getOtaStatus
      */
@@ -257,8 +331,7 @@
     /**
      * Perform OTA if current OS is not the latest one.
      *
-     * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
-     *     but is here to future-proof the APIs.
+     * @param slotId ID of the SIM slot to use for the operation.
      * @param statusChangedCallback Function called when OTA status changed.
      */
     public abstract void onStartOtaIfNecessary(
@@ -281,8 +354,7 @@
     /**
      * Return metadata for subscriptions which are available for download for this device.
      *
-     * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
-     *     but is here to future-proof the APIs.
+     * @param slotId ID of the SIM slot to use for the operation.
      * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the
      *     eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM)}
      *     should be returned to allow the user to consent to this operation first.
@@ -302,13 +374,44 @@
      * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the
      *     eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM}
      *     should be returned to allow the user to consent to this operation first.
+     * @param resolvedBundle The bundle containing information on resolved errors. It can contain
+     *     a string of confirmation code for the key {@link #EXTRA_RESOLUTION_CONFIRMATION_CODE},
+     *     and a boolean for key {@link #EXTRA_RESOLUTION_ALLOW_POLICY_RULES} indicating whether
+     *     the user allows profile policy rules or not.
+     * @return a DownloadSubscriptionResult instance including a result code, a resolvable errors
+     *     bit map, and original the card Id. The result code may be one of the predefined
+     *     {@code RESULT_} constants or any implementation-specific code starting with
+     *     {@link #RESULT_FIRST_USER}. The resolvable error bit map can be either 0 or values
+     *     defined in {@code RESOLVABLE_ERROR_}.
+     * @see android.telephony.euicc.EuiccManager#downloadSubscription
+     */
+    public abstract DownloadSubscriptionResult onDownloadSubscription(int slotId,
+            @NonNull DownloadableSubscription subscription, boolean switchAfterDownload,
+            boolean forceDeactivateSim, @Nullable Bundle resolvedBundle);
+
+    /**
+     * Download the given subscription.
+     *
+     * @param slotId ID of the SIM slot to use for the operation.
+     * @param subscription The subscription to download.
+     * @param switchAfterDownload If true, the subscription should be enabled upon successful
+     *     download.
+     * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the
+     *     eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM}
+     *     should be returned to allow the user to consent to this operation first.
      * @return the result of the download operation. May be one of the predefined {@code RESULT_}
      *     constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
      * @see android.telephony.euicc.EuiccManager#downloadSubscription
+     *
+     * @deprecated From Q, please use the above
+     * {@link #onDownloadSubscription(int, DownloadableSubscription, boolean, boolean, Bundle)}.
      */
-    public abstract int onDownloadSubscription(int slotId,
-            DownloadableSubscription subscription, boolean switchAfterDownload,
-            boolean forceDeactivateSim);
+    @Deprecated public @Result int onDownloadSubscription(int slotId,
+            @NonNull DownloadableSubscription subscription, boolean switchAfterDownload,
+            boolean forceDeactivateSim) {
+        throw new UnsupportedOperationException("onDownloadSubscription(int, "
+            + "DownloadableSubscription, boolean, boolean) is deprecated.");
+    }
 
     /**
      * Return a list of all @link EuiccProfileInfo}s.
@@ -318,7 +421,7 @@
      * @see android.telephony.SubscriptionManager#getAvailableSubscriptionInfoList
      * @see android.telephony.SubscriptionManager#getAccessibleSubscriptionInfoList
      */
-    public abstract GetEuiccProfileInfoListResult onGetEuiccProfileInfoList(int slotId);
+    public abstract @NonNull GetEuiccProfileInfoListResult onGetEuiccProfileInfoList(int slotId);
 
     /**
      * Return info about the eUICC chip/device.
@@ -327,7 +430,7 @@
      * @return the {@link EuiccInfo} for the eUICC chip/device.
      * @see android.telephony.euicc.EuiccManager#getEuiccInfo
      */
-    public abstract EuiccInfo onGetEuiccInfo(int slotId);
+    public abstract @NonNull EuiccInfo onGetEuiccInfo(int slotId);
 
     /**
      * Delete the given subscription.
@@ -341,7 +444,7 @@
      *     constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
      * @see android.telephony.euicc.EuiccManager#deleteSubscription
      */
-    public abstract int onDeleteSubscription(int slotId, String iccid);
+    public abstract @Result int onDeleteSubscription(int slotId, String iccid);
 
     /**
      * Switch to the given subscription.
@@ -357,7 +460,7 @@
      *     constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
      * @see android.telephony.euicc.EuiccManager#switchToSubscription
      */
-    public abstract int onSwitchToSubscription(int slotId, @Nullable String iccid,
+    public abstract @Result int onSwitchToSubscription(int slotId, @Nullable String iccid,
             boolean forceDeactivateSim);
 
     /**
@@ -379,8 +482,7 @@
      * <p>This is intended to be used for device resets. As such, the reset should be performed even
      * if an active SIM must be deactivated in order to access the eUICC.
      *
-     * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
-     *     but is here to future-proof the APIs.
+     * @param slotId ID of the SIM slot to use for the operation.
      * @return the result of the erase operation. May be one of the predefined {@code RESULT_}
      *     constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
      * @see android.telephony.euicc.EuiccManager#eraseSubscriptions
@@ -395,8 +497,7 @@
      * should persist some bit that will remain accessible after the factory reset to bypass this
      * flow when this method is called.
      *
-     * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
-     *     but is here to future-proof the APIs.
+     * @param slotId ID of the SIM slot to use for the operation.
      * @return the result of the operation. May be one of the predefined {@code RESULT_} constants
      *     or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
      */
@@ -408,13 +509,26 @@
     private class IEuiccServiceWrapper extends IEuiccService.Stub {
         @Override
         public void downloadSubscription(int slotId, DownloadableSubscription subscription,
-                boolean switchAfterDownload, boolean forceDeactivateSim,
+                boolean switchAfterDownload, boolean forceDeactivateSim, Bundle resolvedBundle,
                 IDownloadSubscriptionCallback callback) {
             mExecutor.execute(new Runnable() {
                 @Override
                 public void run() {
-                    int result = EuiccService.this.onDownloadSubscription(
-                            slotId, subscription, switchAfterDownload, forceDeactivateSim);
+                    DownloadSubscriptionResult result;
+                    try {
+                        result =
+                            EuiccService.this.onDownloadSubscription(
+                                slotId, subscription, switchAfterDownload, forceDeactivateSim,
+                                resolvedBundle);
+                    } catch (AbstractMethodError e) {
+                        Log.w(TAG, "The new onDownloadSubscription(int, "
+                                + "DownloadableSubscription, boolean, boolean, Bundle) is not "
+                                + "implemented. Fall back to the old one.", e);
+                        int resultCode = EuiccService.this.onDownloadSubscription(
+                                slotId, subscription, switchAfterDownload, forceDeactivateSim);
+                        result = new DownloadSubscriptionResult(resultCode,
+                            0 /* resolvableErrors */, TelephonyManager.INVALID_CARD_ID);
+                    }
                     try {
                         callback.onComplete(result);
                     } catch (RemoteException e) {
diff --git a/core/java/android/service/euicc/IDownloadSubscriptionCallback.aidl b/core/java/android/service/euicc/IDownloadSubscriptionCallback.aidl
index 6893c85..50ecbeb 100644
--- a/core/java/android/service/euicc/IDownloadSubscriptionCallback.aidl
+++ b/core/java/android/service/euicc/IDownloadSubscriptionCallback.aidl
@@ -16,7 +16,9 @@
 
 package android.service.euicc;
 
+import android.service.euicc.DownloadSubscriptionResult;
+
 /** @hide */
 oneway interface IDownloadSubscriptionCallback {
-    void onComplete(int result);
+    void onComplete(in DownloadSubscriptionResult result);
 }
\ No newline at end of file
diff --git a/core/java/android/service/euicc/IEuiccService.aidl b/core/java/android/service/euicc/IEuiccService.aidl
index 45be527..c2cdf09 100644
--- a/core/java/android/service/euicc/IEuiccService.aidl
+++ b/core/java/android/service/euicc/IEuiccService.aidl
@@ -30,11 +30,12 @@
 import android.service.euicc.ISwitchToSubscriptionCallback;
 import android.service.euicc.IUpdateSubscriptionNicknameCallback;
 import android.telephony.euicc.DownloadableSubscription;
+import android.os.Bundle;
 
 /** @hide */
 oneway interface IEuiccService {
     void downloadSubscription(int slotId, in DownloadableSubscription subscription,
-            boolean switchAfterDownload, boolean forceDeactivateSim,
+            boolean switchAfterDownload, boolean forceDeactivateSim, in Bundle resolvedBundle,
             in IDownloadSubscriptionCallback callback);
     void getDownloadableSubscriptionMetadata(int slotId, in DownloadableSubscription subscription,
             boolean forceDeactivateSim, in IGetDownloadableSubscriptionMetadataCallback callback);
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index af7e93e..30d9804 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -29,7 +29,7 @@
 
 /**
  * The current condition of an {@link android.app.AutomaticZenRule}, provided by the
- * {@link ConditionProviderService} that owns the rule. Used to tell the system to enter Do Not
+ * app that owns the rule. Used to tell the system to enter Do Not
  * Disturb mode and request that the system exit Do Not Disturb mode.
  */
 public final class Condition implements Parcelable {
@@ -48,8 +48,8 @@
 
     /**
      * Indicates that Do Not Disturb should be turned off. Note that all Conditions from all
-     * {@link ConditionProviderService} providers must be off for Do Not Disturb to be turned off on
-     * the device.
+     * {@link android.app.AutomaticZenRule} providers must be off for Do Not Disturb to be turned
+     * off on the device.
      */
     public static final int STATE_FALSE = 0;
     /**
@@ -154,7 +154,7 @@
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
 
-        // id is guarantreed not to be null.
+        // id is guaranteed not to be null.
         proto.write(ConditionProto.ID, id.toString());
         proto.write(ConditionProto.SUMMARY, summary);
         proto.write(ConditionProto.LINE_1, line1);
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index 5203c8f..45480cb 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -59,7 +59,16 @@
  *
  *  <p> Condition providers cannot be bound by the system on
  * {@link ActivityManager#isLowRamDevice() low ram} devices</p>
+ *
+ * @deprecated Instead of using an automatically bound service, use
+ * {@link android.app.NotificationManager#setAutomaticZenRuleState(String, Condition)} to tell the
+ * system about the state of your rule. In order to maintain a link from
+ * Settings to your rule configuration screens, provide a configuration activity that handles
+ * {@link android.app.NotificationManager#ACTION_AUTOMATIC_ZEN_RULE} on your
+ * {@link android.app.AutomaticZenRule} via
+ * {@link android.app.AutomaticZenRule#setConfigurationActivity(ComponentName)}.
  */
+@Deprecated
 public abstract class ConditionProviderService extends Service {
     private final String TAG = ConditionProviderService.class.getSimpleName()
             + "[" + getClass().getSimpleName() + "]";
@@ -79,26 +88,38 @@
     /**
      * The name of the {@code meta-data} tag containing a localized name of the type of zen rules
      * provided by this service.
+     *
+     * @deprecated see {@link android.app.NotificationManager#META_DATA_AUTOMATIC_RULE_TYPE}.
      */
+    @Deprecated
     public static final String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
 
     /**
      * The name of the {@code meta-data} tag containing the {@link ComponentName} of an activity
      * that allows users to configure the conditions provided by this service.
+     *
+     * @deprecated see {@link android.app.NotificationManager#ACTION_AUTOMATIC_ZEN_RULE}.
      */
+    @Deprecated
     public static final String META_DATA_CONFIGURATION_ACTIVITY =
             "android.service.zen.automatic.configurationActivity";
 
     /**
      * The name of the {@code meta-data} tag containing the maximum number of rule instances that
      * can be created for this rule type. Omit or enter a value <= 0 to allow unlimited instances.
+     *
+     * @deprecated see {@link android.app.NotificationManager#META_DATA_RULE_INSTANCE_LIMIT}.
      */
+    @Deprecated
     public static final String META_DATA_RULE_INSTANCE_LIMIT =
             "android.service.zen.automatic.ruleInstanceLimit";
 
     /**
      * A String rule id extra passed to {@link #META_DATA_CONFIGURATION_ACTIVITY}.
+     *
+     * @deprecated see {@link android.app.NotificationManager#EXTRA_AUTOMATIC_RULE_ID}.
      */
+    @Deprecated
     public static final String EXTRA_RULE_ID = "android.service.notification.extra.RULE_ID";
 
     /**
@@ -171,7 +192,11 @@
      * service that has an {@link android.app.AutomaticZenRule#getConditionId()} equal to this
      * {@link Condition#id}.
      * @param condition the condition that has changed.
+     *
+     * @deprecated see
+     * {@link android.app.NotificationManager#setAutomaticZenRuleState(String, Condition)}.
      */
+    @Deprecated
     public final void notifyCondition(Condition condition) {
         if (condition == null) return;
         notifyConditions(new Condition[]{ condition });
@@ -181,7 +206,11 @@
      * Informs the notification manager that the state of one or more Conditions has changed. See
      * {@link #notifyCondition(Condition)} for restrictions.
      * @param conditions the changed conditions.
+     *
+     * @deprecated see
+     *       {@link android.app.NotificationManager#setAutomaticZenRuleState(String, Condition)}.
      */
+    @Deprecated
     public final void notifyConditions(Condition... conditions) {
         if (!isBound() || conditions == null) return;
         try {
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index c850a4e..dd0242a 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -19,7 +19,7 @@
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.annotation.IntDef;
-import android.annotation.Nullable;
+import android.annotation.NonNull;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
@@ -179,13 +179,13 @@
      * @param isExpanded whether the notification is expanded.
      */
     public void onNotificationExpansionChanged(
-            String key, boolean isUserAction, boolean isExpanded) {}
+            @NonNull String key, boolean isUserAction, boolean isExpanded) {}
 
     /**
      * Implement this to know when a direct reply is sent from a notification.
      * @param key the notification key
      */
-    public void onNotificationDirectReply(String key) {}
+    public void onNotificationDirectReply(@NonNull String key) {}
 
     /**
      * Implement this to know when a suggested reply is sent.
@@ -193,7 +193,9 @@
      * @param reply the reply that is just sent
      * @param source the source that provided the reply, e.g. SOURCE_FROM_APP
      */
-    public void onSuggestedReplySent(String key, CharSequence reply, @Source int source) {}
+    public void onSuggestedReplySent(@NonNull String key, @NonNull CharSequence reply,
+            @Source int source) {
+    }
 
     /**
      * Implement this to know when an action is clicked.
@@ -201,7 +203,8 @@
      * @param action the action that is just clicked
      * @param source the source that provided the action, e.g. SOURCE_FROM_APP
      */
-    public void onActionClicked(String key, @Nullable Notification.Action action, int source) {
+    public void onActionClicked(@NonNull String key, @NonNull Notification.Action action,
+            @Source int source) {
     }
 
     /**
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 1fe97b7..0e63cd3 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -98,7 +98,7 @@
  */
 public abstract class NotificationListenerService extends Service {
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final String TAG = getClass().getSimpleName();
 
     /**
@@ -294,7 +294,7 @@
     /**
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     protected INotificationManager mNoMan;
 
     /**
@@ -516,7 +516,7 @@
     }
 
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     protected final INotificationManager getNotificationInterface() {
         if (mNoMan == null) {
             mNoMan = INotificationManager.Stub.asInterface(
@@ -1482,7 +1482,7 @@
         private boolean mShowBadge;
         private @UserSentiment int mUserSentiment = USER_SENTIMENT_NEUTRAL;
         private boolean mHidden;
-        private boolean mAudiblyAlerted;
+        private long mLastAudiblyAlertedMs;
         private boolean mNoisy;
         private ArrayList<Notification.Action> mSmartActions;
         private ArrayList<CharSequence> mSmartReplies;
@@ -1650,12 +1650,12 @@
         }
 
         /**
-         * Returns whether this notification alerted the user via sound or vibration.
+         * Returns the last time this notification alerted the user via sound or vibration.
          *
-         * @return true if the notification alerted the user, false otherwise.
+         * @return the time of the last alerting behavior, in milliseconds.
          */
-        public boolean audiblyAlerted() {
-            return mAudiblyAlerted;
+        public long getLastAudiblyAlertedMillis() {
+            return mLastAudiblyAlertedMs;
         }
 
         /** @hide */
@@ -1672,7 +1672,7 @@
                 CharSequence explanation, String overrideGroupKey,
                 NotificationChannel channel, ArrayList<String> overridePeople,
                 ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge,
-                int userSentiment, boolean hidden, boolean audiblyAlerted,
+                int userSentiment, boolean hidden, long lastAudiblyAlertedMs,
                 boolean noisy, ArrayList<Notification.Action> smartActions,
                 ArrayList<CharSequence> smartReplies) {
             mKey = key;
@@ -1690,7 +1690,7 @@
             mShowBadge = showBadge;
             mUserSentiment = userSentiment;
             mHidden = hidden;
-            mAudiblyAlerted = audiblyAlerted;
+            mLastAudiblyAlertedMs = lastAudiblyAlertedMs;
             mNoisy = noisy;
             mSmartActions = smartActions;
             mSmartReplies = smartReplies;
@@ -1743,7 +1743,7 @@
         private ArrayMap<String, Boolean> mShowBadge;
         private ArrayMap<String, Integer> mUserSentiment;
         private ArrayMap<String, Boolean> mHidden;
-        private ArrayMap<String, Boolean> mAudiblyAlerted;
+        private ArrayMap<String, Long> mLastAudiblyAlerted;
         private ArrayMap<String, Boolean> mNoisy;
         private ArrayMap<String, ArrayList<Notification.Action>> mSmartActions;
         private ArrayMap<String, ArrayList<CharSequence>> mSmartReplies;
@@ -1776,7 +1776,7 @@
                     getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key),
                     getChannel(key), getOverridePeople(key), getSnoozeCriteria(key),
                     getShowBadge(key), getUserSentiment(key), getHidden(key),
-                    getAudiblyAlerted(key), getNoisy(key), getSmartActions(key),
+                    getLastAudiblyAlerted(key), getNoisy(key), getSmartActions(key),
                     getSmartReplies(key));
             return rank >= 0;
         }
@@ -1915,14 +1915,14 @@
             return hidden == null ? false : hidden.booleanValue();
         }
 
-        private boolean getAudiblyAlerted(String key) {
+        private long getLastAudiblyAlerted(String key) {
             synchronized (this) {
-                if (mAudiblyAlerted == null) {
-                    buildAudiblyAlertedLocked();
+                if (mLastAudiblyAlerted == null) {
+                    buildLastAudiblyAlertedLocked();
                 }
             }
-            Boolean audiblyAlerted = mAudiblyAlerted.get(key);
-            return audiblyAlerted == null ? false : audiblyAlerted.booleanValue();
+            Long lastAudibleAlerted = mLastAudiblyAlerted.get(key);
+            return lastAudibleAlerted == null ? -1 : lastAudibleAlerted.longValue();
         }
 
         private boolean getNoisy(String key) {
@@ -1994,6 +1994,14 @@
             return newMap;
         }
 
+        private ArrayMap<String, Long> buildLongMapFromBundle(Bundle bundle) {
+            ArrayMap<String, Long> newMap = new ArrayMap<>(bundle.size());
+            for (String key : bundle.keySet()) {
+                newMap.put(key, bundle.getLong(key));
+            }
+            return newMap;
+        }
+
         // Locked by 'this'
         private void buildVisibilityOverridesLocked() {
             mVisibilityOverrides = buildIntMapFromBundle(mRankingUpdate.getVisibilityOverrides());
@@ -2070,8 +2078,8 @@
         }
 
         // Locked by 'this'
-        private void buildAudiblyAlertedLocked() {
-            mAudiblyAlerted = buildBooleanMapFromBundle(mRankingUpdate.getAudiblyAlerted());
+        private void buildLastAudiblyAlertedLocked() {
+            mLastAudiblyAlerted = buildLongMapFromBundle(mRankingUpdate.getLastAudiblyAlerted());
         }
 
         // Locked by 'this'
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index f80df93..ebaeff8 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -39,7 +39,7 @@
     private final Bundle mHidden;
     private final Bundle mSmartActions;
     private final Bundle mSmartReplies;
-    private final Bundle mAudiblyAlerted;
+    private final Bundle mLastAudiblyAlerted;
     private final Bundle mNoisy;
 
     public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
@@ -47,7 +47,7 @@
             int[] importance, Bundle explanation, Bundle overrideGroupKeys,
             Bundle channels, Bundle overridePeople, Bundle snoozeCriteria,
             Bundle showBadge, Bundle userSentiment, Bundle hidden, Bundle smartActions,
-            Bundle smartReplies, Bundle audiblyAlerted, Bundle noisy) {
+            Bundle smartReplies, Bundle lastAudiblyAlerted, Bundle noisy) {
         mKeys = keys;
         mInterceptedKeys = interceptedKeys;
         mVisibilityOverrides = visibilityOverrides;
@@ -63,7 +63,7 @@
         mHidden = hidden;
         mSmartActions = smartActions;
         mSmartReplies = smartReplies;
-        mAudiblyAlerted = audiblyAlerted;
+        mLastAudiblyAlerted = lastAudiblyAlerted;
         mNoisy = noisy;
     }
 
@@ -84,7 +84,7 @@
         mHidden = in.readBundle();
         mSmartActions = in.readBundle();
         mSmartReplies = in.readBundle();
-        mAudiblyAlerted = in.readBundle();
+        mLastAudiblyAlerted = in.readBundle();
         mNoisy = in.readBundle();
     }
 
@@ -110,7 +110,7 @@
         out.writeBundle(mHidden);
         out.writeBundle(mSmartActions);
         out.writeBundle(mSmartReplies);
-        out.writeBundle(mAudiblyAlerted);
+        out.writeBundle(mLastAudiblyAlerted);
         out.writeBundle(mNoisy);
     }
 
@@ -185,8 +185,8 @@
         return mSmartReplies;
     }
 
-    public Bundle getAudiblyAlerted() {
-        return mAudiblyAlerted;
+    public Bundle getLastAudiblyAlerted() {
+        return mLastAudiblyAlerted;
     }
 
     public Bundle getNoisy() {
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 84826e0..ad10cc9 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
@@ -35,7 +36,7 @@
     private final String pkg;
     @UnsupportedAppUsage
     private final int id;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final String tag;
     private final String key;
     private String groupKey;
@@ -44,13 +45,13 @@
     @UnsupportedAppUsage
     private final int uid;
     private final String opPkg;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final int initialPid;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final Notification notification;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final UserHandle user;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final long postTime;
 
     private Context mContext; // used for inflation & icon expansion
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 0e2ae83..bec654a 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -16,7 +16,6 @@
 
 package android.service.notification;
 
-import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
@@ -126,6 +125,15 @@
     private static final String STATE_TAG = "state";
     private static final String STATE_ATT_CHANNELS_BYPASSING_DND = "areChannelsBypassingDnd";
 
+    // zen policy visual effects attributes
+    private static final String SHOW_ATT_FULL_SCREEN_INTENT = "showFullScreenIntent";
+    private static final String SHOW_ATT_LIGHTS = "showLights";
+    private static final String SHOW_ATT_PEEK = "shoePeek";
+    private static final String SHOW_ATT_STATUS_BAR_ICONS = "showStatusBarIcons";
+    private static final String SHOW_ATT_BADGES = "showBadges";
+    private static final String SHOW_ATT_AMBIENT = "showAmbient";
+    private static final String SHOW_ATT_NOTIFICATION_LIST = "showNotificationList";
+
     private static final String CONDITION_ATT_ID = "id";
     private static final String CONDITION_ATT_SUMMARY = "summary";
     private static final String CONDITION_ATT_LINE1 = "line1";
@@ -134,6 +142,8 @@
     private static final String CONDITION_ATT_STATE = "state";
     private static final String CONDITION_ATT_FLAGS = "flags";
 
+    private static final String ZEN_POLICY_TAG = "zen_policy";
+
     private static final String MANUAL_TAG = "manual";
     private static final String AUTOMATIC_TAG = "automatic";
 
@@ -142,6 +152,7 @@
     private static final String RULE_ATT_SNOOZING = "snoozing";
     private static final String RULE_ATT_NAME = "name";
     private static final String RULE_ATT_COMPONENT = "component";
+    private static final String RULE_ATT_CONFIG_ACTIVITY = "configActivity";
     private static final String RULE_ATT_ZEN = "zen";
     private static final String RULE_ATT_CONDITION_ID = "conditionId";
     private static final String RULE_ATT_CREATION_TIME = "creationTime";
@@ -269,7 +280,7 @@
         return buffer.toString();
     }
 
-    private Diff diff(ZenModeConfig to) {
+    public Diff diff(ZenModeConfig to) {
         final Diff d = new Diff();
         if (to == null) {
             return d.addLine("config", "delete");
@@ -623,7 +634,6 @@
     public static ZenRule readRuleXml(XmlPullParser parser) {
         final ZenRule rt = new ZenRule();
         rt.enabled = safeBoolean(parser, RULE_ATT_ENABLED, true);
-        rt.snoozing = safeBoolean(parser, RULE_ATT_SNOOZING, false);
         rt.name = parser.getAttributeValue(null, RULE_ATT_NAME);
         final String zen = parser.getAttributeValue(null, RULE_ATT_ZEN);
         rt.zenMode = tryParseZenMode(zen, -1);
@@ -633,6 +643,12 @@
         }
         rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID);
         rt.component = safeComponentName(parser, RULE_ATT_COMPONENT);
+        rt.configurationActivity = safeComponentName(parser, RULE_ATT_CONFIG_ACTIVITY);
+        rt.pkg = (rt.component != null)
+                ? rt.component.getPackageName()
+                : (rt.configurationActivity != null)
+                        ? rt.configurationActivity.getPackageName()
+                        : null;
         rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0);
         rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER);
         rt.condition = readConditionXml(parser);
@@ -644,12 +660,12 @@
             rt.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         }
         rt.modified = safeBoolean(parser, RULE_ATT_MODIFIED, false);
+        rt.zenPolicy = readZenPolicyXml(parser);
         return rt;
     }
 
     public static void writeRuleXml(ZenRule rule, XmlSerializer out) throws IOException {
         out.attribute(null, RULE_ATT_ENABLED, Boolean.toString(rule.enabled));
-        out.attribute(null, RULE_ATT_SNOOZING, Boolean.toString(rule.snoozing));
         if (rule.name != null) {
             out.attribute(null, RULE_ATT_NAME, rule.name);
         }
@@ -657,6 +673,10 @@
         if (rule.component != null) {
             out.attribute(null, RULE_ATT_COMPONENT, rule.component.flattenToString());
         }
+        if (rule.configurationActivity != null) {
+            out.attribute(null, RULE_ATT_CONFIG_ACTIVITY,
+                    rule.configurationActivity.flattenToString());
+        }
         if (rule.conditionId != null) {
             out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString());
         }
@@ -667,6 +687,9 @@
         if (rule.condition != null) {
             writeConditionXml(rule.condition, out);
         }
+        if (rule.zenPolicy != null) {
+            writeZenPolicyXml(rule.zenPolicy, out);
+        }
         out.attribute(null, RULE_ATT_MODIFIED, Boolean.toString(rule.modified));
     }
 
@@ -697,6 +720,141 @@
         out.attribute(null, CONDITION_ATT_FLAGS, Integer.toString(c.flags));
     }
 
+    /**
+     * Read the zen policy from xml
+     * Returns null if no zen policy exists
+     */
+    public static ZenPolicy readZenPolicyXml(XmlPullParser parser) {
+        boolean policySet = false;
+
+        ZenPolicy.Builder builder = new ZenPolicy.Builder();
+        final int calls = safeInt(parser, ALLOW_ATT_CALLS_FROM, ZenPolicy.PEOPLE_TYPE_UNSET);
+        final int messages = safeInt(parser, ALLOW_ATT_MESSAGES_FROM, ZenPolicy.PEOPLE_TYPE_UNSET);
+        final int repeatCallers = safeInt(parser, ALLOW_ATT_REPEAT_CALLERS, ZenPolicy.STATE_UNSET);
+        final int alarms = safeInt(parser, ALLOW_ATT_ALARMS, ZenPolicy.STATE_UNSET);
+        final int media = safeInt(parser, ALLOW_ATT_MEDIA, ZenPolicy.STATE_UNSET);
+        final int system = safeInt(parser, ALLOW_ATT_SYSTEM, ZenPolicy.STATE_UNSET);
+        final int events = safeInt(parser, ALLOW_ATT_EVENTS, ZenPolicy.STATE_UNSET);
+        final int reminders = safeInt(parser, ALLOW_ATT_REMINDERS, ZenPolicy.STATE_UNSET);
+
+        if (calls != ZenPolicy.PEOPLE_TYPE_UNSET) {
+            builder.allowCalls(calls);
+            policySet = true;
+        }
+        if (messages != ZenPolicy.PEOPLE_TYPE_UNSET) {
+            builder.allowMessages(messages);
+            policySet = true;
+        }
+        if (repeatCallers != ZenPolicy.STATE_UNSET) {
+            builder.allowRepeatCallers(repeatCallers == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+        if (alarms != ZenPolicy.STATE_UNSET) {
+            builder.allowAlarms(alarms == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+        if (media != ZenPolicy.STATE_UNSET) {
+            builder.allowMedia(media == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+        if (system != ZenPolicy.STATE_UNSET) {
+            builder.allowSystem(system == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+        if (events != ZenPolicy.STATE_UNSET) {
+            builder.allowEvents(events == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+        if (reminders != ZenPolicy.STATE_UNSET) {
+            builder.allowReminders(reminders == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+
+        final int fullScreenIntent = safeInt(parser, SHOW_ATT_FULL_SCREEN_INTENT,
+                ZenPolicy.STATE_UNSET);
+        final int lights = safeInt(parser, SHOW_ATT_LIGHTS, ZenPolicy.STATE_UNSET);
+        final int peek = safeInt(parser, SHOW_ATT_PEEK, ZenPolicy.STATE_UNSET);
+        final int statusBar = safeInt(parser, SHOW_ATT_STATUS_BAR_ICONS, ZenPolicy.STATE_UNSET);
+        final int badges = safeInt(parser, SHOW_ATT_BADGES, ZenPolicy.STATE_UNSET);
+        final int ambient = safeInt(parser, SHOW_ATT_AMBIENT, ZenPolicy.STATE_UNSET);
+        final int notificationList = safeInt(parser, SHOW_ATT_NOTIFICATION_LIST,
+                ZenPolicy.STATE_UNSET);
+
+        if (fullScreenIntent != ZenPolicy.STATE_UNSET) {
+            builder.showFullScreenIntent(fullScreenIntent == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+        if (lights != ZenPolicy.STATE_UNSET) {
+            builder.showLights(lights == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+        if (peek != ZenPolicy.STATE_UNSET) {
+            builder.showPeeking(peek == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+        if (statusBar != ZenPolicy.STATE_UNSET) {
+            builder.showStatusBarIcons(statusBar == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+        if (badges != ZenPolicy.STATE_UNSET) {
+            builder.showBadges(badges == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+        if (ambient != ZenPolicy.STATE_UNSET) {
+            builder.showInAmbientDisplay(ambient == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+        if (notificationList != ZenPolicy.STATE_UNSET) {
+            builder.showInNotificationList(notificationList == ZenPolicy.STATE_ALLOW);
+            policySet = true;
+        }
+
+        if (policySet) {
+            return builder.build();
+        }
+        return null;
+    }
+
+    /**
+     * Writes zen policy to xml
+     */
+    public static void writeZenPolicyXml(ZenPolicy policy, XmlSerializer out)
+            throws IOException {
+        writeZenPolicyState(ALLOW_ATT_CALLS_FROM, policy.getPriorityCallSenders(), out);
+        writeZenPolicyState(ALLOW_ATT_MESSAGES_FROM, policy.getPriorityMessageSenders(), out);
+        writeZenPolicyState(ALLOW_ATT_REPEAT_CALLERS, policy.getPriorityCategoryRepeatCallers(),
+                out);
+        writeZenPolicyState(ALLOW_ATT_ALARMS, policy.getPriorityCategoryAlarms(), out);
+        writeZenPolicyState(ALLOW_ATT_MEDIA, policy.getPriorityCategoryMedia(), out);
+        writeZenPolicyState(ALLOW_ATT_SYSTEM, policy.getPriorityCategorySystem(), out);
+        writeZenPolicyState(ALLOW_ATT_REMINDERS, policy.getPriorityCategoryReminders(), out);
+        writeZenPolicyState(ALLOW_ATT_EVENTS, policy.getPriorityCategoryEvents(), out);
+
+        writeZenPolicyState(SHOW_ATT_FULL_SCREEN_INTENT, policy.getVisualEffectFullScreenIntent(),
+                out);
+        writeZenPolicyState(SHOW_ATT_LIGHTS, policy.getVisualEffectLights(), out);
+        writeZenPolicyState(SHOW_ATT_PEEK, policy.getVisualEffectPeek(), out);
+        writeZenPolicyState(SHOW_ATT_STATUS_BAR_ICONS, policy.getVisualEffectStatusBar(), out);
+        writeZenPolicyState(SHOW_ATT_BADGES, policy.getVisualEffectBadge(), out);
+        writeZenPolicyState(SHOW_ATT_AMBIENT, policy.getVisualEffectAmbient(), out);
+        writeZenPolicyState(SHOW_ATT_NOTIFICATION_LIST, policy.getVisualEffectNotificationList(),
+                out);
+    }
+
+    private static void writeZenPolicyState(String attr, int val, XmlSerializer out)
+            throws IOException {
+        if (Objects.equals(attr, ALLOW_ATT_CALLS_FROM)
+                || Objects.equals(attr, ALLOW_ATT_MESSAGES_FROM)) {
+            if (val != ZenPolicy.PEOPLE_TYPE_UNSET) {
+                out.attribute(null, attr, Integer.toString(val));
+            }
+        } else {
+            if (val != ZenPolicy.STATE_UNSET) {
+                out.attribute(null, attr, Integer.toString(val));
+            }
+        }
+    }
+
     public static boolean isValidHour(int val) {
         return val >= 0 && val < 24;
     }
@@ -789,7 +947,7 @@
 
         if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_REMINDERS,
                 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REMINDERS, defaultPolicy))) {
-            priorityCategories |= PRIORITY_CATEGORY_REMINDERS;
+            priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
         }
 
         if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_EVENTS,
@@ -830,15 +988,30 @@
             priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM;
         }
 
-        if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT,
+        boolean suppressFullScreenIntent = !zenPolicy.isVisualEffectAllowed(
+                ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT,
                 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT,
-                        defaultPolicy))) {
+                        defaultPolicy));
+
+        boolean suppressLights = !zenPolicy.isVisualEffectAllowed(
+                ZenPolicy.VISUAL_EFFECT_LIGHTS,
+                isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_LIGHTS,
+                        defaultPolicy));
+
+        boolean suppressAmbient = !zenPolicy.isVisualEffectAllowed(
+                ZenPolicy.VISUAL_EFFECT_AMBIENT,
+                isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_AMBIENT,
+                        defaultPolicy));
+
+        if (suppressFullScreenIntent && suppressLights && suppressAmbient) {
+            suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
+        }
+
+        if (suppressFullScreenIntent) {
             suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
         }
 
-        if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_LIGHTS,
-                isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_LIGHTS,
-                        defaultPolicy))) {
+        if (suppressLights) {
             suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
         }
 
@@ -846,6 +1019,7 @@
                 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_PEEK,
                         defaultPolicy))) {
             suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_PEEK;
+            suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_ON;
         }
 
         if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_STATUS_BAR,
@@ -860,9 +1034,7 @@
             suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE;
         }
 
-        if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_AMBIENT,
-                isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_AMBIENT,
-                        defaultPolicy))) {
+        if (suppressAmbient) {
             suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
         }
 
@@ -889,14 +1061,31 @@
             case ZenPolicy.PEOPLE_TYPE_ANYONE:
                 return Policy.PRIORITY_SENDERS_ANY;
             case ZenPolicy.PEOPLE_TYPE_CONTACTS:
+
                 return Policy.PRIORITY_SENDERS_CONTACTS;
             case ZenPolicy.PEOPLE_TYPE_STARRED:
             default:
                 return Policy.PRIORITY_SENDERS_STARRED;
         }
-
     }
 
+
+    /**
+     * Maps NotificationManager.Policy senders type to ZenPolicy.PeopleType
+     */
+    public static @ZenPolicy.PeopleType int getZenPolicySenders(int senders) {
+        switch (senders) {
+            case Policy.PRIORITY_SENDERS_ANY:
+                return ZenPolicy.PEOPLE_TYPE_ANYONE;
+            case Policy.PRIORITY_SENDERS_CONTACTS:
+                return ZenPolicy.PEOPLE_TYPE_CONTACTS;
+            case Policy.PRIORITY_SENDERS_STARRED:
+            default:
+                return ZenPolicy.PEOPLE_TYPE_STARRED;
+        }
+    }
+
+
     public Policy toNotificationPolicy() {
         int priorityCategories = 0;
         int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS;
@@ -1452,12 +1641,15 @@
         public Uri conditionId;          // required for automatic
         public Condition condition;      // optional
         public ComponentName component;  // optional
+        public ComponentName configurationActivity; // optional
         public String id;                // required for automatic (unique)
         @UnsupportedAppUsage
         public long creationTime;        // required for automatic
-        public String enabler;          // package name, only used for manual rules.
+        // package name, only used for manual rules when they have turned DND on.
+        public String enabler;
         public ZenPolicy zenPolicy;
         public boolean modified;    // rule has been modified from initial creation
+        public String pkg;
 
         public ZenRule() { }
 
@@ -1471,6 +1663,7 @@
             conditionId = source.readParcelable(null);
             condition = source.readParcelable(null);
             component = source.readParcelable(null);
+            configurationActivity = source.readParcelable(null);
             if (source.readInt() == 1) {
                 id = source.readString();
             }
@@ -1480,6 +1673,7 @@
             }
             zenPolicy = source.readParcelable(null);
             modified = source.readInt() == 1;
+            pkg = source.readString();
         }
 
         @Override
@@ -1501,6 +1695,7 @@
             dest.writeParcelable(conditionId, 0);
             dest.writeParcelable(condition, 0);
             dest.writeParcelable(component, 0);
+            dest.writeParcelable(configurationActivity, 0);
             if (id != null) {
                 dest.writeInt(1);
                 dest.writeString(id);
@@ -1516,6 +1711,7 @@
             }
             dest.writeParcelable(zenPolicy, 0);
             dest.writeInt(modified ? 1 : 0);
+            dest.writeString(pkg);
         }
 
         @Override
@@ -1528,7 +1724,9 @@
                     .append(",zenMode=").append(Global.zenModeToString(zenMode))
                     .append(",conditionId=").append(conditionId)
                     .append(",condition=").append(condition)
+                    .append(",pkg=").append(pkg)
                     .append(",component=").append(component)
+                    .append(",configActivity=").append(configurationActivity)
                     .append(",creationTime=").append(creationTime)
                     .append(",enabler=").append(enabler)
                     .append(",zenPolicy=").append(zenPolicy)
@@ -1537,6 +1735,7 @@
         }
 
         /** @hide */
+        // TODO: add configuration activity
         public void writeToProto(ProtoOutputStream proto, long fieldId) {
             final long token = proto.start(fieldId);
 
@@ -1600,6 +1799,9 @@
             if (!Objects.equals(component, to.component)) {
                 d.addLine(item, "component", component, to.component);
             }
+            if (!Objects.equals(configurationActivity, to.configurationActivity)) {
+                d.addLine(item, "configActivity", configurationActivity, to.configurationActivity);
+            }
             if (!Objects.equals(id, to.id)) {
                 d.addLine(item, "id", id, to.id);
             }
@@ -1615,6 +1817,9 @@
             if (modified != to.modified) {
                 d.addLine(item, "modified", modified, to.modified);
             }
+            if (pkg != to.pkg) {
+                d.addLine(item, "pkg", pkg, to.pkg);
+            }
         }
 
         @Override
@@ -1629,20 +1834,22 @@
                     && Objects.equals(other.conditionId, conditionId)
                     && Objects.equals(other.condition, condition)
                     && Objects.equals(other.component, component)
+                    && Objects.equals(other.configurationActivity, configurationActivity)
                     && Objects.equals(other.id, id)
                     && Objects.equals(other.enabler, enabler)
                     && Objects.equals(other.zenPolicy, zenPolicy)
+                    && Objects.equals(other.pkg, pkg)
                     && other.modified == modified;
         }
 
         @Override
         public int hashCode() {
             return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
-                    component, id, enabler, zenPolicy, modified);
+                    component, configurationActivity, pkg, id, enabler, zenPolicy, modified);
         }
 
         public boolean isAutomaticActive() {
-            return enabled && !snoozing && component != null && isTrueOrUnknown();
+            return enabled && !snoozing && pkg != null && isTrueOrUnknown();
         }
 
         public boolean isTrueOrUnknown() {
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 194147c..6392704 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -327,6 +327,32 @@
     }
 
     /**
+     * Whether this policy hides all visual effects
+     * @hide
+     */
+    public boolean shouldHideAllVisualEffects() {
+        for (int i = 0; i < mVisualEffects.size(); i++) {
+            if (mVisualEffects.get(i) != STATE_DISALLOW) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Whether this policy shows all visual effects
+     * @hide
+     */
+    public boolean shouldShowAllVisualEffects() {
+        for (int i = 0; i < mVisualEffects.size(); i++) {
+            if (mVisualEffects.get(i) != STATE_ALLOW) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
      * Builder class for {@link ZenPolicy} objects.
      * Provides a convenient way to set the various fields of a {@link ZenPolicy}.  If a field
      * is not set, it is (@link STATE_UNSET} and will not change the current set policy.
@@ -339,6 +365,17 @@
         }
 
         /**
+         * @hide
+         */
+        public Builder(ZenPolicy policy) {
+            if (policy != null) {
+                mZenPolicy = policy.copy();
+            } else {
+                mZenPolicy = new ZenPolicy();
+            }
+        }
+
+        /**
          * Builds the current ZenPolicy.
          */
         public ZenPolicy build() {
@@ -533,6 +570,34 @@
         }
 
         /**
+         * Whether to allow {@link PriorityCategory} sounds to play when DND is active.
+         * @hide
+         */
+        public Builder allowCategory(@PriorityCategory int category, boolean allow) {
+            switch (category) {
+                case PRIORITY_CATEGORY_ALARMS:
+                    allowAlarms(allow);
+                    break;
+                case PRIORITY_CATEGORY_MEDIA:
+                    allowMedia(allow);
+                    break;
+                case PRIORITY_CATEGORY_SYSTEM:
+                    allowSystem(allow);
+                    break;
+                case PRIORITY_CATEGORY_REMINDERS:
+                    allowReminders(allow);
+                    break;
+                case PRIORITY_CATEGORY_EVENTS:
+                    allowEvents(allow);
+                    break;
+                case PRIORITY_CATEGORY_REPEAT_CALLERS:
+                    allowRepeatCallers(allow);
+                    break;
+            }
+            return this;
+        }
+
+        /**
          * Whether {@link Notification#fullScreenIntent full screen intents} that are intercepted
          * by DND are shown.
          */
@@ -601,6 +666,38 @@
                     show ? STATE_ALLOW : STATE_DISALLOW);
             return this;
         }
+
+        /**
+         * Whether notifications intercepted by DND are prevented from appearing for
+         * {@link VisualEffect}
+         * @hide
+         */
+        public Builder showVisualEffect(@VisualEffect int effect, boolean show) {
+            switch (effect) {
+                case VISUAL_EFFECT_FULL_SCREEN_INTENT:
+                    showFullScreenIntent(show);
+                    break;
+                case VISUAL_EFFECT_LIGHTS:
+                    showLights(show);
+                    break;
+                case VISUAL_EFFECT_PEEK:
+                    showPeeking(show);
+                    break;
+                case VISUAL_EFFECT_STATUS_BAR:
+                    showStatusBarIcons(show);
+                    break;
+                case VISUAL_EFFECT_BADGE:
+                    showBadges(show);
+                    break;
+                case VISUAL_EFFECT_AMBIENT:
+                    showInAmbientDisplay(show);
+                    break;
+                case VISUAL_EFFECT_NOTIFICATION_LIST:
+                    showInNotificationList(show);
+                    break;
+            }
+            return this;
+        }
     }
 
     @Override
@@ -640,8 +737,8 @@
                 .append('{')
                 .append("priorityCategories=[").append(priorityCategoriesToString())
                 .append("], visualEffects=[").append(visualEffectsToString())
-                .append(", priorityCalls=").append(stateToString(mPriorityCalls))
-                .append("], priorityMessages=").append(stateToString(mPriorityMessages))
+                .append("], priorityCalls=").append(peopleTypeToString(mPriorityCalls))
+                .append(", priorityMessages=").append(peopleTypeToString(mPriorityMessages))
                 .append('}')
                 .toString();
     }
@@ -726,7 +823,23 @@
             case STATE_ALLOW:
                 return "allow";
         }
-        return null;
+        return "invalidState{" + state + "}";
+    }
+
+    private String peopleTypeToString(@PeopleType int peopleType) {
+        switch (peopleType) {
+            case PEOPLE_TYPE_ANYONE:
+                return "anyone";
+            case PEOPLE_TYPE_CONTACTS:
+                return "contacts";
+            case PEOPLE_TYPE_NONE:
+                return "none";
+            case PEOPLE_TYPE_STARRED:
+                return "starred_contacts";
+            case STATE_UNSET:
+                return "unset";
+        }
+        return "invalidPeopleType{" + peopleType + "}";
     }
 
     @Override
@@ -859,27 +972,6 @@
     /**
      * @hide
      */
-    public boolean areValuesSet() {
-        return getPriorityCategoryReminders() != STATE_UNSET
-                || getPriorityCategoryEvents() != STATE_UNSET
-                || getPriorityCategoryMessages() != STATE_UNSET
-                || getPriorityCategoryCalls() != STATE_UNSET
-                || getPriorityCategoryRepeatCallers() != STATE_UNSET
-                || getPriorityCategoryAlarms() != STATE_UNSET
-                || getPriorityCategoryMedia() != STATE_UNSET
-                || getPriorityCategorySystem() != STATE_UNSET
-                || getVisualEffectFullScreenIntent() != STATE_UNSET
-                || getVisualEffectLights() != STATE_UNSET
-                || getVisualEffectPeek() != STATE_UNSET
-                || getVisualEffectStatusBar() != STATE_UNSET
-                || getVisualEffectBadge() != STATE_UNSET
-                || getVisualEffectAmbient() != STATE_UNSET
-                || getVisualEffectNotificationList() != STATE_UNSET;
-    }
-
-    /**
-     * @hide
-     */
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
 
diff --git a/core/java/android/service/quicksettings/Tile.java b/core/java/android/service/quicksettings/Tile.java
index 4b81a72..6b569cf 100644
--- a/core/java/android/service/quicksettings/Tile.java
+++ b/core/java/android/service/quicksettings/Tile.java
@@ -15,6 +15,7 @@
  */
 package android.service.quicksettings;
 
+import android.annotation.Nullable;
 import android.graphics.drawable.Icon;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -62,6 +63,7 @@
     private IBinder mToken;
     private Icon mIcon;
     private CharSequence mLabel;
+    private CharSequence mSubtitle;
     private CharSequence mContentDescription;
     // Default to active until clients of the new API can update.
     private int mState = STATE_ACTIVE;
@@ -152,6 +154,22 @@
     }
 
     /**
+     * Gets the current subtitle for the tile.
+     */
+    @Nullable
+    public CharSequence getSubtitle() {
+        return mSubtitle;
+    }
+
+    /**
+     * Set the subtitle for the tile. Will be displayed as the secondary label.
+     * @param subtitle the subtitle to show.
+     */
+    public void setSubtitle(@Nullable CharSequence subtitle) {
+        this.mSubtitle = subtitle;
+    }
+
+    /**
      * Gets the current content description for the tile.
      */
     public CharSequence getContentDescription() {
@@ -195,6 +213,7 @@
         }
         dest.writeInt(mState);
         TextUtils.writeToParcel(mLabel, dest, flags);
+        TextUtils.writeToParcel(mSubtitle, dest, flags);
         TextUtils.writeToParcel(mContentDescription, dest, flags);
     }
 
@@ -206,6 +225,7 @@
         }
         mState = source.readInt();
         mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+        mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
         mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
     }
 
diff --git a/core/java/android/service/wallpaper/IWallpaperConnection.aidl b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
index a976d0e..f334d9d 100644
--- a/core/java/android/service/wallpaper/IWallpaperConnection.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
@@ -27,5 +27,5 @@
     void attachEngine(IWallpaperEngine engine, int displayId);
     void engineShown(IWallpaperEngine engine);
     ParcelFileDescriptor setWallpaper(String name);
-    void onWallpaperColorsChanged(in WallpaperColors colors);
+    void onWallpaperColorsChanged(in WallpaperColors colors, int displayId);
 }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index a095b0d..984846e 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -25,6 +25,7 @@
 import android.app.WallpaperColors;
 import android.app.WallpaperInfo;
 import android.app.WallpaperManager;
+import android.content.Context;
 import android.content.Intent;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
@@ -34,6 +35,7 @@
 import android.graphics.drawable.Drawable;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -52,12 +54,13 @@
 import android.view.InputDevice;
 import android.view.InputEvent;
 import android.view.InputEventReceiver;
+import android.view.InsetsState;
 import android.view.MotionEvent;
+import android.view.SurfaceControl;
 import android.view.SurfaceHolder;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowInsets;
-import android.view.InsetsState;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 
@@ -196,7 +199,7 @@
 
         final Object mLock = new Object();
         boolean mOffsetMessageEnqueued;
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         float mPendingXOffset;
         float mPendingYOffset;
         float mPendingXOffsetStep;
@@ -211,9 +214,12 @@
         private final Supplier<Long> mClockFunction;
         private final Handler mHandler;
 
-        Display mDisplay;
+        private Display mDisplay;
+        private Context mDisplayContext;
         private int mDisplayState;
 
+        SurfaceControl mSurfaceControl = new SurfaceControl();
+
         final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
             {
                 mRequestedFormat = PixelFormat.RGBX_8888;
@@ -639,7 +645,7 @@
             try {
                 final WallpaperColors newColors = onComputeColors();
                 if (mConnection != null) {
-                    mConnection.onWallpaperColorsChanged(newColors);
+                    mConnection.onWallpaperColorsChanged(newColors, mDisplay.getDisplayId());
                 } else {
                     Log.w(TAG, "Can't notify system because wallpaper connection "
                             + "was not established.");
@@ -840,8 +846,12 @@
                         mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
                             View.VISIBLE, 0, -1, mWinFrame, mOverscanInsets, mContentInsets,
                             mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
-                            mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface,
+                            mDisplayCutout, mMergedConfiguration, mSurfaceControl,
                             mInsetsState);
+                    if (mSurfaceControl.isValid()) {
+                        mSurfaceHolder.mSurface.copyFrom(mSurfaceControl);
+                        mSurfaceControl.release();
+                    }
 
                     if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
                             + ", frame=" + mWinFrame);
@@ -1038,6 +1048,7 @@
             mIWallpaperEngine.mDisplayManager.registerDisplayListener(mDisplayListener,
                     mCaller.getHandler());
             mDisplay = mIWallpaperEngine.mDisplay;
+            mDisplayContext = createDisplayContext(mDisplay);
             mDisplayState = mDisplay.getState();
 
             if (DEBUG) Log.v(TAG, "onCreate(): " + this);
@@ -1050,6 +1061,18 @@
         }
 
         /**
+         * The {@link Context} with resources that match the current display the wallpaper is on.
+         * For multiple display environment, multiple engines can be created to render on each
+         * display, but these displays may have different densities. Use this context to get the
+         * corresponding resources for currently display, avoiding the context of the service.
+         *
+         * @return A {@link Context} for current display.
+         */
+        public Context getDisplayContext() {
+            return mDisplayContext;
+        }
+
+        /**
          * Executes life cycle event and updates internal ambient mode state based on
          * message sent from handler.
          *
@@ -1450,7 +1473,7 @@
                         break;
                     }
                     try {
-                        mConnection.onWallpaperColorsChanged(mEngine.onComputeColors());
+                        mConnection.onWallpaperColorsChanged(mEngine.onComputeColors(), mDisplayId);
                     } catch (RemoteException e) {
                         // Connection went away, nothing to do in here.
                     }
diff --git a/core/java/android/text/method/HideReturnsTransformationMethod.java b/core/java/android/text/method/HideReturnsTransformationMethod.java
index e753754..440a4b1 100644
--- a/core/java/android/text/method/HideReturnsTransformationMethod.java
+++ b/core/java/android/text/method/HideReturnsTransformationMethod.java
@@ -17,6 +17,7 @@
 package android.text.method;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 
 /**
  * This transformation method causes any carriage return characters (\r)
@@ -50,6 +51,6 @@
         return sInstance;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static HideReturnsTransformationMethod sInstance;
 }
diff --git a/core/java/android/text/method/PasswordTransformationMethod.java b/core/java/android/text/method/PasswordTransformationMethod.java
index 479fdf4..c96fc5d 100644
--- a/core/java/android/text/method/PasswordTransformationMethod.java
+++ b/core/java/android/text/method/PasswordTransformationMethod.java
@@ -18,6 +18,7 @@
 
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.Rect;
+import android.os.Build;
 import android.os.Handler;
 import android.os.SystemClock;
 import android.text.Editable;
@@ -264,6 +265,6 @@
 
     @UnsupportedAppUsage
     private static PasswordTransformationMethod sInstance;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static char DOT = '\u2022';
 }
diff --git a/core/java/android/text/style/BulletSpan.java b/core/java/android/text/style/BulletSpan.java
index 679698b..9b1dfbf 100644
--- a/core/java/android/text/style/BulletSpan.java
+++ b/core/java/android/text/style/BulletSpan.java
@@ -24,6 +24,7 @@
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.os.Build;
 import android.os.Parcel;
 import android.text.Layout;
 import android.text.ParcelableSpan;
@@ -69,14 +70,14 @@
     private static final int STANDARD_COLOR = 0;
 
     @Px
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final int mGapWidth;
     @Px
     private final int mBulletRadius;
     @ColorInt
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final int mColor;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final boolean mWantColor;
 
     /**
diff --git a/core/java/android/text/style/LineBackgroundSpan.java b/core/java/android/text/style/LineBackgroundSpan.java
index 5a55fd7..e43fd83 100644
--- a/core/java/android/text/style/LineBackgroundSpan.java
+++ b/core/java/android/text/style/LineBackgroundSpan.java
@@ -118,7 +118,7 @@
                 int lineNumber) {
             final int originColor = paint.getColor();
             paint.setColor(mColor);
-            canvas.drawRect(left, right, top, bottom, paint);
+            canvas.drawRect(left, top, right, bottom, paint);
             paint.setColor(originColor);
         }
     }
diff --git a/core/java/android/transition/Scene.java b/core/java/android/transition/Scene.java
index b1fc17a..8d4db54 100644
--- a/core/java/android/transition/Scene.java
+++ b/core/java/android/transition/Scene.java
@@ -96,7 +96,7 @@
      * the hierarchy specified by the layoutId resource file.
      *
      * <p>This method is hidden because layoutId-based scenes should be
-     * created by the caching factory method {@link Scene#getCurrentScene(View)}.</p>
+     * created by the caching factory method {@link Scene#getCurrentScene(ViewGroup)}.</p>
      *
      * @param sceneRoot The root of the hierarchy in which scene changes
      * and transitions will take place.
@@ -194,28 +194,28 @@
     }
 
     /**
-     * Set the scene that the given view is in. The current scene is set only
-     * on the root view of a scene, not for every view in that hierarchy. This
+     * Set the scene that the given ViewGroup is in. The current scene is set only
+     * on the root ViewGroup of a scene, not for every view in that hierarchy. This
      * information is used by Scene to determine whether there is a previous
      * scene which should be exited before the new scene is entered.
      *
-     * @param sceneRoot The view on which the current scene is being set
+     * @param sceneRoot The ViewGroup on which the current scene is being set
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
-    static void setCurrentScene(@NonNull View sceneRoot, @Nullable Scene scene) {
+    static void setCurrentScene(@NonNull ViewGroup sceneRoot, @Nullable Scene scene) {
         sceneRoot.setTagInternal(com.android.internal.R.id.current_scene, scene);
     }
 
     /**
-     * Gets the current {@link Scene} set on the given view. A scene is set on a view
-     * only if that view is the scene root.
+     * Gets the current {@link Scene} set on the given ViewGroup. A scene is set on a ViewGroup
+     * only if that ViewGroup is the scene root.
      *
-     * @param sceneRoot The view on which the current scene will be returned
-     * @return The current Scene set on this view. A value of null indicates that
+     * @param sceneRoot The ViewGroup on which the current scene will be returned
+     * @return The current Scene set on this ViewGroup. A value of null indicates that
      * no Scene is currently set.
      */
     @Nullable
-    public static Scene getCurrentScene(@NonNull View sceneRoot) {
+    public static Scene getCurrentScene(@NonNull ViewGroup sceneRoot) {
         return (Scene) sceneRoot.getTag(com.android.internal.R.id.current_scene);
     }
 
diff --git a/core/java/android/util/DebugUtils.java b/core/java/android/util/DebugUtils.java
index e87fcff..af73a16 100644
--- a/core/java/android/util/DebugUtils.java
+++ b/core/java/android/util/DebugUtils.java
@@ -17,6 +17,7 @@
 package android.util;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import java.io.PrintWriter;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
@@ -109,7 +110,7 @@
     }
 
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static void buildShortClassTag(Object cls, StringBuilder out) {
         if (cls == null) {
             out.append("null");
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index ade7577..8b97e0e 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -46,6 +46,7 @@
         DEFAULT_FLAGS.put("settings_mobile_network_v2", "true");
         DEFAULT_FLAGS.put("settings_network_and_internet_v2", "false");
         DEFAULT_FLAGS.put("settings_seamless_transfer", "false");
+        DEFAULT_FLAGS.put("settings_slice_injection", "false");
         DEFAULT_FLAGS.put("settings_systemui_theme", "true");
         DEFAULT_FLAGS.put("settings_wifi_dpp", "false");
         DEFAULT_FLAGS.put("settings_wifi_mac_randomization", "false");
diff --git a/core/java/android/util/Range.java b/core/java/android/util/Range.java
index 5524506..f31ddd9 100644
--- a/core/java/android/util/Range.java
+++ b/core/java/android/util/Range.java
@@ -28,7 +28,7 @@
  * "integers from 1 to 100 inclusive."
  * </p>
  * <p>
- * All ranges are bounded, and the left side of the range is always {@code >=}
+ * All ranges are bounded, and the left side of the range is always {@code <=}
  * the right side of the range.
  * </p>
  *
diff --git a/core/java/android/util/Slog.java b/core/java/android/util/Slog.java
index c9fc3f2..a85120f 100644
--- a/core/java/android/util/Slog.java
+++ b/core/java/android/util/Slog.java
@@ -17,6 +17,7 @@
 package android.util;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 
 /**
  * @hide
@@ -105,7 +106,7 @@
      * will always be handled asynchronously.  Primarily for use by coding running within
      * the system process.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static int wtfStack(String tag, String msg) {
         return Log.wtf(Log.LOG_ID_SYSTEM, tag, msg, null, true, true);
     }
diff --git a/core/java/android/util/SparseSetArray.java b/core/java/android/util/SparseSetArray.java
index d100f12..680e85f 100644
--- a/core/java/android/util/SparseSetArray.java
+++ b/core/java/android/util/SparseSetArray.java
@@ -55,6 +55,13 @@
     }
 
     /**
+     * @return the set of items at index n
+     */
+    public ArraySet<T> get(int n) {
+        return mData.get(n);
+    }
+
+    /**
      * Remove a value from index n.
      * @return TRUE when the value existed at the given index and removed, FALSE otherwise.
      */
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 57d55bf..91bc3eb 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.SystemClock;
 
 import libcore.timezone.CountryTimeZones;
@@ -289,7 +290,7 @@
     }
 
     /** @hide Just for debugging; not internationalized. */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static void formatDuration(long duration, PrintWriter pw, int fieldLen) {
         synchronized (sFormatSync) {
             int len = formatDurationLocked(duration, fieldLen);
@@ -306,7 +307,7 @@
     }
 
     /** @hide Just for debugging; not internationalized. */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static void formatDuration(long duration, PrintWriter pw) {
         formatDuration(duration, pw, 0);
     }
diff --git a/core/java/android/view/AccessibilityIterators.java b/core/java/android/view/AccessibilityIterators.java
index 9f7560c..54cfc00 100644
--- a/core/java/android/view/AccessibilityIterators.java
+++ b/core/java/android/view/AccessibilityIterators.java
@@ -147,6 +147,9 @@
         @Override
         public void onConfigurationChanged(Configuration globalConfig) {
             final Locale locale = globalConfig.getLocales().get(0);
+            if (locale == null) {
+                return;
+            }
             if (!mLocale.equals(locale)) {
                 mLocale = locale;
                 onLocaleChanged(locale);
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 5e6d3d1..c06a1fe 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -71,41 +71,18 @@
     void dispatchGetNewSurface();
 
     /**
-     * Tell the window that it is either gaining or losing focus.
-     *
-     * @param hasFocus       {@code true} if window has focus, {@code false} otherwise.
-     * @param inTouchMode    {@code true} if screen is in touch mode, {@code false} otherwise.
-     * @param reportToClient {@code true} when need to report to child view with
-     *                       {@link View#onWindowFocusChanged(boolean)}, {@code false} otherwise.
-     * <p>
-     * Note: In the previous design, there is only one window focus state tracked by
-     * WindowManagerService.
-     * For multi-display, the window focus state is tracked by each display independently.
-     * <p>
-     * It will introduce a problem if the window was already focused on one display and then
-     * switched to another display, since the window focus state on each display is independent,
-     * there is no global window focus state in WindowManagerService, so the window focus state of
-     * the former display remains unchanged.
-     * <p>
-     * When switched back to former display, some flows that rely on the global window focus state
-     * in view root will be missed due to the window focus state remaining unchanged.
-     * (i.e: Showing single IME window when switching between displays.)
-     * <p>
-     * To solve the problem, WindowManagerService tracks the top focused display change and then
-     * callbacks to the client via this method to make sure that the client side will request the
-     * IME on the top focused display, and then set {@param reportToClient} as {@code false} to
-     * ignore reporting to the application, since its focus remains unchanged on its display.
-     *
+     * Tell the window that it is either gaining or losing focus.  Keep it up
+     * to date on the current state showing navigational focus (touch mode) too.
      */
-    void windowFocusChanged(boolean hasFocus, boolean inTouchMode, boolean reportToClient);
-    
+    void windowFocusChanged(boolean hasFocus, boolean inTouchMode);
+
     void closeSystemDialogs(String reason);
-    
+
     /**
      * Called for wallpaper windows when their offsets change.
      */
     void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, boolean sync);
-    
+
     void dispatchWallpaperCommand(String action, int x, int y,
             int z, in Bundle extras, boolean sync);
 
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c4be0e5..330d72f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -107,18 +107,14 @@
       */
     void endProlongedAnimations();
 
-    // Re-evaluate the current orientation from the caller's state.
-    // If there is a change, the new Configuration is returned and the
-    // caller must call setNewConfiguration() sometime later.
-    Configuration updateOrientationFromAppTokens(in Configuration currentConfig,
-            IBinder freezeThisOneIfNeeded, int displayId);
-
     void startFreezingScreen(int exitAnim, int enterAnim);
     void stopFreezingScreen();
 
     // these require DISABLE_KEYGUARD permission
-    void disableKeyguard(IBinder token, String tag);
-    void reenableKeyguard(IBinder token);
+    /** @deprecated use Activity.setShowWhenLocked instead. */
+    void disableKeyguard(IBinder token, String tag, int userId);
+    /** @deprecated use Activity.setShowWhenLocked instead. */
+    void reenableKeyguard(IBinder token, int userId);
     void exitKeyguardSecurely(IOnKeyguardExitResult callback);
     boolean isKeyguardLocked();
     boolean isKeyguardSecure();
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 9762586..658f06a 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -99,7 +99,7 @@
             out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
             out Rect outOutsets, out Rect outBackdropFrame,
             out DisplayCutout.ParcelableWrapper displayCutout,
-            out MergedConfiguration outMergedConfiguration, out Surface outSurface,
+            out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl,
             out InsetsState insetsState);
 
     /*
@@ -250,4 +250,10 @@
      */
     void updateTapExcludeRegion(IWindow window, int regionId, int left, int top, int width,
             int height);
+
+    /**
+     * Called when the client has changed the local insets state, and now the server should reflect
+     * that new state.
+     */
+    void insetsModified(IWindow window, in InsetsState state);
 }
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
new file mode 100644
index 0000000..7b9f78e
--- /dev/null
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view;
+
+import static android.view.InsetsState.INSET_SIDE_BOTTOM;
+import static android.view.InsetsState.INSET_SIDE_LEFT;
+import static android.view.InsetsState.INSET_SIDE_RIGHT;
+import static android.view.InsetsState.INSET_SIDE_TOP;
+
+import android.annotation.Nullable;
+import android.graphics.Insets;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.os.UidProto.Sync;
+import android.util.ArraySet;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.SparseSetArray;
+import android.view.InsetsState.InsetSide;
+import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
+import android.view.WindowInsets.Type.InsetType;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+/**
+ * Implements {@link WindowInsetsAnimationController}
+ * @hide
+ */
+@VisibleForTesting
+public class InsetsAnimationControlImpl implements WindowInsetsAnimationController {
+
+    private final WindowInsetsAnimationControlListener mListener;
+    private final SparseArray<InsetsSourceConsumer> mConsumers;
+    private final SparseIntArray mTypeSideMap = new SparseIntArray();
+    private final SparseSetArray<InsetsSourceConsumer> mSideSourceMap = new SparseSetArray<>();
+
+    /** @see WindowInsetsAnimationController#getHiddenStateInsets */
+    private final Insets mHiddenInsets;
+
+    /** @see WindowInsetsAnimationController#getShownStateInsets */
+    private final Insets mShownInsets;
+    private final Matrix mTmpMatrix = new Matrix();
+    private final InsetsState mInitialInsetsState;
+    private final @InsetType int mTypes;
+    private final Supplier<SyncRtSurfaceTransactionApplier> mTransactionApplierSupplier;
+
+    private Insets mCurrentInsets;
+
+    @VisibleForTesting
+    public InsetsAnimationControlImpl(SparseArray<InsetsSourceConsumer> consumers, Rect frame,
+            InsetsState state, WindowInsetsAnimationControlListener listener,
+            @InsetType int types,
+            Supplier<SyncRtSurfaceTransactionApplier> transactionApplierSupplier) {
+        mConsumers = consumers;
+        mListener = listener;
+        mTypes = types;
+        mTransactionApplierSupplier = transactionApplierSupplier;
+        mInitialInsetsState = new InsetsState(state);
+        mCurrentInsets = getInsetsFromState(mInitialInsetsState, frame, null /* typeSideMap */);
+        mHiddenInsets = calculateInsets(mInitialInsetsState, frame, consumers, false /* shown */,
+                null /* typeSideMap */);
+        mShownInsets = calculateInsets(mInitialInsetsState, frame, consumers, true /* shown */,
+                mTypeSideMap);
+        buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mConsumers);
+
+        // TODO: Check for controllability first and wait for IME if needed.
+        listener.onReady(this, types);
+    }
+
+    @Override
+    public Insets getHiddenStateInsets() {
+        return mHiddenInsets;
+    }
+
+    @Override
+    public Insets getShownStateInsets() {
+        return mShownInsets;
+    }
+
+    @Override
+    public Insets getCurrentInsets() {
+        return mCurrentInsets;
+    }
+
+    @Override
+    @InsetType
+    public int getTypes() {
+        return mTypes;
+    }
+
+    @Override
+    public void changeInsets(Insets insets) {
+        insets = sanitize(insets);
+        final Insets offset = Insets.subtract(mShownInsets, insets);
+        ArrayList<SurfaceParams> params = new ArrayList<>();
+        if (offset.left != 0) {
+            updateLeashesForSide(INSET_SIDE_LEFT, offset.left, params);
+        }
+        if (offset.top != 0) {
+            updateLeashesForSide(INSET_SIDE_TOP, offset.top, params);
+        }
+        if (offset.right != 0) {
+            updateLeashesForSide(INSET_SIDE_RIGHT, offset.right, params);
+        }
+        if (offset.bottom != 0) {
+            updateLeashesForSide(INSET_SIDE_BOTTOM, offset.bottom, params);
+        }
+        SyncRtSurfaceTransactionApplier applier = mTransactionApplierSupplier.get();
+        applier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
+        mCurrentInsets = insets;
+    }
+
+    @Override
+    public void finish(int shownTypes) {
+        // TODO
+    }
+
+    private Insets calculateInsets(InsetsState state, Rect frame,
+            SparseArray<InsetsSourceConsumer> consumers, boolean shown,
+            @Nullable @InsetSide SparseIntArray typeSideMap) {
+        for (int i = consumers.size() - 1; i >= 0; i--) {
+            state.getSource(consumers.valueAt(i).getType()).setVisible(shown);
+        }
+        return getInsetsFromState(state, frame, typeSideMap);
+    }
+
+    private Insets getInsetsFromState(InsetsState state, Rect frame,
+            @Nullable @InsetSide SparseIntArray typeSideMap) {
+        return state.calculateInsets(frame, false /* isScreenRound */,
+                false /* alwaysConsumerNavBar */, null /* displayCutout */, typeSideMap)
+                .getSystemWindowInsets();
+    }
+
+    private Insets sanitize(Insets insets) {
+        return Insets.max(Insets.min(insets, mShownInsets), mHiddenInsets);
+    }
+
+    private void updateLeashesForSide(@InsetSide int side, int inset,
+            ArrayList<SurfaceParams> surfaceParams) {
+        ArraySet<InsetsSourceConsumer> items = mSideSourceMap.get(side);
+        // TODO: Implement behavior when inset spans over multiple types
+        for (int i = items.size() - 1; i >= 0; i--) {
+            final InsetsSourceConsumer consumer = items.valueAt(i);
+            final InsetsSource source = mInitialInsetsState.getSource(consumer.getType());
+            final SurfaceControl leash = consumer.getControl().getLeash();
+            mTmpMatrix.setTranslate(source.getFrame().left, source.getFrame().top);
+            addTranslationToMatrix(side, inset, mTmpMatrix);
+            surfaceParams.add(new SurfaceParams(leash, 1f, mTmpMatrix, null, 0, 0f));
+        }
+    }
+
+    private void addTranslationToMatrix(@InsetSide int side, int inset, Matrix m) {
+        switch (side) {
+            case INSET_SIDE_LEFT:
+                m.postTranslate(-inset, 0);
+                break;
+            case INSET_SIDE_TOP:
+                m.postTranslate(0, -inset);
+                break;
+            case INSET_SIDE_RIGHT:
+                m.postTranslate(inset, 0);
+                break;
+            case INSET_SIDE_BOTTOM:
+                m.postTranslate(0, inset);
+                break;
+        }
+    }
+
+    private static void buildTypeSourcesMap(SparseIntArray typeSideMap,
+            SparseSetArray<InsetsSourceConsumer> sideSourcesMap,
+            SparseArray<InsetsSourceConsumer> consumers) {
+        for (int i = typeSideMap.size() - 1; i >= 0; i--) {
+            int type = typeSideMap.keyAt(i);
+            int side = typeSideMap.valueAt(i);
+            sideSourcesMap.add(side, consumers.get(type));
+        }
+    }
+}
+
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index fb4f9c0..01af37e 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -19,7 +19,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Rect;
+import android.os.RemoteException;
 import android.util.ArraySet;
+import android.util.Log;
 import android.util.SparseArray;
 import android.view.SurfaceControl.Transaction;
 import android.view.WindowInsets.Type.InsetType;
@@ -28,6 +30,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
 
 /**
  * Implements {@link WindowInsetsController} on the client.
@@ -35,12 +38,17 @@
  */
 public class InsetsController implements WindowInsetsController {
 
+    private final String TAG = "InsetsControllerImpl";
+
     private final InsetsState mState = new InsetsState();
+    private final InsetsState mTmpState = new InsetsState();
+
     private final Rect mFrame = new Rect();
     private final SparseArray<InsetsSourceConsumer> mSourceConsumers = new SparseArray<>();
     private final ViewRootImpl mViewRoot;
 
     private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
+    private final ArrayList<InsetsAnimationControlImpl> mAnimationControls = new ArrayList<>();
 
     public InsetsController(ViewRootImpl viewRoot) {
         mViewRoot = viewRoot;
@@ -59,17 +67,23 @@
             return false;
         }
         mState.set(state);
+        mTmpState.set(state, true /* copySources */);
         applyLocalVisibilityOverride();
         mViewRoot.notifyInsetsChanged();
+        if (!mState.equals(mTmpState)) {
+            sendStateToWindowManager();
+        }
         return true;
     }
 
     /**
      * @see InsetsState#calculateInsets
      */
-    WindowInsets calculateInsets(boolean isScreenRound,
+    @VisibleForTesting
+    public WindowInsets calculateInsets(boolean isScreenRound,
             boolean alwaysConsumeNavBar, DisplayCutout cutout) {
-        return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout);
+        return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout,
+                null /* typeSideMap */);
     }
 
     /**
@@ -116,6 +130,28 @@
         }
     }
 
+    @Override
+    public void controlWindowInsetsAnimation(@InsetType int types,
+            WindowInsetsAnimationControlListener listener) {
+
+        // TODO: Check whether we already have a controller.
+        final ArraySet<Integer> internalTypes = mState.toInternalType(types);
+        final SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>();
+        for (int i = internalTypes.size() - 1; i >= 0; i--) {
+            InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
+            if (consumer.getControl() != null) {
+                consumers.put(consumer.getType(), consumer);
+            } else {
+                // TODO: Let calling app know it's not possible, or wait
+                // TODO: Remove it from types
+            }
+        }
+        final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(consumers,
+                mFrame, mState, listener, types,
+                () -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView));
+        mAnimationControls.add(controller);
+    }
+
     private void applyLocalVisibilityOverride() {
         for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
             final InsetsSourceConsumer controller = mSourceConsumers.valueAt(i);
@@ -134,8 +170,30 @@
         return controller;
     }
 
-    void notifyVisibilityChanged() {
+    @VisibleForTesting
+    public void notifyVisibilityChanged() {
         mViewRoot.notifyInsetsChanged();
+        sendStateToWindowManager();
+    }
+
+    /**
+     * Sends the local visibility state back to window manager.
+     */
+    private void sendStateToWindowManager() {
+        InsetsState tmpState = new InsetsState();
+        for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
+            final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
+            if (consumer.getControl() != null) {
+                tmpState.addSource(mState.getSource(consumer.getType()));
+            }
+        }
+
+        // TODO: Put this on a dispatcher thread.
+        try {
+            mViewRoot.mWindowSession.insetsModified(mViewRoot.mWindow, tmpState);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to call insetsModified", e);
+        }
     }
 
     void dump(String prefix, PrintWriter pw) {
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 0cb8ad7..fbc72a0 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -65,12 +65,17 @@
         return mFrame;
     }
 
+    public boolean isVisible() {
+        return mVisible;
+    }
+
     /**
      * Calculates the insets this source will cause to a client window.
      *
      * @param relativeFrame The frame to calculate the insets relative to.
      * @param ignoreVisibility If true, always reports back insets even if source isn't visible.
-     * @return The resulting insets.
+     * @return The resulting insets. The contract is that only one side will be occupied by a
+     *         source.
      */
     public Insets calculateInsets(Rect relativeFrame, boolean ignoreVisibility) {
         if (!ignoreVisibility && !mVisible) {
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index ec85c4c..145b097 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -35,7 +35,7 @@
     private final InsetsState mState;
     private final InsetsController mController;
     private @Nullable InsetsSourceControl mSourceControl;
-    private boolean mHidden;
+    private boolean mVisible;
 
     public InsetsSourceConsumer(@InternalInsetType int type, InsetsState state,
             Supplier<Transaction> transactionSupplier, InsetsController controller) {
@@ -43,6 +43,7 @@
         mState = state;
         mTransactionSupplier = transactionSupplier;
         mController = controller;
+        mVisible = InsetsState.getDefaultVisibly(type);
     }
 
     public void setControl(@Nullable InsetsSourceControl control) {
@@ -51,8 +52,9 @@
         }
         mSourceControl = control;
         applyHiddenToControl();
-        applyLocalVisibilityOverride();
-        mController.notifyVisibilityChanged();
+        if (applyLocalVisibilityOverride()) {
+            mController.notifyVisibilityChanged();
+        }
     }
 
     @VisibleForTesting
@@ -66,28 +68,32 @@
 
     @VisibleForTesting
     public void show() {
-        setHidden(false);
+        setVisible(true);
     }
 
     @VisibleForTesting
     public void hide() {
-        setHidden(true);
+        setVisible(false);
     }
 
-    void applyLocalVisibilityOverride() {
+    boolean applyLocalVisibilityOverride() {
 
         // If we don't have control, we are not able to change the visibility.
         if (mSourceControl == null) {
-            return;
+            return false;
         }
-        mState.getSource(mType).setVisible(!mHidden);
+        if (mState.getSource(mType).isVisible() == mVisible) {
+            return false;
+        }
+        mState.getSource(mType).setVisible(mVisible);
+        return true;
     }
 
-    private void setHidden(boolean hidden) {
-        if (mHidden == hidden) {
+    private void setVisible(boolean visible) {
+        if (mVisible == visible) {
             return;
         }
-        mHidden = hidden;
+        mVisible = visible;
         applyHiddenToControl();
         applyLocalVisibilityOverride();
         mController.notifyVisibilityChanged();
@@ -100,10 +106,10 @@
 
         // TODO: Animation
         final Transaction t = mTransactionSupplier.get();
-        if (mHidden) {
-            t.hide(mSourceControl.getLeash());
-        } else {
+        if (mVisible) {
             t.show(mSourceControl.getLeash());
+        } else {
+            t.hide(mSourceControl.getLeash());
         }
         t.apply();
     }
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 689b14f..885b3e9 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -17,18 +17,22 @@
 package android.view;
 
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
 import android.view.WindowInsets.Type;
 import android.view.WindowInsets.Type.InsetType;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 
 /**
  * Holder for state of system windows that cause window insets for all other windows in the system.
@@ -77,11 +81,30 @@
     /** A shelf is the same as the navigation bar. */
     public static final int TYPE_SHELF = TYPE_NAVIGATION_BAR;
 
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "INSET_SIDE", value = {
+            INSET_SIDE_LEFT,
+            INSET_SIDE_TOP,
+            INSET_SIDE_RIGHT,
+            INSET_SIDE_BOTTOM,
+            INSET_SIDE_UNKNWON
+    })
+    public @interface InsetSide {}
+    static final int INSET_SIDE_LEFT = 0;
+    static final int INSET_SIDE_TOP = 1;
+    static final int INSET_SIDE_RIGHT = 2;
+    static final int INSET_SIDE_BOTTOM = 3;
+    static final int INSET_SIDE_UNKNWON = 4;
+
     private final ArrayMap<Integer, InsetsSource> mSources = new ArrayMap<>();
 
     public InsetsState() {
     }
 
+    public InsetsState(InsetsState copy) {
+        set(copy);
+    }
+
     /**
      * Calculates {@link WindowInsets} based on the current source configuration.
      *
@@ -89,7 +112,8 @@
      * @return The calculated insets.
      */
     public WindowInsets calculateInsets(Rect frame, boolean isScreenRound,
-            boolean alwaysConsumeNavBar, DisplayCutout cutout) {
+            boolean alwaysConsumeNavBar, DisplayCutout cutout,
+            @Nullable @InsetSide SparseIntArray typeSideMap) {
         Insets systemInsets = Insets.NONE;
         Insets maxInsets = Insets.NONE;
         final Rect relativeFrame = new Rect(frame);
@@ -100,13 +124,13 @@
                 continue;
             }
             systemInsets = processSource(source, systemInsets, relativeFrame,
-                    false /* ignoreVisibility */);
+                    false /* ignoreVisibility */, typeSideMap);
 
             // IME won't be reported in max insets as the size depends on the EditorInfo of the IME
             // target.
             if (source.getType() != TYPE_IME) {
                 maxInsets = processSource(source, maxInsets, relativeFrameMax,
-                        true /* ignoreVisibility */);
+                        true /* ignoreVisibility */, null /* typeSideMap */);
             }
         }
         return new WindowInsets(new Rect(systemInsets), null, new Rect(maxInsets), isScreenRound,
@@ -114,13 +138,39 @@
     }
 
     private Insets processSource(InsetsSource source, Insets insets, Rect relativeFrame,
-            boolean ignoreVisibility) {
+            boolean ignoreVisibility, @Nullable @InsetSide SparseIntArray typeSideMap) {
         Insets currentInsets = source.calculateInsets(relativeFrame, ignoreVisibility);
         insets = Insets.add(currentInsets, insets);
         relativeFrame.inset(insets);
+        if (typeSideMap != null && !Insets.NONE.equals(currentInsets)) {
+            @InsetSide int insetSide = getInsetSide(currentInsets);
+            if (insetSide != INSET_SIDE_UNKNWON) {
+                typeSideMap.put(source.getType(), getInsetSide(currentInsets));
+            }
+        }
         return insets;
     }
 
+    /**
+     * Retrieves the side for a certain {@code insets}. It is required that only one field l/t/r/b
+     * is set in order that this method returns a meaningful result.
+     */
+    private @InsetSide int getInsetSide(Insets insets) {
+        if (insets.left != 0) {
+            return INSET_SIDE_LEFT;
+        }
+        if (insets.top != 0) {
+            return INSET_SIDE_TOP;
+        }
+        if (insets.right != 0) {
+            return INSET_SIDE_RIGHT;
+        }
+        if (insets.bottom != 0) {
+            return INSET_SIDE_BOTTOM;
+        }
+        return INSET_SIDE_UNKNWON;
+    }
+
     public InsetsSource getSource(@InternalInsetType int type) {
         return mSources.computeIfAbsent(type, InsetsSource::new);
     }
@@ -151,6 +201,18 @@
         }
     }
 
+    public void addSource(InsetsSource source) {
+        mSources.put(source.getType(), source);
+    }
+
+    public int getSourcesCount() {
+        return mSources.size();
+    }
+
+    public InsetsSource sourceAt(int index) {
+        return mSources.valueAt(index);
+    }
+
     public static @InternalInsetType ArraySet<Integer> toInternalType(@InsetType int insetTypes) {
         final ArraySet<Integer> result = new ArraySet<>();
         if ((insetTypes & Type.TOP_BAR) != 0) {
@@ -167,6 +229,20 @@
         return result;
     }
 
+    public static boolean getDefaultVisibly(@InsetType int type) {
+        switch (type) {
+            case TYPE_TOP_BAR:
+            case TYPE_SIDE_BAR_1:
+            case TYPE_SIDE_BAR_2:
+            case TYPE_SIDE_BAR_3:
+                return true;
+            case TYPE_IME:
+                return false;
+            default:
+                return true;
+        }
+    }
+
     public void dump(String prefix, PrintWriter pw) {
         pw.println(prefix + "InsetsState");
         for (int i = mSources.size() - 1; i >= 0; i--) {
@@ -174,7 +250,7 @@
         }
     }
 
-    static String typeToString(int type) {
+    public static String typeToString(int type) {
         switch (type) {
             case TYPE_TOP_BAR:
                 return "TYPE_TOP_BAR";
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index a4d3ce7..ffd4156 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -784,24 +784,8 @@
             ta.recycle();
         }
 
-        if (name.equals(TAG_1995)) {
-            // Let's party like it's 1995!
-            return new BlinkLayout(context, attrs);
-        }
-
         try {
-            View view;
-            if (mFactory2 != null) {
-                view = mFactory2.onCreateView(parent, name, context, attrs);
-            } else if (mFactory != null) {
-                view = mFactory.onCreateView(name, context, attrs);
-            } else {
-                view = null;
-            }
-
-            if (view == null && mPrivateFactory != null) {
-                view = mPrivateFactory.onCreateView(parent, name, context, attrs);
-            }
+            View view = tryCreateView(parent, name, context, attrs);
 
             if (view == null) {
                 final Object lastContext = mConstructorArgs[0];
@@ -836,6 +820,48 @@
     }
 
     /**
+     * Tries to create a view from a tag name using the supplied attribute set.
+     *
+     * This method gives the factory provided by {@link LayoutInflater#setFactory} and
+     * {@link LayoutInflater#setFactory2} a chance to create a view. However, it does not apply all
+     * of the general view creation logic, and thus may return {@code null} for some tags. This
+     * method is used by {@link LayoutInflater#inflate} in creating {@code View} objects.
+     *
+     * @hide for use by precompiled layouts.
+     *
+     * @param parent the parent view, used to inflate layout params
+     * @param name the name of the XML tag used to define the view
+     * @param context the inflation context for the view, typically the
+     *                {@code parent} or base layout inflater context
+     * @param attrs the attribute set for the XML tag used to define the view
+     */
+    @UnsupportedAppUsage(trackingBug = 122360734)
+    @Nullable
+    public final View tryCreateView(@Nullable View parent, @NonNull String name,
+        @NonNull Context context,
+        @NonNull AttributeSet attrs) {
+        if (name.equals(TAG_1995)) {
+            // Let's party like it's 1995!
+            return new BlinkLayout(context, attrs);
+        }
+
+        View view;
+        if (mFactory2 != null) {
+            view = mFactory2.onCreateView(parent, name, context, attrs);
+        } else if (mFactory != null) {
+            view = mFactory.onCreateView(name, context, attrs);
+        } else {
+            view = null;
+        }
+
+        if (view == null && mPrivateFactory != null) {
+            view = mPrivateFactory.onCreateView(parent, name, context, attrs);
+        }
+
+        return view;
+    }
+
+    /**
      * Recursive method used to inflate internal (non-root) children. This
      * method calls through to {@link #rInflate} using the parent context as
      * the inflation context.
@@ -921,129 +947,129 @@
             AttributeSet attrs) throws XmlPullParserException, IOException {
         int type;
 
-        if (parent instanceof ViewGroup) {
-            // Apply a theme wrapper, if requested. This is sort of a weird
-            // edge case, since developers think the <include> overwrites
-            // values in the AttributeSet of the included View. So, if the
-            // included View has a theme attribute, we'll need to ignore it.
-            final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
-            final int themeResId = ta.getResourceId(0, 0);
-            final boolean hasThemeOverride = themeResId != 0;
-            if (hasThemeOverride) {
-                context = new ContextThemeWrapper(context, themeResId);
-            }
-            ta.recycle();
-
-            // If the layout is pointing to a theme attribute, we have to
-            // massage the value to get a resource identifier out of it.
-            int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0);
-            if (layout == 0) {
-                final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
-                if (value == null || value.length() <= 0) {
-                    throw new InflateException("You must specify a layout in the"
-                            + " include tag: <include layout=\"@layout/layoutID\" />");
-                }
-
-                // Attempt to resolve the "?attr/name" string to an attribute
-                // within the default (e.g. application) package.
-                layout = context.getResources().getIdentifier(
-                        value.substring(1), "attr", context.getPackageName());
-
-            }
-
-            // The layout might be referencing a theme attribute.
-            if (mTempValue == null) {
-                mTempValue = new TypedValue();
-            }
-            if (layout != 0 && context.getTheme().resolveAttribute(layout, mTempValue, true)) {
-                layout = mTempValue.resourceId;
-            }
-
-            if (layout == 0) {
-                final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
-                throw new InflateException("You must specify a valid layout "
-                        + "reference. The layout ID " + value + " is not valid.");
-            } else {
-                final XmlResourceParser childParser = context.getResources().getLayout(layout);
-
-                try {
-                    final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
-
-                    while ((type = childParser.next()) != XmlPullParser.START_TAG &&
-                            type != XmlPullParser.END_DOCUMENT) {
-                        // Empty.
-                    }
-
-                    if (type != XmlPullParser.START_TAG) {
-                        throw new InflateException(childParser.getPositionDescription() +
-                                ": No start tag found!");
-                    }
-
-                    final String childName = childParser.getName();
-
-                    if (TAG_MERGE.equals(childName)) {
-                        // The <merge> tag doesn't support android:theme, so
-                        // nothing special to do here.
-                        rInflate(childParser, parent, context, childAttrs, false);
-                    } else {
-                        final View view = createViewFromTag(parent, childName,
-                                context, childAttrs, hasThemeOverride);
-                        final ViewGroup group = (ViewGroup) parent;
-
-                        final TypedArray a = context.obtainStyledAttributes(
-                                attrs, R.styleable.Include);
-                        final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID);
-                        final int visibility = a.getInt(R.styleable.Include_visibility, -1);
-                        a.recycle();
-
-                        // We try to load the layout params set in the <include /> tag.
-                        // If the parent can't generate layout params (ex. missing width
-                        // or height for the framework ViewGroups, though this is not
-                        // necessarily true of all ViewGroups) then we expect it to throw
-                        // a runtime exception.
-                        // We catch this exception and set localParams accordingly: true
-                        // means we successfully loaded layout params from the <include>
-                        // tag, false means we need to rely on the included layout params.
-                        ViewGroup.LayoutParams params = null;
-                        try {
-                            params = group.generateLayoutParams(attrs);
-                        } catch (RuntimeException e) {
-                            // Ignore, just fail over to child attrs.
-                        }
-                        if (params == null) {
-                            params = group.generateLayoutParams(childAttrs);
-                        }
-                        view.setLayoutParams(params);
-
-                        // Inflate all children.
-                        rInflateChildren(childParser, view, childAttrs, true);
-
-                        if (id != View.NO_ID) {
-                            view.setId(id);
-                        }
-
-                        switch (visibility) {
-                            case 0:
-                                view.setVisibility(View.VISIBLE);
-                                break;
-                            case 1:
-                                view.setVisibility(View.INVISIBLE);
-                                break;
-                            case 2:
-                                view.setVisibility(View.GONE);
-                                break;
-                        }
-
-                        group.addView(view);
-                    }
-                } finally {
-                    childParser.close();
-                }
-            }
-        } else {
+        if (!(parent instanceof ViewGroup)) {
             throw new InflateException("<include /> can only be used inside of a ViewGroup");
         }
 
+        // Apply a theme wrapper, if requested. This is sort of a weird
+        // edge case, since developers think the <include> overwrites
+        // values in the AttributeSet of the included View. So, if the
+        // included View has a theme attribute, we'll need to ignore it.
+        final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
+        final int themeResId = ta.getResourceId(0, 0);
+        final boolean hasThemeOverride = themeResId != 0;
+        if (hasThemeOverride) {
+            context = new ContextThemeWrapper(context, themeResId);
+        }
+        ta.recycle();
+
+        // If the layout is pointing to a theme attribute, we have to
+        // massage the value to get a resource identifier out of it.
+        int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0);
+        if (layout == 0) {
+            final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
+            if (value == null || value.length() <= 0) {
+                throw new InflateException("You must specify a layout in the"
+                    + " include tag: <include layout=\"@layout/layoutID\" />");
+            }
+
+            // Attempt to resolve the "?attr/name" string to an attribute
+            // within the default (e.g. application) package.
+            layout = context.getResources().getIdentifier(
+                value.substring(1), "attr", context.getPackageName());
+
+        }
+
+        // The layout might be referencing a theme attribute.
+        if (mTempValue == null) {
+            mTempValue = new TypedValue();
+        }
+        if (layout != 0 && context.getTheme().resolveAttribute(layout, mTempValue, true)) {
+            layout = mTempValue.resourceId;
+        }
+
+        if (layout == 0) {
+            final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
+            throw new InflateException("You must specify a valid layout "
+                + "reference. The layout ID " + value + " is not valid.");
+        }
+
+        final XmlResourceParser childParser = context.getResources().getLayout(layout);
+
+        try {
+            final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
+
+            while ((type = childParser.next()) != XmlPullParser.START_TAG &&
+                type != XmlPullParser.END_DOCUMENT) {
+                // Empty.
+            }
+
+            if (type != XmlPullParser.START_TAG) {
+                throw new InflateException(childParser.getPositionDescription() +
+                    ": No start tag found!");
+            }
+
+            final String childName = childParser.getName();
+
+            if (TAG_MERGE.equals(childName)) {
+                // The <merge> tag doesn't support android:theme, so
+                // nothing special to do here.
+                rInflate(childParser, parent, context, childAttrs, false);
+            } else {
+                final View view = createViewFromTag(parent, childName,
+                    context, childAttrs, hasThemeOverride);
+                final ViewGroup group = (ViewGroup) parent;
+
+                final TypedArray a = context.obtainStyledAttributes(
+                    attrs, R.styleable.Include);
+                final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID);
+                final int visibility = a.getInt(R.styleable.Include_visibility, -1);
+                a.recycle();
+
+                // We try to load the layout params set in the <include /> tag.
+                // If the parent can't generate layout params (ex. missing width
+                // or height for the framework ViewGroups, though this is not
+                // necessarily true of all ViewGroups) then we expect it to throw
+                // a runtime exception.
+                // We catch this exception and set localParams accordingly: true
+                // means we successfully loaded layout params from the <include>
+                // tag, false means we need to rely on the included layout params.
+                ViewGroup.LayoutParams params = null;
+                try {
+                    params = group.generateLayoutParams(attrs);
+                } catch (RuntimeException e) {
+                    // Ignore, just fail over to child attrs.
+                }
+                if (params == null) {
+                    params = group.generateLayoutParams(childAttrs);
+                }
+                view.setLayoutParams(params);
+
+                // Inflate all children.
+                rInflateChildren(childParser, view, childAttrs, true);
+
+                if (id != View.NO_ID) {
+                    view.setId(id);
+                }
+
+                switch (visibility) {
+                    case 0:
+                        view.setVisibility(View.VISIBLE);
+                        break;
+                    case 1:
+                        view.setVisibility(View.INVISIBLE);
+                        break;
+                    case 2:
+                        view.setVisibility(View.GONE);
+                        break;
+                }
+
+                group.addView(view);
+            }
+        } finally {
+            childParser.close();
+        }
+
         LayoutInflater.consumeChildElements(parser);
     }
 
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 4a5ccdf..f8d5014 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -195,28 +195,26 @@
             }
             int childHeight = child.getMeasuredHeight();
             MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams();
-            left += params.getMarginStart();
-            int right = left + child.getMeasuredWidth();
+            int layoutLeft;
+            int layoutRight;
             int top = (int) (getPaddingTop() + (ownHeight - childHeight) / 2.0f);
             int bottom = top + childHeight;
-            int layoutLeft = left;
-            int layoutRight = right;
-            if (child == mExpandButton && mShowExpandButtonAtEnd) {
-                layoutRight = end - mContentEndMargin;
-                end = layoutLeft = layoutRight - child.getMeasuredWidth();
-            }
-            if (child == mProfileBadge) {
-                int paddingEnd = getPaddingEnd();
-                if (mShowWorkBadgeAtEnd) {
-                    paddingEnd = mContentEndMargin;
+            if ((child == mExpandButton && mShowExpandButtonAtEnd)
+                    || child == mProfileBadge
+                    || child == mAppOps) {
+                if (end == getMeasuredWidth()) {
+                    layoutRight = end - mContentEndMargin;
+                } else {
+                    layoutRight = end - params.getMarginEnd();
                 }
-                layoutRight = end - paddingEnd;
-                end = layoutLeft = layoutRight - child.getMeasuredWidth();
-            }
-            if (child == mAppOps) {
-                int paddingEnd = mContentEndMargin;
-                layoutRight = end - paddingEnd;
-                end = layoutLeft = layoutRight - child.getMeasuredWidth();
+                layoutLeft = layoutRight - child.getMeasuredWidth();
+                end = layoutLeft - params.getMarginStart();
+            } else {
+                left += params.getMarginStart();
+                int right = left + child.getMeasuredWidth();
+                layoutLeft = left;
+                layoutRight = right;
+                left = right + params.getMarginEnd();
             }
             if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
                 int ltrLeft = layoutLeft;
@@ -224,7 +222,6 @@
                 layoutRight = getWidth() - ltrLeft;
             }
             child.layout(layoutLeft, top, layoutRight, bottom);
-            left = right + params.getMarginEnd();
         }
         updateTouchListener();
     }
@@ -340,7 +337,7 @@
     }
 
     /** Updates icon visibility based on the noisiness of the notification. */
-    public void setAudiblyAlerted(boolean audiblyAlerted) {
+    public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) {
         mAudiblyAlertedIcon.setVisibility(audiblyAlerted ? View.VISIBLE : View.GONE);
     }
 
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index a006e5d..0b6beba 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -67,6 +67,7 @@
             int w, int h, int format, int flags, long parentObject, int windowType, int ownerUid)
             throws OutOfResourcesException;
     private static native long nativeReadFromParcel(Parcel in);
+    private static native long nativeCopyFromSurfaceControl(long nativeObject);
     private static native void nativeWriteToParcel(long nativeObject, Parcel out);
     private static native void nativeRelease(long nativeObject);
     private static native void nativeDestroy(long nativeObject);
@@ -165,10 +166,11 @@
 
     private static native void nativeSetInputWindowInfo(long transactionObj, long nativeObject,
             InputWindowHandle handle);
-
+    private static native void nativeTransferTouchFocus(long transactionObj, IBinder fromToken,
+            IBinder toToken);
 
     private final CloseGuard mCloseGuard = CloseGuard.get();
-    private final String mName;
+    private String mName;
     long mNativeObject; // package visibility only for Surface.java access
 
     // TODO: Move this to native.
@@ -358,6 +360,13 @@
      */
     public static final int WINDOW_TYPE_DONT_SCREENSHOT = 441731;
 
+    public void copyFrom(SurfaceControl other) {
+        mName = other.mName;
+        mWidth = other.mWidth;
+        mHeight = other.mHeight;
+        mNativeObject = nativeCopyFromSurfaceControl(other.mNativeObject);
+    }
+
     /**
      * Builder class for {@link SurfaceControl} objects.
      */
@@ -659,14 +668,29 @@
     }
 
     private SurfaceControl(Parcel in) {
+        readFromParcel(in);
+        mCloseGuard.open("release");
+    }
+
+    public SurfaceControl() {
+        mCloseGuard.open("release");
+    }
+
+    public void readFromParcel(Parcel in) {
+        if (in == null) {
+            throw new IllegalArgumentException("source must not be null");
+        }
+
         mName = in.readString();
         mWidth = in.readInt();
         mHeight = in.readInt();
-        mNativeObject = nativeReadFromParcel(in);
-        if (mNativeObject == 0) {
-            throw new IllegalArgumentException("Couldn't read SurfaceControl from parcel=" + in);
+
+        release();
+        if (in.readInt() != 0) {
+            mNativeObject = nativeReadFromParcel(in);
+        } else {
+            mNativeObject = 0;
         }
-        mCloseGuard.open("release");
     }
 
     @Override
@@ -679,7 +703,16 @@
         dest.writeString(mName);
         dest.writeInt(mWidth);
         dest.writeInt(mHeight);
+        if (mNativeObject == 0) {
+            dest.writeInt(0);
+        } else {
+            dest.writeInt(1);
+        }
         nativeWriteToParcel(mNativeObject, dest);
+
+        if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
+            release();
+        }
     }
 
     /**
@@ -762,6 +795,10 @@
                 "mNativeObject is null. Have you called release() already?");
     }
 
+    public boolean isValid() {
+        return mNativeObject != 0;
+    }
+
     /*
      * set surface parameters.
      * needs to be inside open/closeTransaction block
@@ -1541,6 +1578,21 @@
             return this;
         }
 
+        /**
+         * Transfers touch focus from one window to another. It is possible for multiple windows to
+         * have touch focus if they support split touch dispatch
+         * {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} but this
+         * method only transfers touch focus of the specified window without affecting
+         * other windows that may also have touch focus at the same time.
+         * @param fromToken The token of a window that currently has touch focus.
+         * @param toToken The token of the window that should receive touch focus in
+         * place of the first.
+         */
+        public Transaction transferTouchFocus(IBinder fromToken, IBinder toToken) {
+            nativeTransferTouchFocus(mNativeObject, fromToken, toToken);
+            return this;
+        }
+
         @UnsupportedAppUsage
         public Transaction setMatrix(SurfaceControl sc,
                 float dsdx, float dtdx, float dtdy, float dsdy) {
diff --git a/core/java/android/view/SurfaceSession.java b/core/java/android/view/SurfaceSession.java
index a4fa12a..361ac93 100644
--- a/core/java/android/view/SurfaceSession.java
+++ b/core/java/android/view/SurfaceSession.java
@@ -30,7 +30,6 @@
     private long mNativeClient; // SurfaceComposerClient*
 
     private static native long nativeCreate();
-    private static native long nativeCreateScoped(long surfacePtr);
     private static native void nativeDestroy(long ptr);
     private static native void nativeKill(long ptr);
 
@@ -40,15 +39,6 @@
         mNativeClient = nativeCreate();
     }
 
-    public SurfaceSession(Surface root) {
-        synchronized (root.mLock) {
-            if (root.mNativeObject == 0) {
-                throw new IllegalStateException("Surface is not initialized or has been released");
-            }
-            mNativeClient = nativeCreateScoped(root.mNativeObject);
-        }
-    }
-
     /* no user serviceable parts here ... */
     @Override
     protected void finalize() throws Throwable {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 797d1c5..61fb00d 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -110,7 +110,7 @@
     final ReentrantLock mSurfaceLock = new ReentrantLock();
     @UnsupportedAppUsage
     final Surface mSurface = new Surface();       // Current surface in use
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     boolean mDrawingStopped = true;
     // We use this to track if the application has produced a frame
     // in to the Surface. Up until that point, we should be careful not to punch
@@ -129,7 +129,7 @@
 
     int mSubLayer = APPLICATION_MEDIA_SUBLAYER;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     boolean mIsCreating = false;
     private volatile boolean mRtHandlingPositionUpdates = false;
 
@@ -159,9 +159,9 @@
     boolean mViewVisibility = false;
     boolean mWindowStopped = false;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     int mRequestedWidth = -1;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     int mRequestedHeight = -1;
     /* Set SurfaceView's format to 565 by default to maintain backward
      * compatibility with applications assuming this format.
@@ -172,7 +172,7 @@
     @UnsupportedAppUsage
     boolean mHaveFrame = false;
     boolean mSurfaceCreated = false;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     long mLastLockTime = 0;
 
     boolean mVisible = false;
@@ -182,7 +182,7 @@
     int mSurfaceHeight = -1;
     @UnsupportedAppUsage
     int mFormat = -1;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     final Rect mSurfaceFrame = new Rect();
     int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
     private Translator mTranslator;
@@ -547,7 +547,7 @@
 
                 if (creating) {
                     viewRoot.createBoundsSurface(mSubLayer);
-                    mSurfaceSession = new SurfaceSession(viewRoot.mBoundsSurface);
+                    mSurfaceSession = new SurfaceSession();
                     mDeferredDestroySurfaceControl = mSurfaceControl;
 
                     updateOpaqueFlag();
@@ -559,6 +559,7 @@
                             new SurfaceControl.Builder(mSurfaceSession)
                                     .setBufferSize(mSurfaceWidth, mSurfaceHeight)
                                     .setFormat(mFormat)
+                                    .setParent(viewRoot.getSurfaceControl())
                                     .setFlags(mSurfaceFlags));
                 } else if (mSurfaceControl == null) {
                     return;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
similarity index 72%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
rename to core/java/android/view/SyncRtSurfaceTransactionApplier.java
index 807edf6..0270acb 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
+++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
@@ -11,24 +11,22 @@
  * 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
+ * limitations under the License.
  */
 
-package com.android.systemui.shared.system;
+package android.view;
 
-import android.graphics.HardwareRenderer;
 import android.graphics.Matrix;
 import android.graphics.Rect;
-import android.view.Surface;
-import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
-import android.view.View;
-import android.view.ViewRootImpl;
+
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.function.Consumer;
 
 /**
  * Helper class to apply surface transactions in sync with RenderThread.
+ * @hide
  */
 public class SyncRtSurfaceTransactionApplier {
 
@@ -54,30 +52,32 @@
         if (mTargetViewRootImpl == null) {
             return;
         }
-        mTargetViewRootImpl.registerRtFrameCallback(new HardwareRenderer.FrameDrawingCallback() {
-            @Override
-            public void onFrameDraw(long frame) {
-                if (mTargetSurface == null || !mTargetSurface.isValid()) {
-                    return;
-                }
-                Transaction t = new Transaction();
-                for (int i = params.length - 1; i >= 0; i--) {
-                    SurfaceParams surfaceParams = params[i];
-                    SurfaceControl surface = surfaceParams.surface;
-                    t.deferTransactionUntilSurface(surface, mTargetSurface, frame);
-                    applyParams(t, surfaceParams, mTmpFloat9);
-                }
-                t.setEarlyWakeup();
-                t.apply();
+        mTargetViewRootImpl.registerRtFrameCallback(frame -> {
+            if (mTargetSurface == null || !mTargetSurface.isValid()) {
+                return;
             }
+            Transaction t = new Transaction();
+            for (int i = params.length - 1; i >= 0; i--) {
+                SurfaceParams surfaceParams = params[i];
+                SurfaceControl surface = surfaceParams.surface;
+                t.deferTransactionUntilSurface(surface, mTargetSurface, frame);
+                applyParams(t, surfaceParams, mTmpFloat9);
+            }
+            t.setEarlyWakeup();
+            t.apply();
         });
 
         // Make sure a frame gets scheduled.
         mTargetViewRootImpl.getView().invalidate();
     }
 
-    public static void applyParams(TransactionCompat t, SurfaceParams params) {
-        applyParams(t.mTransaction, params, t.mTmpValues);
+    public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) {
+        t.setMatrix(params.surface, params.matrix, tmpFloat9);
+        t.setWindowCrop(params.surface, params.windowCrop);
+        t.setAlpha(params.surface, params.alpha);
+        t.setLayer(params.surface, params.layer);
+        t.setCornerRadius(params.surface, params.cornerRadius);
+        t.show(params.surface);
     }
 
     /**
@@ -109,15 +109,6 @@
         }
     }
 
-    private static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) {
-        t.setMatrix(params.surface, params.matrix, tmpFloat9);
-        t.setWindowCrop(params.surface, params.windowCrop);
-        t.setAlpha(params.surface, params.alpha);
-        t.setLayer(params.surface, params.layer);
-        t.setCornerRadius(params.surface, params.cornerRadius);
-        t.show(params.surface);
-    }
-
     public static class SurfaceParams {
 
         /**
@@ -129,9 +120,9 @@
          * @param matrix Matrix to apply.
          * @param windowCrop Crop to apply.
          */
-        public SurfaceParams(SurfaceControlCompat surface, float alpha, Matrix matrix,
+        public SurfaceParams(SurfaceControl surface, float alpha, Matrix matrix,
                 Rect windowCrop, int layer, float cornerRadius) {
-            this.surface = surface.mSurfaceControl;
+            this.surface = surface;
             this.alpha = alpha;
             this.matrix = new Matrix(matrix);
             this.windowCrop = new Rect(windowCrop);
@@ -139,11 +130,22 @@
             this.cornerRadius = cornerRadius;
         }
 
-        final SurfaceControl surface;
-        final float alpha;
-        final Matrix matrix;
-        final Rect windowCrop;
-        final int layer;
+        @VisibleForTesting
+        public final SurfaceControl surface;
+
+        @VisibleForTesting
+        public final float alpha;
+
+        @VisibleForTesting
         final float cornerRadius;
+
+        @VisibleForTesting
+        public final Matrix matrix;
+
+        @VisibleForTesting
+        public final Rect windowCrop;
+
+        @VisibleForTesting
+        public final int layer;
     }
 }
diff --git a/core/java/android/view/TouchDelegate.java b/core/java/android/view/TouchDelegate.java
index bef9f07..2ea95e9 100644
--- a/core/java/android/view/TouchDelegate.java
+++ b/core/java/android/view/TouchDelegate.java
@@ -157,6 +157,11 @@
      * Forward hover events to the delegate view if the event is within the bounds
      * specified in the constructor and touch exploration is enabled.
      *
+     * <p>This method is provided for accessibility purposes so touch exploration, which is
+     * commonly used by screen readers, can properly place accessibility focus on views that
+     * use touch delegates. Therefore, touch exploration must be enabled for hover events
+     * to be dispatched through the delegate.</p>
+     *
      * @param event The hover event to forward
      * @return True if the event was consumed by the delegate, false otherwise.
      *
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 4297efb7..4504887 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -109,7 +109,9 @@
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
+import android.view.contentcapture.ContentCaptureContext;
 import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.ContentCaptureSession;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
@@ -121,6 +123,7 @@
 import android.widget.ScrollBarDrawable;
 
 import com.android.internal.R;
+import com.android.internal.util.Preconditions;
 import com.android.internal.view.TooltipPopup;
 import com.android.internal.view.menu.MenuBuilder;
 import com.android.internal.widget.ScrollBarUtils;
@@ -4680,7 +4683,7 @@
      * a long press could be performed before the tap is checked, in which case the tap's action
      * should not be invoked.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private boolean mHasPerformedLongPress;
 
     /**
@@ -5018,6 +5021,13 @@
     private Handler mVisibilityChangeForAutofillHandler;
 
     /**
+     * Used when app developers explicitly set the {@link ContentCaptureSession} associated with the
+     * view (through {@link #setContentCaptureSession(ContentCaptureSession)}.
+     */
+    @Nullable
+    private WeakReference<ContentCaptureSession> mContentCaptureSession;
+
+    /**
      * Simple constructor to use when creating a view from code.
      *
      * @param context The Context the view is running in, through which it can
@@ -8161,7 +8171,7 @@
      * is visible.
      *
      * <p>The populated structure is then passed to the service through
-     * {@link ContentCaptureManager#notifyViewAppeared(ViewStructure)}.
+     * {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)}.
      *
      * <p><b>Note: </b>the following methods of the {@code structure} will be ignored:
      * <ul>
@@ -8977,13 +8987,16 @@
         if (!mContext.isContentCaptureSupported()) return;
 
         // Then check if it's enabled in the context...
-        final ContentCaptureManager cm = mContext.getSystemService(ContentCaptureManager.class);
-        if (cm == null || !cm.isContentCaptureEnabled()) return;
+        final ContentCaptureManager ccm = mContext.getSystemService(ContentCaptureManager.class);
+        if (ccm == null || !ccm.isContentCaptureEnabled()) return;
 
         // ... and finally at the view level
         // NOTE: isImportantForContentCapture() is more expensive than cm.isContentCaptureEnabled()
         if (!isImportantForContentCapture()) return;
 
+        final ContentCaptureSession session = getContentCaptureSession(ccm);
+        if (session == null) return;
+
         if (appeared) {
             if (!isLaidOut() || !isVisibleToUser()
                     || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) {
@@ -8995,10 +9008,10 @@
                 }
                 return;
             }
-            // All good: notify the manager...
-            final ViewStructure structure = cm.newViewStructure(this);
+            // All good: notify it...
+            final ViewStructure structure = session.newViewStructure(this);
             onProvideContentCaptureStructure(structure, /* flags= */ 0);
-            cm.notifyViewAppeared(structure);
+            session.notifyViewAppeared(structure);
             // ...and set the flags
             mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
             mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
@@ -9014,14 +9027,84 @@
                 }
                 return;
             }
-            // All good: notify the manager...
-            cm.notifyViewDisappeared(getAutofillId());
+            // All good: notify it...
+            session.notifyViewDisappeared(getAutofillId());
             // ...and set the flags
             mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
             mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
         }
     }
 
+    /**
+     * Sets the (optional) {@link ContentCaptureSession} associated with this view.
+     *
+     * <p>This method should be called when you need to associate a {@link ContentCaptureContext} to
+     * the Content Capture events associated with this view or its view hierarchy (if it's a
+     * {@link ViewGroup}).
+     *
+     * <p>For example, if your activity is associated with a web domain, you could create a session
+     * {@code onCreate()} and associate it with the root view of the activity:
+     *
+     * <pre>
+     *   ContentCaptureSession oldSession = rootView.getContentCaptureSession();
+     *   if (oldSession != null) {
+     *     ContentCaptureSession newSession = oldSession.createContentCaptureSession(new
+     *        ContentCaptureContext.Builder().setUri(myUrl).build());
+     *     rootView.setContentCaptureSession(newSession);
+     *  }
+     * </pre>
+     *
+     * @param contentCaptureSession a session created by
+     * {@link ContentCaptureSession#createContentCaptureSession(
+     *        android.view.contentcapture.ContentCaptureContext)}.
+     */
+    public void setContentCaptureSession(@NonNull ContentCaptureSession contentCaptureSession) {
+        mContentCaptureSession = new WeakReference<>(
+                Preconditions.checkNotNull(contentCaptureSession));
+    }
+
+    /**
+     * Gets the session used to notify Content Capture events.
+     *
+     * @return session explicitly set by {@link #setContentCaptureSession(ContentCaptureSession)},
+     * inherited by ancestore, default session or {@code null} if content capture is disabled for
+     * this view.
+     */
+    @Nullable
+    public final ContentCaptureSession getContentCaptureSession() {
+        // First try the session explicitly set by setContentCaptureSession()
+        if (mContentCaptureSession != null) return mContentCaptureSession.get();
+
+        // Then the session explicitly set in an ancestor
+        ContentCaptureSession session = null;
+        if (mParent instanceof View) {
+            session = ((View) mParent).getContentCaptureSession();
+        }
+
+        // Finally, if no session was explicitly set, use the context's default session.
+        if (session == null) {
+            final ContentCaptureManager ccm = mContext
+                    .getSystemService(ContentCaptureManager.class);
+            return ccm == null ? null : ccm.getMainContentCaptureSession();
+        }
+        return session;
+    }
+
+    /**
+     * Optimized version of {@link #getContentCaptureSession()} that avoids a service lookup.
+     */
+    @Nullable
+    private ContentCaptureSession getContentCaptureSession(@NonNull ContentCaptureManager ccm) {
+        if (mContentCaptureSession != null) return mContentCaptureSession.get();
+
+        ContentCaptureSession session = null;
+        if (mParent instanceof View) {
+            session = ((View) mParent).getContentCaptureSession();
+        }
+
+        return session != null ? session : ccm.getMainContentCaptureSession();
+    }
+
     @Nullable
     private AutofillManager getAutofillManager() {
         return mContext.getSystemService(AutofillManager.class);
@@ -10405,6 +10488,7 @@
      *
      * @return The {@link WindowInsetsController} or {@code null} if the view isn't attached to a
      *         a window.
+     * @see Window#getInsetsController()
      * @hide pending unhide
      */
     public @Nullable WindowInsetsController getWindowInsetsController() {
@@ -14756,7 +14840,7 @@
      * @param flags Constant indicating the value which should be set
      * @param mask Constant indicating the bit range that should be changed
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     void setFlags(int flags, int mask) {
         final boolean accessibilityEnabled =
                 AccessibilityManager.getInstance(mContext).isEnabled();
@@ -24969,9 +25053,10 @@
         }
 
         final ViewRootImpl root = mAttachInfo.mViewRootImpl;
-        final SurfaceSession session = new SurfaceSession(root.mSurface);
+        final SurfaceSession session = new SurfaceSession();
         final SurfaceControl surfaceControl = new SurfaceControl.Builder(session)
                 .setName("drag surface")
+                .setParent(root.getSurfaceControl())
                 .setBufferSize(shadowSize.x, shadowSize.y)
                 .setFormat(PixelFormat.TRANSLUCENT)
                 .build();
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 741510e..8372032 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -143,7 +143,7 @@
     protected OnHierarchyChangeListener mOnHierarchyChangeListener;
 
     // The view contained within this ViewGroup that has or contains focus.
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private View mFocused;
     // The view contained within this ViewGroup (excluding nested keyboard navigation clusters)
     // that is or contains a default-focus view.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9fe0ddc..c0b4283 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -432,6 +432,7 @@
     // Surface can never be reassigned or cleared (use Surface.clear()).
     @UnsupportedAppUsage
     public final Surface mSurface = new Surface();
+    private final SurfaceControl mSurfaceControl = new SurfaceControl();
 
     /**
      * Child surface of {@code mSurface} with the same bounds as its parent, and crop bounds
@@ -1526,7 +1527,7 @@
      */
     public void createBoundsSurface(int zOrderLayer) {
         if (mSurfaceSession == null) {
-            mSurfaceSession = new SurfaceSession(mSurface);
+            mSurfaceSession = new SurfaceSession();
         }
         if (mBoundsSurfaceControl != null && mBoundsSurface.isValid()) {
             return; // surface control for bounds surface already exists.
@@ -1534,6 +1535,7 @@
 
         mBoundsSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
                 .setName("Bounds for - " + getTitle().toString())
+                .setParent(mSurfaceControl)
                 .build();
 
         setBoundsSurfaceCrop();
@@ -1567,6 +1569,8 @@
 
     private void destroySurface() {
         mSurface.release();
+        mSurfaceControl.release();
+
         mSurfaceSession = null;
 
         if (mBoundsSurfaceControl != null) {
@@ -1876,6 +1880,7 @@
     }
 
     void dispatchApplyInsets(View host) {
+        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchApplyInsets");
         WindowInsets insets = getWindowInsets(true /* forceConstruct */);
         final boolean dispatchCutout = (mWindowAttributes.layoutInDisplayCutoutMode
                 == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS);
@@ -1885,6 +1890,7 @@
             insets = insets.consumeDisplayCutout();
         }
         host.dispatchApplyWindowInsets(insets);
+        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
     }
 
     InsetsController getInsetsController() {
@@ -2763,7 +2769,7 @@
         }
     }
 
-    private void handleWindowFocusChanged(boolean reportToClient) {
+    private void handleWindowFocusChanged() {
         final boolean hasWindowFocus;
         final boolean inTouchMode;
         synchronized (this) {
@@ -2798,9 +2804,8 @@
                         } catch (RemoteException ex) {
                         }
                         // Retry in a bit.
-                        final Message msg = mHandler.obtainMessage(MSG_WINDOW_FOCUS_CHANGED);
-                        msg.arg1 = reportToClient ? 1 : 0;
-                        mHandler.sendMessageDelayed(msg, 500);
+                        mHandler.sendMessageDelayed(mHandler.obtainMessage(
+                                MSG_WINDOW_FOCUS_CHANGED), 500);
                         return;
                     }
                 }
@@ -2817,15 +2822,8 @@
             }
             if (mView != null) {
                 mAttachInfo.mKeyDispatchState.reset();
-                // We dispatch onWindowFocusChanged to child view only when window is gaining /
-                // losing focus.
-                // If the focus is updated from top display change but window focus on the display
-                // remains unchanged, will not callback onWindowFocusChanged again since it may
-                // be redundant & can affect the state when it callbacks.
-                if (reportToClient) {
-                    mView.dispatchWindowFocusChanged(hasWindowFocus);
-                    mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
-                }
+                mView.dispatchWindowFocusChanged(hasWindowFocus);
+                mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
                 if (mAttachInfo.mTooltipHost != null) {
                     mAttachInfo.mTooltipHost.hideTooltip();
                 }
@@ -4426,7 +4424,7 @@
                     }
                     break;
                 case MSG_WINDOW_FOCUS_CHANGED: {
-                    handleWindowFocusChanged(msg.arg1 != 0 /* reportToClient */);
+                    handleWindowFocusChanged();
                 } break;
                 case MSG_DIE:
                     doDie();
@@ -6807,7 +6805,12 @@
                 insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
                 mTmpFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
                 mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
-                mPendingMergedConfiguration, mSurface, mTempInsets);
+                mPendingMergedConfiguration, mSurfaceControl, mTempInsets);
+        if (mSurfaceControl.isValid()) {
+            mSurface.copyFrom(mSurfaceControl);
+        } else {
+            destroySurface();
+        }
 
         mPendingAlwaysConsumeNavBar =
                 (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0;
@@ -7370,7 +7373,7 @@
         }
 
         if (stage != null) {
-            handleWindowFocusChanged(true /* reportToClient */);
+            handleWindowFocusChanged();
             stage.deliver(q);
         } else {
             finishInputEvent(q);
@@ -7710,11 +7713,6 @@
     }
 
     public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
-        windowFocusChanged(hasFocus, inTouchMode, true /* reportToClient */);
-    }
-
-    public void windowFocusChanged(boolean hasFocus, boolean inTouchMode,
-            boolean reportToClient) {
         synchronized (this) {
             mWindowFocusChanged = true;
             mUpcomingWindowFocus = hasFocus;
@@ -7722,7 +7720,6 @@
         }
         Message msg = Message.obtain();
         msg.what = MSG_WINDOW_FOCUS_CHANGED;
-        msg.arg1 = reportToClient ? 1 : 0;
         mHandler.sendMessage(msg);
     }
 
@@ -8284,11 +8281,10 @@
         }
 
         @Override
-        public void windowFocusChanged(boolean hasFocus, boolean inTouchMode,
-                boolean reportToClient) {
+        public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
-                viewAncestor.windowFocusChanged(hasFocus, inTouchMode, reportToClient);
+                viewAncestor.windowFocusChanged(hasFocus, inTouchMode);
             }
         }
 
@@ -8496,6 +8492,10 @@
         mActivityRelaunched = true;
     }
 
+    public SurfaceControl getSurfaceControl() {
+        return mSurfaceControl;
+    }
+
     /**
      * Class for managing the accessibility interaction connection
      * based on the global accessibility state.
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 58ab817..097f368 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -38,6 +38,7 @@
 import android.graphics.drawable.Drawable;
 import android.media.session.MediaController;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -282,7 +283,7 @@
     private WindowControllerCallback mWindowControllerCallback;
     private OnRestrictedCaptionAreaChangedListener mOnRestrictedCaptionAreaChangedListener;
     private Rect mRestrictedCaptionAreaRect;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private WindowManager mWindowManager;
     @UnsupportedAppUsage
     private IBinder mAppToken;
@@ -1296,7 +1297,7 @@
     public abstract void alwaysReadCloseOnTouchAttr();
 
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
         final boolean isOutside =
                 event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(context, event)
diff --git a/core/java/android/view/WindowInsetsAnimationControlListener.java b/core/java/android/view/WindowInsetsAnimationControlListener.java
new file mode 100644
index 0000000..b27a23d
--- /dev/null
+++ b/core/java/android/view/WindowInsetsAnimationControlListener.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.NonNull;
+import android.view.WindowInsets.Type.InsetType;
+import android.view.inputmethod.EditorInfo;
+
+/**
+ * Interface that informs the client about {@link WindowInsetsAnimationController} state changes.
+ * @hide pending unhide
+ */
+public interface WindowInsetsAnimationControlListener {
+
+    /**
+     * Gets called as soon as the animation is ready to be controlled. This may be
+     * delayed when the IME needs to redraw because of an {@link EditorInfo} change, or when the
+     * window is starting up.
+     *
+     * @param controller The controller to control the inset animation.
+     * @param types The {@link InsetType}s it was able to gain control over. Note that this may be
+     *              different than the types passed into
+     *              {@link WindowInsetsController#controlWindowInsetsAnimation} in case the window
+     *              wasn't able to gain the controls because it wasn't the IME target or not
+     *              currently the window that's controlling the system bars.
+     */
+    void onReady(@NonNull WindowInsetsAnimationController controller, @InsetType int types);
+
+    /**
+     * Called when the window no longer has control over the requested types. If it loses control
+     * over one type, the whole control will be cancelled. If none of the requested types were
+     * available when requesting the control, the animation control will be cancelled immediately
+     * without {@link #onReady} being called.
+     */
+    void onCancelled();
+}
diff --git a/core/java/android/view/WindowInsetsAnimationController.java b/core/java/android/view/WindowInsetsAnimationController.java
new file mode 100644
index 0000000..9de517d
--- /dev/null
+++ b/core/java/android/view/WindowInsetsAnimationController.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.NonNull;
+import android.graphics.Insets;
+import android.view.WindowInsets.Type.InsetType;
+
+/**
+ * Interface to control a window inset animation frame-by-frame.
+ * @hide pending unhide
+ */
+public interface WindowInsetsAnimationController {
+
+    /**
+     * Retrieves the {@link Insets} when the windows this animation is controlling are fully hidden.
+     *
+     * @return Insets when the windows this animation is controlling are fully hidden.
+     */
+    @NonNull Insets getHiddenStateInsets();
+
+    /**
+     * Retrieves the {@link Insets} when the windows this animation is controlling are fully shown.
+     * <p>
+     * In case the size of a window causing insets is changing in the middle of the animation, we
+     * execute that height change after this animation has finished.
+     *
+     * @return Insets when the windows this animation is controlling are fully shown.
+     */
+    @NonNull Insets getShownStateInsets();
+
+    /**
+     * @return The current insets on the window. These will follow any animation changes.
+     */
+    @NonNull Insets getCurrentInsets();
+
+    /**
+     * @return The {@link InsetType}s this object is currently controlling.
+     */
+    @InsetType int getTypes();
+
+    /**
+     * Modifies the insets by indirectly moving the windows around in the system that are causing
+     * window insets.
+     * <p>
+     * Note that this will <b>not</b> inform the view system of a full inset change via
+     * {@link View#dispatchApplyWindowInsets} in order to avoid a full layout pass during the
+     * animation. If you'd like to animate views during a window inset animation, use
+     * TODO add link to animation listeners.
+     * <p>
+     * {@link View#dispatchApplyWindowInsets} will instead be called once the animation has
+     * finished, i.e. once {@link #finish} has been called.
+     *
+     * @param insets The new insets to apply. Based on the requested insets, the system will
+     *               calculate the positions of the windows in the system causing insets such that
+     *               the resulting insets of that configuration will match the passed in parameter.
+     *               Note that these insets are being clamped to the range from
+     *               {@link #getHiddenStateInsets} to {@link #getShownStateInsets}
+     */
+    void changeInsets(@NonNull Insets insets);
+
+    /**
+     * @param shownTypes The list of windows causing insets that should remain shown after finishing
+     *                   the animation.
+     */
+    void finish(@InsetType int shownTypes);
+}
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index 7be5f2e..a35be27 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.annotation.NonNull;
 import android.view.WindowInsets.Type.InsetType;
 
 /**
@@ -51,4 +52,15 @@
      *              would like to make disappear.
      */
     void hide(@InsetType int types);
+
+    /**
+     * Lets the application control window inset animations in a frame-by-frame manner by modifying
+     * the position of the windows in the system causing insets directly.
+     *
+     * @param types The {@link InsetType}s the application has requested to control.
+     * @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
+     *                 windows are ready to be controlled, among other callbacks.
+     */
+    void controlWindowInsetsAnimation(@InsetType int types,
+            @NonNull WindowInsetsAnimationControlListener listener);
 }
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 0e1f767..95a346f 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -30,6 +30,9 @@
 
 import dalvik.system.CloseGuard;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Abstraction for an Animation that can be applied to Views, Surfaces, or
  * other objects. See the {@link android.view.animation animation package
@@ -182,10 +185,14 @@
     Interpolator mInterpolator;
 
     /**
-     * The animation listener to be notified when the animation starts, ends or repeats.
+     * An animation listener to be notified when the animation starts, ends or repeats.
      */
-    @UnsupportedAppUsage
-    AnimationListener mListener;
+    private AnimationListener mListener;
+
+    /**
+     * A list of animation listeners to be notified when the animation starts, ends or repeats.
+     */
+    private List<AnimationListener> mListeners;
 
     /**
      * Desired Z order mode during animation.
@@ -371,23 +378,17 @@
         if (mListenerHandler == null) {
             mOnStart = new Runnable() {
                 public void run() {
-                    if (mListener != null) {
-                        mListener.onAnimationStart(Animation.this);
-                    }
+                    dispatchAnimationStart();
                 }
             };
             mOnRepeat = new Runnable() {
                 public void run() {
-                    if (mListener != null) {
-                        mListener.onAnimationRepeat(Animation.this);
-                    }
+                    dispatchAnimationRepeat();
                 }
             };
             mOnEnd = new Runnable() {
                 public void run() {
-                    if (mListener != null) {
-                        mListener.onAnimationEnd(Animation.this);
-                    }
+                    dispatchAnimationEnd();
                 }
             };
         }
@@ -830,6 +831,10 @@
         return true;
     }
 
+    private boolean hasAnimationListener() {
+        return mListener != null || (mListeners != null && !mListeners.isEmpty());
+    }
+
     /**
      * <p>Binds an animation listener to this animation. The animation listener
      * is notified of animation events such as the end of the animation or the
@@ -842,6 +847,32 @@
     }
 
     /**
+     * <p>Adds an animation listener to this animation. The animation listener
+     * is notified of animation events such as the end of the animation or the
+     * repetition of the animation.</p>
+     *
+     * @param listener the animation listener to be notified
+     */
+    public void addAnimationListener(AnimationListener listener) {
+        if (mListeners == null) {
+            mListeners = new ArrayList<>(1);
+        }
+        mListeners.add(listener);
+    }
+
+    /**
+     * <p>Removes an animation listener that has been added with
+     * {@link #addAnimationListener(AnimationListener)}.</p>
+     *
+     * @param listener the animation listener to be removed
+     */
+    public void removeAnimationListener(AnimationListener listener) {
+        if (mListeners != null) {
+            mListeners.remove(listener);
+        }
+    }
+
+    /**
      * Gurantees that this animation has an interpolator. Will use
      * a AccelerateDecelerateInterpolator is nothing else was specified.
      */
@@ -947,26 +978,59 @@
     }
 
     private void fireAnimationStart() {
-        if (mListener != null) {
-            if (mListenerHandler == null) mListener.onAnimationStart(this);
+        if (hasAnimationListener()) {
+            if (mListenerHandler == null) dispatchAnimationStart();
             else mListenerHandler.postAtFrontOfQueue(mOnStart);
         }
     }
 
     private void fireAnimationRepeat() {
-        if (mListener != null) {
-            if (mListenerHandler == null) mListener.onAnimationRepeat(this);
+        if (hasAnimationListener()) {
+            if (mListenerHandler == null) dispatchAnimationRepeat();
             else mListenerHandler.postAtFrontOfQueue(mOnRepeat);
         }
     }
 
     private void fireAnimationEnd() {
-        if (mListener != null) {
-            if (mListenerHandler == null) mListener.onAnimationEnd(this);
+        if (hasAnimationListener()) {
+            if (mListenerHandler == null) dispatchAnimationEnd();
             else mListenerHandler.postAtFrontOfQueue(mOnEnd);
         }
     }
 
+    void dispatchAnimationStart() {
+        if (mListener != null) {
+            mListener.onAnimationStart(this);
+        }
+        if (mListeners != null && !mListeners.isEmpty()) {
+            for (AnimationListener listener : mListeners) {
+                listener.onAnimationStart(this);
+            }
+        }
+    }
+
+    void dispatchAnimationRepeat() {
+        if (mListener != null) {
+            mListener.onAnimationRepeat(this);
+        }
+        if (mListeners != null && !mListeners.isEmpty()) {
+            for (AnimationListener listener : mListeners) {
+                listener.onAnimationRepeat(this);
+            }
+        }
+    }
+
+    void dispatchAnimationEnd() {
+        if (mListener != null) {
+            mListener.onAnimationEnd(this);
+        }
+        if (mListeners != null && !mListeners.isEmpty()) {
+            for (AnimationListener listener : mListeners) {
+                listener.onAnimationEnd(this);
+            }
+        }
+    }
+
     /**
      * Gets the transformation to apply at a specified point in time. Implementations of this
      * method should always replace the specified Transformation or document they are doing
diff --git a/core/java/android/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java
index 767024e..03c6ca6 100644
--- a/core/java/android/view/animation/AnimationSet.java
+++ b/core/java/android/view/animation/AnimationSet.java
@@ -389,16 +389,12 @@
         }
 
         if (started && !mStarted) {
-            if (mListener != null) {
-                mListener.onAnimationStart(this);
-            }
+            dispatchAnimationStart();
             mStarted = true;
         }
 
         if (ended != mEnded) {
-            if (mListener != null) {
-                mListener.onAnimationEnd(this);
-            }
+            dispatchAnimationEnd();
             mEnded = ended;
         }
 
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 9227249..56f973e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -77,7 +77,6 @@
 import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
-
 //TODO: use java.lang.ref.Cleaner once Android supports Java 9
 import sun.misc.Cleaner;
 
@@ -1471,8 +1470,8 @@
     // Note: don't need to use locked suffix because mContext is final.
     private AutofillClient getClient() {
         final AutofillClient client = mContext.getAutofillClient();
-        if (client == null && sDebug) {
-            Log.d(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
+        if (client == null && sVerbose) {
+            Log.v(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
                     + mContext);
         }
         return client;
@@ -2966,12 +2965,13 @@
             if (afm == null) return null;
 
             final View view = afm.getClient().autofillClientFindViewByAutofillIdTraversal(id);
-            // TODO(b/111330312): optimize (for example, use temp rect from attach info) and
-            // fix (for example, take system status bar height into account) logic below
+            final Rect windowVisibleDisplayFrame = new Rect();
+            view.getWindowVisibleDisplayFrame(windowVisibleDisplayFrame);
             final int[] location = new int[2];
             view.getLocationOnScreen(location);
-            final Rect rect = new Rect(location[0], location[1], location[0] + view.getWidth(),
-                    location[1] + view.getHeight());
+            final Rect rect = new Rect(location[0], location[1] - windowVisibleDisplayFrame.top,
+                    location[0] + view.getWidth(),
+                    location[1] - windowVisibleDisplayFrame.top + view.getHeight());
             if (sVerbose) {
                 Log.v(TAG, "Coordinates for " + id + ": " + rect);
             }
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
new file mode 100644
index 0000000..5166831
--- /dev/null
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.autofill.AutofillId;
+import android.view.contentcapture.ViewNode.ViewStructureImpl;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+
+/**
+ * A session that is explicitly created by the app (and hence is a descendant of
+ * {@link MainContentCaptureSession}).
+ *
+ * @hide
+ */
+final class ChildContentCaptureSession extends ContentCaptureSession {
+
+    @NonNull
+    private final MainContentCaptureSession mParent;
+
+    /**
+     * {@link ContentCaptureContext} set by client, or {@code null} when it's the
+     * {@link ContentCaptureManager#getMainContentCaptureSession() default session} for the
+     * context.
+     *
+     * @hide
+     */
+    @NonNull
+    private final ContentCaptureContext mClientContext;
+
+    /** @hide */
+    protected ChildContentCaptureSession(@NonNull MainContentCaptureSession parent,
+            @NonNull ContentCaptureContext clientContext) {
+        mParent = parent;
+        mClientContext = Preconditions.checkNotNull(clientContext);
+    }
+
+    @Override
+    ContentCaptureSession newChild(@NonNull ContentCaptureContext context) {
+        // TODO(b/121033016): implement it
+        throw new UnsupportedOperationException("grand-children not implemented yet");
+    }
+
+    @Override
+    void flush() {
+        mParent.flush();
+    }
+
+    @Override
+    void onDestroy() {
+        mParent.notifyChildSessionFinished(mParent.mId, mId);
+    }
+
+    @Override
+    void internalNotifyViewAppeared(@NonNull ViewStructureImpl node) {
+        mParent.notifyViewAppeared(mId, node);
+    }
+
+    @Override
+    void internalNotifyViewDisappeared(@NonNull AutofillId id) {
+        mParent.notifyViewDisappeared(mId, id);
+    }
+
+    @Override
+    void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
+            int flags) {
+        mParent.notifyViewTextChanged(mId, id, text, flags);
+    }
+    @Override
+    boolean isContentCaptureEnabled() {
+        return mParent.isContentCaptureEnabled();
+    }
+
+    @Override
+    void dump(String prefix, PrintWriter pw) {
+        if (mClientContext != null) {
+            // NOTE: we don't dump clientContent because it could have PII
+            pw.print(prefix); pw.println("hasClientContext");
+        }
+        super.dump(prefix, pw);
+    }
+}
diff --git a/core/java/android/service/contentcapture/InteractionContext.aidl b/core/java/android/view/contentcapture/ContentCaptureContext.aidl
similarity index 89%
rename from core/java/android/service/contentcapture/InteractionContext.aidl
rename to core/java/android/view/contentcapture/ContentCaptureContext.aidl
index 982e095..da492f5 100644
--- a/core/java/android/service/contentcapture/InteractionContext.aidl
+++ b/core/java/android/view/contentcapture/ContentCaptureContext.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.service.contentcapture;
+package android.view.contentcapture;
 
-parcelable InteractionContext;
+parcelable ContentCaptureContext;
diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java
new file mode 100644
index 0000000..2d2987a
--- /dev/null
+++ b/core/java/android/view/contentcapture/ContentCaptureContext.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.TaskInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.View;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Context associated with a {@link ContentCaptureSession}.
+ */
+public final class ContentCaptureContext implements Parcelable {
+
+    /*
+     * IMPLEMENTATION NOTICE:
+     *
+     * This object contains both the info that's explicitly added by apps (hence it's public), but
+     * it also contains info injected by the server (and are accessible through @SystemApi methods).
+     */
+
+    /**
+     * Flag used to indicate that the app explicitly disabled content capture for the activity
+     * (using
+     * {@link android.view.contentcapture.ContentCaptureManager#setContentCaptureEnabled(boolean)}),
+     * in which case the service will just receive activity-level events.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int FLAG_DISABLED_BY_APP = 0x1;
+
+    /**
+     * Flag used to indicate that the activity's window is tagged with
+     * {@link android.view.Display#FLAG_SECURE}, in which case the service will just receive
+     * activity-level events.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int FLAG_DISABLED_BY_FLAG_SECURE = 0x2;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+            FLAG_DISABLED_BY_APP,
+            FLAG_DISABLED_BY_FLAG_SECURE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface ContextCreationFlags{}
+
+    /**
+     * Flag indicating if this object has the app-provided context (which is set on
+     * {@link ContentCaptureSession#createContentCaptureSession(ContentCaptureContext)}).
+     */
+    private final boolean mHasClientContext;
+
+    // Fields below are set by app on Builder
+    private final @Nullable Bundle mExtras;
+    private final @Nullable Uri mUri;
+
+    // Fields below are set by server when the session starts
+    // TODO(b/111276913): create new object for taskId + componentName / reuse on other places
+    private final @Nullable ComponentName mComponentName;
+    private final int mTaskId;
+    private final int mDisplayId;
+    private final int mFlags;
+
+    // Fields below are set by the service upon "delivery" and are not marshalled in the parcel
+    private @Nullable String mParentSessionId;
+
+    /** @hide */
+    public ContentCaptureContext(@Nullable ContentCaptureContext clientContext,
+            @NonNull ComponentName componentName, int taskId, int displayId, int flags) {
+        if (clientContext != null) {
+            mHasClientContext = true;
+            mExtras = clientContext.mExtras;
+            mUri = clientContext.mUri;
+        } else {
+            mHasClientContext = false;
+            mExtras = null;
+            mUri = null;
+        }
+        mComponentName = Preconditions.checkNotNull(componentName);
+        mTaskId = taskId;
+        mDisplayId = displayId;
+        mFlags = flags;
+    }
+
+    private ContentCaptureContext(@NonNull Builder builder) {
+        mHasClientContext = true;
+        mExtras = builder.mExtras;
+        mUri = builder.mUri;
+
+        mComponentName  = null;
+        mTaskId = mFlags = mDisplayId = 0;
+    }
+
+    /**
+     * Gets the (optional) extras set by the app.
+     *
+     * <p>It can be used to provide vendor-specific data that can be modified and examined.
+     *
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    /**
+     * Gets the (optional) URI set by the app.
+     *
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public Uri getUri() {
+        return mUri;
+    }
+
+    /**
+     * Gets the id of the {@link TaskInfo task} associated with this context.
+     *
+     * @hide
+     */
+    @SystemApi
+    public int getTaskId() {
+        return mTaskId;
+    }
+
+    /**
+     * Gets the activity associated with this context, or {@code null} when it is a child session.
+     *
+     * @hide
+     */
+    @SystemApi
+    public @Nullable ComponentName getActivityComponent() {
+        return mComponentName;
+    }
+
+    /**
+     * Gets the id of the session that originated this session (through
+     * {@link ContentCaptureSession#createContentCaptureSession(ContentCaptureContext)}),
+     * or {@code null} if this is the main session associated with the Activity's {@link Context}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public @Nullable ContentCaptureSessionId getParentSessionId() {
+        return mParentSessionId == null ?  null : new ContentCaptureSessionId(mParentSessionId);
+    }
+
+    /** @hide */
+    public void setParentSessionId(@NonNull String parentSessionId) {
+        mParentSessionId = parentSessionId;
+    }
+
+    /**
+     * Gets the ID of the display associated with this context, as defined by
+     * {G android.hardware.display.DisplayManager#getDisplay(int) DisplayManager.getDisplay()}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public int getDisplayId() {
+        return mDisplayId;
+    }
+
+    /**
+     * Gets the flags associated with this context.
+     *
+     * @return any combination of {@link #FLAG_DISABLED_BY_FLAG_SECURE} and
+     * {@link #FLAG_DISABLED_BY_APP}.
+     *
+     * @hide
+     */
+    @SystemApi
+     public @ContextCreationFlags int getFlags() {
+        return mFlags;
+    }
+
+    /**
+     * Builder for {@link ContentCaptureContext} objects.
+     */
+    public static final class Builder {
+        private Bundle mExtras;
+        private Uri mUri;
+
+        /**
+         * Sets extra options associated with this context.
+         *
+         * <p>It can be used to provide vendor-specific data that can be modified and examined.
+         *
+         * @param extras extra options.
+         * @return this builder.
+         */
+        @NonNull
+        public Builder setExtras(@NonNull Bundle extras) {
+            // TODO(b/111276913): check build just once / throw exception / test / document
+            mExtras = Preconditions.checkNotNull(extras);
+            return this;
+        }
+
+        /**
+         * Sets the {@link Uri} associated with this context.
+         *
+         * <p>See {@link View#setContentCaptureSession(ContentCaptureSession)} for an example.
+         *
+         * @param uri URI associated with this context.
+         * @return this builder.
+         */
+        @NonNull
+        public Builder setUri(@NonNull Uri uri) {
+            // TODO(b/111276913): check build just once / throw exception / test / document
+            mUri = Preconditions.checkNotNull(uri);
+            return this;
+        }
+
+        /**
+         * Builds the {@link ContentCaptureContext}.
+         */
+        public ContentCaptureContext build() {
+            // TODO(b/111276913): check build just once / throw exception / test / document
+            // TODO(b/111276913): make sure it at least one property (uri / extras) / test /
+            // throw exception / documment
+            return new ContentCaptureContext(this);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    // TODO(b/111276913): dump to proto as well
+    public void dump(PrintWriter pw) {
+        pw.print("comp="); pw.print(ComponentName.flattenToShortString(mComponentName));
+        pw.print(", taskId="); pw.print(mTaskId);
+        pw.print(", displayId="); pw.print(mDisplayId);
+        if (mParentSessionId != null) {
+            pw.print(", parentId="); pw.print(mParentSessionId);
+        }
+        if (mFlags > 0) {
+            pw.print(", flags="); pw.print(mFlags);
+        }
+        if (mExtras != null) {
+            // NOTE: cannot dump because it could contain PII
+            pw.print(", hasExtras");
+        }
+        if (mUri != null) {
+            // NOTE: cannot dump because it could contain PII
+            pw.print(", hasUri");
+        }
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder("Context[act=")
+                .append(ComponentName.flattenToShortString(mComponentName))
+                .append(", taskId=").append(mTaskId)
+                .append(", displayId=").append(mDisplayId)
+                .append(", flags=").append(mFlags);
+        if (mParentSessionId != null) {
+            builder.append(", parentId=").append(mParentSessionId);
+        }
+        if (mExtras != null) {
+            // NOTE: cannot print because it could contain PII
+            builder.append(", hasExtras");
+        }
+        if (mUri != null) {
+            // NOTE: cannot print because it could contain PII
+            builder.append(", hasUri");
+        }
+        return builder.append(']').toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeInt(mHasClientContext ? 1 : 0);
+        if (mHasClientContext) {
+            parcel.writeParcelable(mUri, flags);
+            parcel.writeBundle(mExtras);
+        }
+        parcel.writeParcelable(mComponentName, flags);
+        if (mComponentName != null) {
+            parcel.writeInt(mTaskId);
+            parcel.writeInt(mDisplayId);
+            parcel.writeInt(mFlags);
+        }
+    }
+
+    public static final Parcelable.Creator<ContentCaptureContext> CREATOR =
+            new Parcelable.Creator<ContentCaptureContext>() {
+
+        @Override
+        public ContentCaptureContext createFromParcel(Parcel parcel) {
+            final boolean hasClientContext = parcel.readInt() == 1;
+
+            final ContentCaptureContext clientContext;
+            if (hasClientContext) {
+                final Builder builder = new Builder();
+                final Uri uri = parcel.readParcelable(null);
+                final Bundle extras = parcel.readBundle();
+                if (uri != null) builder.setUri(uri);
+                if (extras != null) builder.setExtras(extras);
+                // Must reconstruct the client context using the Builder API
+                clientContext = new ContentCaptureContext(builder);
+            } else {
+                clientContext = null;
+            }
+            final ComponentName componentName = parcel.readParcelable(null);
+            if (componentName == null) {
+                // Client-state only
+                return clientContext;
+            } else {
+                final int taskId = parcel.readInt();
+                final int displayId = parcel.readInt();
+                final int flags = parcel.readInt();
+                return new ContentCaptureContext(clientContext, componentName, taskId, displayId,
+                        flags);
+            }
+        }
+
+        @Override
+        public ContentCaptureContext[] newArray(int size) {
+            return new ContentCaptureContext[size];
+        }
+    };
+}
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index 66fa530..9e3da92 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -21,7 +21,6 @@
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.SystemClock;
 import android.view.autofill.AutofillId;
 
 import com.android.internal.util.Preconditions;
@@ -30,65 +29,28 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
-// TODO(b/111276913): add javadocs / implement Parcelable / implement
 /** @hide */
 @SystemApi
 public final class ContentCaptureEvent implements Parcelable {
 
     /** @hide */
-    public static final int TYPE_ACTIVITY_DESTROYED = -2;
+    public static final int TYPE_SESSION_FINISHED = -2;
     /** @hide */
-    public static final int TYPE_ACTIVITY_CREATED = -1;
-
-    /**
-     * Called when the activity is started.
-     *
-     * @deprecated - TODO(b/111276913): we should abstract the Activity lifecycle concepts into
-     * something related to a session and/or domain.
-     */
-    @Deprecated
-    public static final int TYPE_ACTIVITY_STARTED  = 1;
-
-    /**
-     * Called when the activity is resumed.
-     *
-     * @deprecated - TODO(b/111276913): we should abstract the Activity lifecycle concepts into
-     * something related to a session and/or domain.
-     */
-    @Deprecated
-    public static final int TYPE_ACTIVITY_RESUMED = 2;
-
-    /**
-     * Called when the activity is paused.
-     *
-     * @deprecated - TODO(b/111276913): we should abstract the Activity lifecycle concepts into
-     * something related to a session and/or domain.
-     */
-    @Deprecated
-    public static final int TYPE_ACTIVITY_PAUSED = 3;
-
-    /**
-     * Called when the activity is stopped.
-     *
-     * @deprecated - TODO(b/111276913): we should abstract the Activity lifecycle concepts into
-     * something related to a session and/or domain.
-     */
-    @Deprecated
-    public static final int TYPE_ACTIVITY_STOPPED  = 4;
+    public static final int TYPE_SESSION_STARTED = -1;
 
     /**
      * Called when a node has been added to the screen and is visible to the user.
      *
      * <p>The metadata of the node is available through {@link #getViewNode()}.
      */
-    public static final int TYPE_VIEW_APPEARED = 5;
+    public static final int TYPE_VIEW_APPEARED = 1;
 
     /**
      * Called when a node has been removed from the screen and is not visible to the user anymore.
      *
      * <p>The id of the node is available through {@link #getId()}.
      */
-    public static final int TYPE_VIEW_DISAPPEARED = 6;
+    public static final int TYPE_VIEW_DISAPPEARED = 2;
 
     /**
      * Called when the text of a node has been changed.
@@ -96,16 +58,12 @@
      * <p>The id of the node is available through {@link #getId()}, and the new text is
      * available through {@link #getText()}.
      */
-    public static final int TYPE_VIEW_TEXT_CHANGED = 7;
+    public static final int TYPE_VIEW_TEXT_CHANGED = 3;
 
     // TODO(b/111276913): add event to indicate when FLAG_SECURE was changed?
 
     /** @hide */
     @IntDef(prefix = { "TYPE_" }, value = {
-            TYPE_ACTIVITY_STARTED,
-            TYPE_ACTIVITY_PAUSED,
-            TYPE_ACTIVITY_RESUMED,
-            TYPE_ACTIVITY_STOPPED,
             TYPE_VIEW_APPEARED,
             TYPE_VIEW_DISAPPEARED,
             TYPE_VIEW_TEXT_CHANGED
@@ -113,29 +71,32 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventType{}
 
+    private final @NonNull String mSessionId;
     private final int mType;
     private final long mEventTime;
     private final int mFlags;
     private @Nullable AutofillId mId;
     private @Nullable ViewNode mNode;
     private @Nullable CharSequence mText;
+    private @Nullable String mParentSessionId;
+    private @Nullable ContentCaptureContext mClientContext;
 
     /** @hide */
-    public ContentCaptureEvent(int type, long eventTime, int flags) {
+    public ContentCaptureEvent(@NonNull String sessionId, int type, long eventTime, int flags) {
+        mSessionId = sessionId;
         mType = type;
         mEventTime = eventTime;
         mFlags = flags;
     }
 
-
     /** @hide */
-    public ContentCaptureEvent(int type, int flags) {
-        this(type, SystemClock.uptimeMillis(), flags);
+    public ContentCaptureEvent(@NonNull String sessionId, int type, int flags) {
+        this(sessionId, type, System.currentTimeMillis(), flags);
     }
 
     /** @hide */
-    public ContentCaptureEvent(int type) {
-        this(type, /* flags= */ 0);
+    public ContentCaptureEvent(@NonNull String sessionId, int type) {
+        this(sessionId, type, /* flags= */ 0);
     }
 
     /** @hide */
@@ -144,13 +105,61 @@
         return this;
     }
 
+    /**
+     * Used by {@link #TYPE_SESSION_STARTED} and {@link #TYPE_SESSION_FINISHED}.
+     *
+     * @hide
+     */
+    public ContentCaptureEvent setParentSessionId(@NonNull String parentSessionId) {
+        mParentSessionId = parentSessionId;
+        return this;
+    }
+
+    /**
+     * Used by {@link #TYPE_SESSION_STARTED} and {@link #TYPE_SESSION_FINISHED}.
+     *
+     * @hide
+     */
+    public ContentCaptureEvent setClientContext(@NonNull ContentCaptureContext clientContext) {
+        mClientContext = clientContext;
+        return this;
+    }
+
     /** @hide */
+    @NonNull
+    public String getSessionId() {
+        return mSessionId;
+    }
+
+    /**
+     * Used by {@link #TYPE_SESSION_STARTED} and {@link #TYPE_SESSION_FINISHED}.
+     *
+     * @hide
+     */
+    @Nullable
+    public String getParentSessionId() {
+        return mParentSessionId;
+    }
+
+    /**
+     * Used by {@link #TYPE_SESSION_STARTED}.
+     *
+     * @hide
+     */
+    @Nullable
+    public ContentCaptureContext getClientContext() {
+        return mClientContext;
+    }
+
+    /** @hide */
+    @NonNull
     public ContentCaptureEvent setViewNode(@NonNull ViewNode node) {
         mNode = Preconditions.checkNotNull(node);
         return this;
     }
 
     /** @hide */
+    @NonNull
     public ContentCaptureEvent setText(@Nullable CharSequence text) {
         mText = text;
         return this;
@@ -159,9 +168,7 @@
     /**
      * Gets the type of the event.
      *
-     * @return one of {@link #TYPE_ACTIVITY_STARTED}, {@link #TYPE_ACTIVITY_RESUMED},
-     * {@link #TYPE_ACTIVITY_PAUSED}, {@link #TYPE_ACTIVITY_STOPPED},
-     * {@link #TYPE_VIEW_APPEARED}, {@link #TYPE_VIEW_DISAPPEARED},
+     * @return one of {@link #TYPE_VIEW_APPEARED}, {@link #TYPE_VIEW_DISAPPEARED},
      * or {@link #TYPE_VIEW_TEXT_CHANGED}.
      */
     public @EventType int getType() {
@@ -169,7 +176,7 @@
     }
 
     /**
-     * Gets when the event was generated, in ms.
+     * Gets when the event was generated, in millis since epoch.
      */
     public long getEventTime() {
         return mEventTime;
@@ -179,7 +186,7 @@
      * Gets optional flags associated with the event.
      *
      * @return either {@code 0} or
-     * {@link android.view.contentcapture.ContentCaptureManager#FLAG_USER_INPUT}.
+     * {@link android.view.contentcapture.ContentCaptureSession#FLAG_USER_INPUT}.
      */
     public int getFlags() {
         return mFlags;
@@ -226,7 +233,17 @@
             pw.print(", id="); pw.print(mId);
         }
         if (mNode != null) {
-            pw.print(", id="); pw.print(mNode.getAutofillId());
+            pw.print(", mNode.id="); pw.print(mNode.getAutofillId());
+        }
+        if (mSessionId != null) {
+            pw.print(", sessionId="); pw.print(mSessionId);
+        }
+        if (mParentSessionId != null) {
+            pw.print(", parentSessionId="); pw.print(mParentSessionId);
+        }
+        if (mText != null) {
+            // Cannot print content because could have PII
+            pw.print(", text="); pw.print(mText.length()); pw.print("_chars");
         }
     }
 
@@ -257,12 +274,19 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(mSessionId);
         parcel.writeInt(mType);
         parcel.writeLong(mEventTime);
         parcel.writeInt(mFlags);
         parcel.writeParcelable(mId, flags);
         ViewNode.writeToParcel(parcel, mNode, flags);
         parcel.writeCharSequence(mText);
+        if (mType == TYPE_SESSION_STARTED || mType == TYPE_SESSION_FINISHED) {
+            parcel.writeString(mParentSessionId);
+        }
+        if (mType == TYPE_SESSION_STARTED) {
+            parcel.writeParcelable(mClientContext, flags);
+        }
     }
 
     public static final Parcelable.Creator<ContentCaptureEvent> CREATOR =
@@ -270,10 +294,12 @@
 
         @Override
         public ContentCaptureEvent createFromParcel(Parcel parcel) {
+            final String sessionId = parcel.readString();
             final int type = parcel.readInt();
             final long eventTime  = parcel.readLong();
             final int flags = parcel.readInt();
-            final ContentCaptureEvent event = new ContentCaptureEvent(type, eventTime, flags);
+            final ContentCaptureEvent event =
+                    new ContentCaptureEvent(sessionId, type, eventTime, flags);
             final AutofillId id = parcel.readParcelable(null);
             if (id != null) {
                 event.setAutofillId(id);
@@ -283,6 +309,12 @@
                 event.setViewNode(node);
             }
             event.setText(parcel.readCharSequence());
+            if (type == TYPE_SESSION_STARTED || type == TYPE_SESSION_FINISHED) {
+                event.setParentSessionId(parcel.readString());
+            }
+            if (type == TYPE_SESSION_STARTED) {
+                event.setClientContext(parcel.readParcelable(null));
+            }
             return event;
         }
 
@@ -295,14 +327,10 @@
     /** @hide */
     public static String getTypeAsString(@EventType int type) {
         switch (type) {
-            case TYPE_ACTIVITY_STARTED:
-                return "ACTIVITY_STARTED";
-            case TYPE_ACTIVITY_RESUMED:
-                return "ACTIVITY_RESUMED";
-            case TYPE_ACTIVITY_PAUSED:
-                return "ACTIVITY_PAUSED";
-            case TYPE_ACTIVITY_STOPPED:
-                return "ACTIVITY_STOPPED";
+            case TYPE_SESSION_STARTED:
+                return "SESSION_STARTED";
+            case TYPE_SESSION_FINISHED:
+                return "SESSION_FINISHED";
             case TYPE_VIEW_APPEARED:
                 return "VIEW_APPEARED";
             case TYPE_VIEW_DISAPPEARED:
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index cc0264a..9830790 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -15,36 +15,20 @@
  */
 package android.view.contentcapture;
 
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
-
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemService;
+import android.annotation.UiThread;
 import android.content.ComponentName;
 import android.content.Context;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.SystemClock;
 import android.util.Log;
-import android.util.TimeUtils;
-import android.view.View;
-import android.view.ViewStructure;
-import android.view.autofill.AutofillId;
-import android.view.contentcapture.ContentCaptureEvent.EventType;
 
-import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.Preconditions;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.UUID;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /*
@@ -62,63 +46,11 @@
 
     private static final String TAG = ContentCaptureManager.class.getSimpleName();
 
-    // TODO(b/111276913): define a way to dynamically set them(for example, using settings?)
-    private static final boolean VERBOSE = false;
-    private static final boolean DEBUG = true; // STOPSHIP if not set to false
-
-    /**
-     * Used to indicate that a text change was caused by user input (for example, through IME).
-     */
-    //TODO(b/111276913): link to notifyTextChanged() method once available
-    public static final int FLAG_USER_INPUT = 0x1;
-
-    /**
-     * Initial state, when there is no session.
-     *
-     * @hide
-     */
-    public static final int STATE_UNKNOWN = 0;
-
-    /**
-     * Service's startSession() was called, but server didn't confirm it was created yet.
-     *
-     * @hide
-     */
-    public static final int STATE_WAITING_FOR_SERVER = 1;
-
-    /**
-     * Session is active.
-     *
-     * @hide
-     */
-    public static final int STATE_ACTIVE = 2;
-
-    /**
-     * Session is disabled.
-     *
-     * @hide
-     */
-    public static final int STATE_DISABLED = 3;
-
-    /**
-     * Handler message used to flush the buffer.
-     */
-    private static final int MSG_FLUSH = 1;
-
-
     private static final String BG_THREAD_NAME = "intel_svc_streamer_thread";
 
-    /**
-     * Maximum number of events that are buffered before sent to the app.
-     */
-    // TODO(b/111276913): use settings
-    private static final int MAX_BUFFER_SIZE = 100;
-
-    /**
-     * Frequency the buffer is flushed if stale.
-     */
-    // TODO(b/111276913): use settings
-    private static final int FLUSHING_FREQUENCY_MS = 5_000;
+    // TODO(b/121044306): define a way to dynamically set them(for example, using settings?)
+    static final boolean VERBOSE = false;
+    static final boolean DEBUG = true; // STOPSHIP if not set to false
 
     @NonNull
     private final AtomicBoolean mDisabled = new AtomicBoolean();
@@ -129,29 +61,12 @@
     @Nullable
     private final IContentCaptureManager mService;
 
-    @Nullable
-    private String mId;
-
-    private int mState = STATE_UNKNOWN;
-
-    @Nullable
-    private IBinder mApplicationToken;
-
-    @Nullable
-    private ComponentName mComponentName;
-
-    /**
-     * List of events held to be sent as a batch.
-     */
-    @Nullable
-    private ArrayList<ContentCaptureEvent> mEvents;
-
-    // TODO(b/111276913): use UI Thread directly (as calls are one-way) or a shared thread / handler
+    // TODO(b/119220549): use UI Thread directly (as calls are one-way) or a shared thread / handler
     // held at the Application level
+    @NonNull
     private final Handler mHandler;
 
-    // Used just for debugging purposes (on dump)
-    private long mNextFlush;
+    private MainContentCaptureSession mMainSession;
 
     /** @hide */
     public ContentCaptureManager(@NonNull Context context,
@@ -161,290 +76,67 @@
             Log.v(TAG, "Constructor for " + context.getPackageName());
         }
         mService = service;
-        // TODO(b/111276913): use an existing bg thread instead...
+        // TODO(b/119220549): use an existing bg thread instead...
         final HandlerThread bgThread = new HandlerThread(BG_THREAD_NAME);
         bgThread.start();
         mHandler = Handler.createAsync(bgThread.getLooper());
     }
 
-    /** @hide */
-    public void onActivityCreated(@NonNull IBinder token, @NonNull ComponentName componentName) {
-        if (!isContentCaptureEnabled()) return;
-
-        mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleStartSession, this,
-                token, componentName));
-    }
-
-    private void handleStartSession(@NonNull IBinder token, @NonNull ComponentName componentName) {
-        if (mState != STATE_UNKNOWN) {
-            // TODO(b/111276913): revisit this scenario
-            Log.w(TAG, "ignoring handleStartSession(" + token + ") while on state "
-                    + getStateAsString(mState));
-            return;
-        }
-        mState = STATE_WAITING_FOR_SERVER;
-        mId = UUID.randomUUID().toString();
-        mApplicationToken = token;
-        mComponentName = componentName;
-
-        if (VERBOSE) {
-            Log.v(TAG, "handleStartSession(): token=" + token + ", act="
-                    + getActivityDebugName() + ", id=" + mId);
-        }
-        final int flags = 0; // TODO(b/111276913): get proper flags
-
-        try {
-            mService.startSession(mContext.getUserId(), mApplicationToken, componentName,
-                    mId, flags, new IResultReceiver.Stub() {
-                        @Override
-                        public void send(int resultCode, Bundle resultData) {
-                            handleSessionStarted(resultCode);
-                        }
-                    });
-        } catch (RemoteException e) {
-            Log.w(TAG, "Error starting session for " + componentName.flattenToShortString() + ": "
-                    + e);
-        }
-    }
-
-    private void handleSessionStarted(int resultCode) {
-        mState = resultCode;
-        mDisabled.set(mState == STATE_DISABLED);
-        if (VERBOSE) {
-            Log.v(TAG, "onActivityStarted() result: code=" + resultCode + ", id=" + mId
-                    + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get());
-        }
-    }
-
-    private void handleSendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
-        if (mEvents == null) {
-            if (VERBOSE) {
-                Log.v(TAG, "Creating buffer for " + MAX_BUFFER_SIZE + " events");
-            }
-            mEvents = new ArrayList<>(MAX_BUFFER_SIZE);
-        }
-        mEvents.add(event);
-
-        final int numberEvents = mEvents.size();
-
-        // TODO(b/120784831): need to optimize it so we buffer changes until a number of X are
-        // buffered (either total or per autofillid). For
-        // example, if the user typed "a", "b", "c" and the threshold is 3, we should buffer
-        // "a" and "b" then send "abc".
-        final boolean bufferEvent = numberEvents < MAX_BUFFER_SIZE;
-
-        if (bufferEvent && !forceFlush) {
-            handleScheduleFlush();
-            return;
-        }
-
-        if (mState != STATE_ACTIVE) {
-            // Callback from startSession hasn't been called yet - typically happens on system
-            // apps that are started before the system service
-            // TODO(b/111276913): try to ignore session while system is not ready / boot
-            // not complete instead. Similarly, the manager service should return right away
-            // when the user does not have a service set
-            if (VERBOSE) {
-                Log.v(TAG, "Closing session for " + getActivityDebugName()
-                        + " after " + numberEvents + " delayed events and state "
-                        + getStateAsString(mState));
-            }
-            handleResetState();
-            // TODO(b/111276913): blacklist activity / use special flag to indicate that
-            // when it's launched again
-            return;
-        }
-
-        if (mId == null) {
-            // Sanity check - should not happen
-            Log.wtf(TAG, "null session id for " + getActivityDebugName());
-            return;
-        }
-
-        handleForceFlush();
-    }
-
-    private void handleScheduleFlush() {
-        if (mHandler.hasMessages(MSG_FLUSH)) {
-            // "Renew" the flush message by removing the previous one
-            mHandler.removeMessages(MSG_FLUSH);
-        }
-        mNextFlush = SystemClock.elapsedRealtime() + FLUSHING_FREQUENCY_MS;
-        if (VERBOSE) {
-            Log.v(TAG, "Scheduled to flush in " + FLUSHING_FREQUENCY_MS + "ms: " + mNextFlush);
-        }
-        mHandler.sendMessageDelayed(
-                obtainMessage(ContentCaptureManager::handleFlushIfNeeded, this).setWhat(MSG_FLUSH),
-                FLUSHING_FREQUENCY_MS);
-    }
-
-    private void handleFlushIfNeeded() {
-        if (mEvents.isEmpty()) {
-            if (VERBOSE) Log.v(TAG, "Nothing to flush");
-            return;
-        }
-        handleForceFlush();
-    }
-
-    private void handleForceFlush() {
-        final int numberEvents = mEvents.size();
-        try {
-            if (DEBUG) {
-                Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName());
-            }
-            mHandler.removeMessages(MSG_FLUSH);
-            mService.sendEvents(mContext.getUserId(), mId, mEvents);
-            // TODO(b/111276913): decide whether we should clear or set it to null, as each has
-            // its own advantages: clearing will save extra allocations while the session is
-            // active, while setting to null would save memory if there's no more event coming.
-            mEvents.clear();
-        } catch (RemoteException e) {
-            Log.w(TAG, "Error sending " + numberEvents + " for " + getActivityDebugName()
-                    + ": " + e);
-        }
+    @NonNull
+    private static Handler newHandler() {
+        // TODO(b/119220549): use an existing bg thread instead...
+        // TODO(b/119220549): use UI Thread directly (as calls are one-way) or an existing bgThread
+        // or a shared thread / handler held at the Application level
+        final HandlerThread bgThread = new HandlerThread(BG_THREAD_NAME);
+        bgThread.start();
+        return Handler.createAsync(bgThread.getLooper());
     }
 
     /**
-     * Used for intermediate events (i.e, other than created and destroyed).
+     * Gets the main session associated with the context.
      *
-     * @hide
-     */
-    public void onActivityLifecycleEvent(@EventType int type) {
-        if (!isContentCaptureEnabled()) return;
-        if (VERBOSE) {
-            Log.v(TAG, "onActivityLifecycleEvent() for " + getActivityDebugName()
-                    + ": " + ContentCaptureEvent.getTypeAsString(type));
-        }
-        mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleSendEvent, this,
-                new ContentCaptureEvent(type), /* forceFlush= */ true));
-    }
-
-    /** @hide */
-    public void onActivityDestroyed() {
-        if (!isContentCaptureEnabled()) return;
-
-        //TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
-        // id) and send it to the cache of batched commands
-        if (VERBOSE) {
-            Log.v(TAG, "onActivityDestroyed(): state=" + getStateAsString(mState)
-                    + ", mId=" + mId);
-        }
-
-        mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleFinishSession, this));
-    }
-
-    private void handleFinishSession() {
-        //TODO(b/111276913): right now both the ContentEvents and lifecycle sessions are sent
-        // to system_server, so it's ok to call both in sequence here. But once we split
-        // them so the events are sent directly to the service, we need to make sure they're
-        // sent in order.
-        try {
-            if (DEBUG) {
-                Log.d(TAG, "Finishing session " + mId + " with "
-                        + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
-                        + getActivityDebugName());
-            }
-
-            mService.finishSession(mContext.getUserId(), mId, mEvents);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error finishing session " + mId + " for " + getActivityDebugName()
-                    + ": " + e);
-        } finally {
-            handleResetState();
-        }
-    }
-
-    private void handleResetState() {
-        mState = STATE_UNKNOWN;
-        mId = null;
-        mApplicationToken = null;
-        mComponentName = null;
-        mEvents = null;
-        mHandler.removeMessages(MSG_FLUSH);
-    }
-
-    /**
-     * Notifies the Intelligence Service that a node has been added to the view structure.
-     *
-     * <p>Typically called "manually" by views that handle their own virtual view hierarchy, or
-     * automatically by the Android System for views that return {@code true} on
-     * {@link View#onProvideContentCaptureStructure(ViewStructure, int)}.
-     *
-     * @param node node that has been added.
-     */
-    public void notifyViewAppeared(@NonNull ViewStructure node) {
-        Preconditions.checkNotNull(node);
-        if (!isContentCaptureEnabled()) return;
-
-        if (!(node instanceof ViewNode.ViewStructureImpl)) {
-            throw new IllegalArgumentException("Invalid node class: " + node.getClass());
-        }
-
-        mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleSendEvent, this,
-                new ContentCaptureEvent(TYPE_VIEW_APPEARED)
-                        .setViewNode(((ViewNode.ViewStructureImpl) node).mNode),
-                        /* forceFlush= */ false));
-    }
-
-    /**
-     * Notifies the Intelligence Service that a node has been removed from the view structure.
-     *
-     * <p>Typically called "manually" by views that handle their own virtual view hierarchy, or
-     * automatically by the Android System for standard views.
-     *
-     * @param id id of the node that has been removed.
-     */
-    public void notifyViewDisappeared(@NonNull AutofillId id) {
-        Preconditions.checkNotNull(id);
-        if (!isContentCaptureEnabled()) return;
-
-        mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleSendEvent, this,
-                new ContentCaptureEvent(TYPE_VIEW_DISAPPEARED).setAutofillId(id),
-                        /* forceFlush= */ false));
-    }
-
-    /**
-     * Notifies the Intelligence Service that the value of a text node has been changed.
-     *
-     * @param id of the node.
-     * @param text new text.
-     * @param flags either {@code 0} or {@link #FLAG_USER_INPUT} when the value was explicitly
-     * changed by the user (for example, through the keyboard).
-     */
-    public void notifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
-            int flags) {
-        Preconditions.checkNotNull(id);
-
-        if (!isContentCaptureEnabled()) return;
-
-        mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleSendEvent, this,
-                new ContentCaptureEvent(TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id)
-                        .setText(text), /* forceFlush= */ false));
-    }
-
-    /**
-     * Creates a {@link ViewStructure} for a "standard" view.
+     * <p>By default there's just one (associated with the activity lifecycle), but apps could
+     * explicitly add more using
+     * {@link ContentCaptureSession#createContentCaptureSession(ContentCaptureContext)}.
      *
      * @hide
      */
     @NonNull
-    public ViewStructure newViewStructure(@NonNull View view) {
-        return new ViewNode.ViewStructureImpl(view);
+    @UiThread
+    public MainContentCaptureSession getMainContentCaptureSession() {
+        if (mMainSession == null) {
+            mMainSession = new MainContentCaptureSession(mContext, mHandler, mService,
+                    mDisabled);
+            if (VERBOSE) {
+                Log.v(TAG, "getDefaultContentCaptureSession(): created " + mMainSession);
+            }
+        }
+        return mMainSession;
+    }
+
+    /** @hide */
+    public void onActivityStarted(@NonNull IBinder applicationToken,
+            @NonNull ComponentName activityComponent) {
+        // TODO(b/121033016): must start all sessions
+        getMainContentCaptureSession().start(applicationToken, activityComponent);
+    }
+
+    /** @hide */
+    public void onActivityStopped() {
+        // TODO(b/121033016): must finish all sessions
+        getMainContentCaptureSession().destroy();
     }
 
     /**
-     * Creates a {@link ViewStructure} for a "virtual" view, so it can be passed to
-     * {@link #notifyViewAppeared(ViewStructure)} by the view managing the virtual view hierarchy.
+     * Flushes the content of all sessions.
      *
-     * @param parentId id of the virtual view parent (it can be obtained by calling
-     * {@link ViewStructure#getAutofillId()} on the parent).
-     * @param virtualId id of the virtual child, relative to the parent.
+     * <p>Typically called by {@code Activity} when it's paused / resumed.
      *
-     * @return a new {@link ViewStructure} that can be used for Content Capture purposes.
+     * @hide
      */
-    @NonNull
-    public ViewStructure newVirtualViewStructure(@NonNull AutofillId parentId, int virtualId) {
-        return new ViewNode.ViewStructureImpl(parentId, virtualId);
+    public void flush() {
+        // TODO(b/121033016): must flush all sessions
+        getMainContentCaptureSession().flush();
     }
 
     /**
@@ -453,7 +145,7 @@
      */
     @Nullable
     public ComponentName getServiceComponentName() {
-        //TODO(b/111276913): implement
+        //TODO(b/121047489): implement
         return null;
     }
 
@@ -471,71 +163,35 @@
      * it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}.
      */
     public void setContentCaptureEnabled(boolean enabled) {
+        //TODO(b/111276913): implement (need to finish / disable all sessions)
+    }
+
+    /**
+     * Called by the ap to request the Content Capture service to remove user-data associated with
+     * some context.
+     *
+     * @param request object specifying what user data should be removed.
+     */
+    public void removeUserData(@NonNull UserDataRemovalRequest request) {
         //TODO(b/111276913): implement
     }
 
     /** @hide */
     public void dump(String prefix, PrintWriter pw) {
         pw.print(prefix); pw.println("ContentCaptureManager");
-        final String prefix2 = prefix + "  ";
-        pw.print(prefix2); pw.print("mContext: "); pw.println(mContext);
-        pw.print(prefix2); pw.print("user: "); pw.println(mContext.getUserId());
+
+        pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled.get());
+        pw.print(prefix); pw.print("Context: "); pw.println(mContext);
+        pw.print(prefix); pw.print("User: "); pw.println(mContext.getUserId());
         if (mService != null) {
-            pw.print(prefix2); pw.print("mService: "); pw.println(mService);
+            pw.print(prefix); pw.print("Service: "); pw.println(mService);
         }
-        pw.print(prefix2); pw.print("mDisabled: "); pw.println(mDisabled.get());
-        pw.print(prefix2); pw.print("isEnabled(): "); pw.println(isContentCaptureEnabled());
-        if (mId != null) {
-            pw.print(prefix2); pw.print("id: "); pw.println(mId);
-        }
-        pw.print(prefix2); pw.print("state: "); pw.print(mState); pw.print(" (");
-        pw.print(getStateAsString(mState)); pw.println(")");
-        if (mApplicationToken != null) {
-            pw.print(prefix2); pw.print("app token: "); pw.println(mApplicationToken);
-        }
-        if (mComponentName != null) {
-            pw.print(prefix2); pw.print("component name: ");
-            pw.println(mComponentName.flattenToShortString());
-        }
-        if (mEvents != null && !mEvents.isEmpty()) {
-            final int numberEvents = mEvents.size();
-            pw.print(prefix2); pw.print("buffered events: "); pw.print(numberEvents);
-            pw.print('/'); pw.println(MAX_BUFFER_SIZE);
-            if (VERBOSE && numberEvents > 0) {
-                final String prefix3 = prefix2 + "  ";
-                for (int i = 0; i < numberEvents; i++) {
-                    final ContentCaptureEvent event = mEvents.get(i);
-                    pw.print(prefix3); pw.print(i); pw.print(": "); event.dump(pw);
-                    pw.println();
-                }
-            }
-            pw.print(prefix2); pw.print("flush frequency: "); pw.println(FLUSHING_FREQUENCY_MS);
-            pw.print(prefix2); pw.print("next flush: ");
-            TimeUtils.formatDuration(mNextFlush - SystemClock.elapsedRealtime(), pw); pw.println();
-        }
-    }
-
-    /**
-     * Gets a string that can be used to identify the activity on logging statements.
-     */
-    private String getActivityDebugName() {
-        return mComponentName == null ? mContext.getPackageName()
-                : mComponentName.flattenToShortString();
-    }
-
-    @NonNull
-    private static String getStateAsString(int state) {
-        switch (state) {
-            case STATE_UNKNOWN:
-                return "UNKNOWN";
-            case STATE_WAITING_FOR_SERVER:
-                return "WAITING_FOR_SERVER";
-            case STATE_ACTIVE:
-                return "ACTIVE";
-            case STATE_DISABLED:
-                return "DISABLED";
-            default:
-                return "INVALID:" + state;
+        if (mMainSession != null) {
+            final String prefix2 = prefix + "  ";
+            pw.print(prefix); pw.println("Main session:");
+            mMainSession.dump(prefix2, pw);
+        } else {
+            pw.print(prefix); pw.println("No sessions");
         }
     }
 }
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
new file mode 100644
index 0000000..46e6882
--- /dev/null
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import static android.view.contentcapture.ContentCaptureManager.DEBUG;
+import static android.view.contentcapture.ContentCaptureManager.VERBOSE;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewStructure;
+import android.view.autofill.AutofillId;
+import android.view.contentcapture.ViewNode.ViewStructureImpl;
+
+import com.android.internal.util.Preconditions;
+
+import dalvik.system.CloseGuard;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Session used to notify a system-provided Content Capture service about events associated with
+ * views.
+ */
+public abstract class ContentCaptureSession implements AutoCloseable {
+
+    private static final String TAG = ContentCaptureSession.class.getSimpleName();
+
+    /**
+     * Used on {@link #notifyViewTextChanged(AutofillId, CharSequence, int)} to indicate that the
+     *
+     * thext change was caused by user input (for example, through IME).
+     */
+    public static final int FLAG_USER_INPUT = 0x1;
+
+    /**
+     * Initial state, when there is no session.
+     *
+     * @hide
+     */
+    public static final int STATE_UNKNOWN = 0;
+
+    /**
+     * Service's startSession() was called, but server didn't confirm it was created yet.
+     *
+     * @hide
+     */
+    public static final int STATE_WAITING_FOR_SERVER = 1;
+
+    /**
+     * Session is active.
+     *
+     * @hide
+     */
+    public static final int STATE_ACTIVE = 2;
+
+    /**
+     * Session is disabled.
+     *
+     * @hide
+     */
+    public static final int STATE_DISABLED = 3;
+
+    /**
+     * Session is disabled because its id already existed on server.
+     *
+     * @hide
+     */
+    public static final int STATE_DISABLED_DUPLICATED_ID = 4;
+
+    private static final int INITIAL_CHILDREN_CAPACITY = 5;
+
+    private final CloseGuard mCloseGuard = CloseGuard.get();
+
+    /**
+     * Guard use to ignore events after it's destroyed.
+     */
+    @NonNull
+    private final AtomicBoolean mDestroyed = new AtomicBoolean();
+
+    /** @hide */
+    @Nullable
+    protected final String mId = UUID.randomUUID().toString();
+
+    private int mState = STATE_UNKNOWN;
+
+    // Lazily created on demand.
+    private ContentCaptureSessionId mContentCaptureSessionId;
+
+    /**
+     * List of children session.
+     */
+    // TODO(b/121033016): need to synchonize access, either by changing on handler or UI thread
+    // (for now there's no handler on this class, so we need to wait for the next refactoring),
+    // most likely the former (as we have no guarantee that createContentCaptureSession()
+    // it will be called in the UiThread; for example, WebView most likely won't call on it)
+    @Nullable
+    private ArrayList<ContentCaptureSession> mChildren;
+
+    /** @hide */
+    protected ContentCaptureSession() {
+        mCloseGuard.open("destroy");
+    }
+
+    /**
+     * Gets the id used to identify this session.
+     */
+    public final ContentCaptureSessionId getContentCaptureSessionId() {
+        if (mContentCaptureSessionId == null) {
+            mContentCaptureSessionId = new ContentCaptureSessionId(mId);
+        }
+        return mContentCaptureSessionId;
+    }
+
+    /**
+     * Creates a new {@link ContentCaptureSession}.
+     *
+     * <p>See {@link View#setContentCaptureSession(ContentCaptureSession)} for more info.
+     */
+    @NonNull
+    public final ContentCaptureSession createContentCaptureSession(
+            @NonNull ContentCaptureContext context) {
+        final ContentCaptureSession child = newChild(context);
+        if (DEBUG) {
+            Log.d(TAG, "createContentCaptureSession(" + context + ": parent=" + mId + ", child="
+                    + child.mId);
+        }
+        if (mChildren == null) {
+            mChildren = new ArrayList<>(INITIAL_CHILDREN_CAPACITY);
+        }
+        mChildren.add(child);
+        return child;
+    }
+
+    abstract ContentCaptureSession newChild(@NonNull ContentCaptureContext context);
+
+    /**
+     * Flushes the buffered events to the service.
+     */
+    abstract void flush();
+
+    /**
+     * Destroys this session, flushing out all pending notifications to the service.
+     *
+     * <p>Once destroyed, any new notification will be dropped.
+     */
+    public final void destroy() {
+        if (!mDestroyed.compareAndSet(false, true)) {
+            Log.e(TAG, "destroy(): already destroyed");
+            return;
+        }
+
+        mCloseGuard.close();
+
+        //TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
+        // id) and send it to the cache of batched commands
+        if (VERBOSE) {
+            Log.v(TAG, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId);
+        }
+
+        // Finish children first
+        if (mChildren != null) {
+            final int numberChildren = mChildren.size();
+            if (VERBOSE) Log.v(TAG, "Destroying " + numberChildren + " children first");
+            for (int i = 0; i < numberChildren; i++) {
+                final ContentCaptureSession child = mChildren.get(i);
+                try {
+                    child.destroy();
+                } catch (Exception e) {
+                    Log.w(TAG, "exception destroying child session #" + i + ": " + e);
+                }
+            }
+        }
+
+        try {
+            flush();
+        } finally {
+            onDestroy();
+        }
+    }
+
+    abstract void onDestroy();
+
+    /** @hide */
+    @Override
+    public void close() {
+        destroy();
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mCloseGuard != null) {
+                mCloseGuard.warnIfOpen();
+            }
+            destroy();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Notifies the Content Capture Service that a node has been added to the view structure.
+     *
+     * <p>Typically called "manually" by views that handle their own virtual view hierarchy, or
+     * automatically by the Android System for views that return {@code true} on
+     * {@link View#onProvideContentCaptureStructure(ViewStructure, int)}.
+     *
+     * @param node node that has been added.
+     */
+    public final void notifyViewAppeared(@NonNull ViewStructure node) {
+        Preconditions.checkNotNull(node);
+        if (!isContentCaptureEnabled()) return;
+
+        if (!(node instanceof ViewNode.ViewStructureImpl)) {
+            throw new IllegalArgumentException("Invalid node class: " + node.getClass());
+        }
+
+        internalNotifyViewAppeared((ViewStructureImpl) node);
+    }
+
+    abstract void internalNotifyViewAppeared(@NonNull ViewNode.ViewStructureImpl node);
+
+    /**
+     * Notifies the Content Capture Service that a node has been removed from the view structure.
+     *
+     * <p>Typically called "manually" by views that handle their own virtual view hierarchy, or
+     * automatically by the Android System for standard views.
+     *
+     * @param id id of the node that has been removed.
+     */
+    public final void notifyViewDisappeared(@NonNull AutofillId id) {
+        Preconditions.checkNotNull(id);
+        if (!isContentCaptureEnabled()) return;
+
+        internalNotifyViewDisappeared(id);
+    }
+
+    abstract void internalNotifyViewDisappeared(@NonNull AutofillId id);
+
+    /**
+     * Notifies the Intelligence Service that the value of a text node has been changed.
+     *
+     * @param id of the node.
+     * @param text new text.
+     * @param flags either {@code 0} or {@link #FLAG_USER_INPUT} when the value was explicitly
+     * changed by the user (for example, through the keyboard).
+     */
+    public final void notifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
+            int flags) {
+        Preconditions.checkNotNull(id);
+
+        if (!isContentCaptureEnabled()) return;
+
+        internalNotifyViewTextChanged(id, text, flags);
+    }
+
+    abstract void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
+            int flags);
+
+    /**
+     * Creates a {@link ViewStructure} for a "standard" view.
+     *
+     * @hide
+     */
+    @NonNull
+    public final ViewStructure newViewStructure(@NonNull View view) {
+        return new ViewNode.ViewStructureImpl(view);
+    }
+
+    /**
+     * Creates a {@link ViewStructure} for a "virtual" view, so it can be passed to
+     * {@link #notifyViewAppeared(ViewStructure)} by the view managing the virtual view hierarchy.
+     *
+     * @param parentId id of the virtual view parent (it can be obtained by calling
+     * {@link ViewStructure#getAutofillId()} on the parent).
+     * @param virtualId id of the virtual child, relative to the parent.
+     *
+     * @return a new {@link ViewStructure} that can be used for Content Capture purposes.
+     *
+     * @hide
+     */
+    @NonNull
+    public final ViewStructure newVirtualViewStructure(@NonNull AutofillId parentId,
+            int virtualId) {
+        return new ViewNode.ViewStructureImpl(parentId, virtualId);
+    }
+
+    boolean isContentCaptureEnabled() {
+        return !mDestroyed.get();
+    }
+
+    @CallSuper
+    void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+        pw.print(prefix); pw.print("id: "); pw.println(mId);
+        pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed.get());
+        if (mChildren != null && !mChildren.isEmpty()) {
+            final String prefix2 = prefix + "  ";
+            final int numberChildren = mChildren.size();
+            pw.print(prefix); pw.print("number children: "); pw.println(numberChildren);
+            for (int i = 0; i < numberChildren; i++) {
+                final ContentCaptureSession child = mChildren.get(i);
+                pw.print(prefix); pw.print(i); pw.println(": "); child.dump(prefix2, pw);
+            }
+        }
+
+    }
+
+    @Override
+    public String toString() {
+        return mId;
+    }
+
+    /**
+     * @hide
+     */
+    @NonNull
+    protected static String getStateAsString(int state) {
+        switch (state) {
+            case STATE_UNKNOWN:
+                return "UNKNOWN";
+            case STATE_WAITING_FOR_SERVER:
+                return "WAITING_FOR_SERVER";
+            case STATE_ACTIVE:
+                return "ACTIVE";
+            case STATE_DISABLED:
+                return "DISABLED";
+            case STATE_DISABLED_DUPLICATED_ID:
+                return "DISABLED_DUPLICATED_ID";
+            default:
+                return "INVALID:" + state;
+        }
+    }
+}
diff --git a/core/java/android/service/contentcapture/InteractionSessionId.java b/core/java/android/view/contentcapture/ContentCaptureSessionId.java
similarity index 77%
rename from core/java/android/service/contentcapture/InteractionSessionId.java
rename to core/java/android/view/contentcapture/ContentCaptureSessionId.java
index 8411947..d7f9fcc 100644
--- a/core/java/android/service/contentcapture/InteractionSessionId.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSessionId.java
@@ -14,10 +14,9 @@
  * limitations under the License.
  */
 
-package android.service.contentcapture;
+package android.view.contentcapture;
 
 import android.annotation.NonNull;
-import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -25,11 +24,8 @@
 
 /**
  * Identifier for a Content Capture session.
- *
- * @hide
  */
-@SystemApi
-public final class InteractionSessionId implements Parcelable {
+public final class ContentCaptureSessionId implements Parcelable {
 
     private final @NonNull String mValue;
 
@@ -40,7 +36,7 @@
      *
      * @hide
      */
-    public InteractionSessionId(@NonNull String value) {
+    public ContentCaptureSessionId(@NonNull String value) {
         mValue = value;
     }
 
@@ -64,7 +60,7 @@
         if (this == obj) return true;
         if (obj == null) return false;
         if (getClass() != obj.getClass()) return false;
-        final InteractionSessionId other = (InteractionSessionId) obj;
+        final ContentCaptureSessionId other = (ContentCaptureSessionId) obj;
         if (mValue == null) {
             if (other.mValue != null) return false;
         } else if (!mValue.equals(other.mValue)) {
@@ -100,17 +96,17 @@
         parcel.writeString(mValue);
     }
 
-    public static final Parcelable.Creator<InteractionSessionId> CREATOR =
-            new Parcelable.Creator<InteractionSessionId>() {
+    public static final Parcelable.Creator<ContentCaptureSessionId> CREATOR =
+            new Parcelable.Creator<ContentCaptureSessionId>() {
 
         @Override
-        public InteractionSessionId createFromParcel(Parcel parcel) {
-            return new InteractionSessionId(parcel.readString());
+        public ContentCaptureSessionId createFromParcel(Parcel parcel) {
+            return new ContentCaptureSessionId(parcel.readString());
         }
 
         @Override
-        public InteractionSessionId[] newArray(int size) {
-            return new InteractionSessionId[size];
+        public ContentCaptureSessionId[] newArray(int size) {
+            return new ContentCaptureSessionId[size];
         }
     };
 }
diff --git a/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl b/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl
new file mode 100644
index 0000000..8d8117b
--- /dev/null
+++ b/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.contentcapture;
+
+import android.content.pm.ParceledListSlice;
+import android.view.contentcapture.ContentCaptureEvent;
+
+/**
+  * Interface between an app (ContentCaptureManager / ContentCaptureSession) and the app providing
+  * the ContentCaptureService implementation.
+  *
+  * @hide
+  */
+oneway interface IContentCaptureDirectManager {
+    void sendEvents(in ParceledListSlice events);
+}
diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
index 8704dad..01776f8 100644
--- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl
+++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
@@ -17,6 +17,7 @@
 package android.view.contentcapture;
 
 import android.content.ComponentName;
+import android.view.contentcapture.ContentCaptureContext;
 import android.view.contentcapture.ContentCaptureEvent;
 import android.os.IBinder;
 
@@ -25,11 +26,13 @@
 import java.util.List;
 
 /**
- * {@hide}
- */
+  * Interface between an app (ContentCaptureManager / ContentCaptureSession) and the system-server
+  * implementation service (ContentCaptureManagerService).
+  *
+  * @hide
+  */
 oneway interface IContentCaptureManager {
     void startSession(int userId, IBinder activityToken, in ComponentName componentName,
                       String sessionId, int flags, in IResultReceiver result);
-    void finishSession(int userId, String sessionId, in List<ContentCaptureEvent> events);
-    void sendEvents(int userId, in String sessionId, in List<ContentCaptureEvent> events);
+    void finishSession(int userId, String sessionId);
 }
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
new file mode 100644
index 0000000..baf4a35
--- /dev/null
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -0,0 +1,517 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
+import static android.view.contentcapture.ContentCaptureManager.DEBUG;
+import static android.view.contentcapture.ContentCaptureManager.VERBOSE;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.TimeUtils;
+import android.view.autofill.AutofillId;
+import android.view.contentcapture.ViewNode.ViewStructureImpl;
+
+import com.android.internal.os.IResultReceiver;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Main session associated with a context.
+ *
+ * <p>This session is created when the activity starts and finished when it stops; clients can use
+ * it to create children activities.
+ *
+ * <p><b>NOTE: all methods in this class should return right away, or do the real work in a handler
+ * thread. Hence, the only field that must be thread-safe is {@code mEnabled}, which is called at
+ * the beginning of every method.
+ *
+ * @hide
+ */
+public final class MainContentCaptureSession extends ContentCaptureSession {
+
+    private static final String TAG = MainContentCaptureSession.class.getSimpleName();
+
+    /**
+     * Handler message used to flush the buffer.
+     */
+    private static final int MSG_FLUSH = 1;
+
+    /**
+     * Maximum number of events that are buffered before sent to the app.
+     */
+    // TODO(b/121044064): use settings
+    private static final int MAX_BUFFER_SIZE = 100;
+
+    /**
+     * Frequency the buffer is flushed if stale.
+     */
+    // TODO(b/121044064): use settings
+    private static final int FLUSHING_FREQUENCY_MS = 5_000;
+
+    /**
+     * Name of the {@link IResultReceiver} extra used to pass the binder interface to the service.
+     * @hide
+     */
+    public static final String EXTRA_BINDER = "binder";
+
+    @NonNull
+    private final AtomicBoolean mDisabled;
+
+    @NonNull
+    private final Context mContext;
+
+    @NonNull
+    private final Handler mHandler;
+
+    /**
+     * Interface to the system_server binder object - it's only used to start the session (and
+     * notify when the session is finished).
+     */
+    @Nullable
+    private final IContentCaptureManager mSystemServerInterface;
+
+    /**
+     * Direct interface to the service binder object - it's used to send the events, including the
+     * last ones (when the session is finished)
+     */
+    @Nullable
+    private IContentCaptureDirectManager mDirectServiceInterface;
+    @Nullable
+    private DeathRecipient mDirectServiceVulture;
+
+    private int mState = STATE_UNKNOWN;
+
+    @Nullable
+    private IBinder mApplicationToken;
+
+    @Nullable
+    private ComponentName mComponentName;
+
+    /**
+     * List of events held to be sent as a batch.
+     */
+    @Nullable
+    private ArrayList<ContentCaptureEvent> mEvents;
+
+    // Used just for debugging purposes (on dump)
+    private long mNextFlush;
+
+    /** @hide */
+    protected MainContentCaptureSession(@NonNull Context context, @NonNull Handler handler,
+            @Nullable IContentCaptureManager systemServerInterface,
+            @NonNull AtomicBoolean disabled) {
+        mContext = context;
+        mHandler = handler;
+        mSystemServerInterface = systemServerInterface;
+        mDisabled = disabled;
+    }
+
+    @Override
+    ContentCaptureSession newChild(@NonNull ContentCaptureContext clientContext) {
+        final ContentCaptureSession child = new ChildContentCaptureSession(this, clientContext);
+        notifyChildSessionStarted(mId, child.mId, clientContext);
+        return child;
+    }
+
+    /**
+     * Starts this session.
+     *
+     * @hide
+     */
+    void start(@NonNull IBinder applicationToken, @NonNull ComponentName activityComponent) {
+        if (!isContentCaptureEnabled()) return;
+
+        if (VERBOSE) {
+            Log.v(TAG, "start(): token=" + applicationToken + ", comp="
+                    + ComponentName.flattenToShortString(activityComponent));
+        }
+
+        mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleStartSession, this,
+                applicationToken, activityComponent));
+    }
+
+    @Override
+    void flush() {
+        mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleForceFlush, this));
+    }
+
+    @Override
+    void onDestroy() {
+        mHandler.sendMessage(
+                obtainMessage(MainContentCaptureSession::handleDestroySession, this));
+    }
+
+    private void handleStartSession(@NonNull IBinder token, @NonNull ComponentName componentName) {
+        if (mState != STATE_UNKNOWN) {
+            // TODO(b/111276913): revisit this scenario
+            Log.w(TAG, "ignoring handleStartSession(" + token + ") while on state "
+                    + getStateAsString(mState));
+            return;
+        }
+        mState = STATE_WAITING_FOR_SERVER;
+        mApplicationToken = token;
+        mComponentName = componentName;
+
+        if (VERBOSE) {
+            Log.v(TAG, "handleStartSession(): token=" + token + ", act="
+                    + getActivityDebugName() + ", id=" + mId);
+        }
+        final int flags = 0; // TODO(b/111276913): get proper flags
+
+        try {
+            mSystemServerInterface.startSession(mContext.getUserId(), mApplicationToken,
+                    componentName, mId, flags, new IResultReceiver.Stub() {
+                        @Override
+                        public void send(int resultCode, Bundle resultData) {
+                            IBinder binder = null;
+                            if (resultData != null) {
+                                binder = resultData.getBinder(EXTRA_BINDER);
+                                if (binder == null) {
+                                    Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
+                                    handleResetState();
+                                    return;
+                                }
+                            }
+                            handleSessionStarted(resultCode, binder);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error starting session for " + componentName.flattenToShortString() + ": "
+                    + e);
+        }
+    }
+
+    /**
+     * Callback from {@code system_server} after call to
+     * {@link IContentCaptureManager#startSession(int, IBinder, ComponentName, String,
+     * int, IResultReceiver)}.
+     *
+     * @param resultCode session state
+     * @param binder handle to {@code IContentCaptureDirectManager}
+     */
+    private void handleSessionStarted(int resultCode, @Nullable IBinder binder) {
+        mState = resultCode;
+        if (binder != null) {
+            mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder);
+            mDirectServiceVulture = () -> {
+                Log.w(TAG, "Destroying session " + mId + " because service died");
+                destroy();
+            };
+            try {
+                binder.linkToDeath(mDirectServiceVulture, 0);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to link to death on " + binder + ": " + e);
+            }
+        }
+        if (resultCode == STATE_DISABLED || resultCode == STATE_DISABLED_DUPLICATED_ID) {
+            mDisabled.set(true);
+            handleResetSession(/* resetState= */ false);
+        } else {
+            mDisabled.set(false);
+        }
+        if (VERBOSE) {
+            Log.v(TAG, "handleSessionStarted() result: code=" + resultCode + ", id=" + mId
+                    + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()
+                    + ", binder=" + binder);
+        }
+    }
+
+    private void handleSendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
+        if (mEvents == null) {
+            if (VERBOSE) {
+                Log.v(TAG, "Creating buffer for " + MAX_BUFFER_SIZE + " events");
+            }
+            mEvents = new ArrayList<>(MAX_BUFFER_SIZE);
+        }
+
+        if (!mEvents.isEmpty() && event.getType() == TYPE_VIEW_TEXT_CHANGED) {
+            final ContentCaptureEvent lastEvent = mEvents.get(mEvents.size() - 1);
+
+            // TODO(b/121045053): check if flags match
+            if (lastEvent.getType() == TYPE_VIEW_TEXT_CHANGED
+                    && lastEvent.getId().equals(event.getId())) {
+                if (VERBOSE) {
+                    Log.v(TAG, "Buffering VIEW_TEXT_CHANGED event, updated text = "
+                            + event.getText());
+                }
+                lastEvent.setText(event.getText());
+            } else {
+                mEvents.add(event);
+            }
+        } else {
+            mEvents.add(event);
+        }
+
+        final int numberEvents = mEvents.size();
+
+        final boolean bufferEvent = numberEvents < MAX_BUFFER_SIZE;
+
+        if (bufferEvent && !forceFlush) {
+            handleScheduleFlush(/* checkExisting= */ true);
+            return;
+        }
+
+        if (mState != STATE_ACTIVE) {
+            // Callback from startSession hasn't been called yet - typically happens on system
+            // apps that are started before the system service
+            // TODO(b/111276913): try to ignore session while system is not ready / boot
+            // not complete instead. Similarly, the manager service should return right away
+            // when the user does not have a service set
+            if (VERBOSE) {
+                Log.v(TAG, "Closing session for " + getActivityDebugName()
+                        + " after " + numberEvents + " delayed events and state "
+                        + getStateAsString(mState));
+            }
+            handleResetState();
+            // TODO(b/111276913): blacklist activity / use special flag to indicate that
+            // when it's launched again
+            return;
+        }
+
+        handleForceFlush();
+    }
+
+    private void handleScheduleFlush(boolean checkExisting) {
+        if (checkExisting && mHandler.hasMessages(MSG_FLUSH)) {
+            // "Renew" the flush message by removing the previous one
+            mHandler.removeMessages(MSG_FLUSH);
+        }
+        mNextFlush = SystemClock.elapsedRealtime() + FLUSHING_FREQUENCY_MS;
+        if (VERBOSE) {
+            Log.v(TAG, "Scheduled to flush in " + FLUSHING_FREQUENCY_MS + "ms: " + mNextFlush);
+        }
+        mHandler.sendMessageDelayed(
+                obtainMessage(MainContentCaptureSession::handleFlushIfNeeded, this)
+                .setWhat(MSG_FLUSH), FLUSHING_FREQUENCY_MS);
+    }
+
+    private void handleFlushIfNeeded() {
+        if (mEvents.isEmpty()) {
+            if (VERBOSE) Log.v(TAG, "Nothing to flush");
+            return;
+        }
+        handleForceFlush();
+    }
+
+    private void handleForceFlush() {
+        if (mEvents == null) return;
+
+        if (mDirectServiceInterface == null) {
+            if (DEBUG) Log.d(TAG, "handleForceFlush(): hold your horses, client not ready yet!");
+            if (!mHandler.hasMessages(MSG_FLUSH)) {
+                handleScheduleFlush(/* checkExisting= */ false);
+            }
+            return;
+        }
+
+        final int numberEvents = mEvents.size();
+        try {
+            if (DEBUG) {
+                Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName());
+            }
+            mHandler.removeMessages(MSG_FLUSH);
+
+            final ParceledListSlice<ContentCaptureEvent> events = handleClearEvents();
+            mDirectServiceInterface.sendEvents(events);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error sending " + numberEvents + " for " + getActivityDebugName()
+                    + ": " + e);
+        }
+    }
+
+    /**
+     * Resets the buffer and return a {@link ParceledListSlice} with the previous events.
+     */
+    @NonNull
+    private ParceledListSlice<ContentCaptureEvent> handleClearEvents() {
+        // NOTE: we must save a reference to the current mEvents and then set it to to null,
+        // otherwise clearing it would clear it in the receiving side if the service is also local.
+        final List<ContentCaptureEvent> events = mEvents == null
+                ? Collections.emptyList()
+                : mEvents;
+        mEvents = null;
+        return new ParceledListSlice<>(events);
+    }
+
+    private void handleDestroySession() {
+        if (DEBUG) {
+            Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
+                    + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
+                    + getActivityDebugName());
+        }
+
+        try {
+            mSystemServerInterface.finishSession(mContext.getUserId(), mId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error destroying system-service session " + mId + " for "
+                    + getActivityDebugName() + ": " + e);
+        }
+    }
+
+    private void handleResetState() {
+        handleResetSession(/* resetState= */ true);
+    }
+
+    // TODO(b/121033016): once we support multiple sessions, we might need to move some of these
+    // clearings out.
+    private void handleResetSession(boolean resetState) {
+        if (resetState) {
+            mState = STATE_UNKNOWN;
+        }
+
+        // TODO(b/121033016): must reset children (which currently is owned by superclass)
+        mApplicationToken = null;
+        mComponentName = null;
+        mEvents = null;
+        if (mDirectServiceInterface != null) {
+            mDirectServiceInterface.asBinder().unlinkToDeath(mDirectServiceVulture, 0);
+        }
+        mDirectServiceInterface = null;
+        mHandler.removeMessages(MSG_FLUSH);
+    }
+
+    @Override
+    void internalNotifyViewAppeared(@NonNull ViewStructureImpl node) {
+        notifyViewAppeared(mId, node);
+    }
+
+    @Override
+    void internalNotifyViewDisappeared(@NonNull AutofillId id) {
+        notifyViewDisappeared(mId, id);
+    }
+
+    @Override
+    void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
+            int flags) {
+        notifyViewTextChanged(mId, id, text, flags);
+    }
+
+    @Override
+    boolean isContentCaptureEnabled() {
+        return super.isContentCaptureEnabled() && mSystemServerInterface != null
+                && !mDisabled.get();
+    }
+
+    // TODO(b/121033016): refactor "notifyXXXX" methods below to a common "Buffer" object that is
+    // shared between ActivityContentCaptureSession and ChildContentCaptureSession objects. Such
+    // change should also get get rid of the "internalNotifyXXXX" methods above
+    void notifyChildSessionStarted(@NonNull String parentSessionId,
+            @NonNull String childSessionId, @NonNull ContentCaptureContext clientContext) {
+        mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
+                new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
+                        .setParentSessionId(parentSessionId)
+                        .setClientContext(clientContext),
+                        /* forceFlush= */ false));
+    }
+
+    void notifyChildSessionFinished(@NonNull String parentSessionId,
+            @NonNull String childSessionId) {
+        mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
+                new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
+                        .setParentSessionId(parentSessionId), /* forceFlush= */ false));
+    }
+
+    void notifyViewAppeared(@NonNull String sessionId, @NonNull ViewStructureImpl node) {
+        mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
+                new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED)
+                        .setViewNode(node.mNode), /* forceFlush= */ false));
+    }
+
+    void notifyViewDisappeared(@NonNull String sessionId, @NonNull AutofillId id) {
+        mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
+                new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED).setAutofillId(id),
+                        /* forceFlush= */ false));
+    }
+
+    void notifyViewTextChanged(@NonNull String sessionId, @NonNull AutofillId id,
+            @Nullable CharSequence text, int flags) {
+        mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
+                new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id)
+                        .setText(text), /* forceFlush= */ false));
+    }
+
+    @Override
+    void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+        pw.print(prefix); pw.print("id: "); pw.println(mId);
+        pw.print(prefix); pw.print("mContext: "); pw.println(mContext);
+        pw.print(prefix); pw.print("user: "); pw.println(mContext.getUserId());
+        if (mSystemServerInterface != null) {
+            pw.print(prefix); pw.print("mSystemServerInterface: ");
+            pw.println(mSystemServerInterface);
+        }
+        if (mDirectServiceInterface != null) {
+            pw.print(prefix); pw.print("mDirectServiceInterface: ");
+            pw.println(mDirectServiceInterface);
+        }
+        pw.print(prefix); pw.print("mDisabled: "); pw.println(mDisabled.get());
+        pw.print(prefix); pw.print("isEnabled(): "); pw.println(isContentCaptureEnabled());
+        pw.print(prefix); pw.print("state: "); pw.print(mState); pw.print(" (");
+        pw.print(getStateAsString(mState)); pw.println(")");
+        if (mApplicationToken != null) {
+            pw.print(prefix); pw.print("app token: "); pw.println(mApplicationToken);
+        }
+        if (mComponentName != null) {
+            pw.print(prefix); pw.print("component name: ");
+            pw.println(mComponentName.flattenToShortString());
+        }
+        if (mEvents != null && !mEvents.isEmpty()) {
+            final int numberEvents = mEvents.size();
+            pw.print(prefix); pw.print("buffered events: "); pw.print(numberEvents);
+            pw.print('/'); pw.println(MAX_BUFFER_SIZE);
+            if (VERBOSE && numberEvents > 0) {
+                final String prefix3 = prefix + "  ";
+                for (int i = 0; i < numberEvents; i++) {
+                    final ContentCaptureEvent event = mEvents.get(i);
+                    pw.print(prefix3); pw.print(i); pw.print(": "); event.dump(pw);
+                    pw.println();
+                }
+            }
+            pw.print(prefix); pw.print("flush frequency: "); pw.println(FLUSHING_FREQUENCY_MS);
+            pw.print(prefix); pw.print("next flush: ");
+            TimeUtils.formatDuration(mNextFlush - SystemClock.elapsedRealtime(), pw); pw.println();
+        }
+        super.dump(prefix, pw);
+    }
+
+    /**
+     * Gets a string that can be used to identify the activity on logging statements.
+     */
+    private String getActivityDebugName() {
+        return mComponentName == null ? mContext.getPackageName()
+                : mComponentName.flattenToShortString();
+    }
+}
diff --git a/core/java/android/view/contentcapture/UserDataRemovalRequest.java b/core/java/android/view/contentcapture/UserDataRemovalRequest.java
new file mode 100644
index 0000000..0261b70
--- /dev/null
+++ b/core/java/android/view/contentcapture/UserDataRemovalRequest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.List;
+
+/**
+ * Class used by apps to request the Content Capture service to remove user-data associated with
+ * some context.
+ */
+public final class UserDataRemovalRequest implements Parcelable {
+
+    private UserDataRemovalRequest(Builder builder) {
+        // TODO(b/111276913): implement
+    }
+
+    /**
+     * Gets the name of the app that's making the request.
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public String getPackageName() {
+        // TODO(b/111276913): implement
+        // TODO(b/111276913): make sure it's set on system_service so it cannot be faked by app
+        return null;
+    }
+
+    /**
+     * Checks if app is requesting to remove all user data associated with its package.
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean isForEverything() {
+        // TODO(b/111276913): implement
+        return false;
+    }
+
+    /**
+     * Gets the list of {@code Uri}s the apps is requesting to remove.
+     *
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public List<UriRequest> getUriRequests() {
+        // TODO(b/111276913): implement
+        return null;
+    }
+
+    /**
+     * Builder for {@link UserDataRemovalRequest} objects.
+     */
+    public static final class Builder {
+
+        /**
+         * Requests servive to remove all user data associated with the app's package.
+         *
+         * @return this builder
+         */
+        @NonNull
+        public Builder forEverything() {
+            // TODO(b/111276913): implement
+            return this;
+        }
+
+        /**
+         * Request service to remove data associated with a given {@link Uri}.
+         *
+         * @param uri URI being requested to be removed.
+         * @param recursive whether it should remove the data associated with just the URI or its
+         * tree of descendants.
+         *
+         * @return this builder
+         */
+        public Builder addUri(@NonNull Uri uri, boolean recursive) {
+            // TODO(b/111276913): implement
+            return this;
+        }
+
+        /**
+         * Builds the {@link UserDataRemovalRequest}.
+         */
+        @NonNull
+        public UserDataRemovalRequest build() {
+            // TODO(b/111276913): implement / unit test / check built / document exceptions
+            return null;
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        // TODO(b/111276913): implement
+    }
+
+    public static final Parcelable.Creator<UserDataRemovalRequest> CREATOR =
+            new Parcelable.Creator<UserDataRemovalRequest>() {
+
+        @Override
+        public UserDataRemovalRequest createFromParcel(Parcel parcel) {
+            // TODO(b/111276913): implement
+            return null;
+        }
+
+        @Override
+        public UserDataRemovalRequest[] newArray(int size) {
+            return new UserDataRemovalRequest[size];
+        }
+    };
+
+    /**
+     * Representation of a request to remove data associated with an {@link Uri}.
+     * @hide
+     */
+    @SystemApi
+    public final class UriRequest {
+        private final @NonNull Uri mUri;
+        private final boolean mRecursive;
+
+        private UriRequest(@NonNull Uri uri, boolean recursive) {
+            this.mUri = uri;
+            this.mRecursive = recursive;
+        }
+
+        /**
+         * Gets the URI per se.
+         */
+        @NonNull
+        public Uri getUri() {
+            return mUri;
+        }
+
+        /**
+         * Checks whether the request is to remove just the data associated with the URI per se, or
+         * also its descendants.
+         */
+        @NonNull
+        public boolean isRecursive() {
+            return mRecursive;
+        }
+    }
+}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index e57fdff..1ba7d8e 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -26,6 +26,8 @@
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
+import android.app.ActivityThread;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.graphics.Rect;
@@ -37,11 +39,13 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.Trace;
+import android.provider.Settings;
 import android.text.style.SuggestionSpan;
 import android.util.Log;
 import android.util.Pools.Pool;
@@ -76,9 +80,9 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.reflect.Proxy;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -232,6 +236,15 @@
 
     static final String PENDING_EVENT_COUNTER = "aq:imm";
 
+    private static final int NOT_A_SUBTYPE_ID = -1;
+
+    /**
+     * A constant that represents Voice IME.
+     *
+     * @see InputMethodSubtype#getMode()
+     */
+    private static final String SUBTYPE_MODE_VOICE = "voice";
+
     /**
      * Ensures that {@link #sInstance} becomes non-{@code null} for application that have directly
      * or indirectly relied on {@link #sInstance} via reflection or something like that.
@@ -2064,6 +2077,13 @@
     /**
      * Force switch to a new input method component. This can only be called
      * from an application or a service which has a token of the currently active input method.
+     *
+     * <p>On Android {@link Build.VERSION_CODES#Q} and later devices, the undocumented behavior that
+     * token can be {@code null} when the caller has
+     * {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} is deprecated. Instead, update
+     * {@link android.provider.Settings.Secure#DEFAULT_INPUT_METHOD} and
+     * {@link android.provider.Settings.Secure#SELECTED_INPUT_METHOD_SUBTYPE} directly.</p>
+     *
      * @param token Supplies the identifying token given to an input method
      * when it was started, which allows it to perform this operation on
      * itself.
@@ -2075,14 +2095,50 @@
     @Deprecated
     public void setInputMethod(IBinder token, String id) {
         if (token == null) {
-            // Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
-            // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately.
-            // TODO(Bug 114488811): Consider deprecating null token rule.
-            try {
-                mService.setInputMethod(token, id);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
+            // There are still some system components that rely on this undocumented behavior
+            // regarding null IME token with WRITE_SECURE_SETTINGS.  Provide a fallback logic as a
+            // temporary remedy.
+            if (id == null) {
+                return;
             }
+            if (Process.myUid() == Process.SYSTEM_UID) {
+                Log.w(TAG, "System process should not be calling setInputMethod() because almost "
+                        + "always it is a bug under multi-user / multi-profile environment. "
+                        + "Consider interacting with InputMethodManagerService directly via "
+                        + "LocalServices.");
+                return;
+            }
+            final Context fallbackContext = ActivityThread.currentApplication();
+            if (fallbackContext == null) {
+                return;
+            }
+            if (fallbackContext.checkSelfPermission(WRITE_SECURE_SETTINGS)
+                    != PackageManager.PERMISSION_GRANTED) {
+                return;
+            }
+            final List<InputMethodInfo> imis = getEnabledInputMethodList();
+            final int numImis = imis.size();
+            boolean found = false;
+            for (int i = 0; i < numImis; ++i) {
+                final InputMethodInfo imi = imis.get(i);
+                if (id.equals(imi.getId())) {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+                Log.e(TAG, "Ignoring setInputMethod(null, " + id + ") because the specified "
+                        + "id not found in enabled IMEs.");
+                return;
+            }
+            Log.w(TAG, "The undocumented behavior that setInputMethod() accepts null token "
+                    + "when the caller has WRITE_SECURE_SETTINGS is deprecated. This behavior may "
+                    + "be completely removed in a future version.  Update secure settings directly "
+                    + "instead.");
+            final ContentResolver resolver = fallbackContext.getContentResolver();
+            Settings.Secure.putInt(resolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
+                    NOT_A_SUBTYPE_ID);
+            Settings.Secure.putString(resolver, Settings.Secure.DEFAULT_INPUT_METHOD, id);
             return;
         }
         InputMethodPrivilegedOperationsRegistry.get(token).setInputMethod(id);
@@ -2091,6 +2147,12 @@
     /**
      * Force switch to a new input method and subtype. This can only be called
      * from an application or a service which has a token of the currently active input method.
+     *
+     * <p>On Android {@link Build.VERSION_CODES#Q} and later devices, {@code token} cannot be
+     * {@code null} even with {@link android.Manifest.permission#WRITE_SECURE_SETTINGS}. Instead,
+     * update {@link android.provider.Settings.Secure#DEFAULT_INPUT_METHOD} and
+     * {@link android.provider.Settings.Secure#SELECTED_INPUT_METHOD_SUBTYPE} directly.</p>
+     *
      * @param token Supplies the identifying token given to an input method
      * when it was started, which allows it to perform this operation on
      * itself.
@@ -2102,16 +2164,11 @@
      * the service. APIs in this class are intended for app developers interacting with the IME.
      */
     @Deprecated
-    public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
+    public void setInputMethodAndSubtype(@NonNull IBinder token, String id,
+            InputMethodSubtype subtype) {
         if (token == null) {
-            // Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
-            // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately.
-            // TODO(Bug 114488811): Consider deprecating null token rule.
-            try {
-                mService.setInputMethodAndSubtype(token, id, subtype);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
+            Log.e(TAG, "setInputMethodAndSubtype() does not accept null token on Android Q "
+                    + "and later.");
             return;
         }
         InputMethodPrivilegedOperationsRegistry.get(token).setInputMethodAndSubtype(id, subtype);
@@ -2459,34 +2516,25 @@
      * Returns a map of all shortcut input method info and their subtypes.
      */
     public Map<InputMethodInfo, List<InputMethodSubtype>> getShortcutInputMethodsAndSubtypes() {
-        synchronized (mH) {
-            HashMap<InputMethodInfo, List<InputMethodSubtype>> ret = new HashMap<>();
-            try {
-                // TODO: We should change the return type from List<Object> to List<Parcelable>
-                List<Object> info = mService.getShortcutInputMethodsAndSubtypes();
-                // "info" has imi1, subtype1, subtype2, imi2, subtype2, imi3, subtype3..in the list
-                ArrayList<InputMethodSubtype> subtypes = null;
-                if (info != null && !info.isEmpty()) {
-                    final int N = info.size();
-                    for (int i = 0; i < N; ++i) {
-                        Object o = info.get(i);
-                        if (o instanceof InputMethodInfo) {
-                            if (ret.containsKey(o)) {
-                                Log.e(TAG, "IMI list already contains the same InputMethod.");
-                                break;
-                            }
-                            subtypes = new ArrayList<>();
-                            ret.put((InputMethodInfo)o, subtypes);
-                        } else if (subtypes != null && o instanceof InputMethodSubtype) {
-                            subtypes.add((InputMethodSubtype)o);
-                        }
-                    }
+        final List<InputMethodInfo> enabledImes = getEnabledInputMethodList();
+
+        // Ensure we check system IMEs first.
+        enabledImes.sort(Comparator.comparingInt(imi -> imi.isSystem() ? 0 : 1));
+
+        final int numEnabledImes = enabledImes.size();
+        for (int imiIndex = 0; imiIndex < numEnabledImes; ++imiIndex) {
+            final InputMethodInfo imi = enabledImes.get(imiIndex);
+            final List<InputMethodSubtype> subtypes = getEnabledInputMethodSubtypeList(
+                    imi, true);
+            final int subtypeCount = subtypes.size();
+            for (int subtypeIndex = 0; subtypeIndex < subtypeCount; ++subtypeIndex) {
+                final InputMethodSubtype subtype = imi.getSubtypeAt(subtypeIndex);
+                if (SUBTYPE_MODE_VOICE.equals(subtype.getMode())) {
+                    return Collections.singletonMap(imi, Collections.singletonList(subtype));
                 }
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
             }
-            return ret;
         }
+        return Collections.emptyMap();
     }
 
     /**
diff --git a/core/java/android/view/inputmethod/InputMethodSystemProperty.java b/core/java/android/view/inputmethod/InputMethodSystemProperty.java
new file mode 100644
index 0000000..b233b75
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputMethodSystemProperty.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Build;
+import android.os.SystemProperties;
+
+/**
+ * Various (pseudo) constants about IME behaviors.
+ *
+ * @hide
+ */
+public class InputMethodSystemProperty {
+    /**
+     * System property key for the production use. The value must be either empty or a valid
+     * (flattened) component name of the multi-client IME.
+     */
+    private static final String PROP_PROD_MULTI_CLIENT_IME = "ro.sys.multi_client_ime";
+
+    /**
+     * System property key for debugging purpose. The value must be either empty or a valid
+     * (flattened) component name of the multi-client IME.
+     *
+     * <p>This value will be ignored when {@link Build#IS_DEBUGGABLE} returns {@code false}</p>
+     */
+    private static final String PROP_DEBUG_MULTI_CLIENT_IME = "persist.debug.multi_client_ime";
+
+    @Nullable
+    private static ComponentName getMultiClientImeComponentName() {
+        if (Build.IS_DEBUGGABLE) {
+            // If debuggable, allow developers to override the multi-client IME component name
+            // with a different (writable) key.
+            final ComponentName debugIme = ComponentName.unflattenFromString(
+                    SystemProperties.get(PROP_DEBUG_MULTI_CLIENT_IME, ""));
+            if (debugIme != null) {
+                return debugIme;
+            }
+        }
+        return ComponentName.unflattenFromString(
+                SystemProperties.get(PROP_PROD_MULTI_CLIENT_IME, ""));
+    }
+
+    /**
+     * {@link ComponentName} of multi-client IME to be used.
+     *
+     * @hide
+     */
+    @Nullable
+    public static final ComponentName sMultiClientImeComponentName =
+            getMultiClientImeComponentName();
+
+    /**
+     * {@code true} when multi-client IME is enabled.
+     *
+     * @hide
+     */
+    public static final boolean MULTI_CLIENT_IME_ENABLED = (sMultiClientImeComponentName != null);
+}
diff --git a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
index b41096c..77cb4cd 100644
--- a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
+++ b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
@@ -17,6 +17,7 @@
 package android.view.textclassifier;
 
 import android.app.Person;
+import android.content.Context;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 
@@ -28,7 +29,10 @@
 import java.util.ArrayList;
 import java.util.Deque;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
+import java.util.StringJoiner;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
@@ -84,6 +88,29 @@
                 new ActionsSuggestionsModel.ConversationMessage[nativeMessages.size()]);
     }
 
+    /**
+     * Returns the result id for logging.
+     */
+    public static String createResultId(
+            Context context,
+            List<ConversationActions.Message> messages,
+            int modelVersion,
+            List<Locale> modelLocales) {
+        final StringJoiner localesJoiner = new StringJoiner(",");
+        for (Locale locale : modelLocales) {
+            localesJoiner.add(locale.toLanguageTag());
+        }
+        final String modelName = String.format(
+                Locale.US, "%s_v%d", localesJoiner.toString(), modelVersion);
+        final int hash = Objects.hash(
+                messages.stream()
+                        .map(ConversationActions.Message::getText)
+                        .collect(Collectors.toList()),
+                context.getPackageName());
+        return SelectionSessionLogger.SignatureParser.createSignature(
+                SelectionSessionLogger.CLASSIFIER_ID, modelName, hash);
+    }
+
     private static final class PersonEncoder {
         private final Map<Person, Integer> mMapping = new ArrayMap<>();
         private int mNextUserId = FIRST_NON_LOCAL_USER;
diff --git a/core/java/android/view/textclassifier/TextClassifierEvent.java b/core/java/android/view/textclassifier/TextClassifierEvent.java
index 3bb9ee8..f2fea02 100644
--- a/core/java/android/view/textclassifier/TextClassifierEvent.java
+++ b/core/java/android/view/textclassifier/TextClassifierEvent.java
@@ -27,6 +27,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
 
 /**
  * A text classifier event.
@@ -498,4 +499,25 @@
         }
         // TODO: Add build(boolean validate).
     }
+
+    @Override
+    public String toString() {
+        StringBuilder out = new StringBuilder(128);
+        out.append("TextClassifierEvent{");
+        out.append("mEventCategory=").append(mEventCategory);
+        out.append(", mEventType=").append(mEventType);
+        out.append(", mEventContext=").append(mEventContext);
+        out.append(", mResultId=").append(mResultId);
+        out.append(", mEventIndex=").append(mEventIndex);
+        out.append(", mEventTime=").append(mEventTime);
+        out.append(", mExtras=").append(mExtras);
+        out.append(", mRelativeWordStartIndex=").append(mRelativeWordStartIndex);
+        out.append(", mRelativeWordEndIndex=").append(mRelativeWordEndIndex);
+        out.append(", mRelativeSuggestedWordStartIndex=").append(mRelativeSuggestedWordStartIndex);
+        out.append(", mRelativeSuggestedWordEndIndex=").append(mRelativeSuggestedWordEndIndex);
+        out.append(", mActionIndices=").append(Arrays.toString(mActionIndices));
+        out.append(", mLanguage=").append(mLanguage);
+        out.append("}");
+        return out.toString();
+    }
 }
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 9b0f9c6..fcd06c3 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -80,6 +80,8 @@
 
     private static final String LOG_TAG = DEFAULT_LOG_TAG;
 
+    private static final boolean DEBUG = false;
+
     private static final File FACTORY_MODEL_DIR = new File("/etc/textclassifier/");
     // Annotator
     private static final String ANNOTATOR_FACTORY_MODEL_FILENAME_REGEX =
@@ -109,6 +111,8 @@
     @GuardedBy("mLock") // Do not access outside this lock.
     private LangIdModel mLangIdImpl;
     @GuardedBy("mLock") // Do not access outside this lock.
+    private ModelFileManager.ModelFile mActionModelInUse;
+    @GuardedBy("mLock") // Do not access outside this lock.
     private ActionsSuggestionsModel mActionsImpl;
 
     private final Object mLoggerLock = new Object();
@@ -342,8 +346,10 @@
     }
 
     @Override
-    public void onTextClassifierEvent(@NonNull TextClassifierEvent event) {
-        // TODO: Implement.
+    public void onTextClassifierEvent(TextClassifierEvent event) {
+        if (DEBUG) {
+            Log.d(DEFAULT_LOG_TAG, "onTextClassifierEvent() called with: event = [" + event + "]");
+        }
     }
 
     /** @inheritDoc */
@@ -408,7 +414,12 @@
                                 .setConfidenceScore(nativeSuggestion.getScore())
                                 .build());
             }
-            return new ConversationActions(conversationActions, /*id*/ null);
+            String resultId = ActionsSuggestionsHelper.createResultId(
+                    mContext,
+                    request.getConversation(),
+                    mActionModelInUse.getVersion(),
+                    mActionModelInUse.getSupportedLocales());
+            return new ConversationActions(conversationActions, resultId);
         } catch (Throwable t) {
             // Avoid throwing from this method. Log the error.
             Log.e(LOG_TAG, "Error suggesting conversation actions.", t);
@@ -517,6 +528,7 @@
                 try {
                     if (pfd != null) {
                         mActionsImpl = new ActionsSuggestionsModel(pfd.getFd());
+                        mActionModelInUse = bestModel;
                     }
                 } finally {
                     maybeCloseAndLogError(pfd);
diff --git a/core/java/android/webkit/ConsoleMessage.java b/core/java/android/webkit/ConsoleMessage.java
index 6051956..e548497 100644
--- a/core/java/android/webkit/ConsoleMessage.java
+++ b/core/java/android/webkit/ConsoleMessage.java
@@ -17,6 +17,7 @@
 package android.webkit;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 
 /**
  * Public class representing a JavaScript console message from WebCore. This could be a issued
@@ -36,13 +37,13 @@
         DEBUG
     };
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private MessageLevel mLevel;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private String mMessage;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private String mSourceId;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mLineNumber;
 
     public ConsoleMessage(String message, String sourceId, int lineNumber, MessageLevel msgLevel) {
diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java
index 03ff0ca..8a1a0b5 100644
--- a/core/java/android/webkit/UserPackage.java
+++ b/core/java/android/webkit/UserPackage.java
@@ -34,7 +34,7 @@
     private final UserInfo mUserInfo;
     private final PackageInfo mPackageInfo;
 
-    public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.P;
+    public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.Q;
 
     public UserPackage(UserInfo user, PackageInfo packageInfo) {
         this.mUserInfo = user;
diff --git a/core/java/android/webkit/WebResourceResponse.java b/core/java/android/webkit/WebResourceResponse.java
index aae3056..e66596b 100644
--- a/core/java/android/webkit/WebResourceResponse.java
+++ b/core/java/android/webkit/WebResourceResponse.java
@@ -41,13 +41,21 @@
     private InputStream mInputStream;
 
     /**
-     * Constructs a resource response with the given MIME type, encoding, and
-     * input stream. Callers must implement
+     * Constructs a resource response with the given MIME type, character encoding,
+     * and input stream. Callers must implement
      * {@link InputStream#read(byte[]) InputStream.read(byte[])} for the input
      * stream.
      *
-     * @param mimeType the resource response's MIME type, for example text/html
-     * @param encoding the resource response's encoding
+     * <p class="note"><b>Note:</b> The MIME type and character encoding must
+     * be specified as separate parameters (for example {@code "text/html"} and
+     * {@code "utf-8"}), not a single value like the {@code "text/html; charset=utf-8"}
+     * format used in the HTTP Content-Type header. Do not use the value of a HTTP
+     * Content-Encoding header for {@code encoding}, as that header does not specify a
+     * character encoding. Content without a defined character encoding (for example
+     * image resources) should pass {@code null} for {@code encoding}.
+     *
+     * @param mimeType the resource response's MIME type, for example {@code "text/html"}.
+     * @param encoding the resource response's character encoding, for example {@code "utf-8"}.
      * @param data the input stream that provides the resource response's data. Must not be a
      *             StringBufferInputStream.
      */
@@ -63,8 +71,11 @@
      * implement {@link InputStream#read(byte[]) InputStream.read(byte[])} for
      * the input stream.
      *
-     * @param mimeType the resource response's MIME type, for example text/html
-     * @param encoding the resource response's encoding
+     * <p class="note"><b>Note:</b> See {@link #WebResourceResponse(String,String,InputStream)}
+     * for details on what should be specified for {@code mimeType} and {@code encoding}.
+     *
+     * @param mimeType the resource response's MIME type, for example {@code "text/html"}.
+     * @param encoding the resource response's character encoding, for example {@code "utf-8"}.
      * @param statusCode the status code needs to be in the ranges [100, 299], [400, 599].
      *                   Causing a redirect by specifying a 3xx code is not supported.
      * @param reasonPhrase the phrase describing the status code, for example "OK". Must be
diff --git a/core/java/android/webkit/WebSyncManager.java b/core/java/android/webkit/WebSyncManager.java
index 3fa1b01..e44d6eb 100644
--- a/core/java/android/webkit/WebSyncManager.java
+++ b/core/java/android/webkit/WebSyncManager.java
@@ -26,6 +26,7 @@
 abstract class WebSyncManager implements Runnable {
     protected static final java.lang.String LOGTAG = "websync";
     protected android.webkit.WebViewDatabase mDataBase;
+    @UnsupportedAppUsage
     protected android.os.Handler mHandler;
 
     protected WebSyncManager(Context context, String name) {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 414cb8f..bad2dbf 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -850,7 +850,7 @@
 
     /**
      * Asynchronously evaluates JavaScript in the context of the currently displayed page.
-     * If non-null, |resultCallback| will be invoked with any result returned from that
+     * If non-null, {@code resultCallback} will be invoked with any result returned from that
      * execution. This method must be called on the UI thread and the callback will
      * be made on the UI thread.
      * <p>
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index 6ab7f66..ef69b63 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -138,6 +138,20 @@
     }
 
     /**
+     * Call webview draw functor. See API in draw_fn.h.
+     * @param canvas a hardware accelerated canvas (see {@link Canvas#isHardwareAccelerated()}).
+     * @param functor created by AwDrawFn_CreateFunctor in draw_fn.h.
+     */
+    public void drawWebViewFunctor(@NonNull Canvas canvas, int functor) {
+        if (!(canvas instanceof RecordingCanvas)) {
+            // Canvas#isHardwareAccelerated() is only true for subclasses of RecordingCanvas.
+            throw new IllegalArgumentException(canvas.getClass().getName()
+                    + " is not a RecordingCanvas canvas");
+        }
+        ((RecordingCanvas) canvas).drawWebViewFunctor(functor);
+    }
+
+    /**
      * Detaches the draw GL functor.
      *
      * @param nativeDrawGLFunctor the pointer to the native functor that implements
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 46b1f6e..6d88530 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -47,7 +47,7 @@
     // visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote.
     /** @hide */
     private static final String CHROMIUM_WEBVIEW_FACTORY =
-            "com.android.webview.chromium.WebViewChromiumFactoryProviderForP";
+            "com.android.webview.chromium.WebViewChromiumFactoryProviderForQ";
 
     private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create";
 
diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java
index 49e11b8..9f7aa6a 100644
--- a/core/java/android/webkit/WebViewZygote.java
+++ b/core/java/android/webkit/WebViewZygote.java
@@ -159,6 +159,7 @@
                     0,  // runtimeFlags
                     "webview_zygote",  // seInfo
                     sPackage.applicationInfo.primaryCpuAbi,  // abi
+                    TextUtils.join(",", Build.SUPPORTED_ABIS),
                     null);  // instructionSet
 
             // All the work below is usually done by LoadedApk, but the zygote can't talk to
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 07096e8..99895bd 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -29,6 +29,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.TransitionDrawable;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
@@ -316,7 +317,7 @@
     /**
      * The drawable used to draw the selector
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     Drawable mSelector;
 
     /**
@@ -677,7 +678,7 @@
      * ID of the active pointer. This is used to retain consistency during
      * drags/flings if multiple pointers are used.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mActivePointerId = INVALID_POINTER;
 
     /**
@@ -6571,7 +6572,7 @@
             @ViewDebug.IntToString(from = ITEM_VIEW_TYPE_IGNORE, to = "ITEM_VIEW_TYPE_IGNORE"),
             @ViewDebug.IntToString(from = ITEM_VIEW_TYPE_HEADER_OR_FOOTER, to = "ITEM_VIEW_TYPE_HEADER_OR_FOOTER")
         })
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         int viewType;
 
         /**
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index ea1bfc2..d6a0ae4 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
+import android.os.Build;
 import android.os.Bundle;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -105,7 +106,7 @@
     /**
      * Helper for detecting touch gestures.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private GestureDetector mGestureDetector;
 
     /**
@@ -149,7 +150,7 @@
     /**
      * The currently selected item's child.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private View mSelectedChild;
     
     /**
@@ -480,7 +481,7 @@
     /**
      * @return The center of this Gallery.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int getCenterOfGallery() {
         return (getWidth() - mPaddingLeft - mPaddingRight) / 2 + mPaddingLeft;
     }
@@ -488,7 +489,7 @@
     /**
      * @return The center of the given view.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static int getCenterOfView(View view) {
         return view.getLeft() + view.getWidth() / 2;
     }
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index ee04bcf..9b49786 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -28,6 +28,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Trace;
 import android.util.AttributeSet;
@@ -189,7 +190,7 @@
 
     @UnsupportedAppUsage
     Drawable mDivider;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     int mDividerHeight;
 
     Drawable mOverScrollHeader;
@@ -1390,7 +1391,7 @@
      *            startPosition is 0).
      * @return The height of this ListView with the given children.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     final int measureHeightOfChildren(int widthMeasureSpec, int startPosition, int endPosition,
             int maxHeight, int disallowPartialChildPosition) {
         final ListAdapter adapter = mAdapter;
@@ -1533,7 +1534,7 @@
      *
      * @param childCount Number of children
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private void correctTooHigh(int childCount) {
         // First see if the last item is visible. If it is not, it is OK for the
         // top of the list to be pushed up.
@@ -1583,7 +1584,7 @@
      *
      * @param childCount Number of children
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private void correctTooLow(int childCount) {
         // First see if the first item is visible. If it is not, it is OK for the
         // bottom of the list to be pushed down.
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 7d02757..886718d 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -271,7 +271,7 @@
             if (mWindow == null) {
                 synchronized (mLock) {
                     mWindow = new InternalPopupWindow(mView.getContext(), mView.getDisplay(),
-                            mParentSurface.mSurface, mWindowWidth, mWindowHeight,
+                            mParentSurface.mSurfaceControl, mWindowWidth, mWindowHeight,
                             mWindowElevation, mWindowCornerRadius,
                             mOverlay != null ? mOverlay : new ColorDrawable(Color.TRANSPARENT),
                             Handler.getMain() /* draw the magnifier on the UI thread */, mLock,
@@ -528,17 +528,20 @@
                 final int surfaceHeight =
                         viewRootImpl.getHeight() + surfaceInsets.top + surfaceInsets.bottom;
                 validMainWindowSurface =
-                        new SurfaceInfo(mainWindowSurface, surfaceWidth, surfaceHeight, true);
+                        new SurfaceInfo(viewRootImpl.getSurfaceControl(), mainWindowSurface,
+                                surfaceWidth, surfaceHeight, true);
             }
         }
         // Get the surface backing the magnified view, if it is a SurfaceView.
         SurfaceInfo validSurfaceViewSurface = SurfaceInfo.NULL;
         if (mView instanceof SurfaceView) {
+            final SurfaceControl sc = ((SurfaceView) mView).getSurfaceControl();
             final SurfaceHolder surfaceHolder = ((SurfaceView) mView).getHolder();
             final Surface surfaceViewSurface = surfaceHolder.getSurface();
-            if (surfaceViewSurface != null && surfaceViewSurface.isValid()) {
+
+            if (sc != null && sc.isValid()) {
                 final Rect surfaceFrame = surfaceHolder.getSurfaceFrame();
-                validSurfaceViewSurface = new SurfaceInfo(surfaceViewSurface,
+                validSurfaceViewSurface = new SurfaceInfo(sc, surfaceViewSurface,
                         surfaceFrame.right, surfaceFrame.bottom, false);
             }
         }
@@ -733,15 +736,18 @@
      * Contains a surface and metadata corresponding to it.
      */
     private static class SurfaceInfo {
-        public static final SurfaceInfo NULL = new SurfaceInfo(null, 0, 0, false);
+        public static final SurfaceInfo NULL = new SurfaceInfo(null, null, 0, 0, false);
 
         private Surface mSurface;
+        private SurfaceControl mSurfaceControl;
         private int mWidth;
         private int mHeight;
         private boolean mIsMainWindowSurface;
 
-        SurfaceInfo(final Surface surface, final int width, final int height,
+        SurfaceInfo(final SurfaceControl surfaceControl, final Surface surface,
+                final int width, final int height,
                 final boolean isMainWindowSurface) {
+            mSurfaceControl = surfaceControl;
             mSurface = surface;
             mWidth = width;
             mHeight = height;
@@ -819,7 +825,7 @@
         private Bitmap mCurrentContent;
 
         InternalPopupWindow(final Context context, final Display display,
-                final Surface parentSurface, final int width, final int height,
+                final SurfaceControl parentSurfaceControl, final int width, final int height,
                 final float elevation, final float cornerRadius, final Drawable overlay,
                 final Handler handler, final Object lock, final Callback callback) {
             mDisplay = display;
@@ -834,12 +840,13 @@
             // Setup the surface we will use for drawing the content and shadow.
             mSurfaceWidth = mContentWidth + 2 * mOffsetX;
             mSurfaceHeight = mContentHeight + 2 * mOffsetY;
-            mSurfaceSession = new SurfaceSession(parentSurface);
+            mSurfaceSession = new SurfaceSession();
             mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .setBufferSize(mSurfaceWidth, mSurfaceHeight)
                     .setName("magnifier surface")
                     .setFlags(SurfaceControl.HIDDEN)
+                    .setParent(parentSurfaceControl)
                     .build();
             mSurface = new Surface();
             mSurface.copyFrom(mSurfaceControl);
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index 82d77c5..65925b4 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -21,6 +21,7 @@
 import android.content.res.Resources;
 import android.graphics.PixelFormat;
 import android.media.AudioManager;
+import android.os.Build;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Gravity;
@@ -87,9 +88,9 @@
     private WindowManager.LayoutParams mDecorLayoutParams;
     @UnsupportedAppUsage
     private ProgressBar mProgress;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private TextView mEndTime;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private TextView mCurrentTime;
     @UnsupportedAppUsage
     private boolean mShowing;
@@ -107,9 +108,9 @@
     private ImageButton mFfwdButton;
     @UnsupportedAppUsage
     private ImageButton mRewButton;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private ImageButton mNextButton;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private ImageButton mPrevButton;
     private CharSequence mPlayDescription;
     private CharSequence mPauseDescription;
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 1883809..ed6f0d6 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -206,9 +206,9 @@
     private float mElevation;
 
     private Drawable mBackground;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private Drawable mAboveAnchorBackgroundDrawable;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private Drawable mBelowAnchorBackgroundDrawable;
 
     private Transition mEnterTransition;
diff --git a/core/java/android/widget/SimpleAdapter.java b/core/java/android/widget/SimpleAdapter.java
index a27354d..15e1ffa 100644
--- a/core/java/android/widget/SimpleAdapter.java
+++ b/core/java/android/widget/SimpleAdapter.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.net.Uri;
+import android.os.Build;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -59,7 +60,7 @@
     private String[] mFrom;
     private ViewBinder mViewBinder;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private List<? extends Map<String, ?>> mData;
 
     private int mResource;
diff --git a/core/java/android/widget/SimpleCursorAdapter.java b/core/java/android/widget/SimpleCursorAdapter.java
index b670e6c..77fe5d1 100644
--- a/core/java/android/widget/SimpleCursorAdapter.java
+++ b/core/java/android/widget/SimpleCursorAdapter.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.Build;
 import android.view.View;
 
 /**
@@ -52,7 +53,7 @@
      * This field should be made private, so it is hidden from the SDK.
      * {@hide}
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     protected int[] mFrom;
     /**
      * A list of View ids representing the views to which the data must be bound.
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 90da812..0ea8579 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -158,6 +158,7 @@
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
 import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.ContentCaptureSession;
 import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.CorrectionInfo;
@@ -428,7 +429,7 @@
     @ViewDebug.ExportedProperty(category = "text")
     @UnsupportedAppUsage
     private int mCurTextColor;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mCurHintTextColor;
     private boolean mFreezesText;
 
@@ -719,7 +720,7 @@
     @ViewDebug.ExportedProperty(category = "text")
     @UnsupportedAppUsage
     private int mGravity = Gravity.TOP | Gravity.START;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private boolean mHorizontallyScrolling;
 
     private int mAutoLinkMask;
@@ -5095,13 +5096,24 @@
     }
 
     /**
-     * Returns whether the text is allowed to be wider than the View is.
+     * Returns whether the text is allowed to be wider than the View.
+     * If false, the text will be wrapped to the width of the View.
+     *
+     * @attr ref android.R.styleable#TextView_scrollHorizontally
+     * @see #setHorizontallyScrolling(boolean)
+     */
+    public final boolean isHorizontallyScrolling() {
+        return mHorizontallyScrolling;
+    }
+
+    /**
+     * Returns whether the text is allowed to be wider than the View.
      * If false, the text will be wrapped to the width of the View.
      *
      * @attr ref android.R.styleable#TextView_scrollHorizontally
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public boolean getHorizontallyScrolling() {
         return mHorizontallyScrolling;
     }
@@ -10001,7 +10013,7 @@
                         && mSavedMarqueeModeLayout.getLineWidth(0) > width));
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private void startMarquee() {
         // Do not ellipsize EditText
         if (getKeyListener() != null) return;
@@ -10044,7 +10056,7 @@
         }
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private void startStopMarquee(boolean start) {
         if (mEllipsize == TextUtils.TruncateAt.MARQUEE) {
             if (start) {
@@ -10216,12 +10228,19 @@
             }
         }
 
+        // TODO(b/121045053): should use a flag / boolean to keep status of SHOWN / HIDDEN instead
+        // of using isLaidout(), so it's not called in cases where it's laid out but a
+        // notifyAppeared was not sent.
+
         // ContentCapture
         if (isLaidOut() && isImportantForContentCapture() && isTextEditable()) {
             final ContentCaptureManager cm = mContext.getSystemService(ContentCaptureManager.class);
             if (cm != null && cm.isContentCaptureEnabled()) {
-                // TODO(b/111276913): pass flags when edited by user / add CTS test
-                cm.notifyViewTextChanged(getAutofillId(), getText(), /* flags= */ 0);
+                final ContentCaptureSession session = getContentCaptureSession();
+                if (session != null) {
+                    // TODO(b/111276913): pass flags when edited by user / add CTS test
+                    session.notifyViewTextChanged(getAutofillId(), getText(), /* flags= */ 0);
+                }
             }
         }
     }
@@ -10812,6 +10831,25 @@
                         return onTextContextMenuItem(ID_PASTE);
                     }
                     break;
+                case KeyEvent.KEYCODE_INSERT:
+                    if (canCopy()) {
+                        return onTextContextMenuItem(ID_COPY);
+                    }
+                    break;
+            }
+        } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
+            // Handle Shift-only shortcuts.
+            switch (keyCode) {
+                case KeyEvent.KEYCODE_FORWARD_DEL:
+                    if (canCut()) {
+                        return onTextContextMenuItem(ID_CUT);
+                    }
+                    break;
+                case KeyEvent.KEYCODE_INSERT:
+                    if (canPaste()) {
+                        return onTextContextMenuItem(ID_PASTE);
+                    }
+                    break;
             }
         } else if (event.hasModifiers(KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)) {
             // Handle Ctrl-Shift shortcuts.
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index 8b45d99..40b0f13 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -113,7 +113,7 @@
     private int mTargetState = STATE_IDLE;
 
     // All the stuff we need for playing and showing a video
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private SurfaceHolder mSurfaceHolder = null;
     @UnsupportedAppUsage
     private MediaPlayer mMediaPlayer = null;
@@ -565,7 +565,7 @@
         }
     };
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private MediaPlayer.OnErrorListener mErrorListener =
         new MediaPlayer.OnErrorListener() {
         public boolean onError(MediaPlayer mp, int framework_err, int impl_err) {
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index e83e79b..925a589 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -357,15 +357,16 @@
     }
 
     private boolean performTtsPrompt(AlertDialog alertDialog) {
+        final String serviceName = getShortcutFeatureDescription(false /* no summary */);
         final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
-        if (serviceInfo == null) {
+        if (TextUtils.isEmpty(serviceName) || serviceInfo == null) {
             return false;
         }
         if ((serviceInfo.flags & AccessibilityServiceInfo
                 .FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK) == 0) {
             return false;
         }
-        final TtsPrompt tts = new TtsPrompt();
+        final TtsPrompt tts = new TtsPrompt(serviceName);
         alertDialog.setOnDismissListener(dialog -> tts.dismiss());
         return true;
     }
@@ -378,8 +379,9 @@
         private boolean mDismiss;
         private TextToSpeech mTts;
 
-        TtsPrompt() {
-            mText = mContext.getString(R.string.accessibility_shortcut_spoken_feedback);
+        TtsPrompt(String serviceName) {
+            mText = mContext.getString(R.string.accessibility_shortcut_spoken_feedback,
+                    serviceName);
             mTts = mFrameworkObjectProvider.getTextToSpeech(mContext, this);
         }
 
diff --git a/core/java/com/android/internal/app/ColorDisplayController.java b/core/java/com/android/internal/app/ColorDisplayController.java
index 213bb75..c093fe5 100644
--- a/core/java/com/android/internal/app/ColorDisplayController.java
+++ b/core/java/com/android/internal/app/ColorDisplayController.java
@@ -37,12 +37,8 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.time.DateTimeException;
-import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
-import java.time.ZoneId;
-import java.time.format.DateTimeParseException;
 
 /**
  * Controller for managing night display and color mode settings.
@@ -152,28 +148,6 @@
     }
 
     /**
-     * Returns the time when Night display's activation state last changed, or {@code null} if it
-     * has never been changed.
-     */
-    public LocalDateTime getLastActivatedTime() {
-        final ContentResolver cr = mContext.getContentResolver();
-        final String lastActivatedTime = Secure.getStringForUser(
-                cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, mUserId);
-        if (lastActivatedTime != null) {
-            try {
-                return LocalDateTime.parse(lastActivatedTime);
-            } catch (DateTimeParseException ignored) {}
-            // Uses the old epoch time.
-            try {
-                return LocalDateTime.ofInstant(
-                    Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
-                    ZoneId.systemDefault());
-            } catch (DateTimeException|NumberFormatException ignored) {}
-        }
-        return null;
-    }
-
-    /**
      * Returns the current auto mode value controlling when Night display will be automatically
      * activated. One of {@link #AUTO_MODE_DISABLED}, {@link #AUTO_MODE_CUSTOM}, or
      * {@link #AUTO_MODE_TWILIGHT}.
diff --git a/core/java/android/service/contentcapture/InteractionContext.aidl b/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl
similarity index 63%
copy from core/java/android/service/contentcapture/InteractionContext.aidl
copy to core/java/com/android/internal/app/IAppOpsNotedCallback.aidl
index 982e095..fa5c30a 100644
--- a/core/java/android/service/contentcapture/InteractionContext.aidl
+++ b/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
+/*
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
-package android.service.contentcapture;
+package com.android.internal.app;
 
-parcelable InteractionContext;
+// Iterface to observe op note/checks of ops
+oneway interface IAppOpsNotedCallback {
+    void opNoted(int op, int uid, String packageName, int mode);
+}
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 049103b..e59bee4 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -21,6 +21,7 @@
 import android.os.Bundle;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsActiveCallback;
+import com.android.internal.app.IAppOpsNotedCallback;
 
 interface IAppOpsService {
     // These first methods are also called by native code, so must
@@ -61,4 +62,9 @@
     boolean isOperationActive(int code, int uid, String packageName);
 
     void startWatchingModeWithFlags(int op, String packageName, int flags, IAppOpsCallback callback);
+
+    void startWatchingNoted(in int[] ops, IAppOpsNotedCallback callback);
+    void stopWatchingNoted(IAppOpsNotedCallback callback);
+
+    int checkOperationRaw(int code, int uid, String packageName);
 }
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index ef5eb6c..a50c736 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -101,6 +101,7 @@
     private AbsListView mAdapterView;
     private Button mAlwaysButton;
     private Button mOnceButton;
+    private Button mSettingsButton;
     private View mProfileView;
     private int mIconDpi;
     private int mLastSelected = AbsListView.INVALID_POSITION;
@@ -112,6 +113,7 @@
     private String mReferrerPackage;
     private CharSequence mTitle;
     private int mDefaultTitleResId;
+    private boolean mUseLayoutForBrowsables;
 
     // Whether or not this activity supports choosing a default handler for the intent.
     private boolean mSupportsAlwaysUseOption;
@@ -192,6 +194,12 @@
                 com.android.internal.R.string.whichHomeApplicationNamed,
                 com.android.internal.R.string.whichHomeApplicationLabel);
 
+        // SpR.id.buttonecial titles for BROWSABLE components
+        public static final int BROWSABLE_TITLE_RES =
+                com.android.internal.R.string.whichGiveAccessToApplication;
+        public static final int BROWSABLE_NAMED_TITLE_RES =
+                com.android.internal.R.string.whichGiveAccessToApplicationNamed;
+
         public final String action;
         public final int titleRes;
         public final int namedTitleRes;
@@ -283,7 +291,6 @@
         mPackageMonitor.register(this, getMainLooper(), false);
         mRegistered = true;
         mReferrerPackage = getReferrerPackageName();
-        mSupportsAlwaysUseOption = supportsAlwaysUseOption;
 
         final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
         mIconDpi = am.getLauncherLargeIconDensity();
@@ -293,6 +300,14 @@
         mTitle = title;
         mDefaultTitleResId = defaultTitleRes;
 
+        mUseLayoutForBrowsables = getTargetIntent() == null
+                ? false
+                : getTargetIntent().hasCategory(Intent.CATEGORY_BROWSABLE);
+
+        // We don't want to support Always Use if browsable layout is being used,
+        // as to mitigate Intent Capturing vulnerability
+        mSupportsAlwaysUseOption = supportsAlwaysUseOption && !mUseLayoutForBrowsables;
+
         mIconFactory = IconDrawableFactory.newInstance(this, true);
         if (configureContentView(mIntents, initialIntents, rList)) {
             return;
@@ -448,13 +463,23 @@
         mSafeForwardingMode = safeForwarding;
     }
 
-    protected CharSequence getTitleForAction(String action, int defaultTitleRes) {
-        final ActionTitle title = mResolvingHome ? ActionTitle.HOME : ActionTitle.forAction(action);
+    protected CharSequence getTitleForAction(Intent intent, int defaultTitleRes) {
+        final ActionTitle title = mResolvingHome
+                ? ActionTitle.HOME
+                : ActionTitle.forAction(intent.getAction());
+
         // While there may already be a filtered item, we can only use it in the title if the list
         // is already sorted and all information relevant to it is already in the list.
         final boolean named = mAdapter.getFilteredPosition() >= 0;
         if (title == ActionTitle.DEFAULT && defaultTitleRes != 0) {
             return getString(defaultTitleRes);
+        } else if (intent.hasCategory(Intent.CATEGORY_BROWSABLE)) {
+            // If the Intent is BROWSABLE then we need to warn the user that
+            // they're giving access for the activity to open URLs from this specific host
+            return named
+                    ? getString(ActionTitle.BROWSABLE_NAMED_TITLE_RES, intent.getData().getHost(),
+                    mAdapter.getFilteredItem().getDisplayLabel())
+                    : getString(ActionTitle.BROWSABLE_TITLE_RES, intent.getData().getHost());
         } else {
             return named
                     ? getString(title.namedTitleRes, mAdapter.getFilteredItem().getDisplayLabel())
@@ -555,7 +580,7 @@
     @Override
     protected void onRestoreInstanceState(Bundle savedInstanceState) {
         super.onRestoreInstanceState(savedInstanceState);
-        resetAlwaysOrOnceButtonBar();
+        resetButtonBar();
     }
 
     private boolean hasManagedProfile() {
@@ -607,11 +632,23 @@
 
     public void onButtonClick(View v) {
         final int id = v.getId();
-        startSelected(mAdapter.hasFilteredItem() ?
-                        mAdapter.getFilteredPosition():
-                        mAdapterView.getCheckedItemPosition(),
-                id == R.id.button_always,
-                !mAdapter.hasFilteredItem());
+        int which = mAdapter.hasFilteredItem()
+                ? mAdapter.getFilteredPosition()
+                : mAdapterView.getCheckedItemPosition();
+        boolean hasIndexBeenFiltered = !mAdapter.hasFilteredItem();
+        if (id == R.id.button_app_settings) {
+            showSettingsForSelected(which, hasIndexBeenFiltered);
+        } else {
+            startSelected(which, id == R.id.button_always, hasIndexBeenFiltered);
+        }
+    }
+
+    private void showSettingsForSelected(int which, boolean hasIndexBeenFiltered) {
+        ResolveInfo ri = mAdapter.resolveInfoForPosition(which, hasIndexBeenFiltered);
+        Intent in = new Intent().setAction(Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS)
+                .setData(Uri.fromParts("package", ri.activityInfo.packageName, null))
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
+        startActivity(in);
     }
 
     public void startSelected(int which, boolean always, boolean hasIndexBeenFiltered) {
@@ -995,7 +1032,7 @@
         adapterView.setOnItemClickListener(listener);
         adapterView.setOnItemLongClickListener(listener);
 
-        if (mSupportsAlwaysUseOption) {
+        if (mSupportsAlwaysUseOption || mUseLayoutForBrowsables) {
             listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
         }
 
@@ -1017,7 +1054,7 @@
 
         CharSequence title = mTitle != null
                 ? mTitle
-                : getTitleForAction(getTargetIntent().getAction(), mDefaultTitleResId);
+                : getTitleForAction(getTargetIntent(), mDefaultTitleResId);
 
         if (!TextUtils.isEmpty(title)) {
             final TextView titleView = findViewById(R.id.title);
@@ -1051,16 +1088,47 @@
         }
     }
 
-    public void resetAlwaysOrOnceButtonBar() {
-        if (mSupportsAlwaysUseOption) {
-            final ViewGroup buttonLayout = findViewById(R.id.button_bar);
-            if (buttonLayout != null) {
-                buttonLayout.setVisibility(View.VISIBLE);
-                mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
-                mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
+    private void resetButtonBar() {
+        if (!mSupportsAlwaysUseOption && !mUseLayoutForBrowsables) {
+            return;
+        }
+        final ViewGroup buttonLayout = findViewById(R.id.button_bar);
+        if (buttonLayout != null) {
+            buttonLayout.setVisibility(View.VISIBLE);
+            mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
+            mSettingsButton = (Button) buttonLayout.findViewById(R.id.button_app_settings);
+            mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
+
+            if (mUseLayoutForBrowsables) {
+                resetSettingsOrOnceButtonBar();
             } else {
-                Log.e(TAG, "Layout unexpectedly does not have a button bar");
+                resetAlwaysOrOnceButtonBar();
             }
+        } else {
+            Log.e(TAG, "Layout unexpectedly does not have a button bar");
+        }
+    }
+
+    private void resetSettingsOrOnceButtonBar() {
+        //unsetting always button
+        mAlwaysButton.setVisibility(View.GONE);
+
+        // When the items load in, if an item was already selected,
+        // enable the buttons
+        if (mAdapterView != null
+                && mAdapterView.getCheckedItemPosition() != ListView.INVALID_POSITION) {
+            mSettingsButton.setEnabled(true);
+            mOnceButton.setEnabled(true);
+        }
+    }
+
+    private void resetAlwaysOrOnceButtonBar() {
+        // This check needs to be made because layout with default
+        // doesn't have a settings button
+        if (mSettingsButton != null) {
+            //unsetting always button
+            mSettingsButton.setVisibility(View.GONE);
+            mSettingsButton = null;
         }
 
         if (useLayoutWithDefault()
@@ -1625,7 +1693,7 @@
                     @Override
                     public void run() {
                         setTitleAndIcon();
-                        resetAlwaysOrOnceButtonBar();
+                        resetButtonBar();
                         onListRebuilt();
                         mPostListReadyRunnable = null;
                     }
@@ -1986,8 +2054,14 @@
             final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
             if (!useLayoutWithDefault()
                     && (!hasValidSelection || mLastSelected != checkedPos)
-                    && mAlwaysButton != null) {
-                setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
+                    && (mAlwaysButton != null || mSettingsButton != null)) {
+                if (mSettingsButton != null) {
+                    // this implies that the layout for browsables is being used
+                    mSettingsButton.setEnabled(true);
+                } else {
+                    // this implies that mAlwaysButton != null
+                    setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
+                }
                 mOnceButton.setEnabled(hasValidSelection);
                 if (hasValidSelection) {
                     mAdapterView.smoothScrollToPosition(checkedPos);
diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java
index 96d3baf..f61a03b 100644
--- a/core/java/com/android/internal/app/ResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverComparator.java
@@ -22,44 +22,36 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
+import android.content.ServiceConnection;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.ComponentInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
-import android.content.SharedPreferences;
-import android.content.ServiceConnection;
 import android.metrics.LogMaker;
-import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
-import android.os.storage.StorageManager;
 import android.os.UserHandle;
-import android.service.resolver.IResolverRankerService;
 import android.service.resolver.IResolverRankerResult;
+import android.service.resolver.IResolverRankerService;
 import android.service.resolver.ResolverRankerService;
 import android.service.resolver.ResolverTarget;
-import android.text.TextUtils;
-import android.util.ArrayMap;
 import android.util.Log;
+
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
-import java.io.File;
-import java.lang.InterruptedException;
 import java.text.Collator;
 import java.util.ArrayList;
 import java.util.Comparator;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Ranks and compares packages based on usage stats.
@@ -90,6 +82,8 @@
 
     private final Collator mCollator;
     private final boolean mHttp;
+    // can be null if mHttp == false or current user has no default browser package
+    private final String mDefaultBrowserPackageName;
     private final PackageManager mPm;
     private final UsageStatsManager mUsm;
     private final Map<String, UsageStats> mStats;
@@ -184,6 +178,10 @@
         getContentAnnotations(intent);
         mAction = intent.getAction();
         mRankerServiceName = new ComponentName(mContext, this.getClass());
+
+        mDefaultBrowserPackageName = mHttp
+                ? mPm.getDefaultBrowserPackageNameAsUser(UserHandle.myUserId())
+                : null;
     }
 
     // get annotations of content from intent.
@@ -312,7 +310,14 @@
         if (mHttp) {
             // Special case: we want filters that match URI paths/schemes to be
             // ordered before others.  This is for the case when opening URIs,
-            // to make native apps go above browsers.
+            // to make native apps go above browsers - except for 1 even more special case
+            // which is the default browser, as we want that to go above them all.
+            if (isDefaultBrowser(lhs)) {
+                return -1;
+            }
+            if (isDefaultBrowser(rhs)) {
+                return 1;
+            }
             final boolean lhsSpecific = ResolverActivity.isSpecificUriMatch(lhs.match);
             final boolean rhsSpecific = ResolverActivity.isSpecificUriMatch(rhs.match);
             if (lhsSpecific != rhsSpecific) {
@@ -419,6 +424,20 @@
         }
     }
 
+    private boolean isDefaultBrowser(ResolveInfo ri) {
+        // It makes sense to prefer the default browser
+        // only if the targeted user is the current user
+        if (ri.targetUserId != UserHandle.USER_CURRENT) {
+            return false;
+        }
+
+        if (ri.activityInfo.packageName != null
+                && ri.activityInfo.packageName.equals(mDefaultBrowserPackageName)) {
+            return true;
+        }
+        return false;
+    }
+
     // records metrics for evaluation.
     private void logMetrics(int selectedPos) {
         if (mRankerServiceName != null) {
diff --git a/core/java/com/android/internal/app/procstats/AssociationState.java b/core/java/com/android/internal/app/procstats/AssociationState.java
index 4da3391..2df5158 100644
--- a/core/java/com/android/internal/app/procstats/AssociationState.java
+++ b/core/java/com/android/internal/app/procstats/AssociationState.java
@@ -17,6 +17,7 @@
 package com.android.internal.app.procstats;
 
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -192,9 +193,16 @@
          */
         String mProcess;
 
-        SourceKey(int uid, String process) {
+        /**
+         * Optional package name, or null; consider this final.  Not final just to avoid a
+         * temporary object during lookup.
+         */
+        @Nullable String mPackage;
+
+        SourceKey(int uid, String process, String pkg) {
             mUid = uid;
             mProcess = process;
+            mPackage = pkg;
         }
 
         public boolean equals(Object o) {
@@ -202,12 +210,14 @@
                 return false;
             }
             SourceKey s = (SourceKey) o;
-            return s.mUid == mUid && Objects.equals(s.mProcess, mProcess);
+            return s.mUid == mUid && Objects.equals(s.mProcess, mProcess)
+                    && Objects.equals(s.mPackage, mPackage);
         }
 
         @Override
         public int hashCode() {
-            return Integer.hashCode(mUid) ^ (mProcess == null ? 0 : mProcess.hashCode());
+            return Integer.hashCode(mUid) ^ (mProcess == null ? 0 : mProcess.hashCode())
+                    ^ (mPackage == null ? 0 : (mPackage.hashCode() * 33));
         }
 
         @Override
@@ -217,6 +227,8 @@
             UserHandle.formatUid(sb, mUid);
             sb.append(' ');
             sb.append(mProcess);
+            sb.append(' ');
+            sb.append(mPackage);
             sb.append('}');
             return sb.toString();
         }
@@ -227,7 +239,7 @@
      */
     private final ArrayMap<SourceKey, SourceState> mSources = new ArrayMap<>();
 
-    private final SourceKey mTmpSourceKey = new SourceKey(0, null);
+    private final SourceKey mTmpSourceKey = new SourceKey(0, null, null);
 
     private ProcessState mProc;
 
@@ -266,12 +278,13 @@
         mProc = proc;
     }
 
-    public SourceState startSource(int uid, String processName) {
+    public SourceState startSource(int uid, String processName, String packageName) {
         mTmpSourceKey.mUid = uid;
         mTmpSourceKey.mProcess = processName;
+        mTmpSourceKey.mPackage = packageName;
         SourceState src = mSources.get(mTmpSourceKey);
         if (src == null) {
-            SourceKey key = new SourceKey(uid, processName);
+            SourceKey key = new SourceKey(uid, processName, packageName);
             src = new SourceState(key);
             mSources.put(key, src);
         }
@@ -379,6 +392,7 @@
             final SourceState src = mSources.valueAt(isrc);
             out.writeInt(key.mUid);
             stats.writeCommonString(out, key.mProcess);
+            stats.writeCommonString(out, key.mPackage);
             out.writeInt(src.mCount);
             out.writeLong(src.mDuration);
             out.writeInt(src.mActiveCount);
@@ -405,7 +419,8 @@
         for (int isrc = 0; isrc < NSRC; isrc++) {
             final int uid = in.readInt();
             final String procName = stats.readCommonString(in, parcelVersion);
-            final SourceKey key = new SourceKey(uid, procName);
+            final String pkgName = stats.readCommonString(in, parcelVersion);
+            final SourceKey key = new SourceKey(uid, procName, pkgName);
             final SourceState src = new SourceState(key);
             src.mCount = in.readInt();
             src.mDuration = in.readLong();
@@ -445,10 +460,11 @@
         }
     }
 
-    public boolean hasProcess(String procName) {
+    public boolean hasProcessOrPackage(String procName) {
         final int NSRC = mSources.size();
         for (int isrc = 0; isrc < NSRC; isrc++) {
-            if (mSources.keyAt(isrc).mProcess.equals(procName)) {
+            final SourceKey key = mSources.keyAt(isrc);
+            if (procName.equals(key.mProcess) || procName.equals(key.mPackage)) {
                 return true;
             }
         }
@@ -466,14 +482,20 @@
         for (int isrc = 0; isrc < NSRC; isrc++) {
             final SourceKey key = mSources.keyAt(isrc);
             final SourceState src = mSources.valueAt(isrc);
-            if (reqPackage != null && !reqPackage.equals(key.mProcess)) {
+            if (reqPackage != null && !reqPackage.equals(key.mProcess)
+                    && !reqPackage.equals(key.mPackage)) {
                 continue;
             }
             pw.print(prefixInner);
             pw.print("<- ");
             pw.print(key.mProcess);
-            pw.print(" / ");
+            pw.print("/");
             UserHandle.formatUid(pw, key.mUid);
+            if (key.mPackage != null) {
+                pw.print(" (");
+                pw.print(key.mPackage);
+                pw.print(")");
+            }
             pw.println(":");
             pw.print(prefixInner);
             pw.print("   Total count ");
@@ -683,6 +705,7 @@
             final SourceState src = mSources.valueAt(isrc);
             final long sourceToken = proto.start(PackageAssociationProcessStatsProto.SOURCES);
             proto.write(PackageAssociationSourceProcessStatsProto.PROCESS_NAME, key.mProcess);
+            proto.write(PackageAssociationSourceProcessStatsProto.PACKAGE_NAME, key.mPackage);
             proto.write(PackageAssociationSourceProcessStatsProto.PROCESS_UID, key.mUid);
             proto.write(PackageAssociationSourceProcessStatsProto.TOTAL_COUNT, src.mCount);
             long duration = src.mDuration;
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 9ee583a..9b9b771 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -178,7 +178,7 @@
             {"proc", "pkg-proc", "pkg-svc", "pkg-asc", "pkg-all", "all"};
 
     // Current version of the parcel format.
-    private static final int PARCEL_VERSION = 34;
+    private static final int PARCEL_VERSION = 35;
     // In-memory Parcel magic number, used to detect attempts to unmarshall bad data
     private static final int MAGIC = 0x50535454;
 
@@ -1490,7 +1490,7 @@
                                 // package, so that if so we print those.
                                 for (int iasc = 0; iasc < NASCS; iasc++) {
                                     AssociationState asc = pkgState.mAssociations.valueAt(iasc);
-                                    if (asc.hasProcess(reqPackage)) {
+                                    if (asc.hasProcessOrPackage(reqPackage)) {
                                         onlyAssociations = true;
                                         break;
                                     }
@@ -1591,7 +1591,7 @@
                             for (int iasc = 0; iasc < NASCS; iasc++) {
                                 AssociationState asc = pkgState.mAssociations.valueAt(iasc);
                                 if (!pkgMatch && !reqPackage.equals(asc.getProcessName())) {
-                                    if (!onlyAssociations || !asc.hasProcess(reqPackage)) {
+                                    if (!onlyAssociations || !asc.hasProcessOrPackage(reqPackage)) {
                                         continue;
                                     }
                                 }
diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
index d4c451e..2995a8f 100644
--- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
+++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
@@ -16,14 +16,14 @@
 
 package com.android.internal.hardware;
 
-import com.android.internal.R;
-
 import android.content.Context;
 import android.os.Build;
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.text.TextUtils;
 
+import com.android.internal.R;
+
 public class AmbientDisplayConfiguration {
 
     private final Context mContext;
@@ -37,7 +37,8 @@
     public boolean enabled(int user) {
         return pulseOnNotificationEnabled(user)
                 || pulseOnLongPressEnabled(user)
-                || alwaysOnEnabled(user);
+                || alwaysOnEnabled(user)
+                || wakeLockScreenGestureEnabled(user);
     }
 
     public boolean pulseOnNotificationEnabled(int user) {
@@ -68,10 +69,10 @@
 
     public boolean wakeLockScreenGestureEnabled(int user) {
         return boolSettingDefaultOn(Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, user)
-                && wakeLockScreenGestureAvailable();
+                && wakeScreenGestureAvailable();
     }
 
-    public boolean wakeLockScreenGestureAvailable() {
+    public boolean wakeScreenGestureAvailable() {
         return mContext.getResources()
                 .getBoolean(R.bool.config_dozeWakeLockScreenSensorAvailable);
     }
@@ -81,10 +82,6 @@
                 && wakeScreenGestureAvailable();
     }
 
-    public boolean wakeScreenGestureAvailable() {
-        return !TextUtils.isEmpty(wakeScreenSensorType());
-    }
-
     public String doubleTapSensorType() {
         return mContext.getResources().getString(R.string.config_dozeDoubleTapSensorType);
     }
@@ -93,10 +90,6 @@
         return mContext.getResources().getString(R.string.config_dozeLongPressSensorType);
     }
 
-    public String wakeScreenSensorType() {
-        return mContext.getResources().getString(R.string.config_dozeWakeScreenSensorType);
-    }
-
     public boolean pulseOnLongPressEnabled(int user) {
         return pulseOnLongPressAvailable() && boolSettingDefaultOff(
                 Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, user);
diff --git a/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java
similarity index 98%
rename from services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java
rename to core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java
index aaea45e..26cf180 100644
--- a/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.infra;
+package com.android.internal.infra;
 
 import android.annotation.NonNull;
 import android.content.ComponentName;
diff --git a/services/core/java/com/android/server/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java
similarity index 86%
rename from services/core/java/com/android/server/infra/AbstractRemoteService.java
rename to core/java/com/android/internal/infra/AbstractRemoteService.java
index 41dcf89..c94c64a 100644
--- a/services/core/java/com/android/server/infra/AbstractRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractRemoteService.java
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.server.infra;
+package com.android.internal.infra;
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -27,13 +28,14 @@
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
 import android.os.IInterface;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.Slog;
+import android.util.TimeUtils;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.server.FgThread;
 
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
@@ -61,7 +63,10 @@
 //TODO(b/117779333): improve javadoc above instead of using Autofill as an example
 public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I>,
         I extends IInterface> implements DeathRecipient {
-    private static final int MSG_UNBIND = 1;
+    private static final int MSG_BIND = 1;
+    private static final int MSG_UNBIND = 2;
+
+    protected static final long PERMANENT_BOUND_TIMEOUT_MS = 0;
 
     protected static final int LAST_PRIVATE_MSG = MSG_UNBIND;
 
@@ -85,6 +90,9 @@
     private boolean mServiceDied;
     private boolean mCompleted;
 
+    // Used just for debugging purposes (on dump)
+    private long mNextUnbind;
+
     /**
      * Callback called when the service dies.
      *
@@ -99,7 +107,7 @@
         void onServiceDied(T service);
     }
 
-    // NOTE: must be package-protected so this class is not extend outside
+    // NOTE: must be package-protected so this class is not extended outside
     AbstractRemoteService(@NonNull Context context, @NonNull String serviceInterface,
             @NonNull ComponentName componentName, int userId, @NonNull VultureCallback<S> callback,
             boolean bindInstantServiceAllowed, boolean verbose) {
@@ -109,7 +117,7 @@
         mComponentName = componentName;
         mIntent = new Intent(serviceInterface).setComponent(mComponentName);
         mUserId = userId;
-        mHandler = new Handler(FgThread.getHandler().getLooper());
+        mHandler = new Handler(Looper.getMainLooper());
         mBindInstantServiceAllowed = bindInstantServiceAllowed;
     }
 
@@ -155,7 +163,9 @@
     protected abstract I getServiceInterface(@NonNull IBinder service);
 
     /**
-     * Defines How long after the last interaction with the service we would unbind.
+     * Defines how long after the last interaction with the service we would unbind.
+     *
+     * @return time to unbind (in millis), or {@link #PERMANENT_BOUND_TIMEOUT_MS} to not unbind.
      */
     protected abstract long getTimeoutIdleBindMillis();
 
@@ -164,6 +174,15 @@
      */
     protected abstract long getRemoteRequestMillis();
 
+    /**
+     * Gets the currently registered service interface or {@code null} if the service is not
+     * connected.
+     */
+    @Nullable
+    public final I getServiceInterface() {
+        return mService;
+    }
+
     private void handleDestroy() {
         if (checkIfDestroyed()) return;
         handleOnDestroy();
@@ -210,11 +229,23 @@
                 .append(mComponentName.flattenToString()).println();
         pw.append(prefix).append(tab).append("destroyed=")
                 .append(String.valueOf(mDestroyed)).println();
+        final boolean bound = handleIsBound();
         pw.append(prefix).append(tab).append("bound=")
-                .append(String.valueOf(handleIsBound())).println();
+                .append(String.valueOf(bound));
+        final long idleTimeout = getTimeoutIdleBindMillis();
+        if (bound) {
+            if (idleTimeout > 0) {
+                pw.append(" (unbind in : ");
+                TimeUtils.formatDuration(mNextUnbind - SystemClock.elapsedRealtime(), pw);
+                pw.append(")");
+            } else {
+                pw.append(" (permanently bound)");
+            }
+        }
+        pw.println();
         pw.append(prefix).append("mBindInstantServiceAllowed=").println(mBindInstantServiceAllowed);
         pw.append(prefix).append("idleTimeout=")
-            .append(Long.toString(getTimeoutIdleBindMillis() / 1000)).append("s").println();
+            .append(Long.toString(idleTimeout / 1000)).append("s").println();
         pw.append(prefix).append("requestTimeout=")
             .append(Long.toString(getRemoteRequestMillis() / 1000)).append("s").println();
         pw.println();
@@ -226,6 +257,8 @@
      * <p>This request must be responded by the service somehow (typically using a callback),
      * othewise it will trigger a {@link PendingRequest#onTimeout(AbstractRemoteService)} if the
      * service doesn't respond.
+     *
+     * <p><b>NOTE: </b>this request is responsible for calling {@link #scheduleUnbind()}.
      */
     protected void scheduleRequest(@NonNull PendingRequest<S, I> pendingRequest) {
         cancelScheduledUnbind();
@@ -240,7 +273,7 @@
      * a simple {@link Runnable}.
      */
     protected void scheduleAsyncRequest(@NonNull AsyncRequest<I> request) {
-        cancelScheduledUnbind();
+        scheduleUnbind();
         // TODO(b/117779333): fix generics below
         @SuppressWarnings({"unchecked", "rawtypes"})
         final MyAsyncPendingRequest<S, I> asyncRequest = new MyAsyncPendingRequest(this, request);
@@ -252,13 +285,41 @@
         mHandler.removeMessages(MSG_UNBIND);
     }
 
+    /**
+     * Schedules a request to bind to the remote service.
+     *
+     * <p>Typically used on constructor for implementations that need a permanent connection to
+     * the remote service.
+     */
+    protected void scheduleBind() {
+        if (mHandler.hasMessages(MSG_BIND)) {
+            if (mVerbose) Slog.v(mTag, "scheduleBind(): already scheduled");
+            return;
+        }
+        mHandler.sendMessage(obtainMessage(AbstractRemoteService::handleEnsureBound, this)
+                .setWhat(MSG_BIND));
+    }
+
+    /**
+     * Schedules a request to automatically unbind from the service after the
+     * {@link #getTimeoutIdleBindMillis() idle timeout} expires.
+     */
     protected void scheduleUnbind() {
+        final long unbindDelay = getTimeoutIdleBindMillis();
+
+        if (unbindDelay <= 0) {
+            if (mVerbose) Slog.v(mTag, "not scheduling unbind when value is " + unbindDelay);
+            return;
+        }
+
         cancelScheduledUnbind();
-        // TODO(b/111276913): implement "permanent binding"
         // TODO(b/117779333): make sure it's unbound if the service settings changing (right now
         // it's not)
+
+        mNextUnbind = SystemClock.elapsedRealtime() + unbindDelay;
+        if (mVerbose) Slog.v(mTag, "unbinding in " + unbindDelay + "ms: " + mNextUnbind);
         mHandler.sendMessageDelayed(obtainMessage(AbstractRemoteService::handleUnbind, this)
-                .setWhat(MSG_UNBIND), getTimeoutIdleBindMillis());
+                .setWhat(MSG_UNBIND), unbindDelay);
     }
 
     private void handleUnbind() {
@@ -332,6 +393,7 @@
                 mService = null;
             }
         }
+        mNextUnbind = 0;
         mContext.unbindService(mServiceConnection);
     }
 
diff --git a/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java b/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java
similarity index 98%
rename from services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java
rename to core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java
index d32f13b..f0c2233 100644
--- a/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.infra;
+package com.android.internal.infra;
 
 import android.annotation.NonNull;
 import android.content.ComponentName;
diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java
index bc4b5fe..ec02432 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -17,6 +17,7 @@
 package com.android.internal.net;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -52,7 +53,7 @@
     public String name = "";           // 0
     @UnsupportedAppUsage
     public int type = TYPE_PPTP;       // 1
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public String server = "";         // 2
     @UnsupportedAppUsage
     public String username = "";       // 3
diff --git a/core/java/com/android/internal/os/AppZygoteInit.java b/core/java/com/android/internal/os/AppZygoteInit.java
new file mode 100644
index 0000000..afe6dad
--- /dev/null
+++ b/core/java/com/android/internal/os/AppZygoteInit.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.app.LoadedApk;
+import android.content.pm.ApplicationInfo;
+import android.net.LocalSocket;
+import android.util.Log;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Startup class for an Application zygote process.
+ *
+ * See {@link ZygoteInit} for generic zygote startup documentation.
+ *
+ * @hide
+ */
+class AppZygoteInit {
+    public static final String TAG = "AppZygoteInit";
+
+    private static ZygoteServer sServer;
+
+    private static class AppZygoteServer extends ZygoteServer {
+        @Override
+        protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
+                throws IOException {
+            return new AppZygoteConnection(socket, abiList);
+        }
+    }
+
+    private static class AppZygoteConnection extends ZygoteConnection {
+        AppZygoteConnection(LocalSocket socket, String abiList) throws IOException {
+            super(socket, abiList);
+        }
+
+        @Override
+        protected void preload() {
+            // Nothing to preload by default.
+        }
+
+        @Override
+        protected boolean isPreloadComplete() {
+            // App zygotes don't preload any classes or resources or defaults, all of their
+            // preloading is package specific.
+            return true;
+        }
+
+        @Override
+        protected boolean canPreloadApp() {
+            return true;
+        }
+
+        @Override
+        protected void handlePreloadApp(ApplicationInfo appInfo) {
+            Log.i(TAG, "Beginning application preload for " + appInfo.packageName);
+            LoadedApk loadedApk = new LoadedApk(null, appInfo, null, null, false, true, false);
+            ClassLoader loader = loadedApk.getClassLoader();
+            Class<?> cl;
+            Method m;
+            try {
+                cl = Class.forName(appInfo.packageName + ".ZygotePreload", true, loader);
+                m = cl.getMethod("doPreload");
+                m.setAccessible(true);
+                m.invoke(null);
+            } catch (ClassNotFoundException e) {
+                // Don't treat this as an error since an app may not want to do any preloads
+                Log.w(TAG, "No ZygotePreload class found for " + appInfo.packageName);
+            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+                Log.e(TAG, "AppZygote application preload failed for "
+                        + appInfo.packageName, e);
+            }
+            try {
+                DataOutputStream socketOut = getSocketOutputStream();
+                socketOut.writeInt(loader != null ? 1 : 0);
+            } catch (IOException e) {
+                throw new IllegalStateException("Error writing to command socket", e);
+            }
+
+            Log.i(TAG, "Application preload done");
+        }
+    }
+
+    public static void main(String[] argv) {
+        AppZygoteServer server = new AppZygoteServer();
+        ChildZygoteInit.runZygoteServer(server, argv);
+    }
+}
diff --git a/core/java/com/android/internal/os/AtomicFile.java b/core/java/com/android/internal/os/AtomicFile.java
index e177fe3..a72a2f5 100644
--- a/core/java/com/android/internal/os/AtomicFile.java
+++ b/core/java/com/android/internal/os/AtomicFile.java
@@ -17,6 +17,7 @@
 package com.android.internal.os;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.FileUtils;
 import android.util.Log;
 
@@ -51,7 +52,7 @@
         mBackupName = new File(baseName.getPath() + ".bak");
     }
     
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public File getBaseFile() {
         return mBaseName;
     }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 017da55..cc8da5c 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5970,7 +5970,6 @@
             } else {
                 stopAllWifiSignalStrengthTimersLocked(-1);
             }
-            StatsLog.write(StatsLog.WIFI_SIGNAL_STRENGTH_CHANGED, strengthBin);
             mWifiSignalStrengthBin = strengthBin;
         }
     }
@@ -6138,8 +6137,6 @@
         for (int i=0; i<N; i++) {
             final int uid = mapUid(ws.get(i));
             noteWifiScanStartedLocked(uid);
-            StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i),
-                    StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__ON);
         }
 
         final List<WorkChain> workChains = ws.getWorkChains();
@@ -6148,8 +6145,6 @@
                 final WorkChain workChain = workChains.get(i);
                 final int uid = mapUid(workChain.getAttributionUid());
                 noteWifiScanStartedLocked(uid);
-                StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED, workChain.getUids(),
-                        workChain.getTags(), StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__ON);
             }
         }
     }
@@ -6159,8 +6154,6 @@
         for (int i=0; i<N; i++) {
             final int uid = mapUid(ws.get(i));
             noteWifiScanStoppedLocked(uid);
-            StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i),
-                    StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__OFF);
         }
 
         final List<WorkChain> workChains = ws.getWorkChains();
@@ -6169,9 +6162,6 @@
                 final WorkChain workChain = workChains.get(i);
                 final int uid = mapUid(workChain.getAttributionUid());
                 noteWifiScanStoppedLocked(uid);
-                StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED,
-                        workChain.getUids(), workChain.getTags(),
-                        StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__OFF);
             }
         }
     }
@@ -7190,9 +7180,6 @@
                             WIFI_MULTICAST_ENABLED, mBsi.mWifiMulticastTimers, mBsi.mOnBatteryTimeBase);
                 }
                 mWifiMulticastTimer.startRunningLocked(elapsedRealtimeMs);
-                StatsLog.write_non_chained(
-                        StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null,
-                        StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__ON);
             }
             mWifiMulticastWakelockCount++;
         }
@@ -7206,9 +7193,6 @@
             mWifiMulticastWakelockCount--;
             if (mWifiMulticastWakelockCount == 0) {
                 mWifiMulticastTimer.stopRunningLocked(elapsedRealtimeMs);
-                StatsLog.write_non_chained(
-                        StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null,
-                        StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__OFF);
             }
         }
 
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 5465485..051a96c 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -80,7 +80,8 @@
     private final Queue<CallSession> mCallSessionsPool = new ConcurrentLinkedQueue<>();
     private final Object mLock = new Object();
     private final Random mRandom;
-    private long mStartTime = System.currentTimeMillis();
+    private long mStartCurrentTime = System.currentTimeMillis();
+    private long mStartElapsedTime = SystemClock.elapsedRealtime();
     private long mCallStatsCount = 0;
     private boolean mAddDebugEntries = false;
 
@@ -329,8 +330,8 @@
 
         // Debug entries added to help validate the data.
         if (mAddDebugEntries && mBatteryStopwatch != null) {
-            resultCallStats.add(createDebugEntry("start_time_millis", mStartTime));
-            resultCallStats.add(createDebugEntry("end_time_millis", System.currentTimeMillis()));
+            resultCallStats.add(createDebugEntry("start_time_millis", mStartElapsedTime));
+            resultCallStats.add(createDebugEntry("end_time_millis", SystemClock.elapsedRealtime()));
             resultCallStats.add(
                     createDebugEntry("battery_time_millis", mBatteryStopwatch.getMillis()));
         }
@@ -370,7 +371,7 @@
         long totalRecordedCallsCount = 0;
         long totalCpuTime = 0;
         pw.print("Start time: ");
-        pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStartTime));
+        pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStartCurrentTime));
         pw.print("On battery time (ms): ");
         pw.println(mBatteryStopwatch != null ? mBatteryStopwatch.getMillis() : 0);
         pw.println("Sampling interval period: " + mPeriodicSamplingInterval);
@@ -520,7 +521,8 @@
             mCallStatsCount = 0;
             mUidEntries.clear();
             mExceptionCounts.clear();
-            mStartTime = System.currentTimeMillis();
+            mStartCurrentTime = System.currentTimeMillis();
+            mStartElapsedTime = SystemClock.elapsedRealtime();
             if (mBatteryStopwatch != null) {
                 mBatteryStopwatch.reset();
             }
diff --git a/core/java/com/android/internal/os/ChildZygoteInit.java b/core/java/com/android/internal/os/ChildZygoteInit.java
new file mode 100644
index 0000000..f90cd02
--- /dev/null
+++ b/core/java/com/android/internal/os/ChildZygoteInit.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.Log;
+
+/**
+ * ChildZygoteInit is shared by both the Application and WebView zygote to initialize
+ * and run a (child) Zygote server.
+ *
+ * @hide
+ */
+public class ChildZygoteInit {
+    private static final String TAG = "ChildZygoteInit";
+
+    static String parseSocketNameFromArgs(String[] argv) {
+        for (String arg : argv) {
+            if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) {
+                return arg.substring(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG.length());
+            }
+        }
+
+        return null;
+    }
+
+    static String parseAbiListFromArgs(String[] argv) {
+        for (String arg : argv) {
+            if (arg.startsWith(Zygote.CHILD_ZYGOTE_ABI_LIST_ARG)) {
+                return arg.substring(Zygote.CHILD_ZYGOTE_ABI_LIST_ARG.length());
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Starts a ZygoteServer and listens for requests
+     *
+     * @param server An instance of a ZygoteServer to listen on
+     * @param args Passed in arguments for this ZygoteServer
+     */
+    static void runZygoteServer(ZygoteServer server, String[] args) {
+        String socketName = parseSocketNameFromArgs(args);
+        if (socketName == null) {
+            throw new NullPointerException("No socketName specified");
+        }
+
+        String abiList = parseAbiListFromArgs(args);
+        if (abiList == null) {
+            throw new NullPointerException("No abiList specified");
+        }
+
+        try {
+            Os.prctl(OsConstants.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+        } catch (ErrnoException ex) {
+            throw new RuntimeException("Failed to set PR_SET_NO_NEW_PRIVS", ex);
+        }
+
+        final Runnable caller;
+        try {
+            server.registerServerSocketAtAbstractName(socketName);
+
+            // Add the abstract socket to the FD whitelist so that the native zygote code
+            // can properly detach it after forking.
+            Zygote.nativeAllowFileAcrossFork("ABSTRACT/" + socketName);
+
+            // The select loop returns early in the child process after a fork and
+            // loops forever in the zygote.
+            caller = server.runSelectLoop(abiList);
+        } catch (RuntimeException e) {
+            Log.e(TAG, "Fatal exception:", e);
+            throw e;
+        } finally {
+            server.closeServerSocket();
+        }
+
+        // We're in the child process and have exited the select loop. Proceed to execute the
+        // command.
+        if (caller != null) {
+            caller.run();
+        }
+    }
+}
diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java
index 01fd8ba..9a7fb9f 100644
--- a/core/java/com/android/internal/os/LooperStats.java
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -50,7 +50,8 @@
     private int mSamplingInterval;
     private CachedDeviceState.Readonly mDeviceState;
     private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch;
-    private long mStartTime = System.currentTimeMillis();
+    private long mStartCurrentTime = System.currentTimeMillis();
+    private long mStartElapsedTime = SystemClock.elapsedRealtime();
     private boolean mAddDebugEntries = false;
 
     public LooperStats(int samplingInterval, int entriesSizeCap) {
@@ -155,8 +156,8 @@
         maybeAddSpecialEntry(exportedEntries, mHashCollisionEntry);
         // Debug entries added to help validate the data.
         if (mAddDebugEntries && mBatteryStopwatch != null) {
-            exportedEntries.add(createDebugEntry("start_time_millis", mStartTime));
-            exportedEntries.add(createDebugEntry("end_time_millis", System.currentTimeMillis()));
+            exportedEntries.add(createDebugEntry("start_time_millis", mStartElapsedTime));
+            exportedEntries.add(createDebugEntry("end_time_millis", SystemClock.elapsedRealtime()));
             exportedEntries.add(
                     createDebugEntry("battery_time_millis", mBatteryStopwatch.getMillis()));
         }
@@ -173,7 +174,11 @@
 
     /** Returns a timestamp indicating when the statistics were last reset. */
     public long getStartTimeMillis() {
-        return mStartTime;
+        return mStartCurrentTime;
+    }
+
+    public long getStartElapsedTimeMillis() {
+        return mStartElapsedTime;
     }
 
     public long getBatteryTimeMillis() {
@@ -199,7 +204,8 @@
         synchronized (mOverflowEntry) {
             mOverflowEntry.reset();
         }
-        mStartTime = System.currentTimeMillis();
+        mStartCurrentTime = System.currentTimeMillis();
+        mStartElapsedTime = SystemClock.elapsedRealtime();
         if (mBatteryStopwatch != null) {
             mBatteryStopwatch.reset();
         }
diff --git a/core/java/com/android/internal/os/RoSystemProperties.java b/core/java/com/android/internal/os/RoSystemProperties.java
index dc660a4..a319d83 100644
--- a/core/java/com/android/internal/os/RoSystemProperties.java
+++ b/core/java/com/android/internal/os/RoSystemProperties.java
@@ -30,6 +30,14 @@
     public static final String CONTROL_PRIVAPP_PERMISSIONS =
             SystemProperties.get("ro.control_privapp_permissions");
 
+    /**
+     * Property to indicate if a CEC audio device should forward volume keys when system audio
+     * mode is off.
+     */
+    public static final boolean CEC_AUDIO_DEVICE_FORWARD_VOLUME_KEYS_SYSTEM_AUDIO_MODE_OFF =
+            SystemProperties.getBoolean(
+                    "ro.hdmi.cec_audio_device_forward_volume_keys_system_audio_mode_off", false);
+
     // ------ ro.config.* -------- //
     public static final boolean CONFIG_AVOID_GFX_ACCEL =
             SystemProperties.getBoolean("ro.config.avoid_gfx_accel", false);
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index 9f2434e..0b329d7 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -18,11 +18,7 @@
 
 import android.app.ApplicationLoaders;
 import android.net.LocalSocket;
-import android.net.LocalServerSocket;
 import android.os.Build;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
 import android.text.TextUtils;
 import android.util.Log;
 import android.webkit.WebViewFactory;
@@ -44,8 +40,6 @@
 class WebViewZygoteInit {
     public static final String TAG = "WebViewZygoteInit";
 
-    private static ZygoteServer sServer;
-
     private static class WebViewZygoteServer extends ZygoteServer {
         @Override
         protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
@@ -127,48 +121,7 @@
 
     public static void main(String argv[]) {
         Log.i(TAG, "Starting WebViewZygoteInit");
-
-        String socketName = null;
-        for (String arg : argv) {
-            Log.i(TAG, arg);
-            if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) {
-                socketName = arg.substring(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG.length());
-            }
-        }
-        if (socketName == null) {
-            throw new RuntimeException("No " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + " specified");
-        }
-
-        try {
-            Os.prctl(OsConstants.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
-        } catch (ErrnoException ex) {
-            throw new RuntimeException("Failed to set PR_SET_NO_NEW_PRIVS", ex);
-        }
-
-        sServer = new WebViewZygoteServer();
-
-        final Runnable caller;
-        try {
-            sServer.registerServerSocketAtAbstractName(socketName);
-
-            // Add the abstract socket to the FD whitelist so that the native zygote code
-            // can properly detach it after forking.
-            Zygote.nativeAllowFileAcrossFork("ABSTRACT/" + socketName);
-
-            // The select loop returns early in the child process after a fork and
-            // loops forever in the zygote.
-            caller = sServer.runSelectLoop(TextUtils.join(",", Build.SUPPORTED_ABIS));
-        } catch (RuntimeException e) {
-            Log.e(TAG, "Fatal exception:", e);
-            throw e;
-        } finally {
-            sServer.closeServerSocket();
-        }
-
-        // We're in the child process and have exited the select loop. Proceed to execute the
-        // command.
-        if (caller != null) {
-            caller.run();
-        }
+        WebViewZygoteServer server = new WebViewZygoteServer();
+        ChildZygoteInit.runZygoteServer(server, argv);
     }
 }
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 65b9fad..d720c68 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -98,6 +98,12 @@
      */
     public static final String CHILD_ZYGOTE_SOCKET_NAME_ARG = "--zygote-socket=";
 
+    /**
+     * An extraArg passed when a zygote process is forking a child-zygote, specifying the
+     * requested ABI for the child Zygote.
+     */
+    public static final String CHILD_ZYGOTE_ABI_LIST_ARG = "--abi-list=";
+
     private Zygote() {}
 
     /** Called for some security initialization before any fork. */
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index f182c4d..5990d72 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -27,9 +27,11 @@
 import static com.android.internal.os.ZygoteConnectionConstants.MAX_ZYGOTE_ARGC;
 import static com.android.internal.os.ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;
 
+import android.content.pm.ApplicationInfo;
 import android.net.Credentials;
 import android.net.LocalSocket;
 import android.os.FactoryTest;
+import android.os.Parcel;
 import android.os.Process;
 import android.os.SystemProperties;
 import android.os.Trace;
@@ -52,6 +54,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Base64;
 
 /**
  * A connection that can make spawn requests.
@@ -168,6 +171,21 @@
             return null;
         }
 
+        if (canPreloadApp() && parsedArgs.mPreloadApp != null) {
+            byte[] rawParcelData = Base64.getDecoder().decode(parsedArgs.mPreloadApp);
+            Parcel appInfoParcel = Parcel.obtain();
+            appInfoParcel.unmarshall(rawParcelData, 0, rawParcelData.length);
+            appInfoParcel.setDataPosition(0);
+            ApplicationInfo appInfo = ApplicationInfo.CREATOR.createFromParcel(appInfoParcel);
+            appInfoParcel.recycle();
+            if (appInfo != null) {
+                handlePreloadApp(appInfo);
+            } else {
+                throw new IllegalArgumentException("Failed to deserialize --preload-app");
+            }
+            return null;
+        }
+
         if (parsedArgs.apiBlacklistExemptions != null) {
             handleApiBlacklistExemptions(parsedArgs.apiBlacklistExemptions);
             return null;
@@ -341,7 +359,15 @@
 
     protected void handlePreloadPackage(String packagePath, String libsPath, String libFileName,
             String cacheKey) {
-        throw new RuntimeException("Zyogte does not support package preloading");
+        throw new RuntimeException("Zygote does not support package preloading");
+    }
+
+    protected boolean canPreloadApp() {
+        return false;
+    }
+
+    protected void handlePreloadApp(ApplicationInfo aInfo) {
+        throw new RuntimeException("Zygote does not support app preloading");
     }
 
     /**
@@ -467,6 +493,12 @@
         String preloadPackage;
 
         /**
+         * A Base64 string representing a serialize ApplicationInfo Parcel,
+           when using --preload-app.
+          */
+        String mPreloadApp;
+
+        /**
          * The native library path of the package to preload, when using --preload-package.
          */
         String preloadPackageLibs;
@@ -666,6 +698,8 @@
                     instructionSet = arg.substring(arg.indexOf('=') + 1);
                 } else if (arg.startsWith("--app-data-dir=")) {
                     appDataDir = arg.substring(arg.indexOf('=') + 1);
+                } else if (arg.equals("--preload-app")) {
+                    mPreloadApp = args[++curArg];
                 } else if (arg.equals("--preload-package")) {
                     preloadPackage = args[++curArg];
                     preloadPackageLibs = args[++curArg];
@@ -714,6 +748,11 @@
                     throw new IllegalArgumentException(
                             "Unexpected arguments after --preload-package.");
                 }
+            } else if (mPreloadApp != null) {
+                if (args.length - curArg > 0) {
+                    throw new IllegalArgumentException(
+                            "Unexpected arguments after --preload-app.");
+                }
             } else if (expectRuntimeArgs) {
                 if (!seenRuntimeArgs) {
                     throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 8bdb000..c2c6ae6 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -536,9 +536,11 @@
     static ClassLoader createPathClassLoader(String classPath, int targetSdkVersion) {
         String libraryPath = System.getProperty("java.library.path");
 
+        // We use the boot class loader, that's what the runtime expects at AOT.
+        ClassLoader parent = ClassLoader.getSystemClassLoader().getParent();
+
         return ClassLoaderFactory.createClassLoader(classPath, libraryPath, libraryPath,
-                ClassLoader.getSystemClassLoader(), targetSdkVersion, true /* isNamespaceShared */,
-                null /* classLoaderName */);
+                parent, targetSdkVersion, true /* isNamespaceShared */, null /* classLoaderName */);
     }
 
     /**
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 5118e5f..9087dd2 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -67,7 +67,8 @@
             in NotificationVisibility[] noLongerVisibleKeys);
     void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded);
     void onNotificationDirectReplied(String key);
-    void onNotificationSmartRepliesAdded(in String key, in int replyCount);
+    void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount, int smartActionCount,
+            boolean generatedByAsssistant);
     void onNotificationSmartReplySent(in String key, in int replyIndex, in CharSequence reply, boolean generatedByAssistant);
     void onNotificationSettingsViewed(String key);
     void setSystemUiVisibility(int displayId, int vis, int mask, String cause);
diff --git a/core/java/com/android/internal/usb/DumpUtils.java b/core/java/com/android/internal/usb/DumpUtils.java
index cac2265..240c2e7 100644
--- a/core/java/com/android/internal/usb/DumpUtils.java
+++ b/core/java/com/android/internal/usb/DumpUtils.java
@@ -16,12 +16,12 @@
 
 package com.android.internal.usb;
 
-import static android.hardware.usb.UsbPort.MODE_AUDIO_ACCESSORY;
-import static android.hardware.usb.UsbPort.MODE_DEBUG_ACCESSORY;
-import static android.hardware.usb.UsbPort.MODE_DFP;
-import static android.hardware.usb.UsbPort.MODE_DUAL;
-import static android.hardware.usb.UsbPort.MODE_NONE;
-import static android.hardware.usb.UsbPort.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.MODE_AUDIO_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.MODE_DEBUG_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_NONE;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
 
 import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
 
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index f669e94..226b8db 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -24,6 +24,7 @@
 
 import libcore.util.EmptyArray;
 
+import java.io.File;
 import java.lang.reflect.Array;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -42,6 +43,8 @@
     private static final int CACHE_SIZE = 73;
     private static Object[] sCache = new Object[CACHE_SIZE];
 
+    public static final File[] EMPTY_FILE = new File[0];
+
     private ArrayUtils() { /* cannot be instantiated */ }
 
     public static byte[] newUnpaddedByteArray(int minLen) {
@@ -645,6 +648,10 @@
         return (val != null) ? val : EmptyArray.STRING;
     }
 
+    public static @NonNull File[] defeatNullable(@Nullable File[] val) {
+        return (val != null) ? val : EMPTY_FILE;
+    }
+
     /**
      * Throws {@link ArrayIndexOutOfBoundsException} if the index is out of bounds.
      *
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index 69297b5..b0888f2 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -162,7 +163,7 @@
         sCmdToString[CMD_CHANNEL_DISCONNECT - BASE] = "CMD_CHANNEL_DISCONNECT";
         sCmdToString[CMD_CHANNEL_DISCONNECTED - BASE] = "CMD_CHANNEL_DISCONNECTED";
     }
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     protected static String cmdToString(int cmd) {
         cmd -= BASE;
         if ((cmd >= 0) && (cmd < sCmdToString.length)) {
@@ -591,7 +592,7 @@
      * @param srcMsg
      * @param what
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void replyToMessage(Message srcMsg, int what) {
         Message msg = Message.obtain();
         msg.what = what;
diff --git a/core/java/com/android/internal/util/JournaledFile.java b/core/java/com/android/internal/util/JournaledFile.java
index 258db2a..065cc5b2 100644
--- a/core/java/com/android/internal/util/JournaledFile.java
+++ b/core/java/com/android/internal/util/JournaledFile.java
@@ -17,6 +17,7 @@
 package com.android.internal.util;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 
 import java.io.File;
 import java.io.IOException;
@@ -47,7 +48,7 @@
      * it to the real one.  If there is both a real file and a temp one, assumes that the
      * temp one isn't fully written and deletes it.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public File chooseForRead() {
         File result;
         if (mReal.exists()) {
@@ -72,7 +73,7 @@
      * <p>
      * Call {@link #commit} to commit the changes, or {@link #rollback} to forget the changes.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public File chooseForWrite() {
         if (mWriting) {
             throw new IllegalStateException("uncommitted write already in progress");
@@ -100,7 +101,7 @@
     /**
      * Commit changes.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void commit() {
         if (!mWriting) {
             throw new IllegalStateException("no file to commit");
@@ -112,7 +113,7 @@
     /**
      * Roll back changes.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void rollback() {
         if (!mWriting) {
             throw new IllegalStateException("no file to roll back");
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index c8834a8..ae5c67d 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -26,9 +26,9 @@
 import android.view.DragEvent;
 import android.view.IWindow;
 import android.view.IWindowSession;
-import android.view.PointerIcon;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
+import android.view.PointerIcon;
 
 import com.android.internal.os.IResultReceiver;
 
@@ -76,7 +76,7 @@
     }
 
     @Override
-    public void windowFocusChanged(boolean hasFocus, boolean touchEnabled, boolean reportToClient) {
+    public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
     }
 
     @Override
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index f62c440..70f4ed2 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -42,9 +42,6 @@
     List<InputMethodSubtype> getEnabledInputMethodSubtypeList(in String imiId,
             boolean allowsImplicitlySelectedSubtypes);
     InputMethodSubtype getLastInputMethodSubtype();
-    // TODO: We should change the return type from List to List<Parcelable>
-    // Currently there is a bug that aidl doesn't accept List<Parcelable>
-    List getShortcutInputMethodsAndSubtypes();
 
     boolean showSoftInput(in IInputMethodClient client, int flags,
             in ResultReceiver resultReceiver);
@@ -68,10 +65,6 @@
             int displayId);
     void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId);
     boolean isInputMethodPickerShownForTest();
-    // TODO(Bug 114488811): this can be removed once we deprecate special null token rule.
-    void setInputMethod(in IBinder token, String id);
-    // TODO(Bug 114488811): this can be removed once we deprecate special null token rule.
-    void setInputMethodAndSubtype(in IBinder token, String id, in InputMethodSubtype subtype);
     void registerSuggestionSpansForNotification(in SuggestionSpan[] spans);
     boolean notifySuggestionPicked(in SuggestionSpan span, String originalString, int index);
     InputMethodSubtype getCurrentInputMethodSubtype();
diff --git a/core/java/com/android/internal/widget/NumericTextView.java b/core/java/com/android/internal/widget/NumericTextView.java
index 27c5834..d215670 100644
--- a/core/java/com/android/internal/widget/NumericTextView.java
+++ b/core/java/com/android/internal/widget/NumericTextView.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.widget;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
@@ -53,6 +54,7 @@
 
     private OnValueChangedListener mListener;
 
+    @UnsupportedAppUsage
     public NumericTextView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
diff --git a/core/java/com/android/server/net/BaseNetdEventCallback.java b/core/java/com/android/server/net/BaseNetdEventCallback.java
index 97247aa..a65214a 100644
--- a/core/java/com/android/server/net/BaseNetdEventCallback.java
+++ b/core/java/com/android/server/net/BaseNetdEventCallback.java
@@ -32,6 +32,12 @@
     }
 
     @Override
+    public void onNat64PrefixEvent(int netId, boolean added, String prefixString,
+            int prefixLength) {
+        // default no-op
+    }
+
+    @Override
     public void onPrivateDnsValidationEvent(int netId, String ipAddress,
             String hostname, boolean validated) {
         // default no-op
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 43f8d00..dc6a73a 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -271,9 +271,10 @@
         "libhardware",
         "libhardware_legacy",
         "libselinux",
-        "libicuuc",
+        "libandroidicu",
         "libmedia",
         "libmediametrics",
+        "libmeminfo",
         "libaudioclient",
         "libjpeg",
         "libusbhost",
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index c32de0a..eb7338a 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -45,7 +45,7 @@
 
 class BitmapWrapper {
 public:
-    BitmapWrapper(Bitmap* bitmap)
+    explicit BitmapWrapper(Bitmap* bitmap)
         : mBitmap(bitmap) { }
 
     void freePixels() {
@@ -725,9 +725,10 @@
             return NULL;
         }
 
-        // Map the pixels in place and take ownership of the ashmem region.
-        nativeBitmap = sk_sp<Bitmap>(GraphicsJNI::mapAshmemBitmap(env, bitmap.get(),
-                dupFd, const_cast<void*>(blob.data()), size, !isMutable));
+        // Map the pixels in place and take ownership of the ashmem region. We must also respect the
+        // rowBytes value already set on the bitmap instead of attempting to compute our own.
+        nativeBitmap = Bitmap::createFrom(bitmap->info(), bitmap->rowBytes(), dupFd,
+                                          const_cast<void*>(blob.data()), size, !isMutable);
         if (!nativeBitmap) {
             close(dupFd);
             blob.release();
@@ -1097,21 +1098,20 @@
     SkBitmap src;
     hwuiBitmap.getSkBitmap(&src);
 
-    SkBitmap result;
-    HeapAllocator allocator;
-    if (!bitmapCopyTo(&result, hwuiBitmap.info().colorType(), src, &allocator)) {
+    if (src.pixelRef() == nullptr) {
         doThrowRE(env, "Could not copy a hardware bitmap.");
         return NULL;
     }
-    return createBitmap(env, allocator.getStorageObjAndReset(), getPremulBitmapCreateFlags(false));
+
+    sk_sp<Bitmap> bitmap = Bitmap::createFrom(src.info(), *src.pixelRef());
+    return createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(false));
 }
 
 static jobject Bitmap_createHardwareBitmap(JNIEnv* env, jobject, jobject graphicBuffer) {
     sp<GraphicBuffer> buffer(graphicBufferForJavaObject(env, graphicBuffer));
-    // Bitmap::createFrom currently assumes SRGB color space for RGBA images.
     // To support any color space, we need to pass an additional ColorSpace argument to
     // java Bitmap.createHardwareBitmap.
-    sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer);
+    sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, SkColorSpace::MakeSRGB());
     if (!bitmap.get()) {
         ALOGW("failed to create hardware bitmap from graphic buffer");
         return NULL;
diff --git a/core/jni/android/graphics/FontUtils.h b/core/jni/android/graphics/FontUtils.h
index 9f6462e..b36b4e6 100644
--- a/core/jni/android/graphics/FontUtils.h
+++ b/core/jni/android/graphics/FontUtils.h
@@ -29,7 +29,7 @@
 namespace android {
 
 struct FontFamilyWrapper {
-  FontFamilyWrapper(std::shared_ptr<minikin::FontFamily>&& family) : family(family) {}
+  explicit FontFamilyWrapper(std::shared_ptr<minikin::FontFamily>&& family) : family(family) {}
   std::shared_ptr<minikin::FontFamily> family;
 };
 
diff --git a/core/jni/android/graphics/GIFMovie.cpp b/core/jni/android/graphics/GIFMovie.cpp
index dd99b37..f84a4bd 100644
--- a/core/jni/android/graphics/GIFMovie.cpp
+++ b/core/jni/android/graphics/GIFMovie.cpp
@@ -21,7 +21,7 @@
 
 class GIFMovie : public Movie {
 public:
-    GIFMovie(SkStream* stream);
+    explicit GIFMovie(SkStream* stream);
     virtual ~GIFMovie();
 
 protected:
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 26af15e..67d0c8a 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -424,36 +424,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-android::Bitmap* GraphicsJNI::mapAshmemBitmap(JNIEnv* env, SkBitmap* bitmap,
-        int fd, void* addr, size_t size, bool readOnly) {
-    const SkImageInfo& info = bitmap->info();
-    if (info.colorType() == kUnknown_SkColorType) {
-        doThrowIAE(env, "unknown bitmap configuration");
-        return nullptr;
-    }
-
-    if (!addr) {
-        // Map existing ashmem region if not already mapped.
-        int flags = readOnly ? (PROT_READ) : (PROT_READ | PROT_WRITE);
-        size = ashmem_get_size_region(fd);
-        addr = mmap(NULL, size, flags, MAP_SHARED, fd, 0);
-        if (addr == MAP_FAILED) {
-            return nullptr;
-        }
-    }
-
-    // we must respect the rowBytes value already set on the bitmap instead of
-    // attempting to compute our own.
-    const size_t rowBytes = bitmap->rowBytes();
-
-    auto wrapper = new android::Bitmap(addr, fd, size, info, rowBytes);
-    wrapper->getSkBitmap(bitmap);
-    if (readOnly) {
-        bitmap->pixelRef()->setImmutable();
-    }
-    return wrapper;
-}
-
 SkColorSpaceTransferFn GraphicsJNI::getNativeTransferParameters(JNIEnv* env, jobject transferParams) {
     SkColorSpaceTransferFn p;
     p.fA = (float) env->GetDoubleField(transferParams, gTransferParams_aFieldID);
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index cee3c46..b0bd683 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -85,9 +85,6 @@
 
     static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
 
-    static android::Bitmap* mapAshmemBitmap(JNIEnv* env, SkBitmap* bitmap,
-            int fd, void* addr, size_t size, bool readOnly);
-
     /**
      * Given a bitmap we natively allocate a memory block to store the contents
      * of that bitmap.  The memory is then attached to the bitmap via an
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index df24bc4..e97c9bc 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -577,7 +577,7 @@
         }
     }
 
-    static SkScalar getMetricsInternal(jlong paintHandle, Paint::FontMetrics *metrics) {
+    static SkScalar getMetricsInternal(jlong paintHandle, SkFontMetrics *metrics) {
         const int kElegantTop = 2500;
         const int kElegantBottom = -1000;
         const int kElegantAscent = 1900;
@@ -609,7 +609,7 @@
     }
 
     static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) {
-        Paint::FontMetrics metrics;
+        SkFontMetrics metrics;
         SkScalar spacing = getMetricsInternal(paintHandle, &metrics);
 
         if (metricsObj) {
@@ -624,7 +624,7 @@
     }
 
     static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) {
-        Paint::FontMetrics metrics;
+        SkFontMetrics metrics;
 
         getMetricsInternal(paintHandle, &metrics);
         int ascent = SkScalarRoundToInt(metrics.fAscent);
@@ -970,19 +970,19 @@
     }
 
     static jfloat ascent(jlong paintHandle) {
-        Paint::FontMetrics metrics;
+        SkFontMetrics metrics;
         getMetricsInternal(paintHandle, &metrics);
         return SkScalarToFloat(metrics.fAscent);
     }
 
     static jfloat descent(jlong paintHandle) {
-        Paint::FontMetrics metrics;
+        SkFontMetrics metrics;
         getMetricsInternal(paintHandle, &metrics);
         return SkScalarToFloat(metrics.fDescent);
     }
 
     static jfloat getUnderlinePosition(jlong paintHandle) {
-        Paint::FontMetrics metrics;
+        SkFontMetrics metrics;
         getMetricsInternal(paintHandle, &metrics);
         SkScalar position;
         if (metrics.hasUnderlinePosition(&position)) {
@@ -994,7 +994,7 @@
     }
 
     static jfloat getUnderlineThickness(jlong paintHandle) {
-        Paint::FontMetrics metrics;
+        SkFontMetrics metrics;
         getMetricsInternal(paintHandle, &metrics);
         SkScalar thickness;
         if (metrics.hasUnderlineThickness(&thickness)) {
diff --git a/core/jni/android/graphics/text/LineBreaker.cpp b/core/jni/android/graphics/text/LineBreaker.cpp
index e1f2f2b..c23f1e9 100644
--- a/core/jni/android/graphics/text/LineBreaker.cpp
+++ b/core/jni/android/graphics/text/LineBreaker.cpp
@@ -16,8 +16,6 @@
 
 #define LOG_TAG "LineBreaker"
 
-#include "unicode/locid.h"
-#include "unicode/brkiter.h"
 #include "utils/misc.h"
 #include "utils/Log.h"
 #include <nativehelper/ScopedStringChars.h>
diff --git a/core/jni/android/graphics/text/MeasuredText.cpp b/core/jni/android/graphics/text/MeasuredText.cpp
index d7d96fb..68ba38b 100644
--- a/core/jni/android/graphics/text/MeasuredText.cpp
+++ b/core/jni/android/graphics/text/MeasuredText.cpp
@@ -17,8 +17,6 @@
 #define LOG_TAG "MeasuredText"
 
 #include "GraphicsJNI.h"
-#include "unicode/locid.h"
-#include "unicode/brkiter.h"
 #include "utils/misc.h"
 #include "utils/Log.h"
 #include <nativehelper/ScopedStringChars.h>
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 84f53468..fc9ea76 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -102,6 +102,10 @@
     return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags));
 }
 
+static jint saveUnclippedLayer(jlong canvasHandle, jint l, jint t, jint r, jint b) {
+    return reinterpret_cast<jint>(get_canvas(canvasHandle)->saveUnclippedLayer(l, t, r, b));
+}
+
 static bool restore(jlong canvasHandle) {
     Canvas* canvas = get_canvas(canvasHandle);
     if (canvas->getSaveCount() <= 1) {
@@ -651,6 +655,7 @@
     {"nSave","(JI)I", (void*) CanvasJNI::save},
     {"nSaveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
     {"nSaveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
+    {"nSaveUnclippedLayer","(JIIII)I", (void*) CanvasJNI::saveUnclippedLayer},
     {"nGetSaveCount","(J)I", (void*) CanvasJNI::getSaveCount},
     {"nRestore","(J)Z", (void*) CanvasJNI::restore},
     {"nRestoreToCount","(JI)V", (void*) CanvasJNI::restoreToCount},
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 283eb03..80560f8 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -32,6 +32,7 @@
 #include <nativehelper/ScopedLocalRef.h>
 #include <system/audio.h>
 #include <system/audio_policy.h>
+#include "android_media_AudioEffectDescriptor.h"
 #include "android_media_AudioFormat.h"
 #include "android_media_AudioErrors.h"
 #include "android_media_MicrophoneInfo.h"
@@ -427,9 +428,14 @@
 }
 
 static void
-android_media_AudioSystem_recording_callback(int event, const record_client_info_t *clientInfo,
-        const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig,
-        audio_patch_handle_t patchHandle)
+android_media_AudioSystem_recording_callback(int event,
+                                             const record_client_info_t *clientInfo,
+                                             const audio_config_base_t *clientConfig,
+                                             std::vector<effect_descriptor_t> clientEffects,
+                                             const audio_config_base_t *deviceConfig,
+                                             std::vector<effect_descriptor_t> effects __unused,
+                                             audio_patch_handle_t patchHandle,
+                                             audio_source_t source)
 {
     JNIEnv *env = AndroidRuntime::getJNIEnv();
     if (env == NULL) {
@@ -460,14 +466,24 @@
     recParamData[6] = (jint) patchHandle;
     env->SetIntArrayRegion(recParamArray, 0, REC_PARAM_SIZE, recParamData);
 
+    jobjectArray jClientEffects;
+    convertAudioEffectDescriptorVectorFromNative(env, &jClientEffects, clientEffects);
+
+    jobjectArray jEffects;
+    convertAudioEffectDescriptorVectorFromNative(env, &jEffects, effects);
+
     // callback into java
     jclass clazz = env->FindClass(kClassPathName);
-    env->CallStaticVoidMethod(clazz,
-            gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative,
-            event, (jint) clientInfo->uid, clientInfo->session, clientInfo->source, recParamArray);
-    env->DeleteLocalRef(clazz);
 
+    env->CallStaticVoidMethod(clazz,
+                              gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative,
+                              event, (jint) clientInfo->uid, clientInfo->session,
+                              clientInfo->source, clientInfo->port_id, clientInfo->silenced,
+                              recParamArray, jClientEffects, jEffects, source);
+    env->DeleteLocalRef(clazz);
     env->DeleteLocalRef(recParamArray);
+    env->DeleteLocalRef(jClientEffects);
+    env->DeleteLocalRef(jEffects);
 }
 
 static jint
@@ -1890,6 +1906,53 @@
     return jStatus;
 }
 
+static jint android_media_AudioSystem_setUidDeviceAffinities(JNIEnv *env, jobject clazz,
+        jint uid, jintArray deviceTypes, jobjectArray deviceAddresses) {
+    if (deviceTypes == nullptr || deviceAddresses == nullptr) {
+        return (jint) AUDIO_JAVA_BAD_VALUE;
+    }
+    jsize nb = env->GetArrayLength(deviceTypes);
+    if (nb == 0 || nb != env->GetArrayLength(deviceAddresses)) {
+        return (jint) AUDIO_JAVA_BAD_VALUE;
+    }
+    // retrieve all device types
+    std::vector<audio_devices_t> deviceTypesVector;
+    jint* typesPtr = nullptr;
+    typesPtr = env->GetIntArrayElements(deviceTypes, 0);
+    if (typesPtr == nullptr) {
+        return (jint) AUDIO_JAVA_BAD_VALUE;
+    }
+    for (jint i = 0; i < nb; i++) {
+        deviceTypesVector.push_back((audio_devices_t) typesPtr[i]);
+    }
+    env->ReleaseIntArrayElements(deviceTypes, typesPtr, 0);
+
+    // check each address is a string and add device type/address to list for device affinity
+    Vector<AudioDeviceTypeAddr> deviceVector;
+    jclass stringClass = FindClassOrDie(env, "java/lang/String");
+    for (jint i = 0; i < nb; i++) {
+        jobject addrJobj = env->GetObjectArrayElement(deviceAddresses, i);
+        if (!env->IsInstanceOf(addrJobj, stringClass)) {
+            return (jint) AUDIO_JAVA_BAD_VALUE;
+        }
+        String8 address = String8(env->GetStringUTFChars((jstring) addrJobj, NULL));
+        AudioDeviceTypeAddr dev = AudioDeviceTypeAddr(typesPtr[i], address);
+        deviceVector.add(dev);
+    }
+
+    status_t status = AudioSystem::setUidDeviceAffinities((uid_t) uid, deviceVector);
+    return (jint) nativeToJavaStatus(status);
+}
+
+static jint android_media_AudioSystem_removeUidDeviceAffinities(JNIEnv *env, jobject clazz,
+        jint uid) {
+
+    //###
+    status_t status = NO_ERROR;//AudioSystem::removeUidDeviceAffinities();
+    return (jint) nativeToJavaStatus(status);
+}
+
+
 static jint
 android_media_AudioSystem_systemReady(JNIEnv *env, jobject thiz)
 {
@@ -2117,6 +2180,10 @@
                                     (void *)android_media_AudioSystem_getAudioHwSyncForSession},
     {"registerPolicyMixes",    "(Ljava/util/ArrayList;Z)I",
                                             (void *)android_media_AudioSystem_registerPolicyMixes},
+    {"setUidDeviceAffinities", "(I[I[Ljava/lang/String;)I",
+                                        (void *)android_media_AudioSystem_setUidDeviceAffinities},
+    {"removeUidDeviceAffinities", "(I)I",
+                                        (void *)android_media_AudioSystem_removeUidDeviceAffinities},
     {"native_register_dynamic_policy_callback", "()V",
                                     (void *)android_media_AudioSystem_registerDynPolicyCallback},
     {"native_register_recording_callback", "()V",
@@ -2260,7 +2327,7 @@
                     "dynamicPolicyCallbackFromNative", "(ILjava/lang/String;I)V");
     gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative =
             GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName),
-                    "recordingCallbackFromNative", "(IIII[I)V");
+                    "recordingCallbackFromNative", "(IIIIIZ[I[Landroid/media/audiofx/AudioEffect$Descriptor;[Landroid/media/audiofx/AudioEffect$Descriptor;I)V");
 
     jclass audioMixClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMix");
     gAudioMixClass = MakeGlobalRefOrDie(env, audioMixClass);
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 516093e..1065738 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -20,7 +20,6 @@
 #include "android_media_AudioTrack.h"
 
 #include <nativehelper/JNIHelp.h>
-#include <nativehelper/JniConstants.h>
 #include "core_jni_helpers.h"
 
 #include <utils/Log.h>
diff --git a/core/jni/android_media_DeviceCallback.cpp b/core/jni/android_media_DeviceCallback.cpp
index 108fa00..a1a0351 100644
--- a/core/jni/android_media_DeviceCallback.cpp
+++ b/core/jni/android_media_DeviceCallback.cpp
@@ -20,7 +20,6 @@
 
 #include <utils/Log.h>
 #include <nativehelper/JNIHelp.h>
-#include <nativehelper/JniConstants.h>
 #include "core_jni_helpers.h"
 #include <media/AudioSystem.h>
 
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index fa1da4b..888dab1 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -32,6 +32,7 @@
 #include <atomic>
 #include <iomanip>
 #include <string>
+#include <vector>
 
 #include <debuggerd/client.h>
 #include <log/log.h>
@@ -41,6 +42,7 @@
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedUtfChars.h>
 #include "jni.h"
+#include <meminfo/sysmeminfo.h>
 #include <memtrack/memtrack.h>
 #include <memunreachable/memunreachable.h>
 #include "android_os_Debug.h"
@@ -712,6 +714,8 @@
     return vmalloc_allocated_size;
 }
 
+// The 1:1 mapping of MEMINFO_* enums here must match with the constants from
+// Debug.java.
 enum {
     MEMINFO_TOTAL,
     MEMINFO_FREE,
@@ -731,138 +735,43 @@
     MEMINFO_COUNT
 };
 
-static long long get_zram_mem_used()
-{
-#define ZRAM_SYSFS "/sys/block/zram0/"
-    UniqueFile mm_stat_file = MakeUniqueFile(ZRAM_SYSFS "mm_stat", "re");
-    if (mm_stat_file) {
-        long long mem_used_total = 0;
-
-        int matched = fscanf(mm_stat_file.get(), "%*d %*d %lld %*d %*d %*d %*d", &mem_used_total);
-        if (matched != 1)
-            ALOGW("failed to parse " ZRAM_SYSFS "mm_stat");
-
-        return mem_used_total;
-    }
-
-    UniqueFile mem_used_total_file = MakeUniqueFile(ZRAM_SYSFS "mem_used_total", "re");
-    if (mem_used_total_file) {
-        long long mem_used_total = 0;
-
-        int matched = fscanf(mem_used_total_file.get(), "%lld", &mem_used_total);
-        if (matched != 1)
-            ALOGW("failed to parse " ZRAM_SYSFS "mem_used_total");
-
-        return mem_used_total;
-    }
-
-    return 0;
-}
-
 static void android_os_Debug_getMemInfo(JNIEnv *env, jobject clazz, jlongArray out)
 {
-    char buffer[4096];
-    size_t numFound = 0;
-
     if (out == NULL) {
         jniThrowNullPointerException(env, "out == null");
         return;
     }
 
-    int fd = open("/proc/meminfo", O_RDONLY | O_CLOEXEC);
-
-    if (fd < 0) {
-        ALOGW("Unable to open /proc/meminfo: %s\n", strerror(errno));
+    int outLen = env->GetArrayLength(out);
+    if (outLen < MEMINFO_COUNT) {
+        jniThrowRuntimeException(env, "outLen < MEMINFO_COUNT");
         return;
     }
 
-    int len = read(fd, buffer, sizeof(buffer)-1);
-    close(fd);
-
-    if (len < 0) {
-        ALOGW("Empty /proc/meminfo");
+    // Read system memory info including ZRAM. The values are stored in the vector
+    // in the same order as MEMINFO_* enum
+    std::vector<uint64_t> mem(MEMINFO_COUNT);
+    std::vector<std::string> tags(::android::meminfo::SysMemInfo::kDefaultSysMemInfoTags);
+    tags.insert(tags.begin() + MEMINFO_ZRAM_TOTAL, "Zram:");
+    ::android::meminfo::SysMemInfo smi;
+    if (!smi.ReadMemInfo(tags, &mem)) {
+        jniThrowRuntimeException(env, "SysMemInfo read failed");
         return;
     }
-    buffer[len] = 0;
 
-    static const char* const tags[] = {
-            "MemTotal:",
-            "MemFree:",
-            "Buffers:",
-            "Cached:",
-            "Shmem:",
-            "Slab:",
-            "SReclaimable:",
-            "SUnreclaim:",
-            "SwapTotal:",
-            "SwapFree:",
-            "ZRam:",
-            "Mapped:",
-            "VmallocUsed:",
-            "PageTables:",
-            "KernelStack:",
-            NULL
-    };
-    static const int tagsLen[] = {
-            9,
-            8,
-            8,
-            7,
-            6,
-            5,
-            13,
-            11,
-            10,
-            9,
-            5,
-            7,
-            12,
-            11,
-            12,
-            0
-    };
-    long mem[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-
-    char* p = buffer;
-    while (*p && numFound < (sizeof(tagsLen) / sizeof(tagsLen[0]))) {
-        int i = 0;
-        while (tags[i]) {
-            if (strncmp(p, tags[i], tagsLen[i]) == 0) {
-                p += tagsLen[i];
-                while (*p == ' ') p++;
-                char* num = p;
-                while (*p >= '0' && *p <= '9') p++;
-                if (*p != 0) {
-                    *p = 0;
-                    p++;
-                }
-                mem[i] = atoll(num);
-                numFound++;
-                break;
-            }
-            i++;
-        }
-        while (*p && *p != '\n') {
-            p++;
-        }
-        if (*p) p++;
-    }
-
-    mem[MEMINFO_ZRAM_TOTAL] = get_zram_mem_used() / 1024;
-    // Recompute Vmalloc Used since the value in meminfo
-    // doesn't account for I/O remapping which doesn't use RAM.
-    mem[MEMINFO_VMALLOC_USED] = get_allocated_vmalloc_memory() / 1024;
-
-    int maxNum = env->GetArrayLength(out);
-    if (maxNum > MEMINFO_COUNT) {
-        maxNum = MEMINFO_COUNT;
-    }
     jlong* outArray = env->GetLongArrayElements(out, 0);
     if (outArray != NULL) {
-        for (int i=0; i<maxNum; i++) {
+        outLen = MEMINFO_COUNT;
+        for (int i = 0; i < outLen; i++) {
+            // TODO: move get_allocated_vmalloc_memory() to libmeminfo
+            if (i == MEMINFO_VMALLOC_USED) {
+                outArray[i] = get_allocated_vmalloc_memory() / 1024;
+                continue;
+            }
             outArray[i] = mem[i];
         }
     }
+
     env->ReleaseLongArrayElements(out, outArray, 0);
 }
 
diff --git a/core/jni/android_os_SharedMemory.cpp b/core/jni/android_os_SharedMemory.cpp
index f6e5c7a..c33405d 100644
--- a/core/jni/android_os_SharedMemory.cpp
+++ b/core/jni/android_os_SharedMemory.cpp
@@ -20,8 +20,9 @@
 
 #include <cutils/ashmem.h>
 #include <utils/Log.h>
+
+#include <nativehelper/jni_macros.h>
 #include <nativehelper/JNIHelp.h>
-#include <nativehelper/JniConstants.h>
 #include <nativehelper/ScopedLocalRef.h>
 
 #include <algorithm>
@@ -31,10 +32,10 @@
 
 namespace {
 
-static void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
-    static jmethodID ctor = env->GetMethodID(JniConstants::errnoExceptionClass,
-            "<init>", "(Ljava/lang/String;I)V");
+jclass errnoExceptionClass;
+jmethodID errnoExceptionCtor;  // MethodID for ErrnoException.<init>(String,I)
 
+void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
     ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName));
     if (detailMessage.get() == NULL) {
         // Not really much we can do here. We're probably dead in the water,
@@ -42,12 +43,14 @@
         env->ExceptionClear();
     }
 
-    jobject exception = env->NewObject(JniConstants::errnoExceptionClass, ctor,
-            detailMessage.get(), error);
+    jobject exception = env->NewObject(errnoExceptionClass,
+                                       errnoExceptionCtor,
+                                       detailMessage.get(),
+                                       error);
     env->Throw(reinterpret_cast<jthrowable>(exception));
 }
 
-static jobject SharedMemory_create(JNIEnv* env, jobject, jstring jname, jint size) {
+jobject SharedMemory_nCreate(JNIEnv* env, jobject, jstring jname, jint size) {
 
     // Name is optional so we can't use ScopedUtfChars for this as it throws NPE on null
     const char* name = jname ? env->GetStringUTFChars(jname, nullptr) : nullptr;
@@ -69,7 +72,7 @@
     return jniCreateFileDescriptor(env, fd);
 }
 
-static jint SharedMemory_getSize(JNIEnv* env, jobject, jobject fileDescriptor) {
+jint SharedMemory_nGetSize(JNIEnv* env, jobject, jobject fileDescriptor) {
     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
     if (!ashmem_valid(fd)) {
         return -1;
@@ -78,7 +81,7 @@
     return static_cast<jint>(std::min(size, static_cast<size_t>(std::numeric_limits<jint>::max())));
 }
 
-static jint SharedMemory_setProt(JNIEnv* env, jobject, jobject fileDescriptor, jint prot) {
+jint SharedMemory_nSetProt(JNIEnv* env, jobject, jobject fileDescriptor, jint prot) {
     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
     int err = 0;
     if (ashmem_set_prot_region(fd, prot)) {
@@ -87,18 +90,21 @@
     return err;
 }
 
-static const JNINativeMethod methods[] = {
-    {"nCreate", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)SharedMemory_create},
-    {"nGetSize", "(Ljava/io/FileDescriptor;)I", (void*)SharedMemory_getSize},
-    {"nSetProt", "(Ljava/io/FileDescriptor;I)I", (void*)SharedMemory_setProt},
+const JNINativeMethod methods[] = {
+  NATIVE_METHOD(SharedMemory, nCreate, "(Ljava/lang/String;I)Ljava/io/FileDescriptor;"),
+  NATIVE_METHOD(SharedMemory, nGetSize, "(Ljava/io/FileDescriptor;)I"),
+  NATIVE_METHOD(SharedMemory, nSetProt, "(Ljava/io/FileDescriptor;I)I")
 };
 
 } // anonymous namespace
 
 namespace android {
 
-int register_android_os_SharedMemory(JNIEnv* env)
-{
+int register_android_os_SharedMemory(JNIEnv* env) {
+    errnoExceptionClass =
+        MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/system/ErrnoException"));
+    errnoExceptionCtor =
+        GetMethodIDOrDie(env, errnoExceptionClass, "<init>", "(Ljava/lang/String;I)V");
     return RegisterMethodsOrDie(env, "android/os/SharedMemory", methods, NELEM(methods));
 }
 
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 102a0b7..0c1a8aa 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -25,8 +25,12 @@
 #include <cutils/sched_policy.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
+#include <meminfo/sysmeminfo.h>
 #include <processgroup/processgroup.h>
 
+#include <string>
+#include <vector>
+
 #include "core_jni_helpers.h"
 
 #include "android_util_Binder.h"
@@ -39,9 +43,11 @@
 #include <inttypes.h>
 #include <pwd.h>
 #include <signal.h>
+#include <string.h>
 #include <sys/errno.h>
 #include <sys/resource.h>
 #include <sys/stat.h>
+#include <sys/sysinfo.h>
 #include <sys/types.h>
 #include <unistd.h>
 
@@ -603,66 +609,34 @@
     return *((const jint*)v1) - *((const jint*)v2);
 }
 
-static jlong getFreeMemoryImpl(const char* const sums[], const size_t sumsLen[], size_t num)
-{
-    int fd = open("/proc/meminfo", O_RDONLY | O_CLOEXEC);
-
-    if (fd < 0) {
-        ALOGW("Unable to open /proc/meminfo");
-        return -1;
-    }
-
-    char buffer[2048];
-    const int len = read(fd, buffer, sizeof(buffer)-1);
-    close(fd);
-
-    if (len < 0) {
-        ALOGW("Unable to read /proc/meminfo");
-        return -1;
-    }
-    buffer[len] = 0;
-
-    size_t numFound = 0;
-    jlong mem = 0;
-
-    char* p = buffer;
-    while (*p && numFound < num) {
-        int i = 0;
-        while (sums[i]) {
-            if (strncmp(p, sums[i], sumsLen[i]) == 0) {
-                p += sumsLen[i];
-                while (*p == ' ') p++;
-                char* num = p;
-                while (*p >= '0' && *p <= '9') p++;
-                if (*p != 0) {
-                    *p = 0;
-                    p++;
-                    if (*p == 0) p--;
-                }
-                mem += atoll(num) * 1024;
-                numFound++;
-                break;
-            }
-            i++;
-        }
-        p++;
-    }
-
-    return numFound > 0 ? mem : -1;
-}
-
 static jlong android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
 {
-    static const char* const sums[] = { "MemFree:", "Cached:", NULL };
-    static const size_t sumsLen[] = { strlen("MemFree:"), strlen("Cached:"), 0 };
-    return getFreeMemoryImpl(sums, sumsLen, 2);
+    static const std::vector<std::string> memFreeTags = {
+        ::android::meminfo::SysMemInfo::kMemFree,
+        ::android::meminfo::SysMemInfo::kMemCached,
+    };
+    std::vector<uint64_t> mem(memFreeTags.size());
+    ::android::meminfo::SysMemInfo smi;
+
+    if (!smi.ReadMemInfo(memFreeTags, &mem)) {
+        jniThrowRuntimeException(env, "SysMemInfo read failed to get Free Memory");
+        return -1L;
+    }
+
+    jlong sum = 0;
+    std::for_each(mem.begin(), mem.end(), [&](uint64_t val) { sum += val; });
+    return sum * 1024;
 }
 
 static jlong android_os_Process_getTotalMemory(JNIEnv* env, jobject clazz)
 {
-    static const char* const sums[] = { "MemTotal:", NULL };
-    static const size_t sumsLen[] = { strlen("MemTotal:"), 0 };
-    return getFreeMemoryImpl(sums, sumsLen, 1);
+    struct sysinfo si;
+    if (sysinfo(&si) == -1) {
+        ALOGE("sysinfo failed: %s", strerror(errno));
+        return -1;
+    }
+
+    return si.totalram;
 }
 
 void android_os_Process_readProcLines(JNIEnv* env, jobject clazz, jstring fileStr,
diff --git a/core/jni/android_util_jar_StrictJarFile.cpp b/core/jni/android_util_jar_StrictJarFile.cpp
index 4ab8db4..182a621 100644
--- a/core/jni/android_util_jar_StrictJarFile.cpp
+++ b/core/jni/android_util_jar_StrictJarFile.cpp
@@ -22,24 +22,26 @@
 
 #include <log/log.h>
 
+#include <nativehelper/jni_macros.h>
 #include <nativehelper/JNIHelp.h>
-#include <nativehelper/JniConstants.h>
 #include <nativehelper/ScopedLocalRef.h>
 #include <nativehelper/ScopedUtfChars.h>
-#include "jni.h"
+
+#include "core_jni_helpers.h"
 #include "ziparchive/zip_archive.h"
 
-namespace android {
+namespace {
 
+jclass zipEntryClass;
 // The method ID for ZipEntry.<init>(String,String,JJJIII[BJJ)
-static jmethodID zipEntryCtor;
+jmethodID zipEntryCtor;
 
-static void throwIoException(JNIEnv* env, const int32_t errorCode) {
+void throwIoException(JNIEnv* env, const int32_t errorCode) {
   jniThrowException(env, "java/io/IOException", ErrorCodeString(errorCode));
 }
 
-static jobject newZipEntry(JNIEnv* env, const ZipEntry& entry, jstring entryName) {
-  return env->NewObject(JniConstants::zipEntryClass,
+jobject newZipEntry(JNIEnv* env, const ZipEntry& entry, jstring entryName) {
+  return env->NewObject(zipEntryClass,
                         zipEntryCtor,
                         entryName,
                         NULL,  // comment
@@ -52,7 +54,7 @@
                         static_cast<jlong>(entry.offset));
 }
 
-static jlong StrictJarFile_nativeOpenJarFile(JNIEnv* env, jobject, jstring name, jint fd) {
+jlong StrictJarFile_nativeOpenJarFile(JNIEnv* env, jobject, jstring name, jint fd) {
   // Name argument is used for logging, and can be any string.
   ScopedUtfChars nameChars(env, name);
   if (nameChars.c_str() == NULL) {
@@ -90,7 +92,7 @@
 };
 
 
-static jlong StrictJarFile_nativeStartIteration(JNIEnv* env, jobject, jlong nativeHandle,
+jlong StrictJarFile_nativeStartIteration(JNIEnv* env, jobject, jlong nativeHandle,
                                                 jstring prefix) {
   ScopedUtfChars prefixChars(env, prefix);
   if (prefixChars.c_str() == NULL) {
@@ -116,7 +118,7 @@
   return reinterpret_cast<jlong>(handle);
 }
 
-static jobject StrictJarFile_nativeNextEntry(JNIEnv* env, jobject, jlong iterationHandle) {
+jobject StrictJarFile_nativeNextEntry(JNIEnv* env, jobject, jlong iterationHandle) {
   ZipEntry data;
   ZipString entryName;
 
@@ -135,7 +137,7 @@
   return newZipEntry(env, data, entryNameString.get());
 }
 
-static jobject StrictJarFile_nativeFindEntry(JNIEnv* env, jobject, jlong nativeHandle,
+jobject StrictJarFile_nativeFindEntry(JNIEnv* env, jobject, jlong nativeHandle,
                                              jstring entryName) {
   ScopedUtfChars entryNameChars(env, entryName);
   if (entryNameChars.c_str() == NULL) {
@@ -152,11 +154,11 @@
   return newZipEntry(env, data, entryName);
 }
 
-static void StrictJarFile_nativeClose(JNIEnv*, jobject, jlong nativeHandle) {
+void StrictJarFile_nativeClose(JNIEnv*, jobject, jlong nativeHandle) {
   CloseArchive(reinterpret_cast<ZipArchiveHandle>(nativeHandle));
 }
 
-static JNINativeMethod gMethods[] = {
+JNINativeMethod gMethods[] = {
   NATIVE_METHOD(StrictJarFile, nativeOpenJarFile, "(Ljava/lang/String;I)J"),
   NATIVE_METHOD(StrictJarFile, nativeStartIteration, "(JLjava/lang/String;)J"),
   NATIVE_METHOD(StrictJarFile, nativeNextEntry, "(J)Ljava/util/zip/ZipEntry;"),
@@ -164,14 +166,15 @@
   NATIVE_METHOD(StrictJarFile, nativeClose, "(J)V"),
 };
 
+}  // namespace
+
+namespace android {
+
 int register_android_util_jar_StrictJarFile(JNIEnv* env) {
-  jniRegisterNativeMethods(env, "android/util/jar/StrictJarFile", gMethods, NELEM(gMethods));
-
-  zipEntryCtor = env->GetMethodID(JniConstants::zipEntryClass, "<init>",
-      "(Ljava/lang/String;Ljava/lang/String;JJJII[BJ)V");
-  LOG_ALWAYS_FATAL_IF(zipEntryCtor == NULL, "Unable to find ZipEntry.<init>");
-
-  return 0;
+  zipEntryClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/util/zip/ZipEntry"));
+  zipEntryCtor = GetMethodIDOrDie(env, zipEntryClass, "<init>",
+                                  "(Ljava/lang/String;Ljava/lang/String;JJJII[BJ)V");
+  return jniRegisterNativeMethods(env, "android/util/jar/StrictJarFile", gMethods, NELEM(gMethods));
 }
 
 }; // namespace android
diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
index e0fd1a6..40a133b 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -168,6 +168,11 @@
     canvas->drawCircle(xProp, yProp, radiusProp, paintProp);
 }
 
+static void android_view_DisplayListCanvas_drawWebViewFunctor(jlong canvasPtr, jint functor) {
+    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+    canvas->drawWebViewFunctor(functor);
+}
+
 // ----------------------------------------------------------------------------
 // JNI Glue
 // ----------------------------------------------------------------------------
@@ -192,6 +197,7 @@
     { "nDrawTextureLayer",        "(JJ)V",      (void*) android_view_DisplayListCanvas_drawTextureLayer },
     { "nDrawCircle",              "(JJJJJ)V",   (void*) android_view_DisplayListCanvas_drawCircleProps },
     { "nDrawRoundRect",           "(JJJJJJJJ)V",(void*) android_view_DisplayListCanvas_drawRoundRectProps },
+    { "nDrawWebViewFunctor",      "(JI)V",      (void*) android_view_DisplayListCanvas_drawWebViewFunctor },
 };
 
 int register_android_view_DisplayListCanvas(JNIEnv* env) {
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 752624b..0d75de9 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -338,6 +338,11 @@
     return renderNode->stagingProperties().hasOverlappingRendering();
 }
 
+static jboolean android_view_RenderNode_getClipToBounds(jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().getClipToBounds();
+}
+
 static jboolean android_view_RenderNode_getClipToOutline(jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     return renderNode->stagingProperties().getOutline().getShouldClip();
@@ -409,6 +414,11 @@
     return !renderNode->stagingProperties().hasTransformMatrix();
 }
 
+static jint android_view_RenderNode_getLayerType(jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return static_cast<int>(renderNode->stagingProperties().layerProperties().type());
+}
+
 // ----------------------------------------------------------------------------
 // RenderProperties - computed getters
 // ----------------------------------------------------------------------------
@@ -623,10 +633,12 @@
 // ----------------------------------------------------------------------------
     { "nIsValid",              "(J)Z",   (void*) android_view_RenderNode_isValid },
     { "nSetLayerType",         "(JI)Z",  (void*) android_view_RenderNode_setLayerType },
+    { "nGetLayerType",         "(J)I",   (void*) android_view_RenderNode_getLayerType },
     { "nSetLayerPaint",        "(JJ)Z",  (void*) android_view_RenderNode_setLayerPaint },
     { "nSetStaticMatrix",      "(JJ)Z",  (void*) android_view_RenderNode_setStaticMatrix },
     { "nSetAnimationMatrix",   "(JJ)Z",  (void*) android_view_RenderNode_setAnimationMatrix },
     { "nSetClipToBounds",      "(JZ)Z",  (void*) android_view_RenderNode_setClipToBounds },
+    { "nGetClipToBounds",      "(J)Z",   (void*) android_view_RenderNode_getClipToBounds },
     { "nSetClipBounds",        "(JIIII)Z", (void*) android_view_RenderNode_setClipBounds },
     { "nSetClipBoundsEmpty",   "(J)Z",   (void*) android_view_RenderNode_setClipBoundsEmpty },
     { "nSetProjectBackwards",  "(JZ)Z",  (void*) android_view_RenderNode_setProjectBackwards },
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index c745c16..f292d25 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -348,6 +348,15 @@
     transaction->setInputWindowInfo(ctrl, *handle->getInfo());
 }
 
+static void nativeTransferTouchFocus(JNIEnv* env, jclass clazz, jlong transactionObj,
+        jobject fromTokenObj, jobject toTokenObj) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+    sp<IBinder> fromToken(ibinderForJavaObject(env, fromTokenObj));
+    sp<IBinder> toToken(ibinderForJavaObject(env, toTokenObj));
+    transaction->transferTouchFocus(fromToken, toToken);
+}
+
 static void nativeSetColor(JNIEnv* env, jclass clazz, jlong transactionObj,
         jlong nativeObject, jfloatArray fColor) {
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -429,8 +438,9 @@
 static jboolean nativeSetDisplayedContentSamplingEnabled(JNIEnv* env, jclass clazz,
         jobject tokenObj, jboolean enable, jint componentMask, jint maxFrames) {
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
-    return SurfaceComposerClient::setDisplayContentSamplingEnabled(
+    status_t rc = SurfaceComposerClient::setDisplayContentSamplingEnabled(
             token, enable, componentMask, maxFrames);
+    return rc == OK;
 }
 
 static jobject nativeGetDisplayedContentSample(JNIEnv* env, jclass clazz, jobject tokenObj,
@@ -907,6 +917,17 @@
     return reinterpret_cast<jlong>(surface.get());
 }
 
+static jlong nativeCopyFromSurfaceControl(JNIEnv* env, jclass clazz, jlong surfaceControlNativeObj) {
+    sp<SurfaceControl> surface(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj));
+    if (surface == nullptr) {
+        return 0;
+    }
+
+    sp<SurfaceControl> newSurface = new SurfaceControl(surface);
+    newSurface->incStrong((void *)nativeCreate);
+    return reinterpret_cast<jlong>(newSurface.get());
+}
+
 static void nativeWriteToParcel(JNIEnv* env, jclass clazz,
         jlong nativeObject, jobject parcelObj) {
     Parcel* parcel = parcelForJavaObject(env, parcelObj);
@@ -915,7 +936,9 @@
         return;
     }
     SurfaceControl* const self = reinterpret_cast<SurfaceControl *>(nativeObject);
-    self->writeToParcel(parcel);
+    if (self != nullptr) {
+        self->writeToParcel(parcel);
+    }
 }
 
 // ----------------------------------------------------------------------------
@@ -925,6 +948,8 @@
             (void*)nativeCreate },
     {"nativeReadFromParcel", "(Landroid/os/Parcel;)J",
             (void*)nativeReadFromParcel },
+    {"nativeCopyFromSurfaceControl", "(J)J" ,
+            (void*)nativeCopyFromSurfaceControl },
     {"nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
             (void*)nativeWriteToParcel },
     {"nativeRelease", "(J)V",
@@ -1032,7 +1057,9 @@
     {"nativeCaptureLayers", "(Landroid/os/IBinder;Landroid/graphics/Rect;F)Landroid/graphics/GraphicBuffer;",
             (void*)nativeCaptureLayers },
     {"nativeSetInputWindowInfo", "(JJLandroid/view/InputWindowHandle;)V",
-     (void*)nativeSetInputWindowInfo },
+            (void*)nativeSetInputWindowInfo },
+    {"nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;)V",
+            (void*)nativeTransferTouchFocus },
     {"nativeGetDisplayedContentSamplingAttributes",
             "(Landroid/os/IBinder;)Landroid/hardware/display/DisplayedContentSamplingAttributes;",
             (void*)nativeGetDisplayedContentSamplingAttributes },
diff --git a/core/jni/android_view_SurfaceSession.cpp b/core/jni/android_view_SurfaceSession.cpp
index 30c0030..191f748 100644
--- a/core/jni/android_view_SurfaceSession.cpp
+++ b/core/jni/android_view_SurfaceSession.cpp
@@ -46,13 +46,6 @@
     return reinterpret_cast<jlong>(client);
 }
 
-static jlong nativeCreateScoped(JNIEnv* env, jclass clazz, jlong surfaceObject) {
-    Surface *parent = reinterpret_cast<Surface*>(surfaceObject);
-    SurfaceComposerClient* client = new SurfaceComposerClient(parent->getIGraphicBufferProducer());
-    client->incStrong((void*)nativeCreate);
-    return reinterpret_cast<jlong>(client);
-}
-
 static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
     SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr);
     client->decStrong((void*)nativeCreate);
@@ -67,8 +60,6 @@
     /* name, signature, funcPtr */
     { "nativeCreate", "()J",
             (void*)nativeCreate },
-    { "nativeCreateScoped", "(J)J",
-            (void*)nativeCreateScoped },
     { "nativeDestroy", "(J)V",
             (void*)nativeDestroy },
     { "nativeKill", "(J)V",
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 702741e..5a8ab3c 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -31,8 +31,6 @@
 #include <gui/BufferQueue.h>
 #include <gui/Surface.h>
 
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
 #include <private/EGL/cache.h>
 
 #include <utils/Looper.h>
@@ -58,6 +56,7 @@
 #include <renderthread/RenderTask.h>
 #include <renderthread/RenderThread.h>
 #include <pipeline/skia/ShaderCache.h>
+#include <utils/Color.h>
 
 namespace android {
 
@@ -1011,10 +1010,9 @@
                 buffer->getWidth(), buffer->getHeight(), width, height);
         // Continue I guess?
     }
-    sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer);
-    // Bitmap::createFrom currently can only attach to a GraphicBuffer with PIXEL_FORMAT_RGBA_8888
-    // format and SRGB color space.
-    // To support any color space, we could extract it from BufferItem and pass it to Bitmap.
+
+    sk_sp<SkColorSpace> cs = uirenderer::DataSpaceToColorSpace(bufferItem.mDataSpace);
+    sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, cs);
     return bitmap::createBitmap(env, bitmap.release(),
             android::bitmap::kBitmapCreateFlag_Premultiplied);
 }
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 7032081..a8e1427 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -418,7 +418,7 @@
     }
     endmntent(fp);
 
-    for (auto path : toUnmount) {
+    for (const auto& path : toUnmount) {
         if (umount2(path.c_str(), MNT_DETACH)) {
             ALOGW("Failed to unmount %s: %s", path.c_str(), strerror(errno));
         }
@@ -1119,6 +1119,13 @@
     capabilities |= (1LL << CAP_SYS_NICE);
   }
 
+  if (multiuser_get_app_id(uid) == AID_NETWORK_STACK) {
+    capabilities |= (1LL << CAP_NET_ADMIN);
+    capabilities |= (1LL << CAP_NET_BROADCAST);
+    capabilities |= (1LL << CAP_NET_BIND_SERVICE);
+    capabilities |= (1LL << CAP_NET_RAW);
+  }
+
   /*
    * Grant CAP_BLOCK_SUSPEND to processes that belong to GID "wakelock"
    */
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index a398e49..bd87dcc 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -152,7 +152,7 @@
   const bool is_sock;
 
  private:
-  FileDescriptorInfo(int fd);
+  explicit FileDescriptorInfo(int fd);
 
   FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags,
                      int fd_flags, int fs_flags, off_t offset);
@@ -331,11 +331,13 @@
     return false;
   }
 
-  if (TEMP_FAILURE_RETRY(dup2(new_fd, fd)) == -1) {
+  int dupFlags = (fd_flags & FD_CLOEXEC) ? O_CLOEXEC : 0;
+  if (TEMP_FAILURE_RETRY(dup3(new_fd, fd, dupFlags)) == -1) {
     close(new_fd);
-    *error_msg = android::base::StringPrintf("Failed dup2(%d, %d) (%s): %s",
+    *error_msg = android::base::StringPrintf("Failed dup3(%d, %d, %d) (%s): %s",
                                              fd,
                                              new_fd,
+                                             dupFlags,
                                              file_path.c_str(),
                                              strerror(errno));
     return false;
diff --git a/core/jni/fd_utils.h b/core/jni/fd_utils.h
index a3570d7..09022a2 100644
--- a/core/jni/fd_utils.h
+++ b/core/jni/fd_utils.h
@@ -86,7 +86,7 @@
   bool ReopenOrDetach(std::string* error_msg);
 
  private:
-  FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map);
+  explicit FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map);
 
   bool RestatInternal(std::set<int>& open_fds, std::string* error_msg);
 
diff --git a/core/proto/android/os/enums.proto b/core/proto/android/os/enums.proto
index db4a4c4..c357065 100644
--- a/core/proto/android/os/enums.proto
+++ b/core/proto/android/os/enums.proto
@@ -57,6 +57,7 @@
 }
 
 // These constants are defined in hardware/interfaces/thermal/1.0/types.hal
+// and in hardware/interfaces/thermal/2.0/types.hal
 // They are primarily used by android/os/HardwarePropertiesManager.java.
 // Any change to the types in the thermal hal should be made here as well.
 enum TemperatureTypeEnum {
@@ -65,6 +66,16 @@
     TEMPERATURE_TYPE_GPU = 1;
     TEMPERATURE_TYPE_BATTERY = 2;
     TEMPERATURE_TYPE_SKIN = 3;
+    TEMPERATURE_TYPE_USB_PORT = 4;
+    TEMPERATURE_TYPE_POWER_AMPLIFIER = 5;
+
+    // Battery Charge Limit - virtual thermal sensors.
+    TEMPERATURE_TYPE_BCL_VOLTAGE = 6;
+    TEMPERATURE_TYPE_BCL_CURRENT = 7;
+    TEMPERATURE_TYPE_BCL_PERCENTAGE = 8;
+
+    // Neural Processing Unit.
+    TEMPERATURE_TYPE_NPU = 9;
 }
 
 // Wakelock types, primarily used by android/os/PowerManager.java.
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 11bd43b..a914369 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -436,9 +436,11 @@
         // Ordered GPU debug layer list for GLES
         // i.e. <layer1>:<layer2>:...:<layerN>
         optional SettingProto debug_layers_gles = 7;
-        // App opt in to load updated graphics driver instead of
-        // native graphcis driver through developer options.
-        optional SettingProto updated_gfx_driver_dev_opt_in_app = 8;
+        // Apps opt in to load graphics driver from Game Update Package
+        // instead of native graphcis driver through developer options.
+        optional SettingProto gup_dev_opt_in_apps = 8;
+        // Apps on the black list that are forbidden to useGame Update Package.
+        optional SettingProto gup_black_list = 9;
     }
     optional Gpu gpu = 59;
 
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 60561bd..6f9a564 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -82,6 +82,7 @@
     repeated ActivityStackProto stacks = 3;
     optional int32 focused_stack_id = 4;
     optional .com.android.server.wm.IdentifierProto resumed_activity = 5;
+    optional bool single_task_instance = 6;
 }
 
 message ActivityStackProto {
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 7fe3be8..0ec8c1a 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -220,6 +220,13 @@
     // will use heartbeats, false will use a rolling window.
     optional bool use_heartbeats = 23;
 
+    message TimeController {
+        // Whether or not TimeController should skip setting wakeup alarms for jobs that aren't
+        // ready now.
+        optional bool skip_not_ready_jobs = 1;
+    }
+    optional TimeController time_controller = 25;
+
     message QuotaController {
         // How much time each app will have to run jobs within their standby bucket window.
         optional int64 allowed_time_per_period_ms = 1;
@@ -242,8 +249,12 @@
         // expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
         // WINDOW_SIZE_MS.
         optional int64 rare_window_size_ms = 6;
+        // The maximum amount of time an app can have its jobs running within a 24 hour window.
+        optional int64 max_execution_time_ms = 7;
     }
     optional QuotaController quota_controller = 24;
+
+    // Next tag: 26
 }
 
 message StateControllerProto {
diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto
index ce19ce3..71ebcc1 100644
--- a/core/proto/android/service/procstats.proto
+++ b/core/proto/android/service/procstats.proto
@@ -186,7 +186,7 @@
     repeated PackageServiceOperationStatsProto operation_stats = 2;
 }
 
-// Next Tag: 7
+// Next Tag: 8
 message PackageAssociationSourceProcessStatsProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
@@ -194,6 +194,8 @@
     optional int32 process_uid = 1;
     // Process name.
     optional string process_name = 2;
+    // Package name.
+    optional string package_name = 7;
 
     // Total count of the times this association appeared.
     optional int32 total_count = 3;
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index c7a6b68..82460ec 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -141,4 +141,7 @@
   PM_UNINSTALL = 113;
   WIFI_SERVICE_ADD_NETWORK_SUGGESTIONS = 114;
   WIFI_SERVICE_ADD_OR_UPDATE_NETWORK = 115;
+  QUERY_SUMMARY_FOR_DEVICE = 116;
+  REMOVE_CROSS_PROFILE_WIDGET_PROVIDER = 117;
+  ESTABLISH_VPN = 118;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index dca15bd..0778304 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -42,6 +42,8 @@
     <protected-broadcast android:name="android.intent.action.PACKAGE_REMOVED" />
     <protected-broadcast android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
     <protected-broadcast android:name="android.intent.action.PACKAGE_CHANGED" />
+    <protected-broadcast android:name="android.intent.action.PACKAGE_ENABLE_ROLLBACK" />
+    <protected-broadcast android:name="android.intent.action.PACKAGE_ROLLBACK_EXECUTED" />
     <protected-broadcast android:name="android.intent.action.PACKAGE_RESTARTED" />
     <protected-broadcast android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
     <protected-broadcast android:name="android.intent.action.PACKAGE_FIRST_LAUNCH" />
@@ -611,6 +613,7 @@
     <protected-broadcast android:name="android.intent.action.DOCK_ACTIVE" />
 
     <!-- Added in Q -->
+    <protected-broadcast android:name="android.content.pm.action.SESSION_UPDATED" />
 
     <!-- For CarIdlenessTracker -->
     <protected-broadcast android:name="com.android.server.jobscheduler.GARAGE_MODE_ON" />
@@ -1642,6 +1645,12 @@
     <permission android:name="android.permission.NETWORK_BYPASS_PRIVATE_DNS"
         android:protectionLevel="signature" />
 
+    <!-- #SystemApi @hide Allows device mobility state to be set so that Wifi scan interval can be increased
+         when the device is stationary in order to save power.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.WIFI_SET_DEVICE_MOBILITY_STATE"
+        android:protectionLevel="signature|privileged" />
+
     <!-- ======================================= -->
     <!-- Permissions for short range, peripheral networks -->
     <!-- ======================================= -->
@@ -1997,6 +2006,15 @@
     <permission android:name="android.permission.BIND_SCREENING_SERVICE"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Must be required by a {@link android.telecom.PhoneAccountSuggestionService},
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature|privileged
+         @SystemApi
+         @hide
+    -->
+    <permission android:name="android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE"
+        android:protectionLevel="signature|privileged" />
+
     <!-- Must be required by a {@link android.telecom.CallRedirectionService},
          to ensure that only the system can bind to it.
          <p>Protection level: signature|privileged
@@ -3361,6 +3379,11 @@
     <permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS"
          android:protectionLevel="signature|installer|verifier" />
 
+    <!-- @SystemApi Allows the system to read runtime permission state.
+        @hide -->
+    <permission android:name="android.permission.GET_RUNTIME_PERMISSIONS"
+                android:protectionLevel="signature" />
+
     <!-- @hide Allows an application to observe permission changes. -->
     <permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS"
         android:protectionLevel="signature|privileged" />
@@ -3830,6 +3853,16 @@
     <permission android:name="android.permission.BIND_PACKAGE_VERIFIER"
         android:protectionLevel="signature" />
 
+    <!-- @hide Rollback manager needs to have this permission before the PackageManager will
+         trust it to enable rollback.
+    -->
+    <permission android:name="android.permission.PACKAGE_ROLLBACK_AGENT"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi @hide Allows managing apk level rollbacks. -->
+    <permission android:name="android.permission.MANAGE_ROLLBACKS"
+        android:protectionLevel="signature|installer" />
+
     <!-- @SystemApi @hide Allows an application to mark other applications as harmful -->
     <permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS"
         android:protectionLevel="signature|verifier" />
@@ -4321,7 +4354,7 @@
     <permission android:name="android.permission.SMS_FINANCIAL_TRANSACTIONS"
         android:protectionLevel="signature|appop" />
 
-    <!-- Required for apps targeting {@link android.os.Build.VERSION_CODES#P} that want to use
+    <!-- Required for apps targeting {@link android.os.Build.VERSION_CODES#Q} that want to use
          {@link android.app.Notification.Builder#setFullScreenIntent notification full screen
          intents}.  -->
     <permission android:name="android.permission.USE_FULL_SCREEN_INTENT"
@@ -4342,6 +4375,10 @@
          @hide -->
     <permission android:name="android.permission.AMBIENT_WALLPAPER"
                 android:protectionLevel="signature|preinstalled" />
+    <!-- @SystemApi Allows sensor privacy to be modified.
+         @hide -->
+    <permission android:name="android.permission.MANAGE_SENSOR_PRIVACY"
+                android:protectionLevel="signature" />
 
     <application android:process="system"
                  android:persistent="true"
diff --git a/core/res/res/drawable/ic_account_circle.xml b/core/res/res/drawable/ic_account_circle.xml
index f7317db..71691ad 100644
--- a/core/res/res/drawable/ic_account_circle.xml
+++ b/core/res/res/drawable/ic_account_circle.xml
@@ -14,18 +14,14 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="48.0dp"
-        android:height="48.0dp"
-        android:viewportWidth="20.0"
-        android:viewportHeight="20.0">
-    <group
-        android:translateX="-2"
-        android:translateY="-2" >
-        <path
-            android:pathData="M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10c5.52,0 10,-4.48 10,-10C22,6.48 17.52,2 12,2zM18.36,16.83c-1.43,-1.74 -4.9,-2.33 -6.36,-2.33s-4.93,0.59 -6.36,2.33C4.62,15.49 4,13.82 4,12c0,-4.41 3.59,-8 8,-8c4.41,0 8,3.59 8,8C20,13.82 19.38,15.49 18.36,16.83z"
-            android:fillColor="#FFFFFFFF" />
-        <path
-            android:pathData="M12,6c-1.94,0 -3.5,1.56 -3.5,3.5S10.06,13 12,13c1.94,0 3.5,-1.56 3.5,-3.5S13.94,6 12,6z"
-            android:fillColor="#FFFFFFFF" />
-    </group>
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10c5.52,0 10,-4.48 10,-10C22,6.48 17.52,2 12,2zM18.36,16.83c-1.43,-1.74 -4.9,-2.33 -6.36,-2.33s-4.93,0.59 -6.36,2.33C4.62,15.49 4,13.82 4,12c0,-4.41 3.59,-8 8,-8c4.41,0 8,3.59 8,8C20,13.82 19.38,15.49 18.36,16.83z"/>
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M12,6c-1.94,0 -3.5,1.56 -3.5,3.5S10.06,13 12,13c1.94,0 3.5,-1.56 3.5,-3.5S13.94,6 12,6z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_swap.xml b/core/res/res/drawable/ic_battery.xml
similarity index 76%
rename from packages/SystemUI/res/drawable/ic_swap.xml
rename to core/res/res/drawable/ic_battery.xml
index 30da2a9..bd40f4d 100644
--- a/packages/SystemUI/res/drawable/ic_swap.xml
+++ b/core/res/res/drawable/ic_battery.xml
@@ -18,8 +18,8 @@
         android:height="24dp"
         android:viewportWidth="24.0"
         android:viewportHeight="24.0"
-        android:tint="?android:attr/colorForeground">
+        android:tint="?android:attr/colorControlNormal">
     <path
-        android:pathData="M6.99,11L3,15l3.99,4v-3H14v-2H6.99v-3zM21,9l-3.99,-4v3H10v2h7.01v3L21,9z"
-        android:fillColor="#FFFFFF"/>
-</vector>
\ No newline at end of file
+        android:fillColor="#FF000000"
+        android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v15.33C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V5.33C17,4.6 16.4,4 15.67,4z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_corp_badge.xml b/core/res/res/drawable/ic_corp_badge.xml
index 1dad977..5ab5045 100644
--- a/core/res/res/drawable/ic_corp_badge.xml
+++ b/core/res/res/drawable/ic_corp_badge.xml
@@ -15,22 +15,11 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="20dp"
-        android:height="20dp"
-        android:viewportWidth="48"
-        android:viewportHeight="48">
-
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
     <path
-        android:fillType="evenOdd"
-        android:strokeColor="#1A73E8"
-        android:strokeWidth="4"
-        android:pathData="M 24 2 C 36.1502644963 2 46 11.8497355037 46 24 C 46 36.1502644963 36.1502644963 46 24 46 C 11.8497355037 46 2 36.1502644963 2 24 C 2 11.8497355037 11.8497355037 2 24 2 Z" />
-    <group
-        android:translateX="10.400000"
-        android:translateY="10.400000">
-        <path
-            android:fillColor="#1A73E8"
-            android:strokeWidth="1"
-            android:pathData="M24.2971429,5.38947368 L18.9485714,5.38947368 L18.9485714,2.80902256 C18.9485714,1.37687218 17.7585143,0.228571429 16.2742857,0.228571429 L10.9257143,0.228571429 C9.44148571,0.228571429 8.25142857,1.37687218 8.25142857,2.80902256 L8.25142857,5.38947368 L2.90285714,5.38947368 C1.41862857,5.38947368 0.241942857,6.53777444 0.241942857,7.96992481 L0.228571429,22.162406 C0.228571429,23.5945564 1.41862857,24.7428571 2.90285714,24.7428571 L24.2971429,24.7428571 C25.7813714,24.7428571 26.9714286,23.5945564 26.9714286,22.162406 L26.9714286,7.96992481 C26.9714286,6.53777444 25.7813714,5.38947368 24.2971429,5.38947368 Z M13.6,17.0015038 C12.1291429,17.0015038 10.9257143,15.8403008 10.9257143,14.4210526 C10.9257143,13.0018045 12.1291429,11.8406015 13.6,11.8406015 C15.0708571,11.8406015 16.2742857,13.0018045 16.2742857,14.4210526 C16.2742857,15.8403008 15.0708571,17.0015038 13.6,17.0015038 Z M16.2742857,5.38947368 L10.9257143,5.38947368 L10.9257143,2.80902256 L16.2742857,2.80902256 L16.2742857,5.38947368 Z" />
-    </group>
+        android:fillColor="@*android:color/accent_device_default_light"
+        android:pathData="M20,6h-4V4c0,-1.11 -0.89,-2 -2,-2h-4C8.89,2 8,2.89 8,4v2H4C2.89,6 2.01,6.89 2.01,8L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8C22,6.89 21.11,6 20,6zM10,4h4v2h-4V4zM20,19H4V8h16V19z"/>
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_0_bars.xml b/core/res/res/drawable/ic_signal_wifi_badged_0_bars.xml
deleted file mode 100644
index 606b686..0000000
--- a/core/res/res/drawable/ic_signal_wifi_badged_0_bars.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:viewportWidth="18"
-    android:viewportHeight="18"
-    android:width="26dp"
-    android:height="24dp">
-    <group
-        android:translateX="386"
-        android:translateY="-298">
-        <path
-            android:pathData="M-377 308.5c0 -2.5 2 -4.5 4.5 -4.5l3.5 0 0.79999 -1c-0.29999 -0.29999 -3.70001 -3 -8.79999 -3 -5.09998 0 -8.5 2.79999 -8.79999 3l8.79999 11 0 0 0 0 1.60001 -2c-0.9 -0.89999 -1.60001 -2.10001 -1.60001 -3.5z"
-            android:fillColor="#FFFFFF"
-            android:fillAlpha="0.3" />
-    </group>
-</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_1_bar.xml b/core/res/res/drawable/ic_signal_wifi_badged_1_bar.xml
deleted file mode 100644
index a4c5bc2..0000000
--- a/core/res/res/drawable/ic_signal_wifi_badged_1_bar.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:viewportWidth="18"
-    android:viewportHeight="18"
-    android:width="26dp"
-    android:height="24dp">
-    <group
-        android:translateX="386"
-        android:translateY="-298">
-        <path
-            android:pathData="M-377 308.5c0 -2.5 2 -4.5 4.5 -4.5l3.5 0 0.79999 -1c-0.29999 -0.29999 -3.70001 -3 -8.79999 -3 -5.09998 0 -8.5 2.79999 -8.79999 3l8.79999 11 0 0 0 0 1.60001 -2c-0.9 -0.89999 -1.60001 -2.10001 -1.60001 -3.5z"
-            android:fillColor="#FFFFFF"
-            android:fillAlpha="0.3" />
-        <path
-            android:pathData="M-377 308.5c0 -0.20001 0 -0.29999 0.10001 -0.5 0 0 0 0 -0.10001 0 -2.10001 0 -3.60001 1.20001 -3.79999 1.29999L-377 314l1.60001 -2.10001c-0.9 -0.79998 -1.60001 -2 -1.60001 -3.39999z"
-            android:fillColor="#FFFFFF" />
-    </group>
-</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_2_bars.xml b/core/res/res/drawable/ic_signal_wifi_badged_2_bars.xml
deleted file mode 100644
index 9c27833..0000000
--- a/core/res/res/drawable/ic_signal_wifi_badged_2_bars.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:viewportWidth="18"
-    android:viewportHeight="18"
-    android:width="26dp"
-    android:height="24dp">
-    <group
-        android:translateX="386"
-        android:translateY="-298">
-        <path
-            android:pathData="M-377 308.5c0 -2.5 2 -4.5 4.5 -4.5l3.5 0 0.79999 -1c-0.29999 -0.29999 -3.70001 -3 -8.79999 -3 -5.09998 0 -8.5 2.79999 -8.79999 3l8.79999 11 0 0 0 0 1.60001 -2c-0.9 -0.89999 -1.60001 -2.10001 -1.60001 -3.5z"
-            android:fillColor="#FFFFFF"
-            android:fillAlpha="0.3" />
-        <path
-            android:pathData="M-377 308.5c0 -0.89999 0.29999 -1.70001 0.70001 -2.5 -0.20001 0 -0.5 0 -0.70001 0 -2.79999 0 -4.79999 1.60001 -5 1.79999l5 6.20001 0 0 0 0 1.60001 -2c-1 -0.89999 -1.60001 -2.10001 -1.60001 -3.5z"
-            android:fillColor="#FFFFFF" />
-    </group>
-</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_3_bars.xml b/core/res/res/drawable/ic_signal_wifi_badged_3_bars.xml
deleted file mode 100644
index 6d693f1..0000000
--- a/core/res/res/drawable/ic_signal_wifi_badged_3_bars.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:viewportWidth="18"
-    android:viewportHeight="18"
-    android:width="26dp"
-    android:height="24dp">
-    <group
-        android:translateX="386"
-        android:translateY="-298">
-        <path
-            android:pathData="M-377 308.5c0 -2.5 2 -4.5 4.5 -4.5l3.5 0 0.79999 -1c-0.29999 -0.29999 -3.70001 -3 -8.79999 -3 -5.09998 0 -8.5 2.79999 -8.79999 3l8.79999 11 0 0 0 0 1.60001 -2c-0.9 -0.89999 -1.60001 -2.10001 -1.60001 -3.5z"
-            android:fillColor="#FFFFFF"
-            android:fillAlpha="0.3" />
-        <path
-            android:pathData="M-375.39999 311.89999c-1 -0.79998 -1.60001 -2.1 -1.60001 -3.39999 0 -1.79999 1.10001 -3.39999 2.70001 -4.10001C-375.09998 304.19998 -376 304 -377 304c-3.60001 0 -6 1.89999 -6.29999 2.20001L-377 314l0 0 0 0"
-            android:fillColor="#FFFFFF" />
-    </group>
-</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_4_bars.xml b/core/res/res/drawable/ic_signal_wifi_badged_4_bars.xml
deleted file mode 100644
index c48fa36..0000000
--- a/core/res/res/drawable/ic_signal_wifi_badged_4_bars.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:viewportWidth="18"
-    android:viewportHeight="18"
-    android:width="26dp"
-    android:height="24dp">
-    <group
-        android:translateX="386"
-        android:translateY="-298">
-        <path
-            android:pathData="M-377 308.5c0 -2.5 2 -4.5 4.5 -4.5l3.5 0 0.79999 -1c-0.29999 -0.29999 -3.70001 -3 -8.79999 -3 -5.09998 0 -8.5 2.79999 -8.79999 3l8.79999 11 0 0 0 0 1.60001 -2c-0.9 -0.89999 -1.60001 -2.10001 -1.60001 -3.5z"
-            android:fillColor="#FFFFFF" />
-    </group>
-</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_4k.xml b/core/res/res/drawable/ic_signal_wifi_badged_4k.xml
deleted file mode 100644
index 0868845..0000000
--- a/core/res/res/drawable/ic_signal_wifi_badged_4k.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:viewportWidth="18"
-    android:viewportHeight="18"
-    android:width="26dp"
-    android:height="24dp">
-    <group
-        android:translateX="386"
-        android:translateY="-298">
-        <path
-            android:pathData="M-373.04999 308.79999l0.5 0 0 0.89999 -0.5 0 0 1.20001 -1.1 0 0 -1.20001 -1.9 0 -0.1 -0.70001 1.89999 -3.70001 1.10001 0 0 3.50003 0.1 0zm-1.89999 0l0.89999 0 0 -1.9 0 0 -0.89999 1.9z"
-            android:fillColor="#FFFFFF" />
-        <path
-            android:pathData="M-370.44998 308.70001l-0.5 0.60001 0 1.70001 -1.10001 0 0 -5.70001 1.10001 0 0 2.5 0.39999 -0.60001 1.10001 -1.89999 1.39999 0 -1.6 2.5 1.70001 3.20001 -1.29999 0 -1.20001 -2.30002z"
-            android:fillColor="#FFFFFF" />
-    </group>
-</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_hd.xml b/core/res/res/drawable/ic_signal_wifi_badged_hd.xml
deleted file mode 100644
index 657f5ed..0000000
--- a/core/res/res/drawable/ic_signal_wifi_badged_hd.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:viewportWidth="18"
-    android:viewportHeight="18"
-    android:width="26dp"
-    android:height="24dp">
-    <group
-        android:translateX="386"
-        android:translateY="-298">
-        <path
-            android:pathData="M-371.79999 311l-1.1 0 0 -2.29999 -0.79999 0 0 2.29999 -1.10001 0 0 -5.70001 1.10001 0 0 2.39999 0.79999 0 0 -2.39999 1.1 0 0 5.70001z"
-            android:fillColor="#FFFFFF" />
-        <path
-            android:pathData="M-371.33557 310.98651l0 -5.68701 1.39068 0c0.27848 0 0.53336 0.0532 0.76465 0.16016 0.2313 0.10693 0.42954 0.2622 0.59568 0.46679 0.16519 0.20459 0.29357 0.45557 0.38421 0.75391 0.0906 0.29834 0.13593 0.63867 0.13593 1.02148l0 0.88672c0 0.38281 -0.0453 0.72363 -0.13593 1.021 -0.0906 0.29785 -0.21902 0.5498 -0.38421 0.7539 -0.16614 0.20411 -0.36628 0.35938 -0.59946 0.46485 -0.23316 0.10547 -0.49182 0.1582 -0.77786 0.1582l-1.37369 0zm1.06879 -4.76904l0 3.85107 0.26333 0c0.15491 0 0.28452 -0.0283 0.38971 -0.084 0.10516 -0.0557 0.19077 -0.14356 0.25681 -0.26367 0.066 -0.12012 0.11331 -0.27198 0.14184 -0.4585 0.0285 -0.18652 0.0424 -0.41113 0.0424 -0.67383l0 -0.89453c0 -0.26562 -0.0138 -0.49219 -0.0424 -0.67969 -0.0285 -0.1875 -0.0758 -0.33984 -0.14102 -0.45703 -0.0644 -0.11719 -0.1492 -0.20312 -0.25275 -0.25781 -0.10437 -0.0547 -0.23071 -0.082 -0.37991 -0.082l-0.27801 0z"
-            android:fillColor="#FFFFFF" />
-    </group>
-</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_ld.xml b/core/res/res/drawable/ic_signal_wifi_badged_ld.xml
deleted file mode 100644
index e2971aa..0000000
--- a/core/res/res/drawable/ic_signal_wifi_badged_ld.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:viewportWidth="18"
-    android:viewportHeight="18"
-    android:width="26dp"
-    android:height="24dp">
-    <group
-        android:translateX="386"
-        android:translateY="-298">
-        <path
-            android:pathData="M-371.33557 310.98651l0 -5.68701 1.39068 0c0.27848 0 0.53336 0.0532 0.76465 0.16016 0.2313 0.10693 0.42954 0.2622 0.59568 0.46679 0.16519 0.20459 0.29357 0.45557 0.38421 0.75391 0.0906 0.29834 0.13593 0.63867 0.13593 1.02148l0 0.88672c0 0.38281 -0.0453 0.72363 -0.13593 1.021 -0.0906 0.29785 -0.21902 0.5498 -0.38421 0.7539 -0.16614 0.20411 -0.36628 0.35938 -0.59946 0.46485 -0.23316 0.10547 -0.49182 0.1582 -0.77786 0.1582l-1.37369 0zm1.06879 -4.76904l0 3.85107 0.26333 0c0.15491 0 0.28452 -0.0283 0.38971 -0.084 0.10516 -0.0557 0.19077 -0.14356 0.25681 -0.26367 0.066 -0.12012 0.11331 -0.27198 0.14184 -0.4585 0.0285 -0.18652 0.0424 -0.41113 0.0424 -0.67383l0 -0.89453c0 -0.26562 -0.0138 -0.49219 -0.0424 -0.67969 -0.0285 -0.1875 -0.0758 -0.33984 -0.14102 -0.45703 -0.0644 -0.11719 -0.1492 -0.20312 -0.25275 -0.25781 -0.10437 -0.0547 -0.23071 -0.082 -0.37991 -0.082l-0.27801 0z"
-            android:fillColor="#FFFFFF" />
-        <path
-            android:pathData="M-373.13333 310.13333l1.33334 0 0 0.86667 -2.46667 0 0 -5.66666 1.13333 0 0 4.79999z"
-            android:fillColor="#FFFFFF" />
-    </group>
-</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_sd.xml b/core/res/res/drawable/ic_signal_wifi_badged_sd.xml
deleted file mode 100644
index b073be3..0000000
--- a/core/res/res/drawable/ic_signal_wifi_badged_sd.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:viewportWidth="18"
-    android:viewportHeight="18"
-    android:width="26dp"
-    android:height="24dp">
-    <group
-        android:translateX="386"
-        android:translateY="-298">
-        <path
-            android:pathData="M-371.33557 310.98651l0 -5.68701 1.39068 0c0.27848 0 0.53336 0.0532 0.76465 0.16016 0.2313 0.10693 0.42954 0.2622 0.59568 0.46679 0.16519 0.20459 0.29357 0.45557 0.38421 0.75391 0.0906 0.29834 0.13593 0.63867 0.13593 1.02148l0 0.88672c0 0.38281 -0.0453 0.72363 -0.13593 1.021 -0.0906 0.29785 -0.21902 0.5498 -0.38421 0.7539 -0.16614 0.20411 -0.36628 0.35938 -0.59946 0.46485 -0.23316 0.10547 -0.49182 0.1582 -0.77786 0.1582l-1.37369 0zm1.06879 -4.76904l0 3.85107 0.26333 0c0.15491 0 0.28452 -0.0283 0.38971 -0.084 0.10516 -0.0557 0.19077 -0.14356 0.25681 -0.26367 0.066 -0.12012 0.11331 -0.27198 0.14184 -0.4585 0.0285 -0.18652 0.0424 -0.41113 0.0424 -0.67383l0 -0.89453c0 -0.26562 -0.0138 -0.49219 -0.0424 -0.67969 -0.0285 -0.1875 -0.0758 -0.33984 -0.14102 -0.45703 -0.0644 -0.11719 -0.1492 -0.20312 -0.25275 -0.25781 -0.10437 -0.0547 -0.23071 -0.082 -0.37991 -0.082l-0.27801 0z"
-            android:fillColor="#FFFFFF" />
-        <path
-            android:pathData="M-372.87598 309.47461c0 -0.10645 -0.01 -0.20117 -0.0303 -0.28223 -0.0205 -0.0811 -0.0576 -0.15527 -0.11035 -0.22265 -0.0537 -0.0674 -0.12598 -0.12891 -0.21777 -0.18457 -0.0908 -0.0566 -0.20704 -0.11231 -0.34668 -0.16797 -0.24903 -0.0889 -0.47657 -0.18457 -0.68165 -0.28614 -0.20605 -0.10156 -0.38281 -0.2207 -0.53027 -0.35839 -0.14746 -0.13721 -0.26172 -0.29639 -0.34277 -0.47803 -0.0811 -0.18164 -0.12207 -0.39697 -0.12207 -0.646 0 -0.23144 0.042 -0.4414 0.12793 -0.63086 0.085 -0.18945 0.20312 -0.35205 0.35644 -0.48779 0.15235 -0.13623 0.33496 -0.2417 0.54883 -0.31641 0.21289 -0.0752 0.44824 -0.1123 0.70508 -0.1123 0.2666 0 0.50683 0.0425 0.71973 0.12744 0.21386 0.0854 0.39648 0.2041 0.54687 0.35645 0.15137 0.15234 0.26758 0.333 0.34766 0.54101 0.0791 0.2085 0.11914 0.43604 0.11914 0.68262l-1.07422 0c0 -0.11963 -0.0127 -0.22998 -0.0381 -0.33154 -0.0254 -0.10205 -0.0654 -0.18897 -0.12011 -0.26123 -0.0547 -0.0723 -0.125 -0.12891 -0.20997 -0.16993 -0.085 -0.0405 -0.1875 -0.0605 -0.30664 -0.0605 -0.11132 0 -0.208 0.0171 -0.29004 0.0513 -0.0811 0.0342 -0.14843 0.0815 -0.20117 0.14111 -0.0537 0.0596 -0.0928 0.13037 -0.11816 0.21143 -0.0254 0.0815 -0.0381 0.16894 -0.0381 0.26318 0 0.0937 0.0166 0.17725 0.0508 0.24951 0.0342 0.0723 0.0869 0.14014 0.15625 0.20361 0.0703 0.064 0.16016 0.125 0.26856 0.18311 0.10937 0.0586 0.23926 0.11963 0.38965 0.1831 0.24316 0.084 0.46093 0.17823 0.65136 0.2837 0.19043 0.10498 0.35059 0.22998 0.48047 0.37158 0.12891 0.14258 0.22754 0.30762 0.29493 0.49316 0.0674 0.1875 0.10156 0.40235 0.10156 0.64649 0 0.24121 -0.04 0.458 -0.12012 0.64843 -0.0801 0.19043 -0.19434 0.35059 -0.3418 0.48145 -0.14746 0.13086 -0.32617 0.23144 -0.53711 0.30176 -0.21093 0.0693 -0.44726 0.10449 -0.70898 0.10449 -0.23633 0 -0.46777 -0.0361 -0.69531 -0.1084 -0.22754 -0.0723 -0.43067 -0.18359 -0.61035 -0.33203 -0.17872 -0.14844 -0.32325 -0.33691 -0.43262 -0.56543 -0.10938 -0.22852 -0.16309 -0.49805 -0.16309 -0.80859l1.07813 0c0 0.17089 0.0166 0.31445 0.0498 0.43261 0.0332 0.11817 0.084 0.21485 0.15235 0.29004 0.0684 0.0752 0.15429 0.12891 0.25683 0.16211 0.10352 0.0332 0.22461 0.0488 0.36426 0.0488 0.11719 0 0.21582 -0.0156 0.2959 -0.0488 0.0801 -0.0332 0.14355 -0.0781 0.19238 -0.13574 0.0478 -0.0566 0.082 -0.125 0.10254 -0.2041 0.0205 -0.0781 0.0303 -0.16504 0.0303 -0.25879z"
-            android:fillColor="#FFFFFF" />
-    </group>
-</vector>
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 7dee2af..ded2b35 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -31,7 +31,7 @@
             android:layout_alwaysShow="true"
             android:elevation="8dp"
             android:paddingStart="16dp"
-            android:background="@color/white" >
+            android:background="?attr/colorBackgroundFloating" >
         <TextView android:id="@+id/profile_button"
                   android:layout_width="wrap_content"
                   android:layout_height="48dp"
@@ -73,7 +73,7 @@
             android:id="@+id/resolver_list"
             android:clipToPadding="false"
             android:scrollbarStyle="outsideOverlay"
-            android:background="@color/white"
+            android:background="?attr/colorBackgroundFloating"
             android:elevation="8dp"
             android:listSelector="@color/transparent"
             android:divider="@null"
@@ -84,7 +84,7 @@
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:layout_alwaysShow="true"
-              android:background="@color/white"
+              android:background="?attr/colorBackgroundFloating"
               android:text="@string/noApplications"
               android:padding="32dp"
               android:gravity="center"
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 5ba1cf2..0f53549 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -111,6 +111,7 @@
         android:background="@null"
         android:layout_width="@dimen/notification_header_expand_icon_size"
         android:layout_height="@dimen/notification_header_expand_icon_size"
+        android:layout_marginStart="4dp"
         android:paddingTop="@dimen/notification_expand_button_padding_top"
         android:visibility="gone"
         android:contentDescription="@string/expand_button_content_description_collapsed"
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index 373354a..0697b0e 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -129,6 +129,18 @@
             android:enabled="false"
             android:text="@string/activity_resolver_use_always"
             android:onClick="onButtonClick" />
+
+        <Button
+            android:id="@+id/button_app_settings"
+            android:layout_width="wrap_content"
+            android:layout_gravity="end"
+            android:maxLines="2"
+            android:minHeight="@dimen/alert_dialog_button_bar_height"
+            style="?attr/buttonBarPositiveButtonStyle"
+            android:layout_height="wrap_content"
+            android:enabled="false"
+            android:text="@string/activity_resolver_app_settings"
+            android:onClick="onButtonClick" />
     </LinearLayout>
 
 </com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 266e846..3f23da6 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Laat die program toe om toegang tot die foonfunksies van die toestel te verkry. Hierdie toestemming laat die program toe om te bepaal wat die foonnommer en toestel-IDs is, of die oproep aan die gang is, en die afgeleë nommer wat deur \'n oproep verbind word."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"roeteer oproepe deur die stelsel"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Laat die program toe om sy oproepe deur die stelsel te stuur om die oproepervaring te verbeter."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"sien en beheer oproepe deur die stelsel."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Laat die program toe om deurlopende oproepe op die toestel te sien en te beheer. Dit sluit inligting in soos oproepnommers vir oproepe en die toedrag van die oproepe."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"gaan voort met \'n oproep uit \'n ander program"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Laat die program toe om \'n oproep voort te sit wat in \'n ander program begin is."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"lees foonnommers"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Laat die program toe om met kortveldkommunikasie- (NFC) merkers, kaarte en lesers te kommunikeer."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"deaktiveer jou skermslot"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Laat die program toe om die sleutelslot en enige verwante wagwoordsekuriteit te deaktiveer. Byvoorbeeld, die foon deaktiveer die sleutelslot wanneer ’n oproep inkom, en atkiveer dit dan weer wanneer die oproep eindig."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"kry en versoek skermslotkompleksiteit"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Laat die program toe om die skermslot-kompleksiteitvlak (hoog, medium, laag of geen) te leer, wat die moontlike omvang van die lengte en soort skermslot aandui. Hierdie program kan ook aan gebruikers voorstel dat hulle die skermslot na \'n sekere vlak toe opdateer, maar gebruikers kan dit vrylik ignoreer en weggaan. Let daarop dat die skermslot nie in skoonteks geberg word nie sodat die program nie die presiese wagwoord ken nie."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"gebruik biometriese hardeware"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Laat die program toe om biometriese hardeware vir stawing te gebruik"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"bestuur vingerafdrukhardeware"</string>
@@ -1086,7 +1090,7 @@
     <string name="email" msgid="4560673117055050403">"E-pos"</string>
     <string name="email_desc" msgid="3638665569546416795">"Stuur e-pos aan gekose adres"</string>
     <string name="dial" msgid="1253998302767701559">"Bel"</string>
-    <string name="dial_desc" msgid="6573723404985517250">"Bel gekose foonnommer"</string>
+    <string name="dial_desc" msgid="6573723404985517250">"Bel dié foonnommer"</string>
     <string name="map" msgid="5441053548030107189">"Kaart"</string>
     <string name="map_desc" msgid="1836995341943772348">"Soek geselekteerde adres"</string>
     <string name="browse" msgid="1245903488306147205">"Maak oop"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet dateer tans oop …"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Toestel dateer tans op …"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Foon begin tans …"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android begin tans …"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet begin tans …"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Toestel begin tans …"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimeer tans berging."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tik om alle netwerke te sien"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Koppel"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Alle netwerke"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Gekoppel aan Wi‑Fi-netwerk wat voorgestel is deur <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Wil jy <xliff:g id="NAME">%s</xliff:g> toelaat om netwerke vir jou voor te stel?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ja"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nee"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi sal outomaties aanskakel"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Wanneer jy naby \'n gestoorde hoëgehaltenetwerk is"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Moenie weer aanskakel nie"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index f3b4df7..ca1aabd 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"መተግበሪያው የመሳሪያውን የስልክ ባህሪያት ላይ እንዲደርስ ይፈቅድለታል። ይህ ፈቃድ መተግበሪያው የስልክ ቁጥሩን እና የመሳሪያውን መታወቂያዎች፣ ጥሪ የነቃ እንደሆነ፣ እና በጥሪ የተገናኘውን የሩቅ ቁጥር እንዲወስን ይፈቅድለታል።"</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ጥሪዎችን በስርዓቱ በኩል አዙር"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"መተግበሪያው የጥሪ ተሞክሮን እንዲያሻሽል ጥሪዎቹን በስርዓቱ በኩል እንዲያዞር ያስችለዋል።"</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"በሥርዓቱ በኩል ጥሪዎችን ይመልከቱ እና ይቆጣጠሩ።"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"መተግበሪያው በመሣሪያው ላይ በመካሄድ ላይ ያሉ ጥሪዎችን እንዲመለከት እና እንዲቆጣጠር ይፈቅድለታል። ይህ ለጥሪዎች እንደ የጥሪ ቁጥሮች እና የጥሪዎች ሁኔታ የመሰለ መረጃን ያካትታል።"</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"በሌላ መተግበሪያ የተጀመረ ጥሪን መቀጠል"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"መተግበሪያው በሌላ መተግበሪያ ውስጥ የተጀመረ ጥሪ እንዲቀጥል ያስችለዋል።"</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ስልክ ቁጥሮች ያንብቡ"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"ከቅርብ ግኑኙነት መስክ (NFC) መለያዎች፣ ካርዶች እና አንባቢ ጋር ለማገናኘት ለመተግበሪያው ይፈቅዳሉ።"</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"የማያ ገጽዎን መቆለፊያ ያሰናክሉ"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"መተግበሪያው መቆለፊያውና ማንኛውም የተጎዳኘ የይለፍ ቃል ደህንነት እንዲያሰናክል ይፈቅድለታል። ለምሳሌ ስልኩ ገቢ የስልክ ጥሪ በሚቀበልበት ጊዜ መቆለፊያውን ያሰናክልና ከዚያም ጥሪው ሲጠናቀቅ መቆለፊያውን በድጋሚ ያነቃዋል።"</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"የማያ ገጽ መቆለፊያ ውስብስብነት ያግኙ እና ይጠይቁ"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"መተግበሪያው የማያ ገጽ መቆለፊያው ውስብስብነት ደረጃ (ከፍተኛ፣ መካከለኛ፣ ዝቅተኛ ወይም ምንም) እንዲያውቅ ያስችለዋል፣ ይህም ሊሆኑ የሚችለው የማያ ገጽ መቆለፊያው ርዝመት እና አይነት ክልል ያመለክታል። መተግበሪያው እንዲሁም ለተጠቃሚዎች የማያ ገጽ መቆለፊያውን ወደተወሰነ ደረጃ እንዲያዘምኑት ሊጠቁማቸው ይችላል። የማያ ገጽ መቆለፊያው በስነጣ አልባ ጽሑፍ እንደማይከማች ልብ ይበሉ፣ በዚህም መተግበሪያው ትክክለኛውን የይለፍ ቃል አያውቅም።"</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"ባዮሜትራዊ ሃርድዌርን መጠቀም"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"መተግበሪያው የባዮሜትራዊ ሃርድዌር ለማረጋገጥ ስራ እንዲጠቀም ያስችለዋል"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"የጣት አሻራ ሃርድዌርን አስተዳድር"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ጡባዊ በመዘመን ላይ ነው…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"መሣሪያ በመዘመን ላይ ነው…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"ስልክ በመጀመር ላይ ነው…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android በመጀመር ላይ ነው…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"ጡባዊ በመጀመር ላይ ነው…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"መሣሪያ በመጀመር ላይ ነው…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"ማከማቻን በማመቻቸት ላይ።"</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"ሁሉንም አውታረ መረቦችን ለማየት መታ ያድርጉ"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"አገናኝ"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"ሁሉም አውታረ መረቦች"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"በ<xliff:g id="NAME">%s</xliff:g> ከተጠቆመው Wi-Fi ጋር ተገናኝቷል"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"<xliff:g id="NAME">%s</xliff:g> አውታረ መረቦችን ለእርስዎ እንዲጠቁሙ ይፈልጋሉ?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"አዎ"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"አይ"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi በራስ-ሰር ይበራል"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"ከፍተኛ ጥራት ያለው የተቀመጠ አውታረ መረብ አቅራቢያ ሲሆኑ"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"መልሰህ አታብራ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 0789013..4557563 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -313,7 +313,7 @@
     <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"‏هل تريد السماح للتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالتعرّف على نشاطك البدني؟"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"الكاميرا"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"التقاط صور وتسجيل فيديو"</string>
-    <string name="permgrouprequest_camera" msgid="1299833592069671756">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالتقاط الصور وتسجيل الفيديو؟"</string>
+    <string name="permgrouprequest_camera" msgid="1299833592069671756">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالتقاط صور وتسجيل فيديو؟"</string>
     <string name="permgrouplab_calllog" msgid="8798646184930388160">"سجلّ المكالمات"</string>
     <string name="permgroupdesc_calllog" msgid="3006237336748283775">"قراءة سجلّ المكالمات الهاتفية والكتابة إليه"</string>
     <string name="permgrouprequest_calllog" msgid="8487355309583773267">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالدخول إلى سجلات مكالماتك الهاتفية؟"</string>
@@ -459,6 +459,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"للسماح للتطبيق بالدخول إلى ميزات الهاتف في الجهاز. ويتيح هذا الإذن للتطبيق تحديد رقم الهاتف ومعرّفات الجهاز، وما إذا كانت هناك مكالمة نشطة والرقم البعيد الذي تم الاتصال به في المكالمة."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"توجيه المكالمات من خلال النظام"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"يسمح للتطبيق بتوجيه المكالمات من خلال النظام لتحسين تجربة الاتصال."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"رؤية المكالمات والتحكّم فيها من خلال النظام"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"يستطيع التطبيق رؤية المكالمات الجارية على الجهاز والتحكّم فيها. ويشمل ذلك معلومات مثل الأرقام وحالة المكالمات."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"مواصلة مكالمة من تطبيق آخر"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"السماح للتطبيق بمواصلة مكالمة بدأت في تطبيق آخر."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"قراءة أرقام الهواتف"</string>
@@ -519,6 +521,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"‏للسماح للتطبيق بالاتصال بعلامات الاتصال قريب المدى (NFC)، والبطاقات وبرامج القراءة."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"إيقاف قفل الشاشة"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"للسماح للتطبيق بإيقاف تأمين المفاتيح وأي أمان لكلمة مرور مرتبطة. على سبيل المثال، يعطل الهاتف تأمين المفاتيح عند استقبال مكالمة هاتفية واردة، ثم يعيد تفعيل تأمين المفاتيح عند انتهاء المكالمة."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"طلب قفل شاشة صعب والحصول عليه"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"للسماح للتطبيق بمعرفة مستوى صعوبة قفل الشاشة (عالي أو متوسط أو منخفض الصعوبة أو بدون)، والذي يحدّد النطاق المحتمل لطول ونوع قفل الشاشة. ويمكن أن يقترح التطبيق للمستخدمين أيضًا تعديل قفل الشاشة إلى مستوى معيّن، ولهم مطلق الحرية في تجاهل هذا الاقتراح ورفضه. وتجدر الإشارة إلى أنه لا يتم حفظ قفل الشاشة في نص عادي، لذا لا يعرف التطبيق كلمة المرور تحديدًا."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"استخدام الأجهزة البيومترية"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"للسماح للتطبيق باستخدام الأجهزة البيومترية للمصادقة"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"لإدارة أجهزة بصمة الإصبع"</string>
@@ -1163,15 +1167,15 @@
     <string name="deleteText" msgid="6979668428458199034">"حذف"</string>
     <string name="inputMethod" msgid="1653630062304567879">"طريقة الإرسال"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"إجراءات النص"</string>
-    <string name="email" msgid="4560673117055050403">"بريد إلكتروني"</string>
+    <string name="email" msgid="4560673117055050403">"إرسال بريد إلكتروني"</string>
     <string name="email_desc" msgid="3638665569546416795">"مراسلة العنوان المختار عبر البريد الإلكتروني"</string>
     <string name="dial" msgid="1253998302767701559">"اتصال"</string>
     <string name="dial_desc" msgid="6573723404985517250">"الاتصال برقم الهاتف المختار"</string>
-    <string name="map" msgid="5441053548030107189">"خريطة"</string>
+    <string name="map" msgid="5441053548030107189">"فتح تطبيق خرائط"</string>
     <string name="map_desc" msgid="1836995341943772348">"تحديد موقع العنوان المختار"</string>
     <string name="browse" msgid="1245903488306147205">"فتح"</string>
     <string name="browse_desc" msgid="8220976549618935044">"‏فتح عنوان URL المختار"</string>
-    <string name="sms" msgid="4560537514610063430">"رسالة"</string>
+    <string name="sms" msgid="4560537514610063430">"إرسال رسائل قصيرة"</string>
     <string name="sms_desc" msgid="7526588350969638809">"مراسلة رقم الهاتف المختار"</string>
     <string name="add_contact" msgid="7867066569670597203">"إضافة"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"إضافة إلى جهات الاتصال"</string>
@@ -1261,6 +1265,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"جارٍ تحديث الجهاز اللوحي…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"جارٍ تحديث الجهاز…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"جارٍ بدء تشغيل الهاتف…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"‏جارٍ تشغيل Android…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"جارٍ بدء تشغيل الجهاز اللوحي…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"جارٍ بدء تشغيل الجهاز…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"جارٍ تحسين مساحة التخزين."</string>
@@ -1327,6 +1332,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"انقر للاطلاع على جميع الشبكات"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"اتصال"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"جميع الشبكات"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"‏تم الاتصال بشبكة Wi‑Fi التي اقترحها <xliff:g id="NAME">%s</xliff:g>."</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"هل تريد السماح لـ <xliff:g id="NAME">%s</xliff:g> باقتراح شبكات لك؟"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"نعم"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"لا"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"‏سيتم تشغيل شبكة Wi-Fi تلقائيًا."</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"عندما تكون بالقرب من شبكة محفوظة عالية الجودة"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"عدم إعادة التشغيل"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index f716579..a4f950a 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -35,10 +35,10 @@
     <string name="mmiError" msgid="5154499457739052907">"সংযোগৰ সমস্যা বা MMI ক\'ড মান্য নহয়।"</string>
     <string name="mmiFdnError" msgid="5224398216385316471">"কেৱল ফিক্সড ডায়েলিং নম্বৰৰ বাবে কার্য সীমাবদ্ধ কৰা হৈছে।"</string>
     <string name="mmiErrorWhileRoaming" msgid="762488890299284230">"আপুনি ৰ\'মিঙত থকাৰ সময়ত কল ফৰৱাৰ্ডিঙৰ ছেটিংসমূহ সলনি কৰিব নোৱাৰি।"</string>
-    <string name="serviceEnabled" msgid="8147278346414714315">"সেৱা সক্ষম কৰা হ\'ল।"</string>
-    <string name="serviceEnabledFor" msgid="6856228140453471041">"সেৱা সক্ষম কৰা হ\'ল:"</string>
-    <string name="serviceDisabled" msgid="1937553226592516411">"সেৱা অক্ষম কৰা হ\'ল।"</string>
-    <string name="serviceRegistered" msgid="6275019082598102493">"পঞ্জীকৰণ সফল হ\'ল।"</string>
+    <string name="serviceEnabled" msgid="8147278346414714315">"সেৱা সক্ষম কৰা হ’ল।"</string>
+    <string name="serviceEnabledFor" msgid="6856228140453471041">"সেৱা সক্ষম কৰা হ’ল:"</string>
+    <string name="serviceDisabled" msgid="1937553226592516411">"সেৱা অক্ষম কৰা হ’ল।"</string>
+    <string name="serviceRegistered" msgid="6275019082598102493">"পঞ্জীকৰণ সফল হ’ল।"</string>
     <string name="serviceErased" msgid="1288584695297200972">"সফলভাৱে মচা হ\'ল৷"</string>
     <string name="passwordIncorrect" msgid="7612208839450128715">"ভুল পাছৱৰ্ড৷"</string>
     <string name="mmiComplete" msgid="8232527495411698359">"MMI সম্পূৰ্ণ হ’ল।"</string>
@@ -66,7 +66,7 @@
     <string name="PwdMmi" msgid="7043715687905254199">"পাছৱর্ড সলনি কৰা"</string>
     <string name="PinMmi" msgid="3113117780361190304">"পিন সলনি কৰা"</string>
     <string name="CnipMmi" msgid="3110534680557857162">"বর্তমান কল কৰা নম্বৰ"</string>
-    <string name="CnirMmi" msgid="3062102121430548731">"বর্তমান কল কৰা নম্বৰ সীমিত কৰা হ\'ল"</string>
+    <string name="CnirMmi" msgid="3062102121430548731">"বর্তমান কল কৰা নম্বৰ সীমিত কৰা হ’ল"</string>
     <string name="ThreeWCMmi" msgid="9051047170321190368">"ত্ৰিপক্ষীয় কলিং"</string>
     <string name="RuacMmi" msgid="7827887459138308886">"আমনিদায়ক কল প্ৰত্যাখ্যান"</string>
     <string name="CndMmi" msgid="3116446237081575808">"কল কৰা নম্বৰত ডেলিভাৰী"</string>
@@ -77,20 +77,20 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"কলাৰ আইডি সীমিত নকৰিবলৈ পূর্বনির্ধাৰণ কৰা হৈছে। পৰৱৰ্তী কল: সীমিত কৰা হোৱা নাই"</string>
     <string name="serviceNotProvisioned" msgid="8614830180508686666">"সুবিধা যোগান ধৰা হোৱা নাই।"</string>
     <string name="CLIRPermanent" msgid="3377371145926835671">"আপুনি কলাৰ আইডি ছেটিং সলনি কৰিব নোৱাৰে।"</string>
-    <string name="RestrictedOnDataTitle" msgid="5221736429761078014">"কোনো ম\'বাইল ডেটা সেৱা নাই"</string>
+    <string name="RestrictedOnDataTitle" msgid="5221736429761078014">"কোনো ম’বাইল ডেটা সেৱা নাই"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="6855466023161191166">"জৰুৰীকালীন কল কৰাৰ সুবিধা উপলব্ধ নহয়"</string>
     <string name="RestrictedOnNormalTitle" msgid="3179574012752700984">"কোনো ভইচ সেৱা নাই"</string>
     <string name="RestrictedOnAllVoiceTitle" msgid="8037246983606545202">"ধ্বনি সেৱা বা জৰুৰীকালীন কলৰ সেৱা উপলব্ধ নহয়"</string>
     <string name="RestrictedStateContent" msgid="6538703255570997248">"আপোনাৰ বাহকে সাময়িকভাৱে অফ কৰি থৈছে"</string>
     <string name="RestrictedStateContentMsimTemplate" msgid="673416791370248176">"আপোনাৰ বাহকে <xliff:g id="SIMNUMBER">%d</xliff:g> ছিমৰ বাবে সাময়িকভাৱে অফ কৰিছে"</string>
-    <string name="NetworkPreferenceSwitchTitle" msgid="6982395015324165258">"ম\'বাইল নেটৱৰ্কৰ লগত সংযোগ কৰিব পৰা নাই"</string>
+    <string name="NetworkPreferenceSwitchTitle" msgid="6982395015324165258">"ম’বাইল নেটৱৰ্কৰ লগত সংযোগ কৰিব পৰা নাই"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="509327194863482733">"পচন্দৰ নেটৱৰ্ক সলনি কৰি চেষ্টা কৰি চাওক। সলনি কৰিবলৈ টিপক।"</string>
     <string name="EmergencyCallWarningTitle" msgid="813380189532491336">"জৰুৰীকালীন কল কৰাৰ সুবিধা উপলব্ধ নহয়"</string>
     <string name="EmergencyCallWarningSummary" msgid="1899692069750260619">"ৱাই-ফাইৰ জৰিয়তে জৰুৰীকালীন কল কৰিব নোৱাৰি"</string>
     <string name="notification_channel_network_alert" msgid="4427736684338074967">"সতৰ্কবাণীসমূহ"</string>
     <string name="notification_channel_call_forward" msgid="2419697808481833249">"কল ফৰৱাৰ্ডিং"</string>
     <string name="notification_channel_emergency_callback" msgid="6686166232265733921">"জৰুৰীকালীন ক\'লবেক ম\'ড"</string>
-    <string name="notification_channel_mobile_data_status" msgid="4575131690860945836">"ম\'বাইল ডেটাৰ স্থিতি"</string>
+    <string name="notification_channel_mobile_data_status" msgid="4575131690860945836">"ম’বাইল ডেটাৰ স্থিতি"</string>
     <string name="notification_channel_sms" msgid="3441746047346135073">"এছএমএছ বার্তাবোৰ"</string>
     <string name="notification_channel_voice_mail" msgid="3954099424160511919">"ভইচমেইলৰ বাৰ্তাসমূহ"</string>
     <string name="notification_channel_wfc" msgid="2130802501654254801">"ৱাই-ফাই কলিং"</string>
@@ -149,7 +149,7 @@
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> ছেকেণ্ডৰ পাছত"</string>
     <string name="cfTemplateRegistered" msgid="5073237827620166285">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ফৰৱাৰ্ড কৰা নহ\'ল"</string>
     <string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ফৰৱাৰ্ড কৰা নহ\'ল"</string>
-    <string name="fcComplete" msgid="3118848230966886575">"সুবিধাৰ ক\'ড সম্পূর্ণ হ\'ল।"</string>
+    <string name="fcComplete" msgid="3118848230966886575">"সুবিধাৰ ক\'ড সম্পূর্ণ হ’ল।"</string>
     <string name="fcError" msgid="3327560126588500777">"সংযোগত সমস্যা হৈছে বা সুবিধাৰ ক\'ড অমান্য।"</string>
     <string name="httpErrorOk" msgid="1191919378083472204">"ঠিক আছে"</string>
     <string name="httpError" msgid="7956392511146698522">"ইণ্টাৰনেট সম্পর্কীয় আসোঁৱাহ হ\'ল৷"</string>
@@ -176,13 +176,13 @@
     <string name="low_memory" product="tv" msgid="516619861191025923">"টিভিৰ সঞ্চয়াগাৰ ভৰি পৰিছে। খালী ঠাই উলিয়াবলৈ কিছুমান ফাইল মচক।"</string>
     <string name="low_memory" product="default" msgid="3475999286680000541">"ফ\'নৰ সঞ্চয়াগাৰত খালী ঠাই নাই। ঠাই খালী কৰিবলৈ কিছুমান ফাইল মচক।"</string>
     <plurals name="ssl_ca_cert_warning" formatted="false" msgid="5106721205300213569">
-      <item quantity="one">প্ৰমাণপত্ৰ প্ৰদানকাৰী কৰ্তৃপক্ষ ইনষ্টল কৰা হ\'ল</item>
-      <item quantity="other">প্ৰমাণপত্ৰ প্ৰদানকাৰী কৰ্তৃপক্ষ ইনষ্টল কৰা হ\'ল</item>
+      <item quantity="one">প্ৰমাণপত্ৰ প্ৰদানকাৰী কৰ্তৃপক্ষ ইনষ্টল কৰা হ’ল</item>
+      <item quantity="other">প্ৰমাণপত্ৰ প্ৰদানকাৰী কৰ্তৃপক্ষ ইনষ্টল কৰা হ’ল</item>
     </plurals>
     <string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"এটা অজ্ঞাত তৃতীয় পক্ষৰদ্বাৰা"</string>
     <string name="ssl_ca_cert_noti_by_administrator" msgid="3541729986326153557">"আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইলৰ প্ৰশাসকে পৰ্যবেক্ষণ কৰি আছে"</string>
     <string name="ssl_ca_cert_noti_managed" msgid="4030263497686867141">"<xliff:g id="MANAGING_DOMAIN">%s</xliff:g>ৰ দ্বাৰা"</string>
-    <string name="work_profile_deleted" msgid="5005572078641980632">"কৰ্মস্থানৰ প্ৰ\'ফাইল মচা হ\'ল"</string>
+    <string name="work_profile_deleted" msgid="5005572078641980632">"কৰ্মস্থানৰ প্ৰ\'ফাইল মচা হ’ল"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"কৰ্মস্থানৰ প্ৰ\'ফাইলৰ প্ৰশাসক এপ্ নাই বা ব্যৱহাৰযোগ্য হৈ থকা নাই। যাৰ ফলত আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইল আৰু ইয়াৰ লগত জড়িত অন্য ডেটাসমূহ মচা হৈছে। সহায়ৰ বাবে আপোনাৰ প্ৰশাসকৰ সৈতে সম্পর্ক কৰক।"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইল এই ডিভাইচটোত আৰু উপলব্ধ নহয়"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"বহুতবাৰ ভুলকৈ পাছৱৰ্ড দিয়া হৈছে"</string>
@@ -203,9 +203,9 @@
     <string name="silent_mode_silent" msgid="319298163018473078">"ৰিংগাৰ অফ আছে"</string>
     <string name="silent_mode_vibrate" msgid="7072043388581551395">"ৰিংগাৰ কম্পন অৱস্থাত আছে"</string>
     <string name="silent_mode_ring" msgid="8592241816194074353">"ৰিংগাৰ অন আছে"</string>
-    <string name="reboot_to_update_title" msgid="6212636802536823850">"Android ছিষ্টেমৰ আপডেট"</string>
-    <string name="reboot_to_update_prepare" msgid="6305853831955310890">"আপডেট সাজু কৰি থকা হৈছে…"</string>
-    <string name="reboot_to_update_package" msgid="3871302324500927291">"পেকেজ আপডেট কৰা প্ৰক্ৰিয়া চলি আছে…"</string>
+    <string name="reboot_to_update_title" msgid="6212636802536823850">"Android ছিষ্টেমৰ আপডে’ট"</string>
+    <string name="reboot_to_update_prepare" msgid="6305853831955310890">"আপডে’ট সাজু কৰি থকা হৈছে…"</string>
+    <string name="reboot_to_update_package" msgid="3871302324500927291">"পেকেজ আপডে’ট কৰা প্ৰক্ৰিয়া চলি আছে…"</string>
     <string name="reboot_to_update_reboot" msgid="6428441000951565185">"ৰিষ্টাৰ্ট কৰা হৈছে…"</string>
     <string name="reboot_to_reset_title" msgid="4142355915340627490">"ফেক্টৰী ডেটা ৰিছেট কৰক"</string>
     <string name="reboot_to_reset_message" msgid="2432077491101416345">"ৰিষ্টার্ট হৈ আছে…"</string>
@@ -314,20 +314,20 @@
     <string name="permgrouplab_aural" msgid="965607064083134896">"সংগীত"</string>
     <string name="permgroupdesc_aural" msgid="4870189506255958055">"আপোনাৰ সংগীত এক্সেছ কৰিবলৈ"</string>
     <string name="permgrouprequest_aural" msgid="6787926123071735620">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক আপোনাৰ সংগীত এক্সেছ কৰিবলৈ দিবনে?"</string>
-    <string name="permgrouplab_visual" msgid="8030190588123857921">"ফট\' আৰু ভিডিঅ’সমূহ"</string>
+    <string name="permgrouplab_visual" msgid="8030190588123857921">"ফট’ আৰু ভিডিঅ’"</string>
     <string name="permgroupdesc_visual" msgid="3415827902566663546">"আপোনাৰ ফট’ আৰু ভিডিঅ’সমূহ এক্সেছ কৰিবলৈ"</string>
     <string name="permgrouprequest_visual" msgid="6907523945030290376">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক আপোনাৰ ফট’ আৰু ভিডিঅ’সমূহ এক্সেছ কৰিবলৈ দিবনে?"</string>
-    <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"ৱিণ্ড\' সমল বিচাৰি উলিয়াওক"</string>
-    <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"আপুনি যোগাযোগ কৰি থকা ৱিণ্ড\'খনৰ সমল পৰীক্ষা কৰক।"</string>
-    <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"স্পৰ্শৰ দ্বাৰা অন্বেষণ কৰাৰ সুবিধা অন কৰক"</string>
-    <string name="capability_desc_canRequestTouchExploration" msgid="7543249041581408313">"টেপ কৰা বস্তুসমূহ ডাঙৰকৈ কোৱা হ\'ব আৰু আঙুলিৰ স্পৰ্শেৰে নিৰ্দেশ দি স্ক্ৰীণ অন্বেষণ কৰিব পাৰিব।"</string>
-    <string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"আপুনি লিখা পাঠ নিৰীক্ষণ কৰক"</string>
-    <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ক্ৰেডিট কাৰ্ডৰ নম্বৰ আৰু পাছৱৰ্ডৰ দৰে ব্যক্তিগত ডেটা অন্তৰ্ভুক্ত হ\'ব পাৰে।"</string>
-    <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ডিছপ্লেৰ বিবৰ্ধন নিয়ন্ত্ৰণ কৰক"</string>
-    <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ডিছপ্লেৰ জুমৰ স্তৰ আৰু অৱস্থান নিয়ন্ত্ৰণ কৰক।"</string>
+    <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"ৱিণ্ড’ সমল বিচাৰি উলিওৱাৰ"</string>
+    <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"আপুনি চাই থকা ৱিণ্ড’খনৰ সমল পৰীক্ষা কৰাৰ।"</string>
+    <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"স্পৰ্শৰ দ্বাৰা অন্বেষণ কৰাৰ সুবিধা অন কৰাৰ"</string>
+    <string name="capability_desc_canRequestTouchExploration" msgid="7543249041581408313">"আঙুলিৰে টিপা বস্তুসমূহ ডাঙৰকৈ কৈ শুনোৱা হ’ব আৰু আঙুলিৰ স্পৰ্শেৰে নিৰ্দেশ দি স্ক্ৰীণ অন্বেষণ কৰিব পাৰিব।"</string>
+    <string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"আপুনি লিখা পাঠ নিৰীক্ষণ কৰাৰ"</string>
+    <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ক্ৰেডিট কাৰ্ডৰ নম্বৰ আৰু পাছৱৰ্ডৰ দৰে ব্যক্তিগত ডেটা ইয়াত অন্তৰ্ভুক্ত।"</string>
+    <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ডিছপ্লে’ৰ বিবৰ্ধন নিয়ন্ত্ৰণ কৰাৰ"</string>
+    <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ডিছপ্লে’ৰ জুমৰ স্তৰ আৰু অৱস্থান নিয়ন্ত্ৰণ কৰাৰ।"</string>
     <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"আঙুলিৰ স্পৰ্শেৰে নিৰ্দেশ কৰা কার্যসমূহ কৰক"</string>
     <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"টেপ কৰা, ছোৱাইপ কৰা, পিঞ্চ কৰা আৰু আঙুলিৰ স্পৰ্শেৰে নিৰ্দেশ কৰা অন্যান্য কাৰ্যসমূহ কৰিব পাৰে।"</string>
-    <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"ফিংগাৰপ্ৰিণ্ট নিৰ্দেশসমূহ"</string>
+    <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"ফিংগাৰপ্ৰিণ্ট নিৰ্দেশ"</string>
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"ডিভাইচটোৰ ফিংগাৰপ্ৰিণ্ট ছেন্সৰত দিয়া নিৰ্দেশ বুজিব পাৰে।"</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"স্থিতি দণ্ড অক্ষম কৰক বা সলনি কৰক"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"স্থিতি দণ্ড অক্ষম কৰিবলৈ বা ছিষ্টেম আইকন আঁতৰাবলৈ এপটোক অনুমতি দিয়ে।"</string>
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"ডিভাইচত থকা ফ\'নৰ সুবিধাসমূহ ব্য়ৱহাৰ কৰিবলৈ এপটোক অনুমতি দিয়ে৷ এই অনুমতিয়ে কোনো কল সক্ৰিয় হৈ থাককেই বা নাথাকক আৰু দূৰবৰ্তী নম্বৰটো কলৰ দ্বাৰা সংযোজিত হওকেই বা নহওক এপটোক ফ\'ন নম্বৰ আৰু ডিভাইচৰ পৰিচয় নিৰ্ধাৰণ কৰিবলৈ অনুমতি দিয়ে৷"</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ছিষ্টেমৰ জৰিয়তে কল কৰিব পাৰে"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"কল কৰাৰ অভিজ্ঞতাক উন্নত কৰিবলৈ এপটোক ছিষ্টেমৰ জৰিয়তে কলসমূহ কৰিবলৈ দিয়ে।"</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ছিষ্টেমৰ জৰিয়তে কলবোৰ চোৱা আৰু নিয়ন্ত্ৰণ কৰা।"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"এপটোক ডিভাইচত চলি থকা কল চাবলৈ আৰু নিয়ন্ত্ৰণ কৰিবলৈ অনুমতি দিয়ে। কলৰ সংখ্যা আৰু কলবোৰৰ স্থিতি ইয়াত অন্তৰ্ভুক্ত হয়।"</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"অইন এটা এপত আৰম্ভ হোৱা কল এটা অব্যাহত ৰাখিব পাৰে"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"এপটোক এনে কল কৰিবলৈ দিয়ে যিটোৰ আৰম্ভণি অইন এটা এপত হৈছিল।"</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ফ\'ন নম্বৰসমূহ পঢ়ে"</string>
@@ -482,7 +484,7 @@
     <string name="permlab_changeTetherState" msgid="5952584964373017960">"টেডাৰিং সংযোগ সলনি কৰক"</string>
     <string name="permdesc_changeTetherState" msgid="1524441344412319780">"টেডাৰ হৈ থকা ইণ্টাৰনেট সংযোগৰ অৱস্থা সলনি কৰিবলৈ এপটোক অনুমতি দিয়ে৷"</string>
     <string name="permlab_accessWifiState" msgid="5202012949247040011">"ৱাই-ফাইৰ সংযোগবোৰ চাওক"</string>
-    <string name="permdesc_accessWifiState" msgid="5002798077387803726">"ৱাই-ফাই সক্ষম কৰা হ\'ল নে নাই আৰু সংযোগ হৈ থকা ৱাই-ফাই ডিভাইচসমূহৰ নামবোৰৰ দৰে ৱাই-ফাইৰ ইণ্টাৰনেট সম্পর্কীয় তথ্য চাবলৈ এপক অনুমতি দিয়ে।"</string>
+    <string name="permdesc_accessWifiState" msgid="5002798077387803726">"ৱাই-ফাই সক্ষম কৰা হ’ল নে নাই আৰু সংযোগ হৈ থকা ৱাই-ফাই ডিভাইচসমূহৰ নামবোৰৰ দৰে ৱাই-ফাইৰ ইণ্টাৰনেট সম্পর্কীয় তথ্য চাবলৈ এপক অনুমতি দিয়ে।"</string>
     <string name="permlab_changeWifiState" msgid="6550641188749128035">"ৱাই-ফাই সংযোগ কৰক আৰু ইয়াৰ সংযোগ বিচ্ছিন্ন কৰক"</string>
     <string name="permdesc_changeWifiState" msgid="7137950297386127533">"এপটোক ৱাই-ফাই এক্সেছ পইণ্টলৈ সংযোগ কৰিবলৈ আৰু তাৰ সংযোগ বিচ্ছিন্ন কৰিবলৈ আৰু ৱাই-ফাই নেটৱৰ্কসমূহৰ বাবে ডিভাইচ কনফিগাৰেশ্বনত সাল-সলনি কৰিবলৈ অনুমতি দিয়ে৷"</string>
     <string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"ৱাই-ফাই মাল্টিকাষ্ট প্ৰচাৰৰ অনুমতি দিয়ক"</string>
@@ -506,7 +508,9 @@
     <string name="permlab_nfc" msgid="4423351274757876953">"নিয়েৰ ফিল্ড কমিউনিকেশ্বন নিয়ন্ত্ৰণ কৰক"</string>
     <string name="permdesc_nfc" msgid="7120611819401789907">"এপটোক নিয়েৰ ফিল্ড কমিউনিকেশ্বন (NFC) টেগ, কাৰ্ড আৰু ৰিডাৰসমূহৰ সৈতে যোগাযোগ কৰিবলৈ অনুমতি দিয়ে।"</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"আপোনাৰ স্ক্ৰীণ ল\'ক অক্ষম কৰক"</string>
-    <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"এপটোক কী ল\'ক আৰু জড়িত হোৱা যিকোনো পাছৱৰ্ডৰ সুৰক্ষা অক্ষম কৰিব দিয়ে৷ উদাহৰণ স্বৰূপে, কোনো অন্তৰ্গামী ফ\'ন কল উঠোৱাৰ সময়ত ফ\'নটোৱে কী-লকটো অক্ষম কৰে, তাৰপিছত কল শেষ হ\'লেই কী লকটো পুনৰ সক্ষম কৰে৷"</string>
+    <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"এপটোক কী ল\'ক আৰু জড়িত হোৱা যিকোনো পাছৱৰ্ডৰ সুৰক্ষা অক্ষম কৰিব দিয়ে৷ উদাহৰণস্বৰূপে, কোনো অন্তৰ্গামী ফ\'ন কল উঠোৱাৰ সময়ত ফ\'নটোৱে কী-লকটো অক্ষম কৰে, তাৰ পিছত কল শেষ হ\'লেই কী লকটো পুনৰ সক্ষম কৰে৷"</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"স্ক্ৰীণ ল’কৰ জটিলতা লাভ কৰক আৰু অনুৰোধ কৰক"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"স্ক্ৰীণ ল’কৰ জটিলতাৰ স্তৰ (উচ্চ, মধ্যম, নিম্ন বা একেবাৰে নাই), যি স্ক্ৰীণ ল’কৰ সম্ভাব্য দৈৰ্ঘ্য বা স্ক্ৰীণ ল’কৰ প্ৰকাৰ দৰ্শায় সেইবোৰ শিকিবলৈ এপক অনুমতি দিয়ে। লগতে এপটোৱে ব্যৱহাৰকাৰীক স্ক্ৰীণ ল’কটো এটা নিৰ্দিষ্ট স্তৰলৈ আপডে’ট কৰিবলৈ পৰামৰ্শ দিব পাৰে যিটো ব্যৱহাৰকাৰীয়ে উপেক্ষা কৰি পৰৱর্তী পৃষ্ঠালৈ যাব পাৰে। মনত ৰাখিব যে স্ক্ৰীণ ল’কটো সাধাৰণ পাঠ হিচাপে সঞ্চয় কৰা নহয় সেয়ে এপটোৱে সঠিক পাছৱৰ্ডটো জানিব নোৱাৰে।"</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"বায়োমেট্ৰিক হাৰ্ডৱেৰ ব্য়ৱহাৰ কৰক"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"বিশ্বাসযোগ্য়তা প্ৰমাণীকৰণৰ বাবে এপক বায়োমেট্ৰিক হাৰ্ডৱেৰ ব্য়ৱহাৰ কৰিবলৈ অনুমতি দিয়ে"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"ফিংগাৰপ্ৰিণ্ট হাৰ্ডৱেৰ পৰিচালনা কৰিব পাৰে"</string>
@@ -542,15 +546,15 @@
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
     <string name="fingerprint_authenticated" msgid="5309333983002526448">"ফিংগাৰপ্ৰিণ্টৰ সত্যাপন কৰা হ’ল"</string>
-    <string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ\'ল"</string>
-    <string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ\'ল, অনুগ্ৰহ কৰি ‘নিশ্চিত কৰক’ বুটামটো টিপক"</string>
+    <string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ’ল"</string>
+    <string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ’ল, অনুগ্ৰহ কৰি ‘নিশ্চিত কৰক’ বুটামটো টিপক"</string>
     <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ফিংগাৰপ্ৰিণ্ট হাৰ্ডৱেৰ নাই।"</string>
     <string name="fingerprint_error_no_space" msgid="1055819001126053318">"ফিংগাৰপ্ৰিণ্ট সঞ্চয় কৰিব পৰা নগ\'ল। পূর্বে সঞ্চিত ফিংগাৰপ্ৰিণ্ট এটা আঁতৰাওক।"</string>
     <string name="fingerprint_error_timeout" msgid="3927186043737732875">"ফিংগাৰপ্ৰিণ্ট গ্ৰহণৰ সময়সীমা উকলি গৈছে। আকৌ চেষ্টা কৰক।"</string>
-    <string name="fingerprint_error_canceled" msgid="4402024612660774395">"ফিংগাৰপ্ৰিণ্ট কাৰ্য বাতিল কৰা হ\'ল।"</string>
+    <string name="fingerprint_error_canceled" msgid="4402024612660774395">"ফিংগাৰপ্ৰিণ্ট কাৰ্য বাতিল কৰা হ’ল।"</string>
     <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"ব্যৱহাৰকাৰীয়ে ফিংগাৰপ্ৰিণ্ট ক্ৰিয়া বাতিল কৰিছে।"</string>
     <string name="fingerprint_error_lockout" msgid="5536934748136933450">"অত্যধিক ভুল প্ৰয়াস। কিছুসময়ৰ পাছত আকৌ চেষ্টা কৰক।"</string>
-    <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"অত্যধিক প্ৰয়াস। ফিংগাৰপ্ৰিণ্ট ছেন্সৰ অক্ষম কৰা হ\'ল।"</string>
+    <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"অত্যধিক প্ৰয়াস। ফিংগাৰপ্ৰিণ্ট ছেন্সৰ অক্ষম কৰা হ’ল।"</string>
     <string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"আকৌ চেষ্টা কৰক।"</string>
     <string name="fingerprint_error_no_fingerprints" msgid="7654382120628334248">"কোনো ফিংগাৰপ্ৰিণ্ট যোগ কৰা নহ\'ল।"</string>
     <string name="fingerprint_error_hw_not_present" msgid="5729436878065119329">"এই ডিভাইচটোত ফিংগাৰপ্ৰিণ্ট ছেন্সৰ নাই"</string>
@@ -593,7 +597,7 @@
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"ছিংকৰ ছেটিংসমূহ পঢ়ক"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"একাউণ্টৰ ছিংক ছেটিংবোৰ পঢ়িবলৈ এপক অনুমতি দিয়ে। যেনে, People এপ কোনো একাউণ্টত ছিংক কৰা হৈছে নে নাই সেয়া নির্ধাৰণ কৰিব পাৰে।"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ছিংকক অন আৰু অফ ট\'গল কৰক"</string>
-    <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"এপটোক কোনো একাউণ্টৰ ছিংক সম্পৰ্কীয় ছেটিংসমূহ সংশোধন কৰিবলৈ অনুমতি দিয়ে৷ উদাহৰণ স্বৰূপে, এই কাৰ্যক কোনো একাউণ্টৰ জৰিয়তে People এপটোৰ ছিংক সক্ষম কৰিবলৈ ব্যৱহাৰ কৰিব পাৰি৷"</string>
+    <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"এপটোক কোনো একাউণ্টৰ ছিংক সম্পৰ্কীয় ছেটিংসমূহ সংশোধন কৰিবলৈ অনুমতি দিয়ে৷ উদাহৰণস্বৰূপে, এই কাৰ্যক কোনো একাউণ্টৰ জৰিয়তে People এপটোৰ ছিংক সক্ষম কৰিবলৈ ব্যৱহাৰ কৰিব পাৰি৷"</string>
     <string name="permlab_readSyncStats" msgid="7396577451360202448">"ছিংকৰ পৰিসংখ্যা পঢ়ক"</string>
     <string name="permdesc_readSyncStats" msgid="1510143761757606156">"ছিংকৰ কাৰ্যক্ৰমসমূহৰ ইতিহাস আৰু ছিংক কৰা ডেটাৰ পৰিমাণসহ কোনো একাউণ্টৰ ছিংকৰ তথ্য পঢ়িবলৈ এপক অনুমতি দিয়ে।"</string>
     <string name="permlab_sdcardRead" msgid="1438933556581438863">"আপোনাৰ শ্বেয়াৰ কৰি ৰখা সঞ্চয়াগাৰৰ সমল পঢ়িব পাৰে"</string>
@@ -679,7 +683,7 @@
     <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"স্ক্ৰীণ লকৰ কিছুমান সুবিধা ব্যৱহাৰ হোৱাত বাধা দিয়ক।"</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"ঘৰ"</item>
-    <item msgid="869923650527136615">"ম\'বাইল"</item>
+    <item msgid="869923650527136615">"ম’বাইল"</item>
     <item msgid="7897544654242874543">"কৰ্মস্থান"</item>
     <item msgid="1103601433382158155">"কর্মস্থানৰ ফেক্সৰ নম্বৰ"</item>
     <item msgid="1735177144948329370">"ঘৰৰ ফেক্স নম্বৰ"</item>
@@ -722,7 +726,7 @@
   </string-array>
     <string name="phoneTypeCustom" msgid="1644738059053355820">"নিজৰ উপযোগিতা অনুযায়ী"</string>
     <string name="phoneTypeHome" msgid="2570923463033985887">"ঘৰৰ ফ\'ন নম্বৰ"</string>
-    <string name="phoneTypeMobile" msgid="6501463557754751037">"ম\'বাইল"</string>
+    <string name="phoneTypeMobile" msgid="6501463557754751037">"ম’বাইল"</string>
     <string name="phoneTypeWork" msgid="8863939667059911633">"কৰ্মস্থানৰ ফ\'ন নম্বৰ"</string>
     <string name="phoneTypeFaxWork" msgid="3517792160008890912">"কর্মস্থানৰ ফেক্সৰ নম্বৰ"</string>
     <string name="phoneTypeFaxHome" msgid="2067265972322971467">"ঘৰৰ ফেক্স নম্বৰ"</string>
@@ -737,7 +741,7 @@
     <string name="phoneTypeRadio" msgid="4093738079908667513">"ৰেডিঅ’ ফ\'ন নম্বৰ"</string>
     <string name="phoneTypeTelex" msgid="3367879952476250512">"টেলেক্স ফ\'ন নম্বৰ"</string>
     <string name="phoneTypeTtyTdd" msgid="8606514378585000044">"TTY TDD"</string>
-    <string name="phoneTypeWorkMobile" msgid="1311426989184065709">"কৰ্মস্থানৰ ম\'বাইল নম্বৰ"</string>
+    <string name="phoneTypeWorkMobile" msgid="1311426989184065709">"কৰ্মস্থানৰ ম’বাইল নম্বৰ"</string>
     <string name="phoneTypeWorkPager" msgid="649938731231157056">"কৰ্মস্থানৰ পেজাৰৰ নম্বৰ"</string>
     <string name="phoneTypeAssistant" msgid="5596772636128562884">"সহায়ক"</string>
     <string name="phoneTypeMms" msgid="7254492275502768992">"এমএমএছ"</string>
@@ -749,7 +753,7 @@
     <string name="emailTypeHome" msgid="449227236140433919">"ঘৰ"</string>
     <string name="emailTypeWork" msgid="3548058059601149973">"কৰ্মস্থান"</string>
     <string name="emailTypeOther" msgid="2923008695272639549">"অন্যান্য"</string>
-    <string name="emailTypeMobile" msgid="119919005321166205">"ম\'বাইল"</string>
+    <string name="emailTypeMobile" msgid="119919005321166205">"ম’বাইল"</string>
     <string name="postalTypeCustom" msgid="8903206903060479902">"নিজৰ উপযোগিতা অনুযায়ী"</string>
     <string name="postalTypeHome" msgid="8165756977184483097">"ঘৰৰ ঠিকনা"</string>
     <string name="postalTypeWork" msgid="5268172772387694495">"কৰ্মস্থানৰ ঠিকনা"</string>
@@ -802,7 +806,7 @@
     <string name="keyguard_label_text" msgid="861796461028298424">"আনলক কৰিবলৈ মেনু টিপাৰ পিছত ০ টিপক।"</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"জৰুৰীকালীন নম্বৰ"</string>
     <string name="lockscreen_carrier_default" msgid="6169005837238288522">"কোনো সেৱা নাই"</string>
-    <string name="lockscreen_screen_locked" msgid="7288443074806832904">"স্ক্ৰীণ লক কৰা হ\'ল।"</string>
+    <string name="lockscreen_screen_locked" msgid="7288443074806832904">"স্ক্ৰীণ লক কৰা হ’ল।"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"আনলক কৰিবলৈ বা জৰুৰীকালীন কল কৰিবলৈ মেনু টিপক।"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"আনলক কৰিবলৈ মেনু টিপক।"</string>
     <string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"আনলক কৰিবলৈ আর্হি আঁকক"</string>
@@ -812,7 +816,7 @@
     <string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"আকৌ চেষ্টা কৰক"</string>
     <string name="lockscreen_password_wrong" msgid="5737815393253165301">"আকৌ চেষ্টা কৰক"</string>
     <string name="lockscreen_storage_locked" msgid="9167551160010625200">"সকলো সুবিধা আৰু ডেটাৰ বাবে আনলক কৰক"</string>
-    <string name="faceunlock_multiple_failures" msgid="754137583022792429">"গৰাকীৰ মুখাৱয়বৰ দ্বাৰা আনলক কৰা সর্বধিক সীমা অতিক্ৰম কৰা হ\'ল"</string>
+    <string name="faceunlock_multiple_failures" msgid="754137583022792429">"গৰাকীৰ মুখাৱয়বৰ দ্বাৰা আনলক কৰা সর্বধিক সীমা অতিক্ৰম কৰা হ’ল"</string>
     <string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"কোনো ছিম কাৰ্ড নাই"</string>
     <string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"টে\'বলেটত ছিম কার্ড নাই।"</string>
     <string name="lockscreen_missing_sim_message" product="tv" msgid="1943633865476989599">"টিভিত ছিম কার্ড নাই।"</string>
@@ -849,7 +853,7 @@
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"<xliff:g id="NUMBER">%d</xliff:g> ছেকেণ্ডৰ পাছত চেষ্টা কৰক।"</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"আর্হি পাহৰিলে নেকি?"</string>
     <string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"একাউণ্ট আনলক"</string>
-    <string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"বহুতবাৰ ভুলকৈ আর্হি অঁকা হ\'ল"</string>
+    <string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"বহুতবাৰ ভুলকৈ আর্হি অঁকা হ’ল"</string>
     <string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"আনলক কৰিবলৈ নিজৰ Google একাউণ্টৰ জৰিয়তে ছাইন ইন কৰক।"</string>
     <string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"ব্যৱহাৰকাৰীৰ নাম (ইমেইল)"</string>
     <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"পাছৱৰ্ড"</string>
@@ -860,11 +864,11 @@
     <string name="lockscreen_unlock_label" msgid="737440483220667054">"আনলক"</string>
     <string name="lockscreen_sound_on_label" msgid="9068877576513425970">"ধ্বনি অন হৈ আছে"</string>
     <string name="lockscreen_sound_off_label" msgid="996822825154319026">"ধ্বনি অফ হৈ আছে"</string>
-    <string name="lockscreen_access_pattern_start" msgid="3941045502933142847">"আর্হি অঁকা কার্য আৰম্ভ হ\'ল"</string>
-    <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"আর্হি মচি পেলোৱা হ\'ল"</string>
+    <string name="lockscreen_access_pattern_start" msgid="3941045502933142847">"আর্হি অঁকা কার্য আৰম্ভ হ’ল"</string>
+    <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"আর্হি মচি পেলোৱা হ’ল"</string>
     <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"আৰ্হিত এটা বিন্দু যোগ কৰিছে"</string>
-    <string name="lockscreen_access_pattern_cell_added_verbose" msgid="7264580781744026939">"চেল <xliff:g id="CELL_INDEX">%1$s</xliff:g> যোগ কৰা হ\'ল"</string>
-    <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"আর্হি অঁকা সর্ম্পূণ হ\'ল"</string>
+    <string name="lockscreen_access_pattern_cell_added_verbose" msgid="7264580781744026939">"চেল <xliff:g id="CELL_INDEX">%1$s</xliff:g> যোগ কৰা হ’ল"</string>
+    <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"আর্হি অঁকা সর্ম্পূণ হ’ল"</string>
     <string name="lockscreen_access_pattern_area" msgid="400813207572953209">"আর্হিৰ ক্ষেত্ৰ।"</string>
     <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. ৱিজেট %3$d-ৰ %2$d।"</string>
     <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"ৱিজেট যোগ কৰক।"</string>
@@ -876,9 +880,9 @@
     <string name="keyguard_accessibility_status" msgid="8008264603935930611">"স্থিতি"</string>
     <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"কেমেৰা"</string>
     <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"মিডিয়াৰ নিয়ন্ত্ৰণসমূহ"</string>
-    <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"ৱিজেটৰ পুনঃক্ৰম আৰম্ভ হ\'ল।"</string>
-    <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"ৱিজেটৰ পুনঃক্ৰম সমাপ্ত হ\'ল।"</string>
-    <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"ৱিজেট <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> মচা হ\'ল।"</string>
+    <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"ৱিজেটৰ পুনঃক্ৰম আৰম্ভ হ’ল।"</string>
+    <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"ৱিজেটৰ পুনঃক্ৰম সমাপ্ত হ’ল।"</string>
+    <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"ৱিজেট <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> মচা হ’ল।"</string>
     <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"আনলক ক্ষেত্ৰ বিস্তাৰ কৰক।"</string>
     <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"শ্লাইডৰদ্বাৰা আনলক।"</string>
     <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"আৰ্হিৰদ্বাৰা আনলক।"</string>
@@ -934,7 +938,7 @@
     <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"আপোনাৰ টিভিত সঞ্চয় কৰি ৰখা ব্ৰাউজাৰৰ বুকমার্ক আৰু ব্ৰাউজাৰৰ ইতিহাস সংশোধন কৰিবলৈ এপক অনুমতি দিয়ে। টোকা: এই অনুমতি তৃতীয় পক্ষৰ ব্ৰাউজাৰবোৰ বা ৱেব ব্ৰাউজিংৰ ক্ষমতা থকা অন্য এপ্লিকেশ্বনবোৰৰ দ্বাৰা বলৱৎ নহ\'বও পাৰে।"</string>
     <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"আপোনাৰ ফ\'নত সঞ্চয় কৰি ৰখা ব্ৰাউজাৰৰ বুকমার্ক আৰু ব্ৰাউজাৰৰ ইতিহাস সংশোধন কৰিবলৈ এপক অনুমতি দিয়ে। টোকা: এই অনুমতি তৃতীয় পক্ষৰ ব্ৰাউজাৰবোৰ বা ৱেব ব্ৰাউজিং কৰিব পৰা অন্য এপ্লিকেশ্বনবোৰৰ দ্বাৰা বলৱৎ নহ\'বও পাৰে।"</string>
     <string name="permlab_setAlarm" msgid="1379294556362091814">"এলাৰ্ম ছেট কৰক"</string>
-    <string name="permdesc_setAlarm" msgid="316392039157473848">"এপটোক ইনষ্টল হৈ থকা এলাৰ্ম ক্লক এপত এলাৰ্ম ছেট কৰিবলৈ অনুমতি দিয়ে। কিছুমান এলাৰ্ম ক্লক এপত এই সুবিধাটো প্ৰযোজ্য নহ\'ব পাৰে।"</string>
+    <string name="permdesc_setAlarm" msgid="316392039157473848">"এপটোক ইনষ্টল হৈ থকা এলাৰ্ম ক্লক এপত এলাৰ্ম ছেট কৰিবলৈ অনুমতি দিয়ে। কিছুমান এলাৰ্ম ক্লক এপত এই সুবিধাটো প্ৰযোজ্য নহ’ব পাৰে।"</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"ভইচমেইল যোগ কৰক"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"আপোনাৰ ভইচমেইল ইনবক্সত বাৰ্তাবোৰ যোগ কৰিবলৈ এপটোক অনুমতি দিয়ক।"</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ব্ৰাউজাৰৰ জিঅ\'লোকেশ্বনৰ অনুমতিসমূহ সংশোধন কৰক"</string>
@@ -944,7 +948,7 @@
     <string name="save_password_remember" msgid="6491879678996749466">"মনত ৰাখিব"</string>
     <string name="save_password_never" msgid="8274330296785855105">"কেতিয়াও মনত নাৰাখিব"</string>
     <string name="open_permission_deny" msgid="7374036708316629800">"এই পৃষ্ঠাটো খুলিবলৈ আপোনাৰ অনুমতি নাই।"</string>
-    <string name="text_copied" msgid="4985729524670131385">"ক্লিপব\'র্ডলৈ বাৰ্তা প্ৰতিলিপি কৰা হ\'ল।"</string>
+    <string name="text_copied" msgid="4985729524670131385">"ক্লিপব\'র্ডলৈ বাৰ্তা প্ৰতিলিপি কৰা হ’ল।"</string>
     <string name="more_item_label" msgid="4650918923083320495">"অধিক"</string>
     <string name="prepend_shortcut_label" msgid="2572214461676015642">"মেনু+"</string>
     <string name="menu_meta_shortcut_label" msgid="4647153495550313570">"মেটা+"</string>
@@ -1083,22 +1087,22 @@
     <string name="deleteText" msgid="6979668428458199034">"মচক"</string>
     <string name="inputMethod" msgid="1653630062304567879">"ইনপুট পদ্ধতি"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"পাঠ বিষয়ক কাৰ্য"</string>
-    <string name="email" msgid="4560673117055050403">"ইমেইল"</string>
-    <string name="email_desc" msgid="3638665569546416795">"বাছনি কৰা ঠিকনাত ইমেইল কৰক"</string>
+    <string name="email" msgid="4560673117055050403">"ইমেইল কৰক"</string>
+    <string name="email_desc" msgid="3638665569546416795">"বাছনি কৰা ঠিকনালৈ ইমেইল পঠিয়াওক"</string>
     <string name="dial" msgid="1253998302767701559">"কল কৰক"</string>
     <string name="dial_desc" msgid="6573723404985517250">"বাছনি কৰা ফ\'ন নাম্বাৰত কল কৰক"</string>
-    <string name="map" msgid="5441053548030107189">"মেপ"</string>
+    <string name="map" msgid="5441053548030107189">"মেপ খোলক"</string>
     <string name="map_desc" msgid="1836995341943772348">"বাছনি কৰা ঠিকনাটো বিচাৰি উলিয়াওক"</string>
     <string name="browse" msgid="1245903488306147205">"খোলক"</string>
     <string name="browse_desc" msgid="8220976549618935044">"বাছনি কৰা URL খোলক"</string>
-    <string name="sms" msgid="4560537514610063430">"বাৰ্তা"</string>
-    <string name="sms_desc" msgid="7526588350969638809">"বাছনি কৰা ফ\'ন নাম্বাৰত বাৰ্তা পঠিয়াওক"</string>
+    <string name="sms" msgid="4560537514610063430">"বাৰ্তা পঠিয়াওক"</string>
+    <string name="sms_desc" msgid="7526588350969638809">"বাছনি কৰা ফ’ন নম্বৰলৈ বাৰ্তা পঠিয়াওক"</string>
     <string name="add_contact" msgid="7867066569670597203">"যোগ দিয়ক"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"সর্ম্পকসূচীত যোগ কৰক"</string>
     <string name="view_calendar" msgid="979609872939597838">"চাওক"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"কেলেণ্ডাৰত বাছনি কৰা সময় চাওক"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"সূচী"</string>
-    <string name="add_calendar_event_desc" msgid="4326891793260687388">"বাছনি কৰা সময়ৰ বাবে কাৰ্যক্ৰমৰ তালিকা বনাওক"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"সময়সূচী নিৰ্ধাৰণ কৰক"</string>
+    <string name="add_calendar_event_desc" msgid="4326891793260687388">"বাছনি কৰা তাৰিখটো কাৰ্যক্ৰমৰ সময় হিচাপে নিৰ্ধাৰণ কৰক"</string>
     <string name="view_flight" msgid="7691640491425680214">"ট্ৰেক কৰক"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"বাছনি কৰা বিমানটো ট্ৰেক কৰক"</string>
     <string name="translate" msgid="9218619809342576858">"অনুবাদ কৰক"</string>
@@ -1141,12 +1145,12 @@
     <string name="whichImageCaptureApplicationLabel" msgid="6390303445371527066">"প্ৰতিচ্ছবি তোলক"</string>
     <string name="alwaysUse" msgid="4583018368000610438">"এই কার্যৰ বাবে পূর্বনির্ধাৰিত ধৰণে ব্যৱহাৰ কৰক।"</string>
     <string name="use_a_different_app" msgid="8134926230585710243">"এটা পৃথক এপ্ ব্যৱহাৰ কৰক"</string>
-    <string name="clearDefaultHintMsg" msgid="3252584689512077257">"ছিষ্টেমৰ ছেটিংসমূহ &gt; এপসমূহ &gt; ডাউনল\'ড কৰা সমল-লৈ গৈ ডিফ\'ল্ট মচক৷"</string>
+    <string name="clearDefaultHintMsg" msgid="3252584689512077257">"ছিষ্টেমৰ ছেটিংসমূহ &gt; এপসমূহ &gt; ডাউনল’ড কৰা সমল-লৈ গৈ ডিফ\'ল্ট মচক৷"</string>
     <string name="chooseActivity" msgid="7486876147751803333">"কোনো কার্য বাছনি কৰক"</string>
     <string name="chooseUsbActivity" msgid="6894748416073583509">"ইউএছবি ডিভাইচৰ বাবে এটা এপ্ বাছনি কৰক"</string>
     <string name="noApplications" msgid="2991814273936504689">"কোনো এপে এই কাৰ্য কৰিব নোৱাৰে।"</string>
-    <string name="aerr_application" msgid="250320989337856518">"<xliff:g id="APPLICATION">%1$s</xliff:g> বন্ধ হ\'ল"</string>
-    <string name="aerr_process" msgid="6201597323218674729">"<xliff:g id="PROCESS">%1$s</xliff:g> বন্ধ হ\'ল"</string>
+    <string name="aerr_application" msgid="250320989337856518">"<xliff:g id="APPLICATION">%1$s</xliff:g> বন্ধ হ’ল"</string>
+    <string name="aerr_process" msgid="6201597323218674729">"<xliff:g id="PROCESS">%1$s</xliff:g> বন্ধ হ’ল"</string>
     <string name="aerr_application_repeated" msgid="3146328699537439573">"<xliff:g id="APPLICATION">%1$s</xliff:g> বাৰে বাৰে বন্ধ হৈ গৈছে"</string>
     <string name="aerr_process_repeated" msgid="6235302956890402259">"<xliff:g id="PROCESS">%1$s</xliff:g> বাৰে বাৰে বন্ধ হৈ গৈছে"</string>
     <string name="aerr_restart" msgid="7581308074153624475">"আকৌ এপটো খোলক"</string>
@@ -1169,22 +1173,23 @@
     <string name="launch_warning_original" msgid="188102023021668683">"<xliff:g id="APP_NAME">%1$s</xliff:g>ক পূৰ্বতে লঞ্চ কৰা হৈছিল৷"</string>
     <string name="screen_compat_mode_scale" msgid="3202955667675944499">"স্কেল"</string>
     <string name="screen_compat_mode_show" msgid="4013878876486655892">"সদায় দেখুৱাওক"</string>
-    <string name="screen_compat_mode_hint" msgid="1064524084543304459">"ছিষ্টেমৰ ছেটিংসমূহ &gt; এপসমূহ &gt; ডাউনল\'ড কৰা সমল-লৈ গৈ ইয়াক আকৌ সক্ষম কৰক।"</string>
-    <string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ বর্তমানৰ ডিছপ্লেৰ আকাৰ ছেটিং ব্যৱহাৰ কৰিব নোৱাৰে আৰু ই সঠিকভাৱে নচলিবও পাৰে।"</string>
+    <string name="screen_compat_mode_hint" msgid="1064524084543304459">"ছিষ্টেমৰ ছেটিংসমূহ &gt; এপসমূহ &gt; ডাউনল’ড কৰা সমল-লৈ গৈ ইয়াক আকৌ সক্ষম কৰক।"</string>
+    <string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ বর্তমানৰ ডিছপ্লে’ৰ আকাৰ ছেটিং ব্যৱহাৰ কৰিব নোৱাৰে আৰু ই সঠিকভাৱে নচলিবও পাৰে।"</string>
     <string name="unsupported_display_size_show" msgid="7969129195360353041">"সদায় দেখুৱাওক"</string>
     <string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"<xliff:g id="APP_NAME">%1$s</xliff:g>ক এটা খাপ নোখোৱা Android OS সংস্কৰণৰ বাবে তৈয়াৰ কৰা হৈছিল, যাৰ ফলত ই অস্বাভাৱিকধৰণে আচৰণ কৰিব পাৰে। এপটোৰ শেহতীয়া সংস্কৰণ উপলব্ধ হ\'ব পাৰে।"</string>
     <string name="unsupported_compile_sdk_show" msgid="2681877855260970231">"সদায় দেখুৱাওক"</string>
-    <string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"আপডেট আছে নেকি চাওক"</string>
+    <string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"আপডে’ট আছে নেকি চাওক"</string>
     <string name="smv_application" msgid="3307209192155442829">"এপটোৱে <xliff:g id="APPLICATION">%1$s</xliff:g> (প্ৰক্ৰিয়াটোৱে <xliff:g id="PROCESS">%2$s</xliff:g>) নিজে বলবৎ কৰা StrictMode নীতি ভংগ কৰিলে।"</string>
     <string name="smv_process" msgid="5120397012047462446">"<xliff:g id="PROCESS">%1$s</xliff:g> প্ৰক্ৰিয়াটোৱে নিজে বলৱৎ কৰা StrictMode নীতি ভংগ কৰিলে।"</string>
-    <string name="android_upgrading_title" product="default" msgid="7513829952443484438">"ফ\'নটো আপডেট হৈ আছে…"</string>
-    <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"টেবলেটটো আপডেট হৈ আছে…"</string>
-    <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"ডিভাইচটো আপডেট হৈ আছে…"</string>
+    <string name="android_upgrading_title" product="default" msgid="7513829952443484438">"ফ\'নটো আপডে’ট হৈ আছে…"</string>
+    <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"টেবলেটটো আপডে’ট হৈ আছে…"</string>
+    <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"ডিভাইচটো আপডে’ট হৈ আছে…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"ফ\'নটো আৰম্ভ হৈছে…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android আৰম্ভ কৰি থকা হৈছে…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"টেবলেটটো আৰম্ভ হৈছে…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"ডিভাইচটো আৰম্ভ হৈছে…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"সঞ্চয়াগাৰ অপ্টিমাইজ কৰি থকা হৈছে।"</string>
-    <string name="android_upgrading_notification_title" product="default" msgid="1511552415039349062">"ছিষ্টেম আপডেট সম্পূৰ্ণ কৰা হৈছে…"</string>
+    <string name="android_upgrading_notification_title" product="default" msgid="1511552415039349062">"ছিষ্টেম আপডে’ট সম্পূৰ্ণ কৰা হৈছে…"</string>
     <string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g>ক আপগ্ৰেড কৰি থকা হৈছে…"</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g>ৰ ভিতৰত <xliff:g id="NUMBER_0">%1$d</xliff:g> এপ্ অপ্টিমাইজ কৰি থকা হৈছে৷"</string>
     <string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g>সাজু কৰি থকা হৈছে।"</string>
@@ -1198,7 +1203,7 @@
     <string name="new_app_action" msgid="6694851182870774403">"<xliff:g id="NEW_APP">%1$s</xliff:g> খোলক"</string>
     <string name="new_app_description" msgid="5894852887817332322">"<xliff:g id="OLD_APP">%1$s</xliff:g> ছেভ নকৰাকৈ বন্ধ হ\'ব"</string>
     <string name="dump_heap_notification" msgid="2618183274836056542">"<xliff:g id="PROC">%1$s</xliff:g> মেম\'ৰিৰ সীমা অতিক্ৰম কৰিছে"</string>
-    <string name="dump_heap_notification_detail" msgid="3993078784053054141">"হীপ ডাম্প সংগ্ৰহ কৰা হ\'ল। শ্বেয়াৰ কৰিবলৈ টিপক"</string>
+    <string name="dump_heap_notification_detail" msgid="3993078784053054141">"হীপ ডাম্প সংগ্ৰহ কৰা হ’ল। শ্বেয়াৰ কৰিবলৈ টিপক"</string>
     <string name="dump_heap_title" msgid="5864292264307651673">"হীপ ডাম্প শ্বেয়াৰ কৰিবনে?"</string>
     <string name="dump_heap_text" msgid="4809417337240334941">"এই <xliff:g id="PROC">%1$s</xliff:g> প্ৰক্ৰিয়াটোৱে তাৰ মেম\'ৰিৰ সীমা <xliff:g id="SIZE">%2$s</xliff:g> অতিক্ৰম কৰিছে। ইয়াৰ বিকাশকৰ্তাৰ সৈতে আপুনি শ্বেয়াৰ কৰিবপৰাকৈ হীপ ডাম্প মজুত আছে। সাৱধান হ\'ব: এই হীপ ডাম্পত এপ্লিকেশ্বনটোৱে ব্যৱহাৰ কৰা আপোনাৰ কোনো ব্য়ক্তিগত তথ্য়ও থাকিব পাৰে।"</string>
     <string name="sendText" msgid="5209874571959469142">"বার্তাৰ বাবে কাৰ্য বাছনি কৰক"</string>
@@ -1234,15 +1239,19 @@
     <string name="wifi_available_title" msgid="3817100557900599505">"পাছৱৰ্ড অবিহনে সংযোগ কৰিবপৰা ৱাই-ফাই নেটৱর্কৰ সৈতে সংযোগ কৰক"</string>
     <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"বাহকৰ ৱাই-ফাই নেটৱৰ্কৰ সৈতে সংযোগ কৰক"</string>
     <string name="wifi_available_title_connecting" msgid="1139126673968899002">"ৱাই-ফাই নেটৱৰ্কৰ সৈতে সংযোগ কৰি থকা হৈছে"</string>
-    <string name="wifi_available_title_connected" msgid="7542672851522241548">"ৱাই-ফাই নেটৱৰ্কৰ সৈতে সংযোগ কৰা হ\'ল"</string>
+    <string name="wifi_available_title_connected" msgid="7542672851522241548">"ৱাই-ফাই নেটৱৰ্কৰ সৈতে সংযোগ কৰা হ’ল"</string>
     <string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"ৱাই-ফাই নেটৱৰ্কৰ সৈতে সংযোগ কৰিবপৰা নগ\'ল"</string>
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"সকলো নেটৱৰ্ক চাবলৈ টিপক"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"সংযোগ কৰক"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"সকলো নেটৱৰ্ক"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g>এ প্ৰস্তাৱ দিয়া ৱাই-ফাই নেটৱৰ্কৰ লগত সংযোগ কৰা হ’ল"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"আপুনি <xliff:g id="NAME">%s</xliff:g>ক আপোনাৰ বাবে নেটৱৰ্কৰ প্ৰস্তাৱ দিয়াৰ অনুমতি দিব বিচাৰেনে?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"হয়"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"নহয়"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"ৱাই-ফাই স্বয়ংক্ৰিয়ভাৱে অন হ\'ব"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"যেতিয়া আপুনি ছেভ কৰি থোৱা উচ্চ মানৰ নেটৱৰ্কৰ কাষত থাকে"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"পুনৰাই অন নকৰিব"</string>
-    <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"ৱাই-ফাই স্বয়ংক্ৰিয়ভাৱে অন কৰা হ\'ল"</string>
+    <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"ৱাই-ফাই স্বয়ংক্ৰিয়ভাৱে অন কৰা হ’ল"</string>
     <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"আপুনি ছেভ কৰি থোৱা নেটৱৰ্ক এটাৰ কাষত আছে: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
     <string name="wifi_available_sign_in" msgid="9157196203958866662">"ৱাই-ফাই নেটৱৰ্কত ছাইন ইন কৰক"</string>
     <string name="network_available_sign_in" msgid="1848877297365446605">"নেটৱৰ্কত ছাইন ইন কৰক"</string>
@@ -1253,11 +1262,11 @@
     <string name="wifi_softap_config_change" msgid="8475911871165857607">"আপোনাৰ হটস্পট ছেটিংসমূহত কৰা সালসলনি"</string>
     <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"আপোনাৰ হটস্পটৰ বেণ্ড সলনি কৰা হ’ল।"</string>
     <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"আপোনাৰ কেৱল ৫গিগাহাৰ্টজৰ প্ৰতি অগ্ৰাধিকাৰ এই ডিভাচইচটোৱে সমৰ্থন নকৰে। ইয়াৰ পৰিৱৰ্তে, ডিভাচইচটোৱে যেতিয়া ৫গিগাহাৰ্টজ বেণ্ড উপলব্ধ হ’ব তেতিয়া সেইয়া ব্যৱহাৰ কৰিব।"</string>
-    <string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>লৈ সলনি কৰা হ\'ল"</string>
+    <string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>লৈ সলনি কৰা হ’ল"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"যেতিয়া <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>ত ইণ্টাৰনেট নাথাকে, তেতিয়া ডিভাইচে <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ক ব্যৱহাৰ কৰে। মাচুল প্ৰযোজ্য হ\'ব পাৰে।"</string>
-    <string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>ৰ পৰা <xliff:g id="NEW_NETWORK">%2$s</xliff:g> লৈ সলনি কৰা হ\'ল"</string>
+    <string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>ৰ পৰা <xliff:g id="NEW_NETWORK">%2$s</xliff:g> লৈ সলনি কৰা হ’ল"</string>
   <string-array name="network_switch_type_name">
-    <item msgid="3979506840912951943">"ম\'বাইল ডেটা"</item>
+    <item msgid="3979506840912951943">"ম’বাইল ডেটা"</item>
     <item msgid="75483255295529161">"ৱাই-ফাই"</item>
     <item msgid="6862614801537202646">"ব্লুটুথ"</item>
     <item msgid="5447331121797802871">"ইথাৰনেট"</item>
@@ -1276,7 +1285,7 @@
     <string name="wifi_p2p_enabled_notification_message" msgid="8064677407830620023">"ছেটিংসমূহৰ বাবে টিপক"</string>
     <string name="accept" msgid="1645267259272829559">"স্বীকাৰ কৰক"</string>
     <string name="decline" msgid="2112225451706137894">"প্ৰত্যাখ্যান কৰক"</string>
-    <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"আমন্ত্ৰণ পঠোৱা হ\'ল"</string>
+    <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"আমন্ত্ৰণ পঠোৱা হ’ল"</string>
     <string name="wifi_p2p_invitation_to_connect_title" msgid="4958803948658533637">"সংযোগ হ\'বলৈ আমন্ত্ৰণ"</string>
     <string name="wifi_p2p_from_message" msgid="570389174731951769">"প্ৰেৰক:"</string>
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"প্ৰতি:"</string>
@@ -1291,30 +1300,30 @@
     <string name="sms_control_yes" msgid="3663725993855816807">"অনুমতি দিয়ক"</string>
     <string name="sms_control_no" msgid="625438561395534982">"অস্বীকাৰ কৰক"</string>
     <string name="sms_short_code_confirm_message" msgid="1645436466285310855">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;এ &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt; লৈ কিবা বার্তা প্ৰেৰণ কৰিব বিচাৰিছে।"</string>
-    <string name="sms_short_code_details" msgid="5873295990846059400">"ইয়াৰ বাবে "<b>" মাছুল ভৰিবলগীয়া হ\'ব পাৰে"</b>"।"</string>
-    <string name="sms_premium_short_code_details" msgid="7869234868023975"><b>"ইয়াৰ বাবে মাছুল ভৰিবলগীয়া হ\'ব পাৰে।"</b></string>
+    <string name="sms_short_code_details" msgid="5873295990846059400">"ইয়াৰ বাবে "<b>" মাচুল ভৰিবলগীয়া হ’ব পাৰে"</b>"।"</string>
+    <string name="sms_premium_short_code_details" msgid="7869234868023975"><b>"ইয়াৰ বাবে মাচুল ভৰিবলগীয়া হ’ব পাৰে।"</b></string>
     <string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"পঠিয়াওক"</string>
     <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"বাতিল কৰক"</string>
     <string name="sms_short_code_remember_choice" msgid="5289538592272218136">"মোৰ পচন্দ মনত ৰাখিব"</string>
     <string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"আপুনি ইয়াক পিছত ছেটিং &gt; এপ্‌-ত সলনি কৰিব পাৰে"</string>
     <string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"যিকোনো সময়ত অনুমতি দিয়ক"</string>
     <string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"কেতিয়াও অনুমতি নিদিব"</string>
-    <string name="sim_removed_title" msgid="6227712319223226185">"ছিম কাৰ্ড আঁতৰোৱা হ\'ল"</string>
+    <string name="sim_removed_title" msgid="6227712319223226185">"ছিম কাৰ্ড আঁতৰোৱা হ’ল"</string>
     <string name="sim_removed_message" msgid="2333164559970958645">"এখন মান্য ছিম কার্ড ব্যৱহাৰ কৰি ৰিষ্টার্ট নকৰা পর্যন্ত ম\'বাইলৰ নেটৱর্ক উপলব্ধ নহয়।"</string>
-    <string name="sim_done_button" msgid="827949989369963775">"সম্পন্ন হ\'ল"</string>
-    <string name="sim_added_title" msgid="3719670512889674693">"ছিম কাৰ্ড যোগ কৰা হ\'ল"</string>
+    <string name="sim_done_button" msgid="827949989369963775">"সম্পন্ন হ’ল"</string>
+    <string name="sim_added_title" msgid="3719670512889674693">"ছিম কাৰ্ড যোগ কৰা হ’ল"</string>
     <string name="sim_added_message" msgid="6599945301141050216">"ম\'বাইলৰ নেটৱর্ক ব্যৱহাৰ কৰিবলৈ আপোনাৰ ডিভাইচটো ৰিষ্টার্ট কৰক।"</string>
     <string name="sim_restart_button" msgid="4722407842815232347">"ৰিষ্টাৰ্ট কৰক"</string>
-    <string name="install_carrier_app_notification_title" msgid="9056007111024059888">"ম\'বাইল সেৱা সক্ৰিয় কৰক"</string>
-    <string name="install_carrier_app_notification_text" msgid="3346681446158696001">"আপোনাৰ নতুন ছিমখন সক্ৰিয় কৰিবলৈ বাহকৰ এপটো ডাউনল\'ড কৰক"</string>
-    <string name="install_carrier_app_notification_text_app_name" msgid="1196505084835248137">"আপোনাৰ নতুন ছিমখন সক্ৰিয় কৰিবলৈ <xliff:g id="APP_NAME">%1$s</xliff:g> এপটো ডাউনল\'ড কৰক"</string>
-    <string name="install_carrier_app_notification_button" msgid="3094206295081900849">"এপ্ ডাউনল\'ড কৰক"</string>
+    <string name="install_carrier_app_notification_title" msgid="9056007111024059888">"ম’বাইল সেৱা সক্ৰিয় কৰক"</string>
+    <string name="install_carrier_app_notification_text" msgid="3346681446158696001">"আপোনাৰ নতুন ছিমখন সক্ৰিয় কৰিবলৈ বাহকৰ এপটো ডাউনল’ড কৰক"</string>
+    <string name="install_carrier_app_notification_text_app_name" msgid="1196505084835248137">"আপোনাৰ নতুন ছিমখন সক্ৰিয় কৰিবলৈ <xliff:g id="APP_NAME">%1$s</xliff:g> এপটো ডাউনল’ড কৰক"</string>
+    <string name="install_carrier_app_notification_button" msgid="3094206295081900849">"এপ্ ডাউনল’ড কৰক"</string>
     <string name="carrier_app_notification_title" msgid="8921767385872554621">"নতুন ছিম ভৰোৱা হৈছে"</string>
     <string name="carrier_app_notification_text" msgid="1132487343346050225">"ছেট আপ কৰিবলৈ টিপক"</string>
     <string name="time_picker_dialog_title" msgid="8349362623068819295">"সময় ছেট কৰক"</string>
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"তাৰিখ ছেট কৰক"</string>
     <string name="date_time_set" msgid="5777075614321087758">"ছেট কৰক"</string>
-    <string name="date_time_done" msgid="2507683751759308828">"সম্পন্ন হ\'ল"</string>
+    <string name="date_time_done" msgid="2507683751759308828">"সম্পন্ন হ’ল"</string>
     <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"নতুন: "</font></string>
     <string name="perms_description_app" msgid="5139836143293299417">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ প্ৰদান কৰা"</string>
     <string name="no_permissions" msgid="7283357728219338112">"কোনো অনুমতিৰ প্ৰয়োজন নাই"</string>
@@ -1322,16 +1331,16 @@
     <string name="dlg_ok" msgid="7376953167039865701">"ঠিক আছে"</string>
     <string name="usb_charging_notification_title" msgid="1595122345358177163">"ইউএছবিৰ জৰিয়তে এই ডিভাইচটো চ্চাৰ্জ কৰি থকা হৈছে"</string>
     <string name="usb_supplying_notification_title" msgid="4631045789893086181">"ইউএছবিৰ জৰিয়তে সংযুক্ত ডিভাইচটো চ্চাৰ্জ কৰি থকা হৈছে"</string>
-    <string name="usb_mtp_notification_title" msgid="4238227258391151029">"ইউএছবি জৰিয়তে ফাইল স্থানান্তৰণ অন কৰা হ\'ল"</string>
-    <string name="usb_ptp_notification_title" msgid="5425857879922006878">"ইউএছবিৰ জৰিয়তে পিটিপি অন কৰা হ\'ল"</string>
-    <string name="usb_tether_notification_title" msgid="3716143122035802501">"ইউএছবি টেডাৰিং অন কৰা হ\'ল"</string>
-    <string name="usb_midi_notification_title" msgid="5356040379749154805">"ইউএছবিৰ জৰিয়তে এমআইডিআই অন কৰা হ\'ল"</string>
-    <string name="usb_accessory_notification_title" msgid="1785694450621427730">"ইউএছবি সহায়ক সামগ্ৰী সংযোগ কৰা হ\'ল"</string>
+    <string name="usb_mtp_notification_title" msgid="4238227258391151029">"ইউএছবি জৰিয়তে ফাইল স্থানান্তৰণ অন কৰা হ’ল"</string>
+    <string name="usb_ptp_notification_title" msgid="5425857879922006878">"ইউএছবিৰ জৰিয়তে পিটিপি অন কৰা হ’ল"</string>
+    <string name="usb_tether_notification_title" msgid="3716143122035802501">"ইউএছবি টেডাৰিং অন কৰা হ’ল"</string>
+    <string name="usb_midi_notification_title" msgid="5356040379749154805">"ইউএছবিৰ জৰিয়তে এমআইডিআই অন কৰা হ’ল"</string>
+    <string name="usb_accessory_notification_title" msgid="1785694450621427730">"ইউএছবি সহায়ক সামগ্ৰী সংযোগ কৰা হ’ল"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"অধিক বিকল্পৰ বাবে টিপক।"</string>
     <string name="usb_power_notification_message" msgid="4647527153291917218">"সংযুক্ত ডিভাইচ চ্চাৰ্জ কৰি থকা হৈছে। অধিক বিকল্পৰ বাবে টিপক।"</string>
     <string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"এনাল\'গ অডিঅ\' সহায়ক সামগ্ৰী পোৱা গৈছে"</string>
     <string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"সংলগ্ন কৰা ডিভাইচটোৱে এই ফ\'নটোৰ সৈতে কাম কৰিব নোৱাৰে। অধিক জানিবলৈ টিপক।"</string>
-    <string name="adb_active_notification_title" msgid="6729044778949189918">"ইউএছবি ডিবাগিং সংযোগ কৰা হ\'ল"</string>
+    <string name="adb_active_notification_title" msgid="6729044778949189918">"ইউএছবি ডিবাগিং সংযোগ কৰা হ’ল"</string>
     <string name="adb_active_notification_message" msgid="7463062450474107752">"ইউএছবি ডিবাগিং বন্ধ কৰিবলৈ টিপক"</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"ইউএছবি ডিবাগিং অক্ষম কৰিবলৈ বাছনি কৰক।"</string>
     <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"বাগ সম্পর্কীয় অভিযোগ গ্ৰহণ কৰি থকা হৈছে…"</string>
@@ -1364,9 +1373,9 @@
     <string name="ext_media_unsupported_notification_title" msgid="3797642322958803257">"<xliff:g id="NAME">%s</xliff:g>ক ব্যৱহাৰ কৰিব নোৱাৰি"</string>
     <string name="ext_media_unsupported_notification_message" msgid="6121601473787888589">"এই ডিভাইচটোৱে <xliff:g id="NAME">%s</xliff:g>ক ব্যৱহাৰ কৰিব নোৱাৰে। ব্যৱহাৰ কৰিব পৰা ফৰ্মেটত ছেট আপ কৰিবলৈ টিপক।"</string>
     <string name="ext_media_unsupported_notification_message" product="tv" msgid="3725436899820390906">"এই ডিভাইচটোৱে <xliff:g id="NAME">%s</xliff:g>ক চলাব নোৱাৰে। চলাব পৰা কোনো ফৰ্মেটত ছেট আপ কৰিবলৈ বাছনি কৰক।"</string>
-    <string name="ext_media_badremoval_notification_title" msgid="3206248947375505416">"<xliff:g id="NAME">%s</xliff:g> অপ্ৰত্য়াশিতভাৱে আঁতৰোৱা হ\'ল"</string>
+    <string name="ext_media_badremoval_notification_title" msgid="3206248947375505416">"<xliff:g id="NAME">%s</xliff:g> অপ্ৰত্য়াশিতভাৱে আঁতৰোৱা হ’ল"</string>
     <string name="ext_media_badremoval_notification_message" msgid="8556885808951260574">"সমল হেৰুওৱাৰ পৰা হাত সাৰিবলৈ আঁতৰোৱাৰ আগতে মিডিয়া বাহিৰ কৰক"</string>
-    <string name="ext_media_nomedia_notification_title" msgid="6593814191061956856">"<xliff:g id="NAME">%s</xliff:g> আঁতৰোৱা হ\'ল"</string>
+    <string name="ext_media_nomedia_notification_title" msgid="6593814191061956856">"<xliff:g id="NAME">%s</xliff:g> আঁতৰোৱা হ’ল"</string>
     <string name="ext_media_nomedia_notification_message" msgid="2110883356419799994">"কিছুমান কাৰ্যক্ষমতাই সঠিকভাৱে কাম নকৰিব পাৰে। নতুন সঞ্চয়াগাৰ ভৰাওক।"</string>
     <string name="ext_media_unmounting_notification_title" msgid="5046532339291216076">"<xliff:g id="NAME">%s</xliff:g> বাহিৰ কৰি থকা হৈছে"</string>
     <string name="ext_media_unmounting_notification_message" msgid="1003926904442321115">"আঁতৰাই নিদিব"</string>
@@ -1377,16 +1386,16 @@
     <string name="ext_media_missing_message" msgid="4012389235250987930">"ডিভাইচ আকৌ ভৰাওক"</string>
     <string name="ext_media_move_specific_title" msgid="1471100343872375842">"<xliff:g id="NAME">%s</xliff:g>ক স্থানান্তৰ কৰি থকা হৈছে"</string>
     <string name="ext_media_move_title" msgid="1022809140035962662">"ডেটা স্থানান্তৰ কৰি থকা হৈছে"</string>
-    <string name="ext_media_move_success_title" msgid="7863652232242276066">"সমলৰ স্থানান্তৰণ সমাপ্ত হ\'ল"</string>
-    <string name="ext_media_move_success_message" msgid="8939137931961728009">"<xliff:g id="NAME">%s</xliff:g>লৈ সমল স্থানান্তৰ কৰা হ\'ল"</string>
+    <string name="ext_media_move_success_title" msgid="7863652232242276066">"সমলৰ স্থানান্তৰণ সমাপ্ত হ’ল"</string>
+    <string name="ext_media_move_success_message" msgid="8939137931961728009">"<xliff:g id="NAME">%s</xliff:g>লৈ সমল স্থানান্তৰ কৰা হ’ল"</string>
     <string name="ext_media_move_failure_title" msgid="1604422634177382092">"সমল স্থানান্তৰ কৰিব পৰা নগ\'ল"</string>
     <string name="ext_media_move_failure_message" msgid="7388950499623016135">"সমল আকৌ স্থানান্তৰ কৰিবলৈ চেষ্টা কৰক"</string>
-    <string name="ext_media_status_removed" msgid="6576172423185918739">"আঁতৰোৱা হ\'ল"</string>
-    <string name="ext_media_status_unmounted" msgid="2551560878416417752">"বাহিৰলৈ উলিওৱা হ\'ল"</string>
+    <string name="ext_media_status_removed" msgid="6576172423185918739">"আঁতৰোৱা হ’ল"</string>
+    <string name="ext_media_status_unmounted" msgid="2551560878416417752">"বাহিৰলৈ উলিওৱা হ’ল"</string>
     <string name="ext_media_status_checking" msgid="6193921557423194949">"পৰীক্ষা কৰি থকা হৈছে…"</string>
     <string name="ext_media_status_mounted" msgid="7253821726503179202">"সাজু"</string>
     <string name="ext_media_status_mounted_ro" msgid="8020978752406021015">"ৰীড-অনলি"</string>
-    <string name="ext_media_status_bad_removal" msgid="8395398567890329422">"বিপজ্জনকভাৱে আঁতৰোৱা হ\'ল"</string>
+    <string name="ext_media_status_bad_removal" msgid="8395398567890329422">"বিপজ্জনকভাৱে আঁতৰোৱা হ’ল"</string>
     <string name="ext_media_status_unmountable" msgid="805594039236667894">"ব্যৱহাৰযোগ্য নহয়"</string>
     <string name="ext_media_status_unsupported" msgid="4691436711745681828">"সঞ্চয়াগাৰ ব্যৱহাৰ কৰিব নোৱাৰি"</string>
     <string name="ext_media_status_ejecting" msgid="5463887263101234174">"বাহিৰলৈ উলিয়াই থকা হৈছে…"</string>
@@ -1409,7 +1418,7 @@
     <string name="ime_action_search" msgid="658110271822807811">"অনুসন্ধান কৰক"</string>
     <string name="ime_action_send" msgid="2316166556349314424">"পঠিয়াওক"</string>
     <string name="ime_action_next" msgid="3138843904009813834">"পৰৱৰ্তী"</string>
-    <string name="ime_action_done" msgid="8971516117910934605">"সম্পন্ন হ\'ল"</string>
+    <string name="ime_action_done" msgid="8971516117910934605">"সম্পন্ন হ’ল"</string>
     <string name="ime_action_previous" msgid="1443550039250105948">"আগৰ"</string>
     <string name="ime_action_default" msgid="2840921885558045721">"কার্য কৰক"</string>
     <string name="dial_number_using" msgid="5789176425167573586">"<xliff:g id="NUMBER">%s</xliff:g> ব্যৱহাৰ কৰি \n নম্বৰটো ডায়েল কৰক"</string>
@@ -1437,7 +1446,7 @@
     <string name="vpn_text" msgid="1610714069627824309">"নেটৱর্ক পৰিচালনা কৰিবলৈ টিপক।"</string>
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g>ৰ সৈতে সংযোগ হৈছে। নেটৱর্ক পৰিচালনা কৰিবলৈ টিপক।"</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"সদা-সক্ৰিয় ভিপিএন সংযোগ কৰি থকা হৈছে…"</string>
-    <string name="vpn_lockdown_connected" msgid="8202679674819213931">"সদা-সক্ৰিয় ভিপিএন সংযোগ কৰা হ\'ল"</string>
+    <string name="vpn_lockdown_connected" msgid="8202679674819213931">"সদা-সক্ৰিয় ভিপিএন সংযোগ কৰা হ’ল"</string>
     <string name="vpn_lockdown_disconnected" msgid="735805531187559719">"সদা-সক্ৰিয় ভিপিএনৰ লগত সংযোগ বিচ্ছিন্ন কৰা হৈছে"</string>
     <string name="vpn_lockdown_error" msgid="3133844445659711681">"সদা-সক্ৰিয় ভিপিএনৰ লগত সংযোগ কৰিব পৰা নাই"</string>
     <string name="vpn_lockdown_config" msgid="8151951501116759194">"নেটৱৰ্ক বা ভিপিএন ছেটিংসমূহ সলনি কৰক"</string>
@@ -1460,7 +1469,7 @@
       <item quantity="one"><xliff:g id="TOTAL">%d</xliff:g>ৰ <xliff:g id="INDEX">%d</xliff:g>টা</item>
       <item quantity="other"><xliff:g id="TOTAL">%d</xliff:g>ৰ <xliff:g id="INDEX">%d</xliff:g>টা</item>
     </plurals>
-    <string name="action_mode_done" msgid="7217581640461922289">"সম্পন্ন হ\'ল"</string>
+    <string name="action_mode_done" msgid="7217581640461922289">"সম্পন্ন হ’ল"</string>
     <string name="progress_erasing" msgid="2569962663843586562">"শ্বেয়াৰ কৰি থোৱা সঞ্চয়াগাৰ মচি থকা হৈছে…"</string>
     <string name="share" msgid="1778686618230011964">"শ্বেয়াৰ কৰক"</string>
     <string name="find" msgid="4808270900322985960">"বিচাৰক"</string>
@@ -1472,7 +1481,7 @@
     <string name="gpsNotifMessage" msgid="1374718023224000702">"<xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)এ অনুৰোধ কৰিছে"</string>
     <string name="gpsVerifYes" msgid="2346566072867213563">"হয়"</string>
     <string name="gpsVerifNo" msgid="1146564937346454865">"নহয়"</string>
-    <string name="sync_too_many_deletes" msgid="5296321850662746890">"মচি পেলোৱাৰ সীমা পাৰ হ\'ল"</string>
+    <string name="sync_too_many_deletes" msgid="5296321850662746890">"মচি পেলোৱাৰ সীমা পাৰ হ’ল"</string>
     <string name="sync_too_many_deletes_desc" msgid="496551671008694245">"এই <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g> ৰ মচি থোৱা <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> টা <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g> বস্তু আছে। আপুনি কি কৰিব বিচাৰে?"</string>
     <string name="sync_really_delete" msgid="2572600103122596243">"বস্তুবোৰ মচক"</string>
     <string name="sync_undo_deletes" msgid="2941317360600338602">"মচা কাৰ্যক আনডু কৰক"</string>
@@ -1501,7 +1510,7 @@
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"বাতিল কৰক"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"মচক"</string>
-    <string name="keyboardview_keycode_done" msgid="1992571118466679775">"সম্পন্ন হ\'ল"</string>
+    <string name="keyboardview_keycode_done" msgid="1992571118466679775">"সম্পন্ন হ’ল"</string>
     <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"ম\'ড সলনি"</string>
     <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"শ্বিফ্ট"</string>
     <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"এণ্টাৰ"</string>
@@ -1525,15 +1534,15 @@
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"সম্পাদনা কৰক"</string>
     <string name="data_usage_warning_title" msgid="6499834033204801605">"ডেটা সকীয়নি"</string>
     <string name="data_usage_warning_body" msgid="7340198905103751676">"আপুনি <xliff:g id="APP">%s</xliff:g> ডেটা ব্যৱহাৰ কৰিছে"</string>
-    <string name="data_usage_mobile_limit_title" msgid="6561099244084267376">"ম\'বাইল ডেটা ব্যৱাহৰৰ সীমা শেষ হৈছে"</string>
+    <string name="data_usage_mobile_limit_title" msgid="6561099244084267376">"ম’বাইল ডেটা ব্যৱাহৰৰ সীমা শেষ হৈছে"</string>
     <string name="data_usage_wifi_limit_title" msgid="5803363779034792676">"ৱাই-ফাই ডেটাৰ সীমাত উপনীত হৈছে"</string>
     <string name="data_usage_limit_body" msgid="2908179506560812973">"আপোনাৰ ডেটা চক্ৰৰ বাকী অংশৰ বাবে ডেটা পজ কৰা হৈছে"</string>
-    <string name="data_usage_mobile_limit_snoozed_title" msgid="3171402244827034372">"ম\'বাইল ডেটাৰ সীমা পাৰ কৰিছে"</string>
+    <string name="data_usage_mobile_limit_snoozed_title" msgid="3171402244827034372">"ম’বাইল ডেটাৰ সীমা পাৰ কৰিছে"</string>
     <string name="data_usage_wifi_limit_snoozed_title" msgid="3547771791046344188">"ৱাই-ফাই ডেটাৰ সীমা পাৰ কৰিছে"</string>
     <string name="data_usage_limit_snoozed_body" msgid="1671222777207603301">"আপুনি নিৰ্ধাৰিত সীমাতকৈ <xliff:g id="SIZE">%s</xliff:g> অধিক ব্যৱহাৰ কৰিছে"</string>
     <string name="data_usage_restricted_title" msgid="5965157361036321914">"নেপথ্য ডেটা সীমিত কৰি ৰখা হৈছে৷"</string>
     <string name="data_usage_restricted_body" msgid="469866376337242726">"সীমাবদ্ধতা আঁতৰাবলৈ টিপক।"</string>
-    <string name="data_usage_rapid_title" msgid="1809795402975261331">"অত্যধিক ম\'বাইল ডেটাৰ ব্যৱহাৰ"</string>
+    <string name="data_usage_rapid_title" msgid="1809795402975261331">"অত্যধিক ম’বাইল ডেটাৰ ব্যৱহাৰ"</string>
     <string name="data_usage_rapid_body" msgid="6897825788682442715">"আপোনাৰ এপসমূহে সচৰাচৰতকৈ অধিক ডেটা ব্যৱহাৰ কৰিছে"</string>
     <string name="data_usage_rapid_app_body" msgid="5396680996784142544">"<xliff:g id="APP">%s</xliff:g>এ সচৰাচৰতকৈ অধিক ডেটা ব্যৱহাৰ কৰিছে"</string>
     <string name="ssl_certificate" msgid="6510040486049237639">"নিৰাপত্তা সম্পৰ্কীয় প্ৰমাণপত্ৰ"</string>
@@ -1568,7 +1577,7 @@
     <string name="default_audio_route_name_usb" msgid="1234984851352637769">"ইউএছবি"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"ছিষ্টেম"</string>
     <string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"ব্লুটুথ অডিঅ\'"</string>
-    <string name="wireless_display_route_description" msgid="9070346425023979651">"ৱায়াৰলেচ ডিছপ্লে\'"</string>
+    <string name="wireless_display_route_description" msgid="9070346425023979651">"ৱায়াৰলেচ ডিছপ্লে’"</string>
     <string name="media_route_button_content_description" msgid="591703006349356016">"কাষ্ট"</string>
     <string name="media_route_chooser_title" msgid="1751618554539087622">"ডিভাইচৰ লগত সংযোগ কৰক"</string>
     <string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"ডিভাইচত স্ক্ৰীণ কাষ্ট কৰক"</string>
@@ -1732,11 +1741,11 @@
     <string name="mediasize_japanese_you4" msgid="2091777168747058008">"You4"</string>
     <string name="mediasize_unknown_portrait" msgid="3088043641616409762">"অজ্ঞাত প\'ৰ্ট্ৰেইট"</string>
     <string name="mediasize_unknown_landscape" msgid="4876995327029361552">"অজ্ঞাত লেণ্ডস্কেইপ"</string>
-    <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"বাতিল কৰা হ\'ল"</string>
+    <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"বাতিল কৰা হ’ল"</string>
     <string name="write_fail_reason_cannot_write" msgid="8132505417935337724">"সমল লিখাত আসোঁৱাহ"</string>
     <string name="reason_unknown" msgid="6048913880184628119">"অজ্ঞাত"</string>
     <string name="reason_service_unavailable" msgid="7824008732243903268">"প্ৰিণ্টিং সেৱা সক্ষম নহয়"</string>
-    <string name="print_service_installed_title" msgid="2246317169444081628">"<xliff:g id="NAME">%s</xliff:g> সেৱা ইনষ্টল কৰা হ\'ল"</string>
+    <string name="print_service_installed_title" msgid="2246317169444081628">"<xliff:g id="NAME">%s</xliff:g> সেৱা ইনষ্টল কৰা হ’ল"</string>
     <string name="print_service_installed_message" msgid="5897362931070459152">"সক্ষম কৰিবলৈ টিপক"</string>
     <string name="restr_pin_enter_admin_pin" msgid="8641662909467236832">"প্ৰশাসকৰ পিন দিয়ক"</string>
     <string name="restr_pin_enter_pin" msgid="3395953421368476103">"পিন দিয়ক"</string>
@@ -1755,14 +1764,14 @@
     <string name="immersive_cling_title" msgid="8394201622932303336">"স্ক্ৰীণ পূৰ্ণৰূপত চাই আছে"</string>
     <string name="immersive_cling_description" msgid="3482371193207536040">"বাহিৰ হ\'বলৈ ওপৰৰপৰা তললৈ ছোৱাইপ কৰক।"</string>
     <string name="immersive_cling_positive" msgid="5016839404568297683">"বুজি পালোঁ"</string>
-    <string name="done_label" msgid="2093726099505892398">"সম্পন্ন কৰা হ\'ল"</string>
+    <string name="done_label" msgid="2093726099505892398">"সম্পন্ন কৰা হ’ল"</string>
     <string name="hour_picker_description" msgid="6698199186859736512">"ঘড়ীৰ বৃত্তাকাৰ শ্লাইডাৰ"</string>
     <string name="minute_picker_description" msgid="8606010966873791190">"মিনিটৰ বৃত্তাকাৰ শ্লাইডাৰ"</string>
     <string name="select_hours" msgid="6043079511766008245">"ঘণ্টা বাছনি কৰক"</string>
     <string name="select_minutes" msgid="3974345615920336087">"মিনিট বাছনি কৰক"</string>
     <string name="select_day" msgid="7774759604701773332">"মাহ আৰু দিন বাছনি কৰক"</string>
     <string name="select_year" msgid="7952052866994196170">"বছৰ বাছনি কৰক"</string>
-    <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> মচা হ\'ল"</string>
+    <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> মচা হ’ল"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"কৰ্মস্থান <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="managed_profile_label_badge_2" msgid="5048136430082124036">"২য় কার্য <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="managed_profile_label_badge_3" msgid="2808305070321719040">"৩য় কার্য <xliff:g id="LABEL">%1$s</xliff:g>"</string>
@@ -1774,7 +1783,7 @@
     <string name="package_deleted_device_owner" msgid="2307122077550236438">"আপোনাৰ প্ৰশাসকে মচিছে"</string>
     <string name="battery_saver_description_with_learn_more" msgid="6323937147992667707">"আপোনাৰ বেটাৰিৰ অৱস্থা উন্নত কৰিবলৈ বেটাৰি সঞ্চয়কাৰীয়ে ডিভাইচৰ কিছুমান সুবিধা অফ কৰে আৰু এপসমূহক সীমিত কৰে। "<annotation id="url">"অধিক জানক"</annotation></string>
     <string name="battery_saver_description" msgid="769989536172631582">"আপোনাৰ বেটাৰিৰ অৱস্থা উন্নত কৰিবলৈ বেটাৰি সঞ্চয়কাৰীয়ে ডিভাইচৰ কিছুমান সুবিধা অফ কৰে আৰু এপসমূহক সীমিত কৰে।"</string>
-    <string name="data_saver_description" msgid="6015391409098303235">"ডেটা ব্য়ৱহাৰ মাত্ৰা কম কৰিবৰ বাবে ডেটা সঞ্চয়কাৰীয়ে কিছুমান এপক নেপথ্য়ত ডেটা প্ৰেৰণ বা সংগ্ৰহ কৰাত বাধা প্ৰদান কৰে। আপুনি বৰ্তমান ব্য়ৱহাৰ কৰি থকা এটা এপে ডেটা ব্য়ৱহাৰ কৰিব পাৰে, কিন্তু সঘনাই এই কার্য কৰিব নোৱাৰিব পাৰে। ইয়াৰ অৰ্থ এইয়ে হ\'ব পাৰে যে, উদাহৰণস্বৰূপে, আপুনি নিটিপা পর্যন্ত প্ৰতিচ্ছবিসমূহ দেখুওৱা নহ\'ব।"</string>
+    <string name="data_saver_description" msgid="6015391409098303235">"ডেটা ব্য়ৱহাৰ মাত্ৰা কম কৰিবৰ বাবে ডেটা সঞ্চয়কাৰীয়ে কিছুমান এপক নেপথ্য়ত ডেটা প্ৰেৰণ বা সংগ্ৰহ কৰাত বাধা প্ৰদান কৰে। আপুনি বৰ্তমান ব্য়ৱহাৰ কৰি থকা এটা এপে ডেটা ব্য়ৱহাৰ কৰিব পাৰে, কিন্তু সঘনাই এই কার্য কৰিব নোৱাৰিব পাৰে। ইয়াৰ অৰ্থ এইয়ে হ\'ব পাৰে যে, উদাহৰণস্বৰূপে, আপুনি নিটিপা পর্যন্ত প্ৰতিচ্ছবিসমূহ দেখুওৱা নহ’ব।"</string>
     <string name="data_saver_enable_title" msgid="4674073932722787417">"ডেটা সঞ্চয়কাৰী অন কৰিবনে?"</string>
     <string name="data_saver_enable_button" msgid="7147735965247211818">"অন কৰক"</string>
     <plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
@@ -1824,14 +1833,14 @@
     <string name="muted_by" msgid="5942954724562097128">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>এ কিছুমান ধ্বনি মিউট কৰি আছে"</string>
     <string name="system_error_wipe_data" msgid="6608165524785354962">"আপোনাৰ ডিভাইচত এটা আভ্যন্তৰীণ সমস্যা আছে আৰু আপুনি ফেক্টৰী ডেটা ৰিছেট নকৰালৈকে ই সুস্থিৰভাৱে কাম নকৰিব পাৰে।"</string>
     <string name="system_error_manufacturer" msgid="8086872414744210668">"আপোনাৰ ডিভাইচত এটা আভ্যন্তৰীণ সমস্যা আছে। সবিশেষ জানিবৰ বাবে আপোনাৰ ডিভাইচ নির্মাতাৰ সৈতে যোগাযোগ কৰক।"</string>
-    <string name="stk_cc_ussd_to_dial" msgid="5214333646366591205">"USSD অনুৰোধ নিয়মীয়া কললৈ সলনি কৰা হ\'ল"</string>
-    <string name="stk_cc_ussd_to_ss" msgid="4884994189414782605">"USSD অনুৰোধ SS অনুৰোধলৈ সলনি কৰা হ\'ল"</string>
-    <string name="stk_cc_ussd_to_ussd" msgid="5728637484565449312">"নতুন USSD অনুৰোধলৈ সলনি কৰা হ\'ল"</string>
-    <string name="stk_cc_ussd_to_dial_video" msgid="4134455726513175559">"USSD SS অনুৰোধ ভিডিঅ\' কললৈ সলনি কৰা হ\'ল"</string>
-    <string name="stk_cc_ss_to_dial" msgid="1360775164651754978">"SS অনুৰোধ নিয়মীয়া কললৈ সলনি কৰা হ\'ল"</string>
-    <string name="stk_cc_ss_to_dial_video" msgid="6577956662913194947">"SS অনুৰোধ ভিডিঅ\' কললৈ সলনি কৰা হ\'ল"</string>
-    <string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS অনুৰোধ USSD অনুৰোধলৈ সলনি কৰা হ\'ল"</string>
-    <string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"নতুন SS অনুৰোধলৈ সলনি কৰা হ\'ল"</string>
+    <string name="stk_cc_ussd_to_dial" msgid="5214333646366591205">"USSD অনুৰোধ নিয়মীয়া কললৈ সলনি কৰা হ’ল"</string>
+    <string name="stk_cc_ussd_to_ss" msgid="4884994189414782605">"USSD অনুৰোধ SS অনুৰোধলৈ সলনি কৰা হ’ল"</string>
+    <string name="stk_cc_ussd_to_ussd" msgid="5728637484565449312">"নতুন USSD অনুৰোধলৈ সলনি কৰা হ’ল"</string>
+    <string name="stk_cc_ussd_to_dial_video" msgid="4134455726513175559">"USSD SS অনুৰোধ ভিডিঅ\' কললৈ সলনি কৰা হ’ল"</string>
+    <string name="stk_cc_ss_to_dial" msgid="1360775164651754978">"SS অনুৰোধ নিয়মীয়া কললৈ সলনি কৰা হ’ল"</string>
+    <string name="stk_cc_ss_to_dial_video" msgid="6577956662913194947">"SS অনুৰোধ ভিডিঅ\' কললৈ সলনি কৰা হ’ল"</string>
+    <string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS অনুৰোধ USSD অনুৰোধলৈ সলনি কৰা হ’ল"</string>
+    <string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"নতুন SS অনুৰোধলৈ সলনি কৰা হ’ল"</string>
     <string name="notification_work_profile_content_description" msgid="4600554564103770764">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
     <string name="notification_alerted_content_description" msgid="1296617716556420585">"সতৰ্ক কৰা হ’ল"</string>
     <string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"বিস্তাৰ কৰক"</string>
@@ -1846,8 +1855,8 @@
     <string name="close_button_text" msgid="3937902162644062866">"বন্ধ কৰক"</string>
     <string name="notification_messaging_title_template" msgid="3452480118762691020">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
     <plurals name="selected_count" formatted="false" msgid="7187339492915744615">
-      <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g>টা বাছনি কৰা হ\'ল</item>
-      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>টা বাছনি কৰা হ\'ল</item>
+      <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g>টা বাছনি কৰা হ’ল</item>
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>টা বাছনি কৰা হ’ল</item>
     </plurals>
     <string name="default_notification_channel_label" msgid="5929663562028088222">"শ্ৰেণীবদ্ধ নকৰা"</string>
     <string name="importance_from_user" msgid="7318955817386549931">"এই জাননীবোৰৰ গুৰুত্ব আপুনি ছেট কৰব লাগিব।"</string>
@@ -1867,8 +1876,8 @@
     <string name="work_mode_off_title" msgid="1118691887588435530">"কৰ্মস্থানৰ প্ৰ\'ফাইল অন কৰিবনে?"</string>
     <string name="work_mode_off_message" msgid="5130856710614337649">"আপোনাৰ কৰ্মস্থানৰ এপসমূহ, জাননীসমূহ, ডেটা আৰু কৰ্মস্থানৰ প্ৰ\'ফাইলৰ অইন সুবিধাসমূহ অন কৰা হ\'ব"</string>
     <string name="work_mode_turn_on" msgid="2062544985670564875">"অন কৰক"</string>
-    <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"এই এপটো Androidৰ এটা পুৰণা সংস্কৰণৰ বাবে প্ৰস্তুত কৰা হৈছিল, আৰু ই বিচৰাধৰণে কাম নকৰিবও পাৰে। ইয়াৰ আপডেট আছে নেকি চাওক, বা বিকাশকৰ্তাৰ সৈতে যোগাযোগ কৰক।"</string>
-    <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"আপডেট আছে নেকি চাওক"</string>
+    <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"এই এপটো Androidৰ এটা পুৰণা সংস্কৰণৰ বাবে প্ৰস্তুত কৰা হৈছিল, আৰু ই বিচৰাধৰণে কাম নকৰিবও পাৰে। ইয়াৰ আপডে’ট আছে নেকি চাওক, বা বিকাশকৰ্তাৰ সৈতে যোগাযোগ কৰক।"</string>
+    <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"আপডে’ট আছে নেকি চাওক"</string>
     <string name="new_sms_notification_title" msgid="8442817549127555977">"আপুনি নতুন বার্তা লাভ কৰিছে"</string>
     <string name="new_sms_notification_content" msgid="7002938807812083463">"চাবলৈ এছএমএছ এপ্ খোলক"</string>
     <string name="user_encrypted_title" msgid="9054897468831672082">"কিছুমান কৰ্মক্ষমতা সীমিত হ\'ব পাৰে"</string>
@@ -1884,7 +1893,7 @@
     <string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="demo_starting_message" msgid="5268556852031489931">"ডেম\' আৰম্ভ কৰি থকা হৈছে…"</string>
     <string name="demo_restarting_message" msgid="952118052531642451">"ডিভাইচটো আকৌ ছেটিং কৰি থকা হৈছে…"</string>
-    <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g>ক অক্ষম কৰা হ\'ল"</string>
+    <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g>ক অক্ষম কৰা হ’ল"</string>
     <string name="conference_call" msgid="3751093130790472426">"কনফাৰেঞ্চ কল"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"টুলটিপ"</string>
     <string name="app_category_game" msgid="5431836943981492993">"গেম"</string>
@@ -1916,10 +1925,10 @@
     <string name="autofill_save_title_with_type" msgid="2339135393607143594"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"ত <xliff:g id="TYPE">%1$s</xliff:g>ক ছেভ কৰিবনে?"</string>
     <string name="autofill_save_title_with_2types" msgid="87616102361154432"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"ত <xliff:g id="TYPE_0">%1$s</xliff:g> আৰু <xliff:g id="TYPE_1">%2$s</xliff:g>ক ছেভ কৰিবনে?"</string>
     <string name="autofill_save_title_with_3types" msgid="4108978552969604555"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"ত <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> আৰু <xliff:g id="TYPE_2">%3$s</xliff:g>ক ছেভ কৰিবনে?"</string>
-    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"ত আপডে\'ট কৰিবনে?"</string>
-    <string name="autofill_update_title_with_type" msgid="4624181147422762233"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"ত <xliff:g id="TYPE">%1$s</xliff:g> আপডে\'ট কৰিবনে?"</string>
-    <string name="autofill_update_title_with_2types" msgid="2300113827053626484"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"ত <xliff:g id="TYPE_0">%1$s</xliff:g> আৰু <xliff:g id="TYPE_1">%2$s</xliff:g> আপডে\'ট কৰিবনে?"</string>
-    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"এই তথ্যবোৰ "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> আৰু <xliff:g id="TYPE_2">%3$s</xliff:g>ত আপডে\'ট কৰিবনে ?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"ত আপডে’ট কৰিবনে?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"ত <xliff:g id="TYPE">%1$s</xliff:g> আপডে’ট কৰিবনে?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"ত <xliff:g id="TYPE_0">%1$s</xliff:g> আৰু <xliff:g id="TYPE_1">%2$s</xliff:g> আপডে’ট কৰিবনে?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"এই তথ্যবোৰ "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> আৰু <xliff:g id="TYPE_2">%3$s</xliff:g>ত আপডে’ট কৰিবনে ?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"ছেভ কৰক"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"নালাগে, ধন্যবাদ"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"আপডে’ট কৰক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 55966cf..076ea8d 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Tətbiqə cihazın telefon funksiyalarına giriş icazəsi verir. Belə icazəli tətbiq bu telefonun nömrəsini və cihaz İD\'ni, zəngin aktiv olub-olmadığını və zəng edilən nömrəni müəyyən edə bilər."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"zəngləri sistem üzərindən yönləndirin"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Tətbiqə, zəng təcrübəsini yaxşılaşdırmaq üçün, zəngləri sistem üzərindən yönləndirməyə icazə verilir."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"zənglərə sistemdə baxın və nəzarət edin."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Tətbiqin cihazda davam edən zəngləri görməsinə və nəzarət etməsinə icazə verin. Bura zəng edən nömrələr və zənglərin statusu haqqında məlumat daxildir."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"zəngə digər tətbiqdən davam edin"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Tətbiqə digər tətbiqdə başlayan zəngə davam etmək icazəsi verilir."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"telefon nömrələrini oxuyun"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Tətbiqə Yaxın Məsafə Kommunikasiyası (NFC) teqləri, kartları və oxuyucuları ilə əlaqə qurmağa icazə verir."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"Ekran kilidini deaktiv edir"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Tətbiqə kilid açarını və təhlükəsizlik parolunu deaktiv etməyə imkan verir. Qanuni misal budur ki, telefon zəng qəbul edən zaman kilidi açır və zəng qurtarandan sonra kilidi bağlayır."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"ekran kilidi mürəkkəbliliyini əldə edin və tələb edin"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Tətbiqə ekran kilidinin uzunluq intervalı və növünü göstərən ekran kilidi mürəkkəbliliyini (yüksək, orta, aşağı və ya heçbiri) öyrənməyə icazə verir. Tətbiq, istifadəçilərə ekran kilidini müəyyən səviyyəyə yeniləməyi təklif edə bilər, lakin istifadəçilər istənilən vaxt bunu iqnor edə bilər. Nəzərə alın ki, ekran kilidi şifrələndiyinə görə tətbiq parolu dəqiq bilmir."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"biometrik proqramdan istifadə edin"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Doğrulama üçün biometrik proqramdan istifadə etməyə imkan verir"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"barmaq izi avadanlığını idarə edin"</string>
@@ -1083,23 +1087,23 @@
     <string name="deleteText" msgid="6979668428458199034">"Sil"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Daxiletmə metodu"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Mətn əməliyyatları"</string>
-    <string name="email" msgid="4560673117055050403">"E-poçt"</string>
+    <string name="email" msgid="4560673117055050403">"E-poçtu aç"</string>
     <string name="email_desc" msgid="3638665569546416795">"Seçilmiş ünvana e-məktub yazın"</string>
-    <string name="dial" msgid="1253998302767701559">"Zəng"</string>
+    <string name="dial" msgid="1253998302767701559">"Zəng edin"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Seçilmiş telefon nömrəsinə zəng edin"</string>
-    <string name="map" msgid="5441053548030107189">"Xəritə"</string>
+    <string name="map" msgid="5441053548030107189">"Xəritəni aç"</string>
     <string name="map_desc" msgid="1836995341943772348">"Seçilmiş ünvanları tapın"</string>
     <string name="browse" msgid="1245903488306147205">"Açın"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Seçilmiş linki açın"</string>
-    <string name="sms" msgid="4560537514610063430">"Mesaj"</string>
+    <string name="sms" msgid="4560537514610063430">"Mesaj yazın"</string>
     <string name="sms_desc" msgid="7526588350969638809">"Seçilmiş telefon nömrəsini mesajla göndərin"</string>
     <string name="add_contact" msgid="7867066569670597203">"Əlavə edin"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Kontakta əlavə edin"</string>
     <string name="view_calendar" msgid="979609872939597838">"Baxın"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"Təqvimdə seçilmiş vaxta baxın"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"Cədvəl"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"Təqvimdə planlayın"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"Tədbiri seçilmiş vaxta planlaşdırın"</string>
-    <string name="view_flight" msgid="7691640491425680214">"Trek"</string>
+    <string name="view_flight" msgid="7691640491425680214">"İzləyin"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"Seçilmiş uçuşu izləyin"</string>
     <string name="translate" msgid="9218619809342576858">"Tərcümə edin"</string>
     <string name="translate_desc" msgid="4502367770068777202">"Seçilmiş mətni tərcümə edin"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Planşet yenilənir…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Cihaz yenilənir…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Telefon başlayır…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android işə başlayır..."</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Planşet başlayır…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Cihaz başlayır…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Yaddaş optimallaşdırılır."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Bütün şəbəkələri görmək üçün klikləyin"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Qoşulun"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Bütün şəbəkələr"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> tərəfindən təklif edilən Wi‑Fi  şəbəkəsinə qoşuldu"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"<xliff:g id="NAME">%s</xliff:g> tərəfindən şəbəkələrin təklif edilməsinə icazə vermək itəyirsiniz?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Bəli"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Xeyr"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi avtomatik olaraq aktiv ediləcək"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Yadda saxlanmış yüksək keyfiyyətli şəbəkələr yaxınlıqda olduqda"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Yenidən aktiv etməyin"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 9c45ba8..0a94acf 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -450,6 +450,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Dozvoljava aplikaciji da pristupa funkcijama telefona na uređaju. Ova dozvola omogućava aplikaciji da utvrdi broj telefona i ID-ove uređaja, zatim da li je poziv aktivan, kao i broj daljinskog uređaja sa kojim je uspostavljen poziv."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"preusmeravanje poziva preko sistema"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Dozvoljava aplikaciji da preusmerava pozive preko sistema da bi poboljšala doživljaj pozivanja."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"pregled i kontrola poziva preko sistema."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Dozvoljava aplikaciji da pregleda i kontroliše trenutne pozive na uređaju. To obuhvata informacije poput brojeva telefona i statusa poziva."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"nastavi poziv u drugoj aplikaciji"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Dozvoljava aplikaciji da nastavi poziv koji je započet u drugoj aplikaciji."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"čitanje brojeva telefona"</string>
@@ -510,6 +512,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Dozvoljava aplikaciji da komunicira sa oznakama, karticama i čitačima komunikacije kratkog dometa (NFC)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"onemogućavanje zaključavanja ekrana"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Dozvoljava aplikaciji da onemogući zaključavanje tastature i sve povezane bezbednosne mere sa lozinkama. Na primer, telefon onemogućava zaključavanje tastature pri prijemu dolaznog telefonskog poziva, a zatim ga ponovo omogućava po završetku poziva."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"dobijanje i traženje nivoa složenosti zaključavanja ekrana"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Dozvoljava aplikaciji da sazna nivo složenosti zaključavanja ekrana (visoka, srednja, niska ili nijedna), što ukazuje na mogući opseg trajanja i tip zaključavanja ekrana. Aplikacija može i da predlaže korisnicima da ažuriraju zaključavanje ekrana na određeni nivo, ali korisnici slobodno mogu da zanemare to i da idu na druge stranice. Imajte na umu da se podaci za zaključavanje ekrana ne čuvaju kao običan tekst, pa aplikacija ne zna tačnu lozinku."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"koristi biometrijski hardver"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Dozvoljava aplikaciji da koristi biometrijski hardver za potvrdu identiteta"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"upravljaj hardverom za otiske prstiju"</string>
@@ -1107,7 +1111,7 @@
     <string name="email_desc" msgid="3638665569546416795">"Pošaljite imejl na izabranu adresu"</string>
     <string name="dial" msgid="1253998302767701559">"Pozovi"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Pozovite izabrani broj telefona"</string>
-    <string name="map" msgid="5441053548030107189">"Mapa"</string>
+    <string name="map" msgid="5441053548030107189">"Prikaži na mapi"</string>
     <string name="map_desc" msgid="1836995341943772348">"Pronađite izabranu adresu"</string>
     <string name="browse" msgid="1245903488306147205">"Otvori"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Otvorite izabrani URL"</string>
@@ -1201,6 +1205,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet se ažurira…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Uređaj se ažurira…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Telefon se pokreće…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android se pokreće…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet se pokreće…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Uređaj se pokreće…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Memorija se optimizuje."</string>
@@ -1261,6 +1266,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Dodirnite da biste videli sve mreže"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Poveži"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Sve mreže"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Povezani ste sa Wi‑Fi mrežom koju predlaže <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Želite li da dozvolite da vam <xliff:g id="NAME">%s</xliff:g> predlaže mreže?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Da"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Ne"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi će se automatski uključiti"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Kada ste u blizini sačuvane mreže visokog kvaliteta"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ne uključuj ponovo"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 930feba..cfe076d 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -453,6 +453,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Дазваляе прыкладанням атрымлiваць доступ да функцый тэлефона на прыладзе. Дзякуючы гэтаму дазволу прыкладанне можа вызначаць iдэнтыфiкатары нумару тэлефона i прылады, незалежна ад таго, цi актыўны выклiк, i аддалены нумар, на якi робiцца выклiк."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"перанакіраванне выклікаў праз сістэму"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Дазваляе праграме перанакіроўваць выклікі праз сістэму ў мэтах паляпшэння выклікаў."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"праглядаць выклікі і кіраваць імі праз сістэму."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Дазваляе праграме праглядаць на прыладзе ўваходныя выклікі і кіраваць імі. Гэта інфармацыя ўключае нумары выклікаў і звесткі пра іх краіну."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"працяг выкліку з іншай праграмы"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Дазваляе праграме працягваць выклік, які пачаўся ў іншай праграме."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"счытваць нумары тэлефонаў"</string>
@@ -513,6 +515,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Дазваляе прыкладаннzv спалучацца з тэгамі, картамі і счытваючымі прыладамі Near Field Communication (NFC)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"адключэнне блакiроўкi экрана"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Дазваляе прыкладанням адключаць блакiроўку клавіятуры і любыя сродкі абароны, звязаныя з паролем. Прыкладам гэтага з\'яўляецца адключэнне тэлефонам блакiроўкi клавіятуры пры атрыманні ўваходнага выкліку і паўторнае ўключэнне блакiроўкi клавіятуры, калі выклік завершаны."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"атрымліваць і адсылаць запыты наконт узроўню складанасці блакіроўкі экрана"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Дазваляе праграме вызначаць узровень складанасці блакіроўкі экрана (высокі, сярэдні, нізкі ці нулявы), які залежыць ад даўжыні пароля і ад тыпу блакіроўкі экрана. Праграма можа прапанаваць карыстальнікам ускладніць блакіроўку экрана, аднак гэту прапанову можна ігнараваць. Заўважце, што праграма не можа ведаць тып і пароль блакіроўкі экрана, таму што яны захоўваюцца ў зашыфраваным выглядзе."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"выкарыстоўваць біяметрычнае абсталяванне"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Дазваляе праграме выкарыстоўваць для аўтэнтыфікацыі біяметрычнае абсталяванне"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"кіраваць апаратнымі сродкамі для адбіткаў пальцаў"</string>
@@ -1123,21 +1127,21 @@
     <string name="deleteText" msgid="6979668428458199034">"Выдалiць"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Метад уводу"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Дзеянні з тэкстам"</string>
-    <string name="email" msgid="4560673117055050403">"Электронная пошта"</string>
+    <string name="email" msgid="4560673117055050403">"Напісаць ліст"</string>
     <string name="email_desc" msgid="3638665569546416795">"Напісаць электронны ліст на выбраны адрас"</string>
     <string name="dial" msgid="1253998302767701559">"Выклікаць"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Звязацца з абанентам"</string>
-    <string name="map" msgid="5441053548030107189">"Карта"</string>
+    <string name="map" msgid="5441053548030107189">"Адкрыць карту"</string>
     <string name="map_desc" msgid="1836995341943772348">"Паказаць выбраны адрас на карце"</string>
     <string name="browse" msgid="1245903488306147205">"Адкрыць"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Адкрыць URL у браўзеры"</string>
-    <string name="sms" msgid="4560537514610063430">"Паведамленне"</string>
+    <string name="sms" msgid="4560537514610063430">"Напісаць SMS"</string>
     <string name="sms_desc" msgid="7526588350969638809">"Адправіць паведамленне на выбраны нумар"</string>
     <string name="add_contact" msgid="7867066569670597203">"Дадаць"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Дадаць у кантакты"</string>
     <string name="view_calendar" msgid="979609872939597838">"Прагледзець"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"Паказаць выбраны час у календары"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"Графік"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"Запланаваць"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"Запланаваць падзею ў выбраны час"</string>
     <string name="view_flight" msgid="7691640491425680214">"Сачыць"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"Адсочваць рэйс"</string>
@@ -1221,6 +1225,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Абнаўленне планшэта…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Абнаўленне прылады…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Запуск тэлефона…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android запускаецца..."</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Запуск планшэта…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Запуск прылады…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Аптымізацыя сховішча."</string>
@@ -1283,6 +1288,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Дакраніцеся, каб убачыць усе сеткі"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Падключыцца"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Усе сеткі"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Падключана да сеткі Wi‑Fi, прапанаванай праграмай \"<xliff:g id="NAME">%s</xliff:g>\""</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Хочаце дазволіць праграме \"<xliff:g id="NAME">%s</xliff:g>\" прапаноўваць вам сеткі?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Так"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Не"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi уключыцца аўтаматычна"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Побач з захаванай сеткай з высакаякасным сігналам"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Не ўключаць зноў"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index d2857e8..8897bac 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Разрешава на приложението достъп до телефонните функции на устройството. Това разрешение позволява на приложението да определя телефонния номер и идентификационния номер на устройството, дали се води разговор и отдалечения номер, до който е установена връзка с обаждането."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"маршрутизиране на обажданията чрез системата"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Разрешава на приложението да маршрутизира обажданията си чрез системата с цел подобряване на свързаната с тях практическа работа."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"вижда и управлява обажданията чрез системата."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Разрешава на приложението да вижда и управлява текущите обаждания на устройството. Това включва различна информация, като например номерата и състоянието на обажданията."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"продължаване на обаждане от друго приложение"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Разрешава на приложението да продължи обаждане, стартирано в друго приложение."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"четене на телефонните номера"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Разрешава на приложението да комуникира с маркери, карти и четци, ползващи комуникация в близкото поле (NFC)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"деактивиране на заключването на екрана ви"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Разрешава на приложението да деактивира заключването на клавиатурата и свързаната защита с парола. Например телефонът деактивира заключването при получаване на входящо обаждане и после го активира отново, когато обаждането завърши."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"получаване и заявяване на сложността на опцията за заключване на екрана"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Разрешава на приложението да разбере нивото на сложност на опцията за заключване на екрана (високо, средно, ниско или липса на такова), което указва възможния диапазон на дължината и типа на опцията. Приложението може също да предложи на потребителите да актуализират опцията за заключване на екрана до определено ниво, но те могат да пренебрегнат това и да излязат от него. Обърнете внимание, че опцията за заключване на екрана не се съхранява като обикновен текст, така че приложението не знае точната парола."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"използване на хардуера за биометрични данни"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Разрешава на приложението да използва хардуера за биометрични данни с цел удостоверяване"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"управление на хардуера за отпечатъци"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Таблетът се актуализира…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Устройството се актуализира…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Телефонът се стартира…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android се стартира…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Таблетът се стартира…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Устройството се стартира…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Хранилището се оптимизира."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Докоснете, за да видите всички мрежи"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Свързване"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Всички мрежи"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Установена е връзка с Wi‑Fi мрежа, предложена от <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Искате ли да разрешите на <xliff:g id="NAME">%s</xliff:g> да ви предлага мрежи?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Да"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Не"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi ще се включи автоматично"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Когато сте в района на запазена мрежа с високо качество"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Без повторно включване"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 80eefc4..26a3ba0 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -318,7 +318,7 @@
     <string name="permgroupdesc_visual" msgid="3415827902566663546">"আপনার ফটো ও ভিডিও অ্যাক্সেস করুন"</string>
     <string name="permgrouprequest_visual" msgid="6907523945030290376">"আপনার ফটো ও ভিডিওতে &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-কে অ্যাক্সেস দেবেন?"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"উইন্ডোর কন্টেন্ট পুনরুদ্ধার করে"</string>
-    <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"আপনি ইন্টারঅ্যাক্ট করছেন এমন একটি উইন্ডোর সামগ্রীকে সযত্নে নিরীক্ষণ করে৷"</string>
+    <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"ব্যবহার করছেন এমন একটি উইন্ডোর কন্টেন্ট নিরীক্ষণ করে৷"</string>
     <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"স্পর্শের মাধ্যমে অন্বেষণ করা চালু করুন"</string>
     <string name="capability_desc_canRequestTouchExploration" msgid="7543249041581408313">"যে আইটেমগুলিতে আলতো চেপেছেন সেগুলি সশব্দে বলবে এবং ইঙ্গিতগুলি ব্যবহার করে স্ক্রিন অন্বেষণ করা যাবে৷"</string>
     <string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"আপনার লেখা পাঠ্যকে নিরীক্ষণ করে"</string>
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"অ্যাপ্লিকেশানটিকে ডিভাইসের ফোন বৈশিষ্ট্যগুলিকে অ্যাক্সেস করার অনুমতি দেয়৷ এই অনুমতিটি অ্যাপ্লিকেশানটিকে একটি কল সক্রিয় থাকা অবস্থায় এবং দূরবর্তী নম্বর একটি কল দ্বারা সংযুক্ত থাকাকালীনও ফোন নম্বর এবং ডিভাইসের IDগুলি নির্ধারণ করার অনুমতি দেয়৷"</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"সিস্টেমের মাধ্যমে কলগুলি রুট করতে দিন"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"কল করার অভিজ্ঞতা উন্নত করার জন্য অ্যাপকে সিস্টেমের মাধ্যমে তার কলগুলি রুট করতে দেয়।"</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"সিস্টেমের মাধ্যমে কল দেখা এবং নিয়ন্ত্রণ করা।"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"ডিভাইসে চালু আছে এমন কল দেখতে এবং নিয়ন্ত্রণ করতে অ্যাপকে অনুমতি দেয়। কল করা হচ্ছে যে নম্বরে সেটি এবং কলের স্ট্যাটাস কী সেই সব তথ্য এতে অন্তর্ভুক্ত।"</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"অন্য কোনও অ্যাপ দিয়ে করে থাকা কল চালিয়ে যান"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"অন্য কোনও অ্যাপ দিয়ে কল করলে এই অ্যাপটিকে সেটি চালিয়ে যেতে দেয়।"</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ফোন নম্বরগুলি পড়া হোক"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"অ্যাপ্লিকেশানকে নিয়ার ফিল্ড কমিউনিকেশন (NFC) ট্যাগ, কার্ড এবং রিডারগুলির সাথে যোগাযোগ করতে দেয়৷"</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"আপনার স্ক্রিন লক অক্ষম করুন"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"কী-লক এবং যেকোনো সংশ্লিষ্ট পাসওয়ার্ড সুরক্ষা অক্ষম করতে অ্যাপ্লিকেশানটিকে মঞ্জুর করে৷ উদাহরণস্বরূপ, একটি ইনকামিং ফোন কল গ্রহণ করার সময়ে ফোনটি কী-লক অক্ষম করে, তারপরে কল শেষ হয়ে গেলে কী-লকটিকে আবার সক্ষম করে৷"</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"স্ক্রিন লক কমপ্লেক্সিটি প্যাটার্নটির জন্য অনুরোধ জানান এবং ব্যবহার করুন"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"স্ক্রিন লক কমপ্লেক্সিটি প্যাটার্নটির লেভেল (বেশি, মাঝারি, কম বা কোনও কিছুই নেই) জানতে অ্যাপকে অনুমতি দিন, যা সম্ভাব্য স্ক্রিন লকের ধরন এবং তা কতটা দীর্ঘ হবে তার ইঙ্গিত দেয়। একটি নির্দিষ্ট লেভেল পর্যন্ত স্ক্রিন লক আপডেট করা যাবে তাও এই অ্যাপটি সাজেস্ট করতে পারে, তবে ব্যবহারকারীর তা উপেক্ষা করার এবং অন্য কোথাও চলে যাওয়ার স্বাধীনতা আছে। মনে রাখবেন প্লেনটেক্সটে স্ক্রিন লক স্টোর করা হয় না, তাই সঠিক পাসওয়ার্ড অ্যাপের পক্ষে জানা সম্ভব নয়।"</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"বায়োমেট্রিক হার্ডওয়্যার ব্যবহার করুন"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"অ্যাপটিকে যাচাইকরণের জন্য বায়োমেট্রিক হার্ডওয়্যার ব্যবহার করার অনুমতি দেয়"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"আঙ্গুলের ছাপ নেওয়ার হার্ডওয়্যার পরিচালনা করুন"</string>
@@ -1083,23 +1087,23 @@
     <string name="deleteText" msgid="6979668428458199034">"মুছুন"</string>
     <string name="inputMethod" msgid="1653630062304567879">"ইনপুট পদ্ধতি"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"পাঠ্য ক্রিয়াগুলি"</string>
-    <string name="email" msgid="4560673117055050403">"ইমেল"</string>
+    <string name="email" msgid="4560673117055050403">"ইমেল করুন"</string>
     <string name="email_desc" msgid="3638665569546416795">"বেছে নেওয়া আইডিতে ইমেল পাঠান"</string>
-    <string name="dial" msgid="1253998302767701559">"কল"</string>
+    <string name="dial" msgid="1253998302767701559">"কল খুলুন"</string>
     <string name="dial_desc" msgid="6573723404985517250">"বেছে নেওয়া ফোন নম্বরে কল করুন"</string>
-    <string name="map" msgid="5441053548030107189">"ম্যাপ"</string>
+    <string name="map" msgid="5441053548030107189">"ম্যাপ খুলুন"</string>
     <string name="map_desc" msgid="1836995341943772348">"বেছে নেওয়া ঠিকানাটি ম্যাপে দেখুন"</string>
     <string name="browse" msgid="1245903488306147205">"খুলুন"</string>
-    <string name="browse_desc" msgid="8220976549618935044">"বেছে নেওয়া ইউআরএলে যান"</string>
-    <string name="sms" msgid="4560537514610063430">"মেসেজ"</string>
+    <string name="browse_desc" msgid="8220976549618935044">"বেছে নেওয়া ইউআরএল-এ যান"</string>
+    <string name="sms" msgid="4560537514610063430">"মেসেজ করুন"</string>
     <string name="sms_desc" msgid="7526588350969638809">"বেছে নেওয়া ফোন নম্বরে মেসেজ পাঠান"</string>
     <string name="add_contact" msgid="7867066569670597203">"যোগ করুন"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"পরিচিতিতে যোগ করুন"</string>
     <string name="view_calendar" msgid="979609872939597838">"দেখুন"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"বেছে নেওয়া দিনটি ক্যালেন্ডারে দেখুন"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"সময়সূচি"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"সময়সূচি সেট করুন"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"বেছে নেওয়া সময়ে ইভেন্ট সেট করুন"</string>
-    <string name="view_flight" msgid="7691640491425680214">"ট্র্যাক"</string>
+    <string name="view_flight" msgid="7691640491425680214">"ট্র্যাক করুন"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"বেছে নেওয়া ফ্লাইট ট্র্যাক করুন"</string>
     <string name="translate" msgid="9218619809342576858">"অনুবাদ করুন"</string>
     <string name="translate_desc" msgid="4502367770068777202">"বেছে নেওয়া টেক্সট অনুবাদ করুন"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ট্যাবলেট আপডেট করা হচ্ছে…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"ডিভাইস আপডেট করা হচ্ছে…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"ফোন চালু করা হচ্ছে…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android চালু হচ্ছে…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"ট্যাবলেট চালু করা হচ্ছে…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"ডিভাইস চালু করা হচ্ছে…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"স্টোরেজ অপ্টিমাইজ করা হচ্ছে৷"</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"সমস্ত নেটওয়ার্ক দেখতে ট্যাপ করুন"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"সংযুক্ত করুন"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"সব নেটওয়ার্ক"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g>-এর সাজেস্ট করা ওয়াই-ফাইয়ে নেটওয়ার্ক কানেক্ট করা হয়েছে"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"আপনি <xliff:g id="NAME">%s</xliff:g>-কে নেটওয়ার্ক সাজেস্ট করতে দিতে চান?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"হ্যাঁ"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"না"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"ওয়াই-ফাই অটোমেটিক চালু হবে"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"যখন আপনি একটি উচ্চ মানের সংরক্ষিত নেটওয়ার্ক কাছাকাছি থাকেন"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"আবার চালু করবেন না"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 718cba4..e336cd0 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -450,6 +450,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Omogućava aplikaciji pristup telefonskim funkcijama uređaja. Ovo odobrenje omogućava aplikaciji određivanje telefonskog i identifikacionog broja uređaja, bez obzira da li je poziv aktivan i da li je uspostavljena veza sa pozivanim brojem."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"usmjeravanje poziva preko sistema"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Dopušta aplikaciji da pozive usmjeri preko sistema radi poboljšanja iskustva pozivanja."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"vidjeti i kontrolirati pozive preko sistema."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Dozvoljava aplikaciji da vidi i kontrolira odlazne pozive na uređaju. To uključuje informacije kao što su brojevi telefona i stanja poziva."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"nastavlja poziv iz druge aplikacije"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Dozvoljava aplikaciji nastavljanje poziva koji je započet u drugoj aplikaciji."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"čitanje telefonskih brojeva"</string>
@@ -510,6 +512,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Dozvoljava aplikaciji komuniciranje sa NFC (komunikacija bliskog polja) oznakama, karticama i čitačima."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"deaktivacija zaključavanja ekrana"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Omogućava aplikaciji deaktivaciju zaključane tastature i svih povezanih zaštita. Naprimjer, telefon deaktivira zaključavanje tastature kod dolaznog telefonskog poziva, a zatim ponovo aktivira zaključavanje tastature kada je poziv završen."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"preuzimanje i traženje nivoa kompleksnosti zaključavanja ekrana"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Omogućava aplikaciji da sazna nivo kompleksnosti zaključavanja ekrana (visoki, srednji, niski ili bez zaključavanja), što naznačava mogući raspon trajanja i vrste zaključavanja ekrana. Aplikacija također može korisnicima predložiti da ažuriraju zaključavanje ekrana do određenog nivoa ali korisnici slobodno mogu ignorirati prijedlog i napustiti stranicu. Važno je napomenuti da se zaključavanje ekrana ne pohranjuje kao obični tekst tako da aplikacija ne zna tačnu lozinku."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"koristi biometrijski hardver za otiske prstij"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Omogućava aplikaciji da za autentifikaciju koristi biometrijski hardver"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"upravljanje hardverom za otiske prstiju"</string>
@@ -1103,28 +1107,28 @@
     <string name="deleteText" msgid="6979668428458199034">"Izbriši"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Način unosa"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Akcije za tekst"</string>
-    <string name="email" msgid="4560673117055050403">"E-pošta"</string>
-    <string name="email_desc" msgid="3638665569546416795">"E-pošta odabrane adrese"</string>
-    <string name="dial" msgid="1253998302767701559">"Pozovite"</string>
-    <string name="dial_desc" msgid="6573723404985517250">"Pozovite odabrani broj telefona"</string>
-    <string name="map" msgid="5441053548030107189">"Mapa"</string>
-    <string name="map_desc" msgid="1836995341943772348">"Lociranje odabrane adrese"</string>
-    <string name="browse" msgid="1245903488306147205">"Otvorite"</string>
-    <string name="browse_desc" msgid="8220976549618935044">"Otvorite odabrani URL"</string>
-    <string name="sms" msgid="4560537514610063430">"Poruka"</string>
-    <string name="sms_desc" msgid="7526588350969638809">"Pošaljite poruku odabranom broju telefona"</string>
-    <string name="add_contact" msgid="7867066569670597203">"Dodajte"</string>
+    <string name="email" msgid="4560673117055050403">"Pošalji e-poruku"</string>
+    <string name="email_desc" msgid="3638665569546416795">"Pošalji e-poruku na odabranu adresu"</string>
+    <string name="dial" msgid="1253998302767701559">"Pozovi"</string>
+    <string name="dial_desc" msgid="6573723404985517250">"Pozovi odabrani broj telefona"</string>
+    <string name="map" msgid="5441053548030107189">"Prikaži na mapi"</string>
+    <string name="map_desc" msgid="1836995341943772348">"Lociraj odabranu adresu"</string>
+    <string name="browse" msgid="1245903488306147205">"Otvori"</string>
+    <string name="browse_desc" msgid="8220976549618935044">"Otvori odabrani URL"</string>
+    <string name="sms" msgid="4560537514610063430">"Pošalji SMS"</string>
+    <string name="sms_desc" msgid="7526588350969638809">"Pošalji SMS odabranom broju telefona"</string>
+    <string name="add_contact" msgid="7867066569670597203">"Dodaj"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Dodaj u kontakte"</string>
     <string name="view_calendar" msgid="979609872939597838">"Prikaži"</string>
-    <string name="view_calendar_desc" msgid="5828320291870344584">"Pogledajte odabrano vrijeme u kalendaru"</string>
+    <string name="view_calendar_desc" msgid="5828320291870344584">"Prikaži odabrano vrijeme u kalendaru"</string>
     <string name="add_calendar_event" msgid="1953664627192056206">"Zakaži"</string>
-    <string name="add_calendar_event_desc" msgid="4326891793260687388">"Zakažite događaj za odabrano vrijeme"</string>
+    <string name="add_calendar_event_desc" msgid="4326891793260687388">"Zakaži događaj za odabrano vrijeme"</string>
     <string name="view_flight" msgid="7691640491425680214">"Prati"</string>
-    <string name="view_flight_desc" msgid="3876322502674253506">"Pratite odabrani let"</string>
+    <string name="view_flight_desc" msgid="3876322502674253506">"Prati odabrani let"</string>
     <string name="translate" msgid="9218619809342576858">"Prevedi"</string>
     <string name="translate_desc" msgid="4502367770068777202">"Prevedi odabrani tekst"</string>
-    <string name="define" msgid="7394820043869954211">"Definirajte"</string>
-    <string name="define_desc" msgid="7910883642444919726">"Definiranje odabranog teksta"</string>
+    <string name="define" msgid="7394820043869954211">"Definiraj"</string>
+    <string name="define_desc" msgid="7910883642444919726">"Definiraj odabrani tekst"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ponestaje prostora za pohranu"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Neke funkcije sistema možda neće raditi"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nema dovoljno prostora za sistem. Obezbijedite 250MB slobodnog prostora i ponovo pokrenite uređaj."</string>
@@ -1203,6 +1207,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Ažuriranje tableta…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Ažuriranje uređaja…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Pokretanje telefona…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android se pokreće..."</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Pokretanje tableta…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Pokretanje uređaja…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimiziranje pohrane."</string>
@@ -1263,6 +1268,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Dodirnite da vidite sve mreže"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Povežite se"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Sve mreže"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Povezani ste na WiFi mrežu koju je predložila aplikacija <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Želite li dozvoliti aplikaciji <xliff:g id="NAME">%s</xliff:g> da vam predlaže mreže?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Da"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Ne"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi veza će se automatski uključiti"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Kada ste u blizini sačuvane mreže visokog kvaliteta"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Nemoj ponovo uključiti"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index c68e524..35c223b 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permet que l\'aplicació accedeixi a les funcions de telèfon del dispositiu. Aquest permís permet que l\'aplicació determini el número de telèfon i els identificadors del dispositiu, si hi ha una trucada activa i el número remot connectat amb una trucada."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"encaminar trucades a través del sistema"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permet que l\'aplicació encamini les trucades a través del sistema per millorar-ne l\'experiència."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"consulta i controla les trucades a través del sistema."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Permet que l\'aplicació consulti i controli les trucades en curs al dispositiu. Inclou informació com ara l\'estat i els números de les trucades."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"Continua una trucada d\'una altra aplicació"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permet que l\'aplicació continuï una trucada que s\'havia iniciat en una altra aplicació."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"llegir els números de telèfon"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Permet que l\'aplicació es comuniqui amb les etiquetes, les targetes i els lectors de Comunicació de camp proper (NFC)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"desactivació del bloqueig de pantalla"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Permet que l\'aplicació desactivi el bloqueig del teclat i qualsevol element de seguretat de contrasenyes associat. Per exemple, el telèfon desactiva el bloqueig del teclat en rebre una trucada entrant i, a continuació, reactiva el bloqueig del teclat quan finalitza la trucada."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"obtenir i sol·licitar el nivell de complexitat del bloqueig de pantalla"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Permet que l\'aplicació conegui el nivell de complexitat del bloqueig de pantalla (alt, mitjà, baix o cap), que indica la llargària i el tipus de bloqueig de pantalla possibles. L\'aplicació també pot suggerir que els usuaris actualitzin el bloqueig de pantalla a un nivell determinat, però els usuaris poden ignorar aquestes recomanacions. Tingues en compte que el bloqueig de pantalla no s\'emmagatzema com a text sense format, de manera que l\'aplicació no coneix la contrasenya exacta."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"utilitza maquinari biomètric"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Permet que l\'aplicació faci servir maquinari biomètric per a l\'autenticació"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"Gestionar el maquinari d\'empremtes digitals"</string>
@@ -1083,7 +1087,7 @@
     <string name="deleteText" msgid="6979668428458199034">"Suprimeix"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Mètode d\'introducció de text"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Accions de text"</string>
-    <string name="email" msgid="4560673117055050403">"Correu electrònic"</string>
+    <string name="email" msgid="4560673117055050403">"Envia un correu"</string>
     <string name="email_desc" msgid="3638665569546416795">"Envia un correu electrònic a l\'adreça seleccionada"</string>
     <string name="dial" msgid="1253998302767701559">"Truca"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Truca al número de telèfon seleccionat"</string>
@@ -1091,12 +1095,12 @@
     <string name="map_desc" msgid="1836995341943772348">"Localitza l\'adreça seleccionada"</string>
     <string name="browse" msgid="1245903488306147205">"Obre"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Obre l\'URL seleccionat"</string>
-    <string name="sms" msgid="4560537514610063430">"Missatge"</string>
+    <string name="sms" msgid="4560537514610063430">"Envia un SMS"</string>
     <string name="sms_desc" msgid="7526588350969638809">"Envia un SMS al número de telèfon seleccionat"</string>
     <string name="add_contact" msgid="7867066569670597203">"Afegeix"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Afegeix als contactes"</string>
     <string name="view_calendar" msgid="979609872939597838">"Mostra"</string>
-    <string name="view_calendar_desc" msgid="5828320291870344584">"Consulta l\'hora seleccionada al calendari"</string>
+    <string name="view_calendar_desc" msgid="5828320291870344584">"Consulta la data seleccionada al calendari"</string>
     <string name="add_calendar_event" msgid="1953664627192056206">"Programa"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"Programa un esdeveniment per a la data seleccionada"</string>
     <string name="view_flight" msgid="7691640491425680214">"Fes un seguiment"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"La tauleta s\'està actualitzant…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"El dispositiu s\'està actualitzant…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"S\'està iniciant el telèfon…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"S\'està iniciant Android…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"S\'està iniciant la tauleta…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"S\'està iniciant el dispositiu…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"S\'està optimitzant l\'emmagatzematge."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Toca per veure totes les xarxes"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Connecta"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Totes les xarxes"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"T\'has connectat a la xarxa Wi‑Fi proposada per l\'aplicació <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Vols permetre que <xliff:g id="NAME">%s</xliff:g> et proposi xarxes?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Sí"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"No"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"La Wi-Fi s\'activarà automàticament"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Quan siguis a prop d\'una xarxa de qualitat desada"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"No tornis a activar"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 4758c07..0061ca7 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -453,6 +453,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Umožňuje aplikaci získat přístup k telefonním funkcím zařízení. Toto oprávnění umožňuje aplikaci zjistit telefonní číslo telefonu, identifikační čísla zařízení, zda zrovna probíhá hovor, a vzdálené číslo, ke kterému je hovor připojen."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"směrování volání prostřednictvím systému"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Umožňuje aplikaci směrovat volání prostřednictvím systému za účelem vylepšení funkcí volání."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"zobrazení a ovládání hovorů v systému."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Umožňuje aplikaci zobrazit a ovládat probíhající hovory v zařízení. Zahrnuje to informace jako zúčastněna čísla a stav hovoru."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"pokračování v hovoru v jiné aplikaci"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Umožňuje aplikace pokračovat v hovoru, který byl zahájen v jiné aplikaci."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"přístup k telefonním číslům"</string>
@@ -513,6 +515,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Umožňuje aplikaci komunikovat se štítky, kartami a čtečkami s podporou technologie NFC."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"vypnutí zámku obrazovky"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Umožňuje aplikaci vypnout zámek kláves a související zabezpečení heslem. Telefon například vypne zámek klávesnice při příchozím hovoru a po skončení hovoru jej zase zapne."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"zjišťování a vyžadování složitosti zámku obrazovky"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Umožňuje aplikaci zjistit úroveň složitosti zámku obrazovky (vysoká, střední, nízká nebo žádná), která ukazuje možnou délku a typ zámku obrazovky. Aplikace také může uživatelům navrhovat, aby zámek obrazovky upravili na určitou úroveň, ale uživatelé mohou návrhy klidně ignorovat a odejít. Zámek obrazovky není uložen jako prostý text, a tak aplikace přesné heslo nezná."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"použití biometrického hardwaru"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Umožňuje aplikaci použít k ověření biometrický hardware"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"správa hardwaru na čtení otisků prstů"</string>
@@ -1131,7 +1135,7 @@
     <string name="map_desc" msgid="1836995341943772348">"Vyhledat vybranou adresu"</string>
     <string name="browse" msgid="1245903488306147205">"Otevřít"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Otevřít vybranou adresu URL"</string>
-    <string name="sms" msgid="4560537514610063430">"Zpráva"</string>
+    <string name="sms" msgid="4560537514610063430">"Napsat zprávu"</string>
     <string name="sms_desc" msgid="7526588350969638809">"Napsat SMS na vybrané telefonní číslo"</string>
     <string name="add_contact" msgid="7867066569670597203">"Přidat"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Přidat do kontaktů"</string>
@@ -1144,7 +1148,7 @@
     <string name="translate" msgid="9218619809342576858">"Přeložit"</string>
     <string name="translate_desc" msgid="4502367770068777202">"Přeložit vybraný text"</string>
     <string name="define" msgid="7394820043869954211">"Definovat"</string>
-    <string name="define_desc" msgid="7910883642444919726">"Definuje vybraný text"</string>
+    <string name="define_desc" msgid="7910883642444919726">"Definovat vybraný text"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"V úložišti je málo místa"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Některé systémové funkce nemusí fungovat"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Pro systém není dostatek místa v úložišti. Uvolněte alespoň 250 MB místa a restartujte zařízení."</string>
@@ -1221,6 +1225,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet se aktualizuje…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Zařízení se aktualizuje…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Telefon se spouští…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Spouštění systému Android…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet se spouští…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Zařízení se spouští…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Probíhá optimalizace úložiště."</string>
@@ -1283,6 +1288,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Klepnutím zobrazíte všechny sítě"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Připojit"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Všechny sítě"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Připojeno k síti Wi-Fi navrhované aplikací <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Chcete aplikaci <xliff:g id="NAME">%s</xliff:g> povolit, aby vám navrhovala sítě?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ano"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Ne"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi se zapne automaticky"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Když budete v dosahu kvalitní uložené sítě"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Znovu nezapínat"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 0167acf..2795efe 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Tillader, at appen kan få adgang til telefonfunktionerne på enheden. Med denne tilladelse kan appen fastslå telefonnummeret og enheds-id\'erne, hvorvidt et opkald er aktivt samt det eksterne nummer, der oprettes forbindelse til via et opkald."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"dirigere opkald gennem systemet"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Tillader appen at dirigere sine opkald gennem systemet for at forbedre opkaldsoplevelsen."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"se og styre opkald via systemet."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Tillader, at appen kan se og styre igangværende opkald på enheden. Dette omfatter oplysninger såsom telefonnumre og status for opkaldene."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"fortsætte et opkald fra en anden app"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Tillader, at appen fortsætter et opkald, der blev startet i en anden app."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"læse telefonnumre"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Tillader, at appen kan kommunikere med NFC-tags (Near Field Communication), -kort og -læsere."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"deaktivere din skærmlås"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Tillader, at appen kan deaktivere tastaturlåsen og anden form for tilknyttet adgangskodesikkerhed. Telefonen deaktiverer f.eks. tastaturlåsen ved indgående telefonopkald og aktiverer tastaturlåsen igen, når opkaldet er afsluttet."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"få og anmode om skærmlåsens kompleksitet"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Giver appen tilladelse til at kende skærmlåsens kompleksitet (høj, medium, lav eller ingen), hvilket indikerer skærmlåsens mulige længde og type. Appen kan også foreslå brugerne at opdatere deres skærmlås til et bestemt niveau, men brugerne kan frit ignorere det og gå videre. Bemærk! Skærmlåsen gemmes ikke som almindelig tekst, så appen kender ikke adgangskoden."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"brug biometrisk hardware"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Tillader, at appen kan bruge biometrisk hardware til godkendelse"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"administrer fingeraftrykhardware"</string>
@@ -1083,15 +1087,15 @@
     <string name="deleteText" msgid="6979668428458199034">"Slet"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Inputmetode"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Teksthandlinger"</string>
-    <string name="email" msgid="4560673117055050403">"E-mail"</string>
+    <string name="email" msgid="4560673117055050403">"Send mail"</string>
     <string name="email_desc" msgid="3638665569546416795">"Send en mail til den valgte adresse"</string>
     <string name="dial" msgid="1253998302767701559">"Ring op"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Ring til det valgte telefonnummer"</string>
-    <string name="map" msgid="5441053548030107189">"Kort"</string>
+    <string name="map" msgid="5441053548030107189">"Åbn kort"</string>
     <string name="map_desc" msgid="1836995341943772348">"Find den valgte adresse"</string>
     <string name="browse" msgid="1245903488306147205">"Åbn"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Åbn den valgte webadresse"</string>
-    <string name="sms" msgid="4560537514610063430">"Besked"</string>
+    <string name="sms" msgid="4560537514610063430">"Send besked"</string>
     <string name="sms_desc" msgid="7526588350969638809">"Send en besked til det valgte telefonnummer"</string>
     <string name="add_contact" msgid="7867066569670597203">"Tilføj"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Føj til kontaktpersoner"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Denne tablet opdaterer…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Enheden opdaterer…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Telefonen starter…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android starter..."</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Denne tablet starter…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Enheden starter…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Lageret optimeres."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tryk for at se alle netværk"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Opret forbindelse"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Alle netværk"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Forbundet til Wi‑Fi-netværket, som <xliff:g id="NAME">%s</xliff:g> har foreslået"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Vil du tillade, at <xliff:g id="NAME">%s</xliff:g> foreslår netværk?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ja"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nej"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi aktiveres automatisk"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Når du er i nærheden af et gemt netværk af høj kvalitet"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Aktivér ikke igen"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index a14a871..6087438 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -324,7 +324,7 @@
     <string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Text bei der Eingabe beobachten"</string>
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Einschließlich personenbezogener Daten wie Kreditkartennummern und Passwörter."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Displayvergrößerung festlegen"</string>
-    <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Legt die Zoom-Stufe des Displays und die Zoom-Position auf dem Display fest."</string>
+    <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Legt die Zoom-Stufe und -Position auf dem Display fest."</string>
     <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Bewegungen möglich"</string>
     <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Tippen, Wischen, Zusammenziehen und andere Bewegungen möglich."</string>
     <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"Bewegungen auf dem Fingerabdrucksensor"</string>
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Ermöglicht der App, auf die Telefonfunktionen des Geräts zuzugreifen. Die Berechtigung erlaubt der App, die Telefonnummer und Geräte-IDs zu erfassen, festzustellen, ob gerade ein Gespräch geführt wird, und die Rufnummer verbundener Anrufer zu lesen."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"Anrufe über das System durchführen"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Ermöglicht der App, Anrufe über das System durchzuführen, um die Anrufqualität zu verbessern."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"Anrufe durch das System einsehen und verwalten."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Ermöglicht der App, aktuelle Anrufe einzusehen und zu verwalten. Dies beinhaltet Informationen wie Nummern für Anrufe und den Status der Anrufe."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"Anruf aus einer anderen App weiterführen"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Ermöglicht der App, einen Anruf weiterzuführen, der in einer anderen App begonnen wurde."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"Telefonnummern vorlesen"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Ermöglicht der App die Kommunikation mit Tags für die Nahfeldkommunikation, Karten und Readern"</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"Displaysperre deaktivieren"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Ermöglicht der App, die Tastensperre sowie den damit verbundenen Passwortschutz zu deaktivieren. Das Telefon deaktiviert die Tastensperre beispielsweise, wenn ein Anruf eingeht, und aktiviert sie wieder, nachdem das Gespräch beendet wurde."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"Komplexität der Displaysperre erfahren und anfragen"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Ermöglicht es der App, die Komplexitätsstufe der Displaysperre (hoch, mittel, niedrig oder keine) zu erfahren, was auf die mögliche Dauer und Art der Displaysperre hinweist. Die App kann Nutzern auch vorschlagen, die Displaysperre auf eine bestimmte Stufe zu aktualisieren, Nutzer können diesen Vorschlag jedoch auch einfach ignorieren und fortfahren. Beachten Sie, dass die Displaysperre nicht im Klartext gespeichert ist, sodass die App nicht das genaue Passwort kennt."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"Biometrische Hardware verwenden"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Erlaubt der App, biometrische Hardware zur Authentifizierung zu verwenden"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"Fingerabdruckhardware verwalten"</string>
@@ -1083,21 +1087,21 @@
     <string name="deleteText" msgid="6979668428458199034">"Löschen"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Eingabemethode"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Textaktionen"</string>
-    <string name="email" msgid="4560673117055050403">"E-Mail"</string>
+    <string name="email" msgid="4560673117055050403">"E-Mail senden"</string>
     <string name="email_desc" msgid="3638665569546416795">"E-Mail an ausgewählte Adresse senden"</string>
     <string name="dial" msgid="1253998302767701559">"Anrufen"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Ausgewählte Telefonnummer anrufen"</string>
-    <string name="map" msgid="5441053548030107189">"Karte"</string>
+    <string name="map" msgid="5441053548030107189">"Karte öffnen"</string>
     <string name="map_desc" msgid="1836995341943772348">"Ausgewählte Adresse finden"</string>
     <string name="browse" msgid="1245903488306147205">"Öffnen"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Ausgewählte URL öffnen"</string>
-    <string name="sms" msgid="4560537514610063430">"SMS"</string>
+    <string name="sms" msgid="4560537514610063430">"SMS senden"</string>
     <string name="sms_desc" msgid="7526588350969638809">"SMS an ausgewählte Telefonnummer senden"</string>
     <string name="add_contact" msgid="7867066569670597203">"Hinzufügen"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Zu Kontakten hinzufügen"</string>
-    <string name="view_calendar" msgid="979609872939597838">"Anzeigen"</string>
+    <string name="view_calendar" msgid="979609872939597838">"Aufrufen"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"Ausgewählte Zeit im Kalender anzeigen"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"Terminübersicht"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"Termin planen"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"Termin für die ausgewählte Zeit planen"</string>
     <string name="view_flight" msgid="7691640491425680214">"Verfolgen"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"Ausgewählten Flug verfolgen"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet wird aktualisiert…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Gerät wird aktualisiert…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Smartphone wird gestartet…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android wird gestartet…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet wird gestartet…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Gerät wird gestartet…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Speicher wird optimiert"</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tippen, um alle Netzwerke zu sehen"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Verbinden"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Alle Netzwerke"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Mit einem von <xliff:g id="NAME">%s</xliff:g> vorgeschlagenen WLAN-Netzwerk verbunden"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Möchten Sie, dass <xliff:g id="NAME">%s</xliff:g> Ihnen Netzwerke vorschlägt?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ja"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nein"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"WLAN wird automatisch aktiviert"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Wenn du in der Nähe eines sicheren gespeicherten Netzwerks bist"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Nicht wieder aktivieren"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 3c251e2..f899309 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Επιτρέπει στην εφαρμογή την πρόσβαση στις λειτουργίες τηλεφώνου της συσκευής. Αυτή η άδεια δίνει τη δυνατότητα στην εφαρμογή να καθορίζει τον αριθμό τηλεφώνου και τα αναγνωριστικά συσκευών, εάν μια κλήση είναι ενεργή, καθώς και τον απομακρυσμένο αριθμό που συνδέεται από μια κλήση."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"δρομολόγηση κλήσεων μέσω του συστήματος"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Επιτρέπει στην εφαρμογή να δρομολογεί τις κλήσεις της μέσω του συστήματος για να βελτιώσει την εμπειρία κλήσης."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"προβολή και έλεγχος κλήσεων μέσω του συστήματος."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Επιτρέπει στην εφαρμογή να βλέπει και να ελέγχει τις εισερχόμενες κλήσεις στη συσκευή. Αυτό περιλαμβάνει πληροφορίες όπως τους αριθμούς κλήσεων για τις κλήσεις και την κατάσταση των κλήσεων."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"συνέχιση κλήσης από άλλη συσκευή"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Επιτρέπει στην εφαρμογή να συνεχίσει μια κλήση η οποία ξεκίνησε σε άλλη εφαρμογή."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ανάγνωση αριθμών τηλεφώνου"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Επιτρέπει στην εφαρμογή την επικοινωνία με ετικέτες, κάρτες και αναγνώστες της Επικοινωνίας κοντινού πεδίου (NFC)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"απενεργοποιεί το κλείδωμα οθόνης"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Επιτρέπει στην εφαρμογή την απενεργοποίηση του κλειδώματος πληκτρολογίου και άλλης σχετικής ασφάλειας με κωδικό πρόσβασης. Για παράδειγμα, το κλείδωμα πληκτρολογίου στο τηλέφωνο απενεργοποιείται όταν λαμβάνεται εισερχόμενη τηλεφωνική κλήση και ενεργοποιείται ξανά όταν η κλήση τερματιστεί."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"λήψη και υποβολή αιτήματος για πολυπλοκότητα οθόνης κλειδώματος"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Επιτρέπει στην εφαρμογή να μάθει το επίπεδο πολυπλοκότητας του κλειδώματος οθόνης (υψηλό, μέσο, χαμηλό ή κανένα), το οποίο υποδεικνύει το πιθανό εύρος του μήκους και του τύπου κλειδώματος οθόνης. Η εφαρμογή μπορεί επίσης να προτείνει στους χρήστες να ενημερώσουν το κλείδωμα οθόνης σε ένα συγκεκριμένο επίπεδο, όμως οι χρήστες μπορούν να την αγνοήσουν και να συνεχίσουν. Λάβετε υπόψη ότι το κλείδωμα οθόνης δεν αποθηκεύεται σε απλό κείμενο. Συνεπώς, η εφαρμογή δεν γνωρίζει τον κωδικό."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"χρήση βιομετρικού εξοπλισμού"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Επιτρέπει στην εφαρμογή να χρησιμοποιεί βιομετρικό εξοπλισμό για έλεγχο ταυτότητας"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"διαχειρίζεται τον εξοπλισμό δακτυλικού αποτυπώματος"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Ενημέρωση tablet…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Ενημέρωση συστήματος…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Εκκίνηση τηλεφώνου…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Εκκίνηση Android…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Εκκίνηση tablet…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Εκκίνηση συσκευής…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Βελτιστοποίηση αποθηκευτικού χώρου."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Πατήστε για να δείτε όλα τα δίκτυα"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Σύνδεση"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Όλα τα δίκτυα"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Σύνδεση στο δίκτυο Wi‑Fi που προτάθηκε από την εφαρμογή <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Θέλετε να επιτρέψετε στην εφαρμογή <xliff:g id="NAME">%s</xliff:g> να σας προτείνει δίκτυα;"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ναι"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Όχι"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Το Wi‑Fi θα ενεργοποιηθεί αυτόματα"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Όταν βρίσκεστε κοντά σε αποθηκευμένο δίκτυο υψηλής ποιότητας"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Να μην ενεργοποιηθεί ξανά"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 4b983c0..7288a80 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active and the remote number connected by a call."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"route calls through the system"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"see and control calls through the system."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Allows the app to see and control ongoing calls on the device. This includes information such as call numbers for calls and the state of the calls."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"continue a call from another app"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Allows the app to continue a call which was started in another app."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"read phone numbers"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Allows the app to communicate with Near Field Communication (NFC) tags, cards and readers."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"disable your screen lock"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Allows the app to disable the keylock and any associated password security. For example, the phone disables the keylock when receiving an incoming phone call, then re-enables the keylock when the call is finished."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"get and request screen lock complexity"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Allows the app to learn the screen lock complexity level (high, medium, low or none), which indicates the possible range of length and type of the screen lock. The app can also suggest to users that they update the screen lock to a certain level but users can freely ignore and navigate away. Note that the screen lock is not stored in plain text so the app does not know the exact password."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"use biometric hardware"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Allows the app to use biometric hardware for authentication"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"manage fingerprint hardware"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet is updating…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Device is updating…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Phone is starting…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android is starting…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet is starting…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Device is starting…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimising storage."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tap to see all networks"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Connect"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"All networks"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Connected to Wi‑Fi network proposed by <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Do you want to let <xliff:g id="NAME">%s</xliff:g> propose networks for you?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Yes"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"No"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi will turn on automatically"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"When you\'re near a high‑quality saved network"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Don\'t turn back on"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 821ac1e..0481755 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active and the remote number connected by a call."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"route calls through the system"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"see and control calls through the system."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Allows the app to see and control ongoing calls on the device. This includes information such as call numbers for calls and the state of the calls."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"continue a call from another app"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Allows the app to continue a call which was started in another app."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"read phone numbers"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Allows the app to communicate with Near Field Communication (NFC) tags, cards and readers."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"disable your screen lock"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Allows the app to disable the keylock and any associated password security. For example, the phone disables the keylock when receiving an incoming phone call, then re-enables the keylock when the call is finished."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"get and request screen lock complexity"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Allows the app to learn the screen lock complexity level (high, medium, low or none), which indicates the possible range of length and type of the screen lock. The app can also suggest to users that they update the screen lock to a certain level but users can freely ignore and navigate away. Note that the screen lock is not stored in plain text so the app does not know the exact password."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"use biometric hardware"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Allows the app to use biometric hardware for authentication"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"manage fingerprint hardware"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet is updating…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Device is updating…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Phone is starting…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android is starting…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet is starting…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Device is starting…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimising storage."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tap to see all networks"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Connect"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"All networks"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Connected to Wi‑Fi network proposed by <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Do you want to let <xliff:g id="NAME">%s</xliff:g> propose networks for you?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Yes"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"No"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi will turn on automatically"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"When you\'re near a high‑quality saved network"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Don\'t turn back on"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 4b983c0..7288a80 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active and the remote number connected by a call."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"route calls through the system"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"see and control calls through the system."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Allows the app to see and control ongoing calls on the device. This includes information such as call numbers for calls and the state of the calls."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"continue a call from another app"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Allows the app to continue a call which was started in another app."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"read phone numbers"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Allows the app to communicate with Near Field Communication (NFC) tags, cards and readers."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"disable your screen lock"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Allows the app to disable the keylock and any associated password security. For example, the phone disables the keylock when receiving an incoming phone call, then re-enables the keylock when the call is finished."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"get and request screen lock complexity"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Allows the app to learn the screen lock complexity level (high, medium, low or none), which indicates the possible range of length and type of the screen lock. The app can also suggest to users that they update the screen lock to a certain level but users can freely ignore and navigate away. Note that the screen lock is not stored in plain text so the app does not know the exact password."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"use biometric hardware"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Allows the app to use biometric hardware for authentication"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"manage fingerprint hardware"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet is updating…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Device is updating…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Phone is starting…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android is starting…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet is starting…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Device is starting…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimising storage."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tap to see all networks"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Connect"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"All networks"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Connected to Wi‑Fi network proposed by <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Do you want to let <xliff:g id="NAME">%s</xliff:g> propose networks for you?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Yes"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"No"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi will turn on automatically"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"When you\'re near a high‑quality saved network"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Don\'t turn back on"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 4b983c0..7288a80 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active and the remote number connected by a call."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"route calls through the system"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"see and control calls through the system."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Allows the app to see and control ongoing calls on the device. This includes information such as call numbers for calls and the state of the calls."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"continue a call from another app"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Allows the app to continue a call which was started in another app."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"read phone numbers"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Allows the app to communicate with Near Field Communication (NFC) tags, cards and readers."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"disable your screen lock"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Allows the app to disable the keylock and any associated password security. For example, the phone disables the keylock when receiving an incoming phone call, then re-enables the keylock when the call is finished."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"get and request screen lock complexity"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Allows the app to learn the screen lock complexity level (high, medium, low or none), which indicates the possible range of length and type of the screen lock. The app can also suggest to users that they update the screen lock to a certain level but users can freely ignore and navigate away. Note that the screen lock is not stored in plain text so the app does not know the exact password."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"use biometric hardware"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Allows the app to use biometric hardware for authentication"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"manage fingerprint hardware"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet is updating…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Device is updating…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Phone is starting…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android is starting…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet is starting…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Device is starting…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimising storage."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tap to see all networks"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Connect"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"All networks"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Connected to Wi‑Fi network proposed by <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Do you want to let <xliff:g id="NAME">%s</xliff:g> propose networks for you?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Yes"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"No"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi will turn on automatically"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"When you\'re near a high‑quality saved network"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Don\'t turn back on"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 3781945..5d3b466 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‏‎‏‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‏‎‏‏‎‏‏‎‎‎‎Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active, and the remote number connected by a call.‎‏‎‎‏‎"</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‏‎‏‏‎‏‏‏‏‎‏‏‎‏‎‎‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‏‎‏‏‏‏‏‎‎‏‏‏‎‎‎‏‎‎‏‎‎‏‎‎‎route calls through the system‎‏‎‎‏‎"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‏‎‎‎‎‏‏‎‏‎‏‏‏‏‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‏‎‎‎‎‎‏‏‏‎‏‎‏‎‏‏‏‎‏‎‏‎‎Allows the app to route its calls through the system in order to improve the calling experience.‎‏‎‎‏‎"</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‏‏‏‎‏‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‏‎‎‏‏‏‎‏‏‏‏‎‏‎‎‏‏‎‏‎‏‎‎‏‎see and control calls through the system.‎‏‎‎‏‎"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‏‏‎‎‏‎‏‎‎‏‏‎‎‏‎‏‎‏‏‎‎‎‎‏‏‎‎‏‏‎‏‏‏‏‎‏‎‎‏‎‎Allows the app to see and control ongoing calls on the device. This includes information such as call numbers for calls and the state of the calls.‎‏‎‎‏‎"</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‎‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‎‏‎‎‏‏‎‎‏‏‎‏‎‏‏‏‎‏‎‎‎‏‏‎‎‏‎‎‏‏‎‎‎‏‎‎‏‎continue a call from another app‎‏‎‎‏‎"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‏‎‎‎‎‏‎‏‎‏‎‎‏‏‎‎‏‏‎‎‏‎‎Allows the app to continue a call which was started in another app.‎‏‎‎‏‎"</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‎‎‏‎‎‏‎‎‎‏‏‏‎‏‎‏‏‏‏‎‏‏‏‏‎‏‎‎‏‎‎‏‎‎‏‏‏‎‎‎‏‏‎‏‎‏‏‎‏‏‎‎‎‎read phone numbers‎‏‎‎‏‎"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‎‎‎‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‎‎‎‎‎‏‏‏‎‏‎‎‏‏‎Allows the app to communicate with Near Field Communication (NFC) tags, cards, and readers.‎‏‎‎‏‎"</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‏‏‏‎‎‎‎‎‏‏‎‏‏‎‏‎‏‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‎‎disable your screen lock‎‏‎‎‏‎"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‏‏‏‎‏‏‏‎‎‏‎‏‏‏‎‏‏‎‏‏‏‏‎‎‏‎‏‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‏‏‎‏‏‎‎‎‎‎Allows the app to disable the keylock and any associated password security. For example, the phone disables the keylock when receiving an incoming phone call, then re-enables the keylock when the call is finished.‎‏‎‎‏‎"</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‎‏‎‎‏‎‎‎‏‏‎‎‎‏‎‎‏‎‎‎‎‎‎‎‏‏‏‎‎‎‎‏‏‎‏‎‎‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎‎‎get and request screen lock complexity‎‏‎‎‏‎"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‏‎‎‎‎‎‎‏‏‎‏‎‏‏‎‏‎‎‏‎‏‏‏‎‎‎‏‏‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‎‏‏‎‎‏‎Allows the app to learn the screen lock complexity level (high, medium, low or none), which indicates the possible range of length and type of the screen lock. The app can also suggest to users that they update the screen lock to a certain level but users can freely ignore and navigate away. Note that the screen lock is not stored in plaintext so the app does not know the exact password.‎‏‎‎‏‎"</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‎‎‎‎‎‎‎‏‎‎‎‏‎‎‎‏‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‏‎‎‎‎‏‎‏‎‏‏‎‎use biometric hardware‎‏‎‎‏‎"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‏‏‎‏‏‎‎‎‎‎‎‏‏‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‏‏‏‎‎‎‎‎‏‏‏‏‏‏‎‎Allows the app to use biometric hardware for authentication‎‏‎‎‏‎"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‏‎‎‎‎‏‎‏‏‎‏‏‎‎‏‏‎‏‏‏‎‏‎‎‎‏‏‏‎‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‏‏‎‎manage fingerprint hardware‎‏‎‎‏‎"</string>
@@ -1179,6 +1183,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‎‎‏‎‏‎‎‏‏‏‏‎‎‎‎‎‎‎‏‏‎‎‎‎Tablet is updating…‎‏‎‎‏‎"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‎‎‏‏‎‏‏‎‏‎‎‎‏‏‎‎‎‏‏‎‎‎‏‏‏‎‏‎‎‎‎‎‎‎‏‎‎‎‎‎‎‏‎‏‏‏‎‏‎‎‏‎‏‎Device is updating…‎‏‎‎‏‎"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‏‏‎‎‎‎‎‎‎‎‎‏‎‎‎‎‏‏‎‏‎‎‎‏‎‏‎‏‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎‎Phone is starting…‎‏‎‎‏‎"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‎‎‎‎‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‏‏‎‏‏‎‎‏‎‏‏‏‏‏‏‎Android is starting…‎‏‎‎‏‎"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‏‏‎‏‎‏‎‎‏‎‎‏‏‎‏‎‎‎‏‏‏‏‎‎‎‎‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎‎‎‏‏‎‎‎‏‏‏‏‎‎‎‎Tablet is starting…‎‏‎‎‏‎"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‎‎‏‏‏‎‏‎‏‎‏‎‎‏‏‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‏‏‎‏‎‎‎‏‏‎‏‎‎‏‏‏‎‏‎Device is starting…‎‏‎‎‏‎"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‎‎‎‎‏‎‏‎‎‏‎‎‏‏‏‎‏‏‏‎‏‏‏‎‎‏‏‎‎‎‎‎‎‎‎‎‎‏‏‎‏‏‏‎‎‎‏‎‏‎‎Optimizing storage.‎‏‎‎‏‎"</string>
@@ -1237,6 +1242,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‎‏‏‏‏‎‏‏‏‏‎‏‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‎‏‏‎‎‏‏‎‎‏‏‎‎‏‏‎‏‎‏‎‏‎Tap to see all networks‎‏‎‎‏‎"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‎‏‎‎‏‏‏‏‏‎‎‎‏‎‏‏‎‏‏‎‎‎‏‏‏‏‏‎‎‎‏‏‏‏‎‏‎‎‏‏‏‏‎‏‏‎‎‎‎‏‎‏‎‎‎Connect‎‏‎‎‏‎"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‏‏‏‏‏‏‏‎‎‏‏‎‏‎‎‏‏‎‏‏‎‎‎‏‏‏‎‏‎‎‎‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎All networks‎‏‎‎‏‎"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‎‏‏‏‎‎‏‎‎‎‎‏‎‎‏‎‏‏‏‏‏‏‎‎‏‏‎‎‎‏‏‎‏‎‏‏‎‏‏‎‎‎‏‏‏‏‎‏‎‎Connected to Wi‑Fi network proposed by ‎‏‎‎‏‏‎<xliff:g id="NAME">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‏‏‎‏‏‎‏‏‏‎‎‎‎‏‏‎‏‏‎‏‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‎‏‎‏‎‏‎‏‏‏‏‎‏‎Do you want to let ‎‏‎‎‏‏‎<xliff:g id="NAME">%s</xliff:g>‎‏‎‎‏‏‏‎ propose networks for you?‎‏‎‎‏‎"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‎‏‎‏‎‏‎‏‎‎‏‎‏‎‏‎‎‏‏‎‎‏‏‎‏‏‏‏‎‏‎‎‏‏‎‏‏‎‎‏‎‏‏‎‏‎‏‎‎‏‎‏‎Yes‎‏‎‎‏‎"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‏‏‏‎‏‎‎‎‎‏‎‏‏‏‏‏‎‎‏‏‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎‎‏‎No‎‏‎‎‏‎"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‎‏‎‏‏‎‎‏‏‎‎‎‎‏‏‎‏‏‎‏‏‏‎‎‏‏‏‎‎‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎‏‎‎‎‏‏‏‎‏‎‎‎Wi‑Fi will turn on automatically‎‏‎‎‏‎"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‎‏‎‏‏‏‏‎‎‏‎‎‎‎‎‎‏‏‎‎‏‏‏‏‏‎‎‏‏‎‏‏‎‎‎‏‎‏‏‎‎‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‎When you\'re near a high quality saved network‎‏‎‎‏‎"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‎‏‎‎‎‏‏‎‏‏‏‏‎‏‎‎‎‎‏‏‎‎‏‎‎‏‎‏‏‏‎‏‏‏‎‏‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‎Don\'t turn back on‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 50ebc49..d369da5 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite que la aplicación acceda a las funciones de teléfono del dispositivo. La aplicación puede utilizar este permiso para descubrir identificadores de dispositivos y números de teléfono, para saber si una llamada está activa y para conocer el número remoto con el que se ha establecido conexión mediante una llamada."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"Transmite llamadas a través del sistema"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permite que la app transmita las llamadas a través del sistema para mejorar la experiencia de llamadas."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"Mirar y controlar las llamadas con el sistema"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Permite que la app vea y controle las llamadas entrantes del dispositivo. Incluye información como los números emisores y el estado de las llamadas."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"continuar llamada de otra app"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permite que la app continúe con una llamada que se inició en otra app."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"leer números de teléfono"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Permite que la aplicación se comunique con lectores, tarjetas y etiquetas de Comunicación de campo cercano (NFC)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"desactivar el bloqueo de pantalla"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Permite que la aplicación desactive el bloqueo del teclado y cualquier protección con contraseña asociada. Por ejemplo, el dispositivo puede desactivar el bloqueo del teclado cuando recibe una llamada telefónica y volver a activarlo cuando finaliza la llamada."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"obtener y solicitar complejidad del bloqueo de pantalla"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Permite que la app conozca el nivel de complejidad del bloqueo de pantalla (alta, media, baja o ninguna), lo que indica el rango de duración posible y el tipo de bloqueo de pantalla. La app también puede sugerirles a los usuarios que actualicen el bloqueo de pantalla a un determinado nivel, aunque ellos pueden ignorar esta sugerencia y seguir navegando. Ten en cuenta que el bloqueo de pantalla no se almacena como texto sin formato, por lo que la app no conoce la contraseña exacta."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"usar hardware biométrico"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Permite que la app use hardware biométrico para realizar la autenticación"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"Administrar el hardware de huellas digitales"</string>
@@ -1088,7 +1092,7 @@
     <string name="dial" msgid="1253998302767701559">"Llamar"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Llamar al número de teléfono seleccionado"</string>
     <string name="map" msgid="5441053548030107189">"Mapa"</string>
-    <string name="map_desc" msgid="1836995341943772348">"Abrir la dirección seleccionada en el mapa"</string>
+    <string name="map_desc" msgid="1836995341943772348">"Buscar la dirección seleccionada"</string>
     <string name="browse" msgid="1245903488306147205">"Abrir"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Abrir URL seleccionada"</string>
     <string name="sms" msgid="4560537514610063430">"Mensaje"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Actualizando tablet…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Actualizando dispositivo…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Iniciando teléfono…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Iniciando Android…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Iniciando tablet…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Iniciando dispositivo…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimizando almacenamiento"</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Presiona para ver todas las redes"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Conectar"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Todas las redes"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Se estableció conexión con la red Wi-Fi sugerida por <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"¿Quieres permitir que <xliff:g id="NAME">%s</xliff:g> te sugiera redes?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Sí"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"No"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Se activará la conexión Wi-Fi automáticamente"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Cuando estés cerca de una red guardada de alta calidad"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"No volver a activar"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 7a992b4..9174248 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite que la aplicación acceda a las funciones de teléfono del dispositivo. La aplicación puede utilizar este permiso para descubrir identificadores de dispositivos y números de teléfono, para saber si una llamada está activa y para conocer el número remoto con el que se ha establecido conexión mediante una llamada."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"direccionar llamadas a través del sistema"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permite a la aplicación direccionar sus llamadas hacia el sistema para mejorar la calidad de estas."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ver y controlar llamadas a través del sistema."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Permite que la aplicación vea y controle las llamadas entrantes en el dispositivo. Esto incluye información como los números de las llamadas y su estado."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"continuar una llamada de otra aplicación"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permite que la aplicación continúe una llamada que se ha iniciado en otra aplicación."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"leer números de teléfono"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Permite que la aplicación se comunique con lectores, tarjetas y etiquetas de Comunicación de campo cercano (NFC)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"inhabilitar el bloqueo de pantalla"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Permite que la aplicación inhabilite el bloqueo del teclado y cualquier protección con contraseña asociada. Por ejemplo, el teléfono puede inhabilitar el bloqueo del teclado cuando se recibe una llamada telefónica y volver a habilitarlo cuando finaliza la llamada."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"obtener y solicitar el nivel de complejidad del bloqueo de pantalla"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Permite que la aplicación entienda el nivel de complejidad del bloqueo de pantalla (alto, medio, bajo o ninguno) que indica la longitud y el tipo del bloqueo de pantalla posibles. La aplicación también puede sugerir a los usuarios que actualicen el bloqueo de pantalla para que tenga un nivel concreto de complejidad, pero los usuarios pueden ignorar la advertencia libremente. El bloqueo de pantalla no se almacena en texto sin formato, así que la aplicación no puede saber cuál es la contraseña exacta."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"usar hardware biométrico"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Permite que la aplicación utilice el hardware biométrico para realizar la autenticación"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"administrar hardware de huellas digitales"</string>
@@ -1083,7 +1087,7 @@
     <string name="deleteText" msgid="6979668428458199034">"Eliminar"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Método de introducción de texto"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Acciones de texto"</string>
-    <string name="email" msgid="4560673117055050403">"Correo electrónico"</string>
+    <string name="email" msgid="4560673117055050403">"Enviar correo"</string>
     <string name="email_desc" msgid="3638665569546416795">"Enviar un correo electrónico a la dirección seleccionada"</string>
     <string name="dial" msgid="1253998302767701559">"Llamar"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Llamar al número de teléfono seleccionado"</string>
@@ -1091,15 +1095,15 @@
     <string name="map_desc" msgid="1836995341943772348">"Buscar la dirección seleccionada"</string>
     <string name="browse" msgid="1245903488306147205">"Abrir"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Abrir la URL seleccionada"</string>
-    <string name="sms" msgid="4560537514610063430">"Mensaje"</string>
-    <string name="sms_desc" msgid="7526588350969638809">"Enviar un mensaje al número de teléfono seleccionado"</string>
+    <string name="sms" msgid="4560537514610063430">"Enviar SMS"</string>
+    <string name="sms_desc" msgid="7526588350969638809">"Enviar SMS al teléfono seleccionado"</string>
     <string name="add_contact" msgid="7867066569670597203">"Añadir"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Añadir a contactos"</string>
     <string name="view_calendar" msgid="979609872939597838">"Ver"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"Ver la hora seleccionada en el calendario"</string>
     <string name="add_calendar_event" msgid="1953664627192056206">"Programar"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"Programar un evento para la hora seleccionada"</string>
-    <string name="view_flight" msgid="7691640491425680214">"Buscar"</string>
+    <string name="view_flight" msgid="7691640491425680214">"Hacer seguimiento"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"Seguir el vuelo seleccionado"</string>
     <string name="translate" msgid="9218619809342576858">"Traducir"</string>
     <string name="translate_desc" msgid="4502367770068777202">"Traducir el texto seleccionado"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"El tablet se está actualizando…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"El dispositivo se está actualizando…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"El teléfono se está iniciando…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android se está iniciando…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"El tablet se está iniciando…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"El dispositivo se está iniciando…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimizando almacenamiento."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Toca para ver todas las redes"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Conectarse"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Todas las redes"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Conexión establecida con la red Wi‑Fi recomendada por <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"¿Quieres que <xliff:g id="NAME">%s</xliff:g> te recomiende redes?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Sí"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"No"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"La conexión Wi‑Fi se activará automáticamente"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Cuando estés cerca de una red de alta calidad guardada"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"No volver a activar"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index e633ab4..f20b35f 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Annab rakendusele juurdepääsu seadme telefonifunktsioonidele. See luba võimaldab rakendusel määrata telefoninumbri ja seadme ID-d ning kontrollida, kas kõne on aktiivne ja kaugnumber on kõne kaudu telefoniga ühendatud."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"kõnede marsruutimine süsteemi kaudu"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Võimaldab rakendusel kõnesid süsteemi kaudu marsruutida, et helistamiskogemust täiustada."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"süsteemi kaudu kõnede vaatamine ja juhtimine."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Lubab rakendusel seadmes vaadata ja juhtida käimasolevaid kõnesid. See hõlmab sellist teavet nagu kõnede numbrid ja olek."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"jätka kõnet teises rakenduses"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Lubab rakendusel jätkata kõnet, mida alustati teises rakenduses."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"lugeda telefoninumbreid"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Võimaldab rakendusel suhelda lähiväljaside (NFC) märgendite, kaartide ja lugeritega."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"keelake ekraanilukk"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Võimaldab rakendusel keelata klahviluku ja muu seotud parooli turvalisuse. Näiteks keelab telefon klahviluku sissetuleva kõne vastuvõtmisel ja lubab klahviluku uuesti, kui kõne on lõpetatud."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"Hankida ja taotleda ekraaniluku keerukuse teavet"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Lubab rakendusel vaadata ekraaniluku keerukuse taset (kõrge, keskmine, madal või puudub), mis näitab ekraaniluku võimalikku pikkust ja tüüpi. Rakendus võib kasutajatele soovitada ka ekraaniluku viimist teatud tasemele, kuid kasutajad võivad seda eirata ja kuvalt lahkuda. Pange tähele, et ekraanilukku ei salvestata lihttekstina, seega ei tea rakendus täpset parooli."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"kasutada biomeetrilist riistvara"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Võimaldab rakendusel autentimiseks kasutada biomeetrilist riistvara"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"sõrmejälje riistvara haldamine"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tahvelarvutit värskendatakse …"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Seadet värskendatakse …"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Telefon käivitub …"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android käivitub ..."</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tahvelarvuti käivitub …"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Seade käivitub …"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Salvestusruumi optimeerimine."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Puudutage kõikide võrkude nägemiseks"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Ühenda"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Kõik võrgud"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Ühendatud rakenduse <xliff:g id="NAME">%s</xliff:g> soovitatud WiFi-võrguga"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Kas soovite lubada rakendusel <xliff:g id="NAME">%s</xliff:g> teile võrke soovitada?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Jah"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Ei"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"WiFi lülitub sisse automaatselt"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Kui olete kvaliteetse salvestatud võrgu läheduses"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ära lülita tagasi sisse"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index ea9414f..153eeae 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Gailuaren telefono-eginbideak atzitzeko baimena ematen die aplikazioei. Baimen horrek aplikazioari telefono-zenbakia eta gailu IDak zein diren, deirik aktibo dagoen eta deia zer zenbakirekin konektatuta dagoen zehazteko baimena ematen die aplikazioei."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"bideratu deiak sistemaren bidez"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Deiak sistemaren bidez bideratzea baimentzen die aplikazioei, deien zerbitzua ahal bezain ona izan dadin."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ikusi eta kontrolatu deiak sistemaren bidez."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Gailuan abian diren deiak eta deion informazioa ikusi eta kontrolatzeko baimena ematen dio aplikazioari; besteak beste, deien zenbakiak eta deien egoera."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"Jarraitu beste aplikazio batean hasitako deia"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Beste aplikazio batean hasitako dei bat jarraitzea baimentzen dio aplikazioari."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"irakurri telefono-zenbakiak"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Near Field Communication (NFC) etiketekin, txartelekin eta irakurgailuekin komunikatzea baimentzen die aplikazioei."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"desgaitu pantailaren blokeoa"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Teklen blokeoa eta erlazionatutako pasahitz-segurtasuna desgaitzeko baimena ematen die aplikazioei. Adibidez, telefonoak teklen blokeoa desgaitzen du telefono-deiak jasotzen dituenean, eta berriro gaitzen du deiak amaitzean."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"lortu eta eskatu pantailaren blokeoaren konplexutasuna"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Pantailaren blokeoaren konplexutasun-maila (handia, ertaina, txikia edo bat ere ez) ezagutzea baimentzen dio aplikazioari; horren bidez, pantailaren blokeoaren luzeraren barruti edo mota posiblea adierazten da. Halaber, erabiltzaileei pantailaren blokeoa maila jakin batera igotzeko iradoki diezaieke aplikazioak, baina erabiltzaileek horri ez ikusi egiteko eta aplikazioa erabiltzen jarraitzeko aukera dute. Kontuan izan pantailaren blokeoa ez dela gordetzen testu arrunt gisa; beraz, aplikazioak ez du jakingo pasahitza zehazki zein den."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"Erabili hardware biometrikoa"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Autentifikatzeko hardware biometrikoa erabiltzea baimentzen die aplikazioei."</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"kudeatu erreferentzia-gako digitalen hardwarea"</string>
@@ -1083,7 +1087,7 @@
     <string name="deleteText" msgid="6979668428458199034">"Ezabatu"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Idazketa-metodoa"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Testu-ekintzak"</string>
-    <string name="email" msgid="4560673117055050403">"Posta"</string>
+    <string name="email" msgid="4560673117055050403">"Bidali mezu bat"</string>
     <string name="email_desc" msgid="3638665569546416795">"Bidali mezu elektroniko bat hautatutako helbidera"</string>
     <string name="dial" msgid="1253998302767701559">"Deitu"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Deitu hautatutako telefono-zenbakira"</string>
@@ -1091,7 +1095,7 @@
     <string name="map_desc" msgid="1836995341943772348">"Bilatu hautatutako helbidea"</string>
     <string name="browse" msgid="1245903488306147205">"Ireki"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Ireki hautatutako URLa"</string>
-    <string name="sms" msgid="4560537514610063430">"Bidali mezua"</string>
+    <string name="sms" msgid="4560537514610063430">"Bidali SMS bat"</string>
     <string name="sms_desc" msgid="7526588350969638809">"Bidali testu-mezu bat hautatutako telefono-zenbakira"</string>
     <string name="add_contact" msgid="7867066569670597203">"Gehitu"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Gehitu kontaktuetan"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tableta eguneratzen ari da…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Gailua eguneratzen ari da…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Telefonoa abiarazten ari da…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android abiarazten ari da…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tableta abiarazten ari da…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Gailua abiarazten ari da…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Memoria optimizatzen."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Sakatu hau sare guztiak ikusteko"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Konektatu"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Sare guztiak"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> aplikazioak iradokitako Wi‑Fi sarera konektatuta"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"<xliff:g id="NAME">%s</xliff:g> aplikazioak sareak iradoki diezazkizun nahi duzu?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Bai"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Ez"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi konexioa automatikoki aktibatuko da"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Gordeta daukazun kalitate handiko sare batetik gertu zaudenean"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ez aktibatu berriro"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 1099169..76845c8 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"به برنامه اجازه می‌دهد به ویژگی‌های تلفن دستگاه شما دسترسی پیدا کند. این مجوز به برنامه اجازه می‌دهد شماره تلفن و شناسه‌های دستگاه، فعال بودن یک تماس و شماره راه دوری که با یک تماس متصل شده است را مشخص کند."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"برقرار کردن تماس‌ها ازطریق سیستم"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"به برنامه امکان می‌دهد برای بهبود تجربه تماس، تماس‌هایش را ازطریق سیستم برقرار کند."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"دیدن و کنترل تماس‌ها ازطریق سیستم."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"به برنامه‌ها اجازه می‌دهد تماس‌های درحال انجام را در این دستگاه ببیند و کنترل کند. این مورد شامل اطلاعاتی مانند شماره تلفن برای تماس‌ها و وضعیت تماس‌ها است."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"ادامه دادن تماس از برنامه‌ای دیگر"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"به برنامه اجازه می‌دهد تماسی را که در برنامه دیگری شروع شده ادامه دهد."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"خواندن شماره تلفن‌ها"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"‏به برنامه اجازه می‎دهد تا با تگهای ارتباط میدان نزدیک (NFC)، کارتها و فایل خوان ارتباط برقرار کند."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"غیرفعال کردن قفل صفحه شما"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"به برنامه امکان می‌دهد قفل کلید و هر گونه امنیت گذرواژه مرتبط را غیرفعال کند. به‌عنوان مثال تلفن هنگام دریافت یک تماس تلفنی ورودی قفل کلید را غیرفعال می‌کند و بعد از پایان تماس، قفل کلید را دوباره فعال می‌کند."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"دریافت و درخواست پیچیدگی قفل صفحه"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"به برنامه اجازه می‌دهد سطح پیچیدگی قفل صفحه (بالا، متوسط، پایین یا هیچ‌کدام) را بیاموزد که نشانگر اشکال مختلف طول و نوع قفل صفحه است. همچنین برنامه می‌تواند به کاربران پیشنهاد دهد قفل صفحه را به سطح خاصی به‌روزرسانی کنند، اما کاربران می‌توانند آزادانه این پیشنهاد را نادیده بگیرند و به سطح دیگری بروند. توجه داشته باشید که قفل صفحه در قالب نوشتار ساده ذخیره نمی‌شود، بنابراین برنامه گذرواژه دقیق را نمی‌داند."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"استفاده از سخت‌افزار بیومتریک"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"به برنامه امکان می‌دهد از سخت‌افزار بیومتریک برای احراز هویت استفاده کند"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"مدیریت سخت‌افزار اثر انگشت"</string>
@@ -1083,27 +1087,27 @@
     <string name="deleteText" msgid="6979668428458199034">"حذف"</string>
     <string name="inputMethod" msgid="1653630062304567879">"روش ورودی"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"کنش‌های متنی"</string>
-    <string name="email" msgid="4560673117055050403">"ایمیل"</string>
+    <string name="email" msgid="4560673117055050403">"فرستادن ایمیل"</string>
     <string name="email_desc" msgid="3638665569546416795">"ارسال ایمیل به نشانی انتخابی"</string>
-    <string name="dial" msgid="1253998302767701559">"تماس"</string>
+    <string name="dial" msgid="1253998302767701559">"تماس گرفتن"</string>
     <string name="dial_desc" msgid="6573723404985517250">"تماس با شماره تلفن انتخابی"</string>
     <string name="map" msgid="5441053548030107189">"نقشه"</string>
     <string name="map_desc" msgid="1836995341943772348">"مکان‌یابی نشانی انتخاب‌شده"</string>
     <string name="browse" msgid="1245903488306147205">"باز کردن"</string>
     <string name="browse_desc" msgid="8220976549618935044">"باز کردن نشانی وب انتخابی"</string>
-    <string name="sms" msgid="4560537514610063430">"پیام"</string>
+    <string name="sms" msgid="4560537514610063430">"فرستادن پیام"</string>
     <string name="sms_desc" msgid="7526588350969638809">"ارسال پیام به شماره تلفن انتخابی"</string>
     <string name="add_contact" msgid="7867066569670597203">"افزودن"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"افزودن به مخاطبین"</string>
     <string name="view_calendar" msgid="979609872939597838">"مشاهده"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"مشاهده زمان انتخابی در تقویم"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"زمان‌بندی"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"زمان‌بندی کردن"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"زمان‌بندی رویداد برای زمان انتخابی"</string>
-    <string name="view_flight" msgid="7691640491425680214">"پیگیری"</string>
+    <string name="view_flight" msgid="7691640491425680214">"انجام پیگیری"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"ردیابی پرواز انتخابی"</string>
     <string name="translate" msgid="9218619809342576858">"ترجمه کردن"</string>
     <string name="translate_desc" msgid="4502367770068777202">"ترجمه کردن نوشتار انتخاب‌شده"</string>
-    <string name="define" msgid="7394820043869954211">"تعریف"</string>
+    <string name="define" msgid="7394820043869954211">"تعریف کردن"</string>
     <string name="define_desc" msgid="7910883642444919726">"تعریف نوشتار انتخابی"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"حافظه درحال پر شدن است"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"برخی از عملکردهای سیستم ممکن است کار نکنند"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"رایانه لوحی درحال به‌روزرسانی است…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"دستگاه درحال به‌روزرسانی است…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"تلفن درحال راه‌اندازی است…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"‏Android در حال راه‌اندازی است..."</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"رایانه لوحی درحال راه‌اندازی است…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"دستگاه درحال راه‌اندازی است…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"بهینه‌سازی فضای ذخیره‌سازی."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"برای دیدن همه شبکه‌ها ضربه بزنید"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"اتصال"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"همه شبکه‌ها"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"‏متصل به شبکه Wi‑Fi پیشنهادی <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"می‌خواهید به <xliff:g id="NAME">%s</xliff:g> اجازه دهید شبکه‌هایی را که به شما پیشنهاد کند؟"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"بله"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"خیر"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"‏Wi‑Fi به‌طور خودکار روشن خواهد شد"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"وقتی نزدیک شبکه ذخیره‌شده با کیفیت بالا هستید"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"دوباره روشن نشود"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 3f8f0d1..16c96cc 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Antaa sovelluksen käyttää laitteen puhelinominaisuuksia. Sovellus voi määrittää puhelinnumeron ja laitteen tunnuksen, puhelun tilan sekä soitetun numeron."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ohjata puhelut järjestelmän kautta"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Tämä sallii sovelluksen ohjata puhelut järjestelmän kautta, mikä auttaa parantamaan puhelujen laatua."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"nähdä puhelut ja päättää niistä järjestelmässä"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Sovellus voi nähdä laitteella käynnissä olevat puhelut ja päättää niistä. Se näkee esimerkiksi puheluihin liittyvät numerot ja niiden tilat."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"jatkaa toisen sovelluksen puhelua"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Antaa sovelluksen jatkaa puhelua, joka aloitettiin toisessa sovelluksessa."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"lukea puhelinnumeroita"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Antaa sovelluksen kommunikoida NFC (Near Field Communication) -tagien, -korttien ja -lukijoiden kanssa."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"poista näytön lukitus käytöstä"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Antaa sovelluksen ottaa näppäinlukon ja siihen liittyvän salasanasuojauksen pois käytöstä. Esimerkki: puhelin poistaa näppäinlukon käytöstä puhelun saapuessa ja asettaa lukon takaisin käyttöön puhelun päättyessä."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"pyytää näytön lukituksen monimutkaisuutta ja nähdä sen"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Sovellus saa luvan selvittää näytön lukituksen monimutkaisuuden (korkea, keskitaso tai matala) eli sen pituusluokan ja tyypin. Sovellus voi myös ehdottaa käyttäjille näytön lukituksen vaihtamista tietylle tasolle, mutta he voivat ohittaa tämän ja poistua. Itse näytön lukitusta ei säilytetä tekstimuodossa, joten sovellus ei tiedä sitä tarkalleen."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"käytä biometristä laitteistoa"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Sallii sovelluksen käyttää biometristä laitteistoa todennukseen"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"sormenjälkilaitteiston hallinnointi"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablettia päivitetään…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Laitetta päivitetään…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Puhelin käynnistyy…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android käynnistyy…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tabletti käynnistyy…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Laite käynnistyy…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimoidaan tallennustilaa."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Napauta, niin näet kaikki verkot."</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Yhdistä"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Kaikki verkot"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Yhdistetty ehdotettuun (<xliff:g id="NAME">%s</xliff:g>) Wi-Fi-verkkoon"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Haluatko, että <xliff:g id="NAME">%s</xliff:g> voi ehdottaa sinulle verkkoja?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Kyllä"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Ei"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi käynnistyy automaattisesti"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Kun olet lähellä laadukasta tallennettua verkkoa"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Älä käynnistä uudelleen"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 2bac413..6fb1f12 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permet à l\'application d\'accéder aux fonctionnalités téléphoniques de l\'appareil. Cette autorisation permet à l\'application de déterminer le numéro de téléphone et les identifiants de l\'appareil, si un appel est actif et le numéro distant connecté par un appel."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"acheminer les appels dans le système"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permet à l\'application d\'acheminer ses appels dans le système afin d\'améliorer l\'expérience d\'appel."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"afficher et gérer les appels à l\'aide du système."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Autorise l\'application à afficher et à gérer les appels sortants sur l\'appareil. Cela comprend de l\'information comme les numéros pour les appels et l\'état des appels."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"continuer un appel d\'une autre application"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permet à l\'application de continuer un appel commencé dans une autre application."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"lire les numéros de téléphone"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Permet à l\'application de communiquer avec des bornes, des cartes et des lecteurs compatibles avec la technologie NFC (communication en champ proche)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"désactiver le verrouillage de l\'écran"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Permet à l\'application de désactiver le verrouillage des touches et toute mesure de sécurité par mot de passe associée. Par exemple, votre téléphone désactive le verrouillage des touches lorsque vous recevez un appel, puis le réactive lorsque vous raccrochez."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"obtenir et demander la complexité de l\'écran de verrouillage"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Autorise l\'application à apprendre le niveau de complexité de l\'écran de verrouillage (élevé, moyen, faible ou aucun), qui indique la gamme possible de longueur et de type de verrouillage d\'écran. L\'application peut aussi suggérer aux utilisateurs de mettre à jour l\'écran de verrouillage afin d\'utiliser un certain niveau de complexité, mais ils peuvent ignorer la suggestion. Notez que le verrouillage d\'écran n\'est pas stocké en texte brut pour de manière à ce que l\'application n\'ait pas accès au mot de passe exact."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"utiliser le matériel biométrique"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Permet à l\'application d\'utiliser du matériel biométrique pour l\'authentification"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"gérer le matériel d\'empreinte digitale"</string>
@@ -1083,24 +1087,24 @@
     <string name="deleteText" msgid="6979668428458199034">"Supprimer"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Mode de saisie"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Actions sur le texte"</string>
-    <string name="email" msgid="4560673117055050403">"Courriel"</string>
+    <string name="email" msgid="4560673117055050403">"Envoyer un courriel"</string>
     <string name="email_desc" msgid="3638665569546416795">"Envoyer un courriel à l\'adresse sélectionnée"</string>
-    <string name="dial" msgid="1253998302767701559">"Appel"</string>
+    <string name="dial" msgid="1253998302767701559">"Appeler"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Téléphoner au numéro sélectionné"</string>
-    <string name="map" msgid="5441053548030107189">"Carte"</string>
+    <string name="map" msgid="5441053548030107189">"Ouvrir une carte"</string>
     <string name="map_desc" msgid="1836995341943772348">"Localiser l\'adresse sélectionnée"</string>
     <string name="browse" msgid="1245903488306147205">"Ouvrir"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Ouvrir l\'adresse URL sélectionnée"</string>
-    <string name="sms" msgid="4560537514610063430">"Message"</string>
+    <string name="sms" msgid="4560537514610063430">"Envoyer un texto"</string>
     <string name="sms_desc" msgid="7526588350969638809">"Envoyer un message texte au numéro de téléphone sélectionné"</string>
     <string name="add_contact" msgid="7867066569670597203">"Ajouter"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Ajouter aux contacts"</string>
     <string name="view_calendar" msgid="979609872939597838">"Afficher"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"Voir la date sélectionnée dans l\'agenda"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"Calendrier"</string>
-    <string name="add_calendar_event_desc" msgid="4326891793260687388">"Planifier un événement pour l\'heure sélectionnée"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"Planifier"</string>
+    <string name="add_calendar_event_desc" msgid="4326891793260687388">"Planifier un événement à l\'heure sélectionnée"</string>
     <string name="view_flight" msgid="7691640491425680214">"Effectuer le suivi"</string>
-    <string name="view_flight_desc" msgid="3876322502674253506">"Faire le suivi du vol sélectionné"</string>
+    <string name="view_flight_desc" msgid="3876322502674253506">"Obtenir des informations sur le vol sélectionné"</string>
     <string name="translate" msgid="9218619809342576858">"Traduire"</string>
     <string name="translate_desc" msgid="4502367770068777202">"Traduire le texte sélectionné"</string>
     <string name="define" msgid="7394820043869954211">"Définir"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Mise à jour de la tablette en cours…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Mise à jour de l\'appareil en cours…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Démarrage du téléphone en cours…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android en cours de démarrage..."</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Démarrage de la tablette en cours…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Démarrage de l\'appareil en cours…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimisation du stockage."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Touchez pour afficher tous les réseaux"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Connexion"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Tous les réseaux"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Connecté au réseau Wi-Fi proposé par <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Voulez-vous autoriser <xliff:g id="NAME">%s</xliff:g> à vous suggérer des réseaux?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Oui"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Non"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Le Wi-Fi s\'activera automatiquement"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Lorsque vous êtes près d\'un réseau enregistré de haute qualité"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ne pas réactiver"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index c0c2ed8..fa8c7f0 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permet à l\'application d\'accéder aux fonctionnalités téléphoniques de l\'appareil. Cette autorisation permet à l\'application de déterminer le numéro de téléphone et les identifiants de l\'appareil, si un appel est actif et le numéro distant connecté par un appel."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"acheminer les appels via le système"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Autorise l\'application à acheminer les appels via le système afin d\'optimiser le confort d\'utilisation."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"voir et contrôler les appels via le système."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Permet à l\'application de voir et contrôler les appels en cours sur l\'appareil. Cela inclut des informations telles que les numéros associés aux appels et l\'état des appels."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"continuer un appel issu d\'une autre application"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Autorise l\'application à continuer un appel qui a été démarré dans une autre application."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"lire les numéros de téléphone"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Permet à l\'application de communiquer avec des tags, des cartes et des lecteurs compatibles avec la technologie NFC (communication en champ proche)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"Désactiver le verrouillage de l\'écran"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Permet à l\'application de désactiver le verrouillage des touches et toute mesure de sécurité via mot de passe associée. Par exemple, votre téléphone désactive le verrouillage des touches lorsque vous recevez un appel, puis le réactive lorsque vous raccrochez."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"obtenir et demander la complexité du verrouillage de l\'écran"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Permet à l\'application de connaître le niveau de complexité du verrouillage de l\'écran (élevé, moyen, faible ou aucun), qui indique les possibilités en matière de longueur du mot de passe et de type de verrouillage de l\'écran. L\'application peut également suggérer aux utilisateurs de modifier la complexité du verrouillage, mais ces derniers peuvent librement l\'ignorer. Remarque : Comme le verrouillage de l\'écran n\'est pas stocké au format texte brut, l\'application ne connaît pas le mot de passe exact."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"utiliser les composants biométriques"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Autoriser l\'application à utiliser les composants biométriques pour l\'authentification"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"Gérer le matériel d\'empreintes digitales"</string>
@@ -1083,16 +1087,16 @@
     <string name="deleteText" msgid="6979668428458199034">"Supprimer"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Mode de saisie"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Actions sur le texte"</string>
-    <string name="email" msgid="4560673117055050403">"E-mail"</string>
+    <string name="email" msgid="4560673117055050403">"Envoyer un e-mail"</string>
     <string name="email_desc" msgid="3638665569546416795">"Envoyer un e-mail à l\'adresse sélectionnée"</string>
     <string name="dial" msgid="1253998302767701559">"Appeler"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Appeler le numéro de téléphone sélectionné"</string>
-    <string name="map" msgid="5441053548030107189">"Carte"</string>
+    <string name="map" msgid="5441053548030107189">"Ouvrir une carte"</string>
     <string name="map_desc" msgid="1836995341943772348">"Localiser l\'adresse sélectionnée"</string>
     <string name="browse" msgid="1245903488306147205">"Ouvrir"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Ouvrir l\'URL sélectionnée"</string>
-    <string name="sms" msgid="4560537514610063430">"Envoyer un message"</string>
-    <string name="sms_desc" msgid="7526588350969638809">"Envoyer un message au numéro de téléphone sélectionné"</string>
+    <string name="sms" msgid="4560537514610063430">"Envoyer un SMS"</string>
+    <string name="sms_desc" msgid="7526588350969638809">"Envoyer un SMS au numéro de téléphone sélectionné"</string>
     <string name="add_contact" msgid="7867066569670597203">"Ajouter"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Ajouter aux contacts"</string>
     <string name="view_calendar" msgid="979609872939597838">"Afficher"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Mise à jour de la tablette…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Mise à jour de l\'appareil…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Démarrage du téléphone…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Démarrage d\'Android en cours"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Démarrage de la tablette…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Démarrage de l\'appareil…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimisation du stockage en cours…"</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Appuyer pour afficher tous les réseaux"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Se connecter"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Tous les réseaux"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Connecté au réseau Wi‑Fi proposé par <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Voulez-vous autoriser <xliff:g id="NAME">%s</xliff:g> à vous proposer des réseaux ?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Oui"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Non"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Le Wi-Fi sera activé automatiquement"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Lorsque vous êtes à proximité d\'un réseau enregistré de haute qualité"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ne pas réactiver"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index c5a4066..2c9b09d 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite á aplicación acceder ás funcións de teléfono do dispositivo. Con este permiso a aplicación pode determinar o número de teléfono e os ID do dispositivo, se unha chamada está activa e o número remoto conectado mediante unha chamada."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"dirixir as chamadas a través do sistema"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permite á aplicación dirixir as súas chamadas a través do sistema para mellorar a túa experiencia durante as chamadas."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"consultar e controlar as chamadas a través do sistema."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Permite que a aplicación consulte e controle as chamadas en curso do dispositivo. Pode acceder a información como os números e os estados das chamadas."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"continuar unha chamada iniciada noutra aplicación"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permite que a aplicación continúe unha chamada que se iniciou noutra aplicación."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ler números de teléfono"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Permite á aplicación comunicarse con etiquetas, tarxetas e lectores Near Field Communication (NFC)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"desactivar o bloqueo da pantalla"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Permite á aplicación desactivar o bloqueo do teclado e calquera seguranza dos contrasinais asociada. Por exemplo, o teléfono desactiva o bloqueo do teclado ao recibir unha chamada telefónica entrante e, a continuación, volve activar o bloqueo do teclado unha vez finalizada a chamada."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"consultar complexidade do bloqueo de pantalla e suxerir aumentala"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Permite que a aplicación consulte o nivel (alto, medio, baixo ou inexistente) de complexidade do bloqueo de pantalla para saber a lonxitude aproximada do contrasinal e o tipo de bloqueo de pantalla. A aplicación tamén pode suxerirlles aos usuarios que aumenten o nivel de complexidade do bloqueo de pantalla, aínda que poden ignorar a suxestión e seguir navegando. Ten en conta que o bloqueo de pantalla non se almacena como texto sen formato, polo que a aplicación non pode consultar o contrasinal exacto."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"utilizar hardware biométrico"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Permite que a aplicación utilice hardware biométrico para a autenticación"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"xestionar hardware de impresión dixital"</string>
@@ -1091,7 +1095,7 @@
     <string name="map_desc" msgid="1836995341943772348">"Localiza o enderezo seleccionado"</string>
     <string name="browse" msgid="1245903488306147205">"Abrir"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Abre o URL seleccionado"</string>
-    <string name="sms" msgid="4560537514610063430">"Mensaxe"</string>
+    <string name="sms" msgid="4560537514610063430">"Enviar SMS"</string>
     <string name="sms_desc" msgid="7526588350969638809">"Envía unha mensaxe ao número de teléfono seleccionado"</string>
     <string name="add_contact" msgid="7867066569670597203">"Engadir"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Engade o elemento aos contactos"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"A tableta estase actualizando…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"O dispositivo estase actualizando…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"O teléfono estase iniciando…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Estase iniciando Android…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"A tableta estase iniciando…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"O dispositivo estase iniciando…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimizando almacenamento."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Toca para ver todas as redes"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Conectarse"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Todas as redes"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Conectácheste á rede wifi proposta pola aplicación <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Queres permitir que a aplicación <xliff:g id="NAME">%s</xliff:g> che propoña redes?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Si"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Non"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"A wifi activarase automaticamente"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Cando esteas preto dunha rede gardada de alta calidade"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Non volver activar"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 4908ed2..da8a6e5 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -322,7 +322,7 @@
     <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"સ્પર્શ કરીને શોધખોળ કરવું ચાલુ કરો"</string>
     <string name="capability_desc_canRequestTouchExploration" msgid="7543249041581408313">"ટૅપ કરેલ આઇટમ મોટેથી બોલવામાં આવશે અને હાવભાવની મદદથી સ્ક્રીનને શોધી શકાય છે."</string>
     <string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"તમે લખો તે ટેક્સ્ટનું અવલોકન કરો"</string>
-    <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ક્રેડિટ કાર્ડ નંબર્સ અને પાસવર્ડ્સ જેવો વ્યક્તિગત ડેટા શામેલ છે."</string>
+    <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ક્રેડિટ કાર્ડ નંબર અને પાસવર્ડ જેવો વ્યક્તિગત ડેટા શામેલ છે."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"પ્રદર્શન વિસ્તૃતિકરણ નિયંત્રિત કરો"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"પ્રદર્શનનું ઝૂમ સ્તર અને સ્થિતિનિર્ધારણ નિયંત્રિત કરો."</string>
     <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"હાવભાવ કરો"</string>
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"એપ્લિકેશનને ફોન સુવિધાઓને ઍક્સેસ કરવાની મંજૂરી આપે છે. આ પરવાનગી એપ્લિકેશનને ફોન નંબર અને ઉપકરણ ID, કૉલ સક્રિય છે અને કોઈ કૉલ દ્વારા કનેક્ટ થયેલ રિમોટ નંબર નિર્ધારિત કરવાની મંજૂરી આપે છે."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"સિસ્ટમ મારફતે કૉલ બીજે વાળો"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"કૉલિંગ અનુભવ સુધારવા માટે ઍપ્લિકેશનને સિસ્ટમ મારફતે કૉલ બીજે વાળવાની મંજૂરી આપે છે."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"સિસ્ટમ મારફતે કૉલ જુઓ અને નિયંત્રિત કરો."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"ઍપને ડિવાઇસ પરના ચાલી રહેલા કૉલને જોવાની અને નિયંત્રિત કરવાની મંજૂરી આપે છે. આમાં કૉલ માટેના કૉલ નંબર અને તેની સ્થિતિ જેવી માહિતી શામેલ હોય છે."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"તૃતીય પક્ષ ઍપમાંનો કૉલ ચાલુ રાખો"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"એક અન્ય તૃતીય પક્ષ ઍપમાં ચાલુ થયેલા કૉલને આ ઍપમાં ચાલુ રાખવાની મંજૂરી આપે છે."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ફોન નંબર વાંચો"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"ઍપ્લિકેશનને નિઅર ફીલ્ડ કમ્યુનિકેશન (NFC) ટૅગ, કાર્ડ અને રીડર સાથે સંચાર કરવાની મંજૂરી આપે છે."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"તમારું સ્ક્રીન લૉક અક્ષમ કરો"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"એપ્લિકેશનને કીલૉક અને કોઈપણ સંકળાયેલ પાસવર્ડ સુરક્ષા અક્ષમ કરવાની મંજૂરી આપે છે. ઉદાહરણ તરીકે, ઇનકમિંગ ફોન કૉલ પ્રાપ્ત કરતી વખતે ફોન, કીલૉકને અક્ષમ કરે છે, પછી કૉલ સમાપ્ત થઈ જવા પર કીલૉક ફરીથી સક્ષમ કરે છે."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"સ્ક્રીન લૉકની જટિલતા મેળવીને વિનંતી કરો"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"ઍપને સ્ક્રીન લૉકની જટિલતાનું લેવલ (ઊંચું, મધ્યમ, નીચું અથવા કોઈ નહીં) જાણવાની મંજૂરી આપે છે, જે સ્ક્રીન લૉકના પ્રકાર અને લંબાઈની સંભવિત શ્રેણી સૂચવે છે. ઍપ વપરાશકર્તાઓને સ્ક્રીન લૉકને ચોક્કસ લેવલ સુધી અપડેટ કરવાનું સૂચન પણ કરી શકે છે, પરંતુ વપરાશકર્તાઓ મુક્ત રીતે અવગણીને નૅવિગેટ કરી શકે છે. નોંધી લો કે સ્ક્રીન લૉકનો plaintextમાં સંગ્રહ કરવામાં આવતો નથી, તેથી ઍપને ચોક્કસ પાસવર્ડની જાણ હોતી નથી."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"બાયોમેટ્રિક હાર્ડવેરનો ઉપયોગ કરો"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"ઍપને પ્રમાણીકરણ માટે બાયોમેટ્રિક હાર્ડવેરનો ઉપયોગ કરવાની મંજૂરી આપે છે"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"ફિંગરપ્રિન્ટ હાર્ડવેરને સંચાલિત કરો"</string>
@@ -1083,11 +1087,11 @@
     <string name="deleteText" msgid="6979668428458199034">"ડિલીટ કરો"</string>
     <string name="inputMethod" msgid="1653630062304567879">"ઇનપુટ પદ્ધતિ"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"ટેક્સ્ટ ક્રિયાઓ"</string>
-    <string name="email" msgid="4560673117055050403">"ઇમેઇલ"</string>
+    <string name="email" msgid="4560673117055050403">"ઇમેઇલ કરો"</string>
     <string name="email_desc" msgid="3638665569546416795">"પસંદ કરેલ ઍડ્રેસ પર ઇમેઇલ મોકલો"</string>
     <string name="dial" msgid="1253998302767701559">"કૉલ કરો"</string>
     <string name="dial_desc" msgid="6573723404985517250">"પસંદ કરેલ ફોન નંબર પર કૉલ કરો"</string>
-    <string name="map" msgid="5441053548030107189">"નકશો"</string>
+    <string name="map" msgid="5441053548030107189">"નકશો ખોલો"</string>
     <string name="map_desc" msgid="1836995341943772348">"પસંદ કરેલ સરનામું શોધો"</string>
     <string name="browse" msgid="1245903488306147205">"ખોલો"</string>
     <string name="browse_desc" msgid="8220976549618935044">"પસંદ કરેલ URL ખોલો"</string>
@@ -1096,7 +1100,7 @@
     <string name="add_contact" msgid="7867066569670597203">"ઉમેરો"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"સંપર્કોમાં ઉમેરો"</string>
     <string name="view_calendar" msgid="979609872939597838">"જુઓ"</string>
-    <string name="view_calendar_desc" msgid="5828320291870344584">"કૅલેન્ડરમાં પસંદ કરેલ સમય જુઓ"</string>
+    <string name="view_calendar_desc" msgid="5828320291870344584">"કૅલેન્ડરમાં પસંદ કરેલો સમય જુઓ"</string>
     <string name="add_calendar_event" msgid="1953664627192056206">"શેડ્યૂલ કરો"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"પસંદ કરેલ સમય માટે ઇવેન્ટ શેડ્યૂલ કરો"</string>
     <string name="view_flight" msgid="7691640491425680214">"ટ્રૅક કરો"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ટૅબ્લેટ અપડેટ થઈ રહ્યું છે…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"ઉપકરણ અપડેટ થઈ રહ્યું છે…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"ફોન શરૂ થઈ રહ્યો છે…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android પ્રારંભ થઈ રહ્યું છે…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"ટૅબ્લેટ શરૂ થઈ રહ્યું છે…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"ઉપકરણ શરૂ થઈ રહ્યું છે…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"સંગ્રહ ઓપ્ટિમાઇઝ કરી રહ્યું છે."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"બધા નેટવર્ક જોવા ટૅપ કરો"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"કનેક્ટ કરો"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"બધા નેટવર્કો"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> દ્વારા પ્રસ્તાવિત વાઇ-ફાઇ નેટવર્ક સાથે કનેક્ટ કર્યું"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"શું તમે <xliff:g id="NAME">%s</xliff:g>ને તમારા માટે નેટવર્કનો પ્રસ્તાવ આપવા દેવા માગો છો?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"હા"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"ના"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"વાઇ-ફાઇ આપમેળે ચાલુ થઈ જશે"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"જ્યારે તમે એક ઉચ્ચ ક્વૉલિટીવાળા સાચવેલ નેટવર્કની નજીક હોવ"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"પાછું ચાલુ કરશો નહીં"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 87e1f97..4349c09 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"ऐप्स  को डिवाइस की फ़ोन सुविधाओं तक पहुंचने देता है. यह अनुमति ऐप्स  को फ़ोन नंबर और डिवाइस आईडी, कॉल सक्रिय है या नहीं, और कॉल द्वारा कनेक्ट किया गया दूरस्‍थ नंबर निर्धारित करने देती है."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"सिस्टम के माध्यम से कॉल रूट करें"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"कॉल करने के अनुभव को बेहतर बनाने के लिए ऐप्लिकेशन को सिस्टम के माध्यम से उसके कॉल रूट करने देती है."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"सिस्टम के ज़रिए कॉल देखना और नियंत्रित करना."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"ऐप्लिकेशन को डिवाइस पर चल रहे कॉल देखने और नियंत्रित करने देती है. इसमें कॉल के नंबर और उनकी स्थिति से जुड़ी जानकारी शामिल है."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"दूसरे ऐप्लिकेशन से शुरू किया गया कॉल जारी रखें"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"इसके ज़रिए आप, किसी ऐप्लिकेशन में शुरू किया गया कॉल दूसरे ऐप्लिकेशन में जारी रख सकते हैं."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"फ़ोन नंबर पढ़ना"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"ऐप्स  को नियर फ़ील्ड कम्यूनिकेशन (NFC) टैग, कार्ड, और रीडर के साथ संचार करने देता है."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"अपना स्‍क्रीन लॉक अक्षम करें"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"ऐप्स को कीलॉक और कोई भी संबद्ध पासवर्ड सुरक्षा अक्षम करने देता है. उदाहरण के लिए, इनकमिंग फ़ोन कॉल प्राप्त करते समय फ़ोन, कीलॉक को अक्षम कर देता है, फिर कॉल समाप्त होने पर कीलॉक को पुन: सक्षम कर देता है."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"स्क्रीन लॉक की कठिनाई का अनुरोध करना और उसे जानना"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"ऐप्लिकेशन को स्क्रीन लॉक की कठिनाई का लेवल (बहुत ज़्यादा, मध्यम, कम या बिल्कुल नहीं) जानने देती है, जो स्क्रीन लॉक की लंबाई और उसके प्रकार की उस सीमा के बारे में संकेत देता है, जो शायद हो सकती है. ऐप्लिकेशन उपयोगकर्ताओं को यह सुझाव भी दे सकता है कि वे स्क्रीन लॉक को एक तय लेवल तक अपडेट करें. लेकिन उपयोगकर्ता इसे बेझिझक अनदेखा करके छोड़ सकते हैं. ध्यान दें कि स्क्रीन लॉक को सादे टेक्स्ट में सेव नहीं किया जाता है इसलिए ऐप्लिकेशन को सटीक पासवर्ड पता नहीं होता है."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"बायोमीट्रिक हार्डवेयर इस्तेमाल करने दें"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"पुष्टि के लिए, ऐप्लिकेशन को बायोमीट्रिक हार्डवेयर इस्तेमाल करने की मंज़ूरी दें"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"उंगली की छाप के लिए हार्डवेयर को प्रबंधित करें"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"टैबलेट अपडेट हो रहा है…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"डिवाइस अपडेट हो रहा है…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"फ़ोन चालू हो रहा है…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android प्रारंभ हो रहा है…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"टैबलेट चालू हो रहा है…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"डिवाइस चालू हो रहा है…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"मेमोरी ऑप्‍टिमाइज़ हो रही है."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"सभी नेटवर्क देखने के लिए यहां पर टैप करें"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"कनेक्ट करें"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"सभी नेटवर्क"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> के दिए हुए वाई-फ़ाई नेटवर्क से कनेक्ट है"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"क्या आप <xliff:g id="NAME">%s</xliff:g> को आपके लिए नेटवर्क ऑफ़र करने देना चाहते हैं?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"हां"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"नहीं"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"वाई-फ़ाई अपने आप चालू हो जाएगा"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"जब आप किसी अच्छी क्वालिटी वाले सेव किए गए नेटवर्क के पास हों"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"वापस चालू न करें"</string>
@@ -1407,7 +1416,7 @@
     <string name="ime_action_go" msgid="8320845651737369027">"जाएं"</string>
     <string name="ime_action_search" msgid="658110271822807811">"खोजें"</string>
     <string name="ime_action_send" msgid="2316166556349314424">"भेजें"</string>
-    <string name="ime_action_next" msgid="3138843904009813834">"आगे"</string>
+    <string name="ime_action_next" msgid="3138843904009813834">"आगे बढ़ें"</string>
     <string name="ime_action_done" msgid="8971516117910934605">"हो गया"</string>
     <string name="ime_action_previous" msgid="1443550039250105948">"पीछे जाएं"</string>
     <string name="ime_action_default" msgid="2840921885558045721">"निष्‍पादित करें"</string>
@@ -1451,7 +1460,7 @@
     <string name="disable_tether_notification_title" msgid="7526977944111313195">"टेदरिंग अक्षम है"</string>
     <string name="disable_tether_notification_message" msgid="2913366428516852495">"जानकारी के लिए अपने एडमिन से संपर्क करें"</string>
     <string name="back_button_label" msgid="2300470004503343439">"वापस जाएं"</string>
-    <string name="next_button_label" msgid="1080555104677992408">"आगे"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"आगे बढ़ें"</string>
     <string name="skip_button_label" msgid="1275362299471631819">"अभी नहीं"</string>
     <string name="no_matches" msgid="8129421908915840737">"कोई मिलान नहीं"</string>
     <string name="find_on_page" msgid="1946799233822820384">"पेज पर ढूंढें"</string>
@@ -1639,7 +1648,7 @@
     <string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"मौजूदा सुलभता सुविधा शुरू करने के लिए \'सुलभता शॉर्टकट\' का फिर से इस्तेमाल करें"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"सुलभता बटन पर टैप करते समय इस्तेमाल की जाने वाली सुविधा चुनें:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"सुविधाओं में बदलाव करने के लिए, सुलभता बटन को दबाकर रखें."</string>
-    <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"बड़ा करें"</string>
+    <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"बड़ा करना"</string>
     <string name="user_switched" msgid="3768006783166984410">"मौजूदा उपयोगकर्ता <xliff:g id="NAME">%1$s</xliff:g>."</string>
     <string name="user_switching_message" msgid="2871009331809089783">"<xliff:g id="NAME">%1$s</xliff:g> पर स्विच किया जा रहा है…"</string>
     <string name="user_logging_out_message" msgid="8939524935808875155">"<xliff:g id="NAME">%1$s</xliff:g> द्वारा प्रस्‍थान किया जा रहा है…"</string>
@@ -1879,7 +1888,7 @@
     <string name="usb_mtp_launch_notification_description" msgid="8541876176425411358">"फ़ाइलें देखने के लिए टैप करें"</string>
     <string name="pin_target" msgid="3052256031352291362">"पिन करें"</string>
     <string name="unpin_target" msgid="3556545602439143442">"अनपिन करें"</string>
-    <string name="app_info" msgid="6856026610594615344">"ऐप की जानकारी"</string>
+    <string name="app_info" msgid="6856026610594615344">"ऐप्लिकेशन की जानकारी"</string>
     <string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="demo_starting_message" msgid="5268556852031489931">"डेमो प्रारंभ हो रहा है…"</string>
     <string name="demo_restarting_message" msgid="952118052531642451">"डिवाइस पुन: रीसेट कर रहा है…"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index ff2afba..2747521 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -450,6 +450,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Aplikaciji omogućuje pristup telefonskim značajkama uređaja. Ta dozvola aplikaciji omogućuje utvrđivanje telefonskog broja i ID-ova uređaja, je li poziv aktivan te udaljeni broj koji je povezan pozivom."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"preusmjeravati pozive putem sustava"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Omogućuje aplikaciji da preusmjerava pozive putem sustava radi poboljšanja doživljaja."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"vidjeti i kontrolirati pozive putem sustava."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Omogućuje aplikaciji da vidi i kontrolira pozive koji su u tijeku na uređaju. To uključuje podatke kao što su brojevi i stanja poziva."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"nastaviti poziv iz neke druge aplikacije"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Omogućuje aplikaciji da nastavi poziv započet u nekoj drugoj aplikaciji."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"čitati telefonske brojeve"</string>
@@ -510,6 +512,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Aplikaciji omogućuje komunikaciju s oznakama, karticama i čitačima komunikacije kratkog dometa (NFC)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"onemogućavanje zaključavanja zaslona"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Aplikaciji omogućuje onemogućavanje zaključavanja tipkovnice i svih pripadajućih sigurnosnih zaporki. Na primjer, telefon onemogućuje zaključavanje tipkovnice kod primanja dolaznog telefonskog poziva, nakon kojeg se zaključavanje tipkovnice ponovo omogućuje."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"dohvatiti i zahtijevati složenost zaključavanja zaslona"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Omogućuje aplikaciji da sazna razinu složenosti zaključavanja zaslona (visoka, srednja, niska ili nijedna), što upućuje na mogući raspon duljine i vrstu zaključavanja zaslona. Aplikacija također korisnicima može predložiti da ažuriraju zaključavanje zaslona na određenu razinu, no korisnici to mogu slobodno zanemariti i nastaviti dalje. Napominjemo da se zaključavanje zaslona ne pohranjuje u običnom tekstu, pa aplikacija ne zna točnu zaporku."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"koristiti biometrijski hardver"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Aplikaciji omogućuje upotrebu biometrijskog hardvera radi autentifikacije"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"upravljanje hardverom za čitanje otisaka prstiju"</string>
@@ -1103,26 +1107,26 @@
     <string name="deleteText" msgid="6979668428458199034">"Izbriši"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Način unosa"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Radnje s tekstom"</string>
-    <string name="email" msgid="4560673117055050403">"E-pošta"</string>
-    <string name="email_desc" msgid="3638665569546416795">"Slanje e-poruke na odabranu adresu"</string>
-    <string name="dial" msgid="1253998302767701559">"Poziv"</string>
-    <string name="dial_desc" msgid="6573723404985517250">"Pozivanje odabranog telefonskog broja"</string>
-    <string name="map" msgid="5441053548030107189">"Karta"</string>
-    <string name="map_desc" msgid="1836995341943772348">"Prikaz odabrane adrese na karti"</string>
+    <string name="email" msgid="4560673117055050403">"Pošalji e-poštu"</string>
+    <string name="email_desc" msgid="3638665569546416795">"Pošalji e-poštu na odabranu adresu"</string>
+    <string name="dial" msgid="1253998302767701559">"Nazovi"</string>
+    <string name="dial_desc" msgid="6573723404985517250">"Nazovi odabrani telefonski broj"</string>
+    <string name="map" msgid="5441053548030107189">"Prikaži na karti"</string>
+    <string name="map_desc" msgid="1836995341943772348">"Prikaži odabrane adrese na karti"</string>
     <string name="browse" msgid="1245903488306147205">"Otvori"</string>
-    <string name="browse_desc" msgid="8220976549618935044">"Otvaranje odabranog URL-a"</string>
-    <string name="sms" msgid="4560537514610063430">"Poruka"</string>
-    <string name="sms_desc" msgid="7526588350969638809">"Slanje poruke na odabrani telefonski broj"</string>
+    <string name="browse_desc" msgid="8220976549618935044">"Otvori odabrani URL"</string>
+    <string name="sms" msgid="4560537514610063430">"Pošalji poruku"</string>
+    <string name="sms_desc" msgid="7526588350969638809">"Pošalji poruku na odabrani telefonski broj"</string>
     <string name="add_contact" msgid="7867066569670597203">"Dodaj"</string>
-    <string name="add_contact_desc" msgid="4830217847004590345">"Dodavanje u kontakte"</string>
+    <string name="add_contact_desc" msgid="4830217847004590345">"Dodaj u kontakte"</string>
     <string name="view_calendar" msgid="979609872939597838">"Prikaži"</string>
-    <string name="view_calendar_desc" msgid="5828320291870344584">"Prikaz odabranog vremena u kalendaru"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"Raspored"</string>
-    <string name="add_calendar_event_desc" msgid="4326891793260687388">"Zakazivanje događaja za određeno vrijeme"</string>
+    <string name="view_calendar_desc" msgid="5828320291870344584">"Prikaži odabrano vrijeme u kalendaru"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"Zakaži"</string>
+    <string name="add_calendar_event_desc" msgid="4326891793260687388">"Zakaži događaj za određeno vrijeme"</string>
     <string name="view_flight" msgid="7691640491425680214">"Prati"</string>
-    <string name="view_flight_desc" msgid="3876322502674253506">"Praćenje odabranog leta"</string>
+    <string name="view_flight_desc" msgid="3876322502674253506">"Prati odabrani let"</string>
     <string name="translate" msgid="9218619809342576858">"Prevedi"</string>
-    <string name="translate_desc" msgid="4502367770068777202">"Prevedite odabrani tekst"</string>
+    <string name="translate_desc" msgid="4502367770068777202">"Prevedi odabrani tekst"</string>
     <string name="define" msgid="7394820043869954211">"Definiraj"</string>
     <string name="define_desc" msgid="7910883642444919726">"Definiraj odabrani tekst"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ponestaje prostora za pohranu"</string>
@@ -1201,6 +1205,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet se ažurira…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Uređaj se ažurira…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Telefon se pokreće…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Pokretanje Androida..."</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet se pokreće…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Uređaj se pokreće…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimiziranje pohrane."</string>
@@ -1261,6 +1266,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Dodirnite za prikaz svih mreža"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Poveži"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Sve mreže"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Povezani ste s Wi‑Fi mrežom koju je predložila aplikacija <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Želite li dopustiti da vam aplikacija <xliff:g id="NAME">%s</xliff:g> predlaže mreže?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Da"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Ne"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi će se uključiti automatski"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Kada ste u blizini spremljene mreže visoke kvalitete"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Više ne uključuj"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 59d5123..10538f4 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Lehetővé teszi az alkalmazás számára, hogy hozzáférjen az eszköz telefonálási funkcióihoz. Az engedéllyel rendelkező alkalmazás meghatározhatja a telefonszámot és eszközazonosítókat, hogy egy hívás aktív-e, valamint híváskor a másik fél telefonszámát."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"rendszeren keresztüli hívásirányítás"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"A telefonálási élmény javítása érdekében lehetővé teszi az alkalmazás számára a rendszeren keresztüli hívásirányítást."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"Hívások megtekintése és vezérlése a rendszeren keresztül"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Engedélyezi az alkalmazásnak az eszközön folyamatban lévő hívások megtekintését és vezérlését. Ebbe beletartoznak az olyan információk is, mint a hívásban részt vevő felek hívószáma és a hívások állapota."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"Másik alkalmazásból indított hívás folytatása"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Engedélyezi az alkalmazásnak, hogy folytassa a hívást, amelyet valamelyik másik alkalmazásban kezdtek meg."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"telefonszámok olvasása"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Lehetővé teszi az alkalmazás számára, hogy NFC (Near Field Communication - kis hatósugarú vezeték nélküli kommunikáció) technológiát használó címkékkel, kártyákkal és leolvasókkal kommunikáljon."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"képernyőzár kikapcsolása"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Lehetővé teszi az alkalmazás számára a billentyűzár és bármely kapcsolódó jelszavas védelem kikapcsolását. Például a telefon feloldja a billentyűzárat bejövő hívás esetén, majd újra bekapcsolja azt a hívás végeztével."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"a képernyőzár összetettségének lekérdezése és lekérése"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Lehetővé teszi az alkalmazás számára, hogy megismerje a képernyőzár összetettségi szintjét (magas, közepes, alacsony vagy nincs), amely jelzi a képernyőzár lehetséges típusát és hosszát. Az alkalmazás ezenkívül javaslatot tehet a felhasználóknak a képernyőzár bizonyos szintre való frissítésére, de ezt a javaslatot a felhasználók figyelmen kívül hagyhatják. A képernyőzárat nem egyszerű szöveges formátumban tárolja a rendszer, ezért az alkalmazás nem fogja tudni a pontos jelszót."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"biometrikus hardver használata"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Lehetővé teszi az alkalmazás számára a biometrikus hardver hitelesítésre való használatát"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"ujjlenyomat-olvasó hardver kezelése"</string>
@@ -1084,7 +1088,7 @@
     <string name="inputMethod" msgid="1653630062304567879">"Beviteli mód"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Műveletek szöveggel"</string>
     <string name="email" msgid="4560673117055050403">"E-mail"</string>
-    <string name="email_desc" msgid="3638665569546416795">"Kiválasztott cím elküldése e-mailben"</string>
+    <string name="email_desc" msgid="3638665569546416795">"E-mail küldése a kiválasztott címre"</string>
     <string name="dial" msgid="1253998302767701559">"Hívás"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Kiválasztott telefonszám hívása"</string>
     <string name="map" msgid="5441053548030107189">"Térkép"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"A táblagép frissítése…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Az eszköz frissítése…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"A telefon indítása…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Az Android indítása…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"A táblagép indítása…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Az eszköz indítása…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Tárhely-optimalizálás."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Koppintással megjelenítheti az összes hálózatot"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Kapcsolódás"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Összes hálózat"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Csatlakozva a(z) <xliff:g id="NAME">%s</xliff:g> által javasolt Wi-Fi-hálózathoz"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Szeretné, hogy a(z) <xliff:g id="NAME">%s</xliff:g> hálózatokat javasoljon Önnek?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Igen"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nem"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"A Wi-Fi automatikusan bekapcsol"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Amikor jó minőségű mentett hálózat közelében tartózkodik"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ne kapcsolódjon vissza"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index d019f0e..fbb5f4b 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Թույլ է տալիս հավելվածին օգտագործել սարքի հեռախոսային գործիքները: Այս թույլտվությունը հավելվածին հնարավորություն է տալիս որոշել հեռախոսահամարը և սարքի ID-ները, արդյոք զանգը ակտիվ է և միացված զանգի հեռակա հեռախոսահամարը:"</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"զանգերն ուղարկել համակարգի միջոցով"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Հավելվածին թույլ է տալիս իր զանգերն ուղարկել համակարգի միջոցով՝ կապի որակը բարձրացնելու նպատակով։"</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"Զանգերի դիտում և վերահսկում համակարգի միջոցով"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Թույլ է տալիս հավելվածին տեսնել և վերահսկել ընթացիկ զանգերը սարքում: Սա ներառում է այնպիսի տեղեկություններ, ինչպիսիք են, օրինակ, հեռախոսահամարները և զանգերի վիճակը:"</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"շարունակել զանգը այլ հավելվածի միջոցով"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Թույլ է տալիս հավելվածին շարունակել մեկ այլ հավելվածի միջոցով սկսած զանգը:"</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"օգտագործել հեռախոսահամարները"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Թույլ է տալիս հավելվածին հաղորդակցվել Մոտ տարածությամբ հաղորդակցման (NFC) պիտակների, քարտերի և ընթերցիչների հետ:"</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"անջատել ձեր էկրանի կողպեքը"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Թույլ է տալիս հավելվածին անջատել ստեղնաշարի կողպումը և ցանկացած կապված գաղտնաբառի պաշտպանվածությունը: Սրա ճիշտ օրինակն է, երբ հեռախոսը անջատում է ստեղնաշարի կողպումը մուտքային զանգ ստանալիս, հետո այն կրկին միացնում է, երբ զանգը ավարտվում է:"</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"էկրանի կողպման բարդության մակարդակի մասին տեղեկությունների և այդ մակարդակի բարձրացման հետ կապված խորհուրդների ստացում"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Հավելվածին հասանելի կդառնան էկրանի կողպման բարդության մակարդակի մասին տեղեկությունները (բարձր, միջին, ցածր կամ ոչ մի), այդ թվում կողպման տեսակի և գաղտնաբառի երկարության մասին տվյալները: Բացի այդ, հավելվածը կկարողանա առաջարկել օգտատերերին բարձրացնել կողպման բարդության մակարդակը: Օգտատերերը կարող են անտեսել այդ առաջարկները: Նկատի ունեցեք, որ գաղտնաբառը չի պահվում բաց տեքստի տեսքով և հասանելի չէ հավելվածին:"</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"կենսաչափական սարքի օգտագործում"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Հավելվածին թույլ է տալիս օգտագործել նույնականացման համար նախատեսված կենսաչափական սարքը"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"կառավարել մատնահետքերի գրանցման սարքը"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Պլանշետը թարմացվում է…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Սարքը թարմացվում է…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Հեռախոսը միանում է…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android-ը մեկնարկում է…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Պլանշետը միանում է…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Սարքը միանում է…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Պահեստի օպտիմալացում:"</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Հպեք՝ բոլոր ցանցերը տեսնելու համար"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Միանալ"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Բոլոր ցանցերը"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Միացել է «<xliff:g id="NAME">%s</xliff:g>» հավելվածի գտած Wi‑Fi ցանցին:"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Թույլատրե՞լ <xliff:g id="NAME">%s</xliff:g> հավելվածին առաջարկել ձեզ ցանցեր:"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Այո"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Ոչ"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi-ն ավտոմատ կմիանա"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Լավ ազդանշանով պահված ցանցի տարածքում գտնվելիս"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Նորից չմիացնել"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index cc70724..3c6e347 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Memungkinkan aplikasi mengakses fitur telepon perangkat. Izin ini memungkinkan aplikasi menentukan nomor telepon dan ID perangkat, apakah suatu panggilan aktif atau tidak, dan nomor jarak jauh yang tersambung oleh sebuah panggilan."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"sambungkan panggilan telepon melalui sistem"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Mengizinkan aplikasi menyambungkan panggilan telepon melalui sistem untuk menyempurnakan pengalaman menelepon."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"melihat dan mengontrol panggilan melalui sistem."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Memungkinkan aplikasi melihat dan mengontrol panggilan telepon yang sedang berlangsung di perangkat. Ini mencakup informasi seperti nomor yang menelepon dan status panggilan telepon."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"lanjutkan panggilan dari aplikasi lain"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Mengizinkan aplikasi melanjutkan panggilan yang dimulai di aplikasi lain."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"membaca nomor telepon"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Mengizinkan apl berkomunikasi dengan tag, kartu, dan alat pembaca Komunikasi Nirkabel Jarak Dekat (NFC)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"nonaktifkan kunci layar Anda"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Memungkinkan aplikasi menonaktifkan kunci tombol dan keamanan sandi apa pun yang terkait. Misalnya, ponsel menonaktifkan kunci tombol saat menerima panggilan telepon masuk, kemudian mengaktifkan kembali kunci tombol ketika panggilan selesai."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"dapatkan dan minta kompleksitas kunci layar"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Mengizinkan aplikasi mempelajari tingkat kompleksitas kunci layar (tinggi, sedang, rendah, atau tidak ada), yang menunjukkan kemungkinan rentang durasi dan jenis kunci layar. Aplikasi juga dapat menyarankan agar pengguna memperbarui kunci layar ke tingkat tertentu, tetapi pengguna dapat mengabaikan dan keluar dengan bebas. Perhatikan bahwa kunci layar tidak disimpan dalam teks biasa, sehingga aplikasi tidak mengetahui sandi yang sebenarnya."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"gunakan hardware biometrik"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Mengizinkan aplikasi menggunakan hardware biometrik untuk autentikasi"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"kelola hardware sidik jari"</string>
@@ -1088,23 +1092,23 @@
     <string name="dial" msgid="1253998302767701559">"Panggil"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Panggil nomor telepon yang dipilih"</string>
     <string name="map" msgid="5441053548030107189">"Peta"</string>
-    <string name="map_desc" msgid="1836995341943772348">"Mencari alamat yang dipilih"</string>
+    <string name="map_desc" msgid="1836995341943772348">"Cari alamat yang dipilih"</string>
     <string name="browse" msgid="1245903488306147205">"Buka"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Buka URL yang dipilih"</string>
-    <string name="sms" msgid="4560537514610063430">"Pesan"</string>
-    <string name="sms_desc" msgid="7526588350969638809">"Mengirimkan SMS ke nomor telepon yang dipilih"</string>
+    <string name="sms" msgid="4560537514610063430">"SMS"</string>
+    <string name="sms_desc" msgid="7526588350969638809">"Kirim SMS ke nomor telepon yang dipilih"</string>
     <string name="add_contact" msgid="7867066569670597203">"Tambahkan"</string>
-    <string name="add_contact_desc" msgid="4830217847004590345">"Menambahkan ke kontak"</string>
+    <string name="add_contact_desc" msgid="4830217847004590345">"Tambahkan ke kontak"</string>
     <string name="view_calendar" msgid="979609872939597838">"Lihat"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"Melihat waktu yang dipilih di kalender"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"Jadwal"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"Jadwalkan"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"Menjadwalkan acara untuk waktu yang dipilih"</string>
-    <string name="view_flight" msgid="7691640491425680214">"Lacak"</string>
-    <string name="view_flight_desc" msgid="3876322502674253506">"Memantau penerbangan yang dipilih"</string>
+    <string name="view_flight" msgid="7691640491425680214">"Pantau"</string>
+    <string name="view_flight_desc" msgid="3876322502674253506">"Pantau penerbangan yang dipilih"</string>
     <string name="translate" msgid="9218619809342576858">"Terjemahkan"</string>
     <string name="translate_desc" msgid="4502367770068777202">"Terjemahkan teks yang dipilih"</string>
-    <string name="define" msgid="7394820043869954211">"Tentukan"</string>
-    <string name="define_desc" msgid="7910883642444919726">"Tentukan teks yang dipilih"</string>
+    <string name="define" msgid="7394820043869954211">"Definisikan"</string>
+    <string name="define_desc" msgid="7910883642444919726">"Definisikan teks yang dipilih"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ruang penyimpanan hampir habis"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Beberapa fungsi sistem mungkin tidak dapat bekerja"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Penyimpanan tidak cukup untuk sistem. Pastikan Anda memiliki 250 MB ruang kosong, lalu mulai ulang."</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Mengupdate tablet…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Mengupdate perangkat…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Menyalakan ponsel…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Memulai Android…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Menyalakan tablet…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Menyalakan perangkat…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Mengoptimalkan penyimpanan."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tap untuk melihat semua jaringan"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Hubungkan"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Semua jaringan"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Terhubung ke jaringan Wi‑Fi yang diusulkan oleh <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Perbolehkan <xliff:g id="NAME">%s</xliff:g> mengusulkan jaringan kepada Anda?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ya"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Tidak"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi akan aktif otomatis"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Saat berada di dekat jaringan berkualitas tinggi yang tersimpan"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Jangan aktifkan kembali"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 72ce56c..f638ff8 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Leyfir forriti að fá aðgang að símaeiginleikum tækisins. Þessi heimild gerir forritinu kleift að komast að símanúmeri og auðkennum tækisins, hvort símtal er í gangi og símanúmeri viðmælanda í símtali."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"senda símtöl gegnum kerfið"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Heimilar forritinu að senda símtöl sín gegnum kerfið til að bæta gæði símtalsins."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"sjá og stjórna símtölum í gegnum kerfið."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Leyfir forritinu að sjá og stjórna hringdum símtölum í tækinu. Þar á meðal eru upplýsingar á borð við símanúmer í símtölum og stöður símtala."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"halda áfram með símtal úr öðru forriti"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Leyfir forritinu að halda áfram með símtal sem hófst í öðru forriti."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"lesa símanúmer"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Leyfir forriti að eiga samskipti við NFC-merki, -spjöld og -lesara (nándarsamskipti)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"slökkva á skjálásnum"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Leyfir forriti að slökkva á símalásnum og öðrum öryggisaðgerðum tengdum aðgangsorði. Til dæmis gerir síminn lásinn óvirkan þegar símtal berst og læsist svo aftur að símtali loknu."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"fá og biðja um flókinn skjálás"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Leyfir forritinu að læra hversu flókinn skjálásinn er (erfiður, miðlungs, léttur eða enginn), sem gefur til kynna lengd og tegund skjálássins. Forritið getur einnig lagt til að notendur geri skjálásinn flóknari upp að tilteknu marki, en notendur geta valið að hunsa það og halda áfram að vafra. Hafðu í huga að skjálásinn er ekki geymdur í ódulkóðuðum texta svo forritið veit ekki nákvæmt aðgangsorð."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"nota búnað fyrir líffræðileg gögn"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Leyfir forritinu að nota búnað fyrir líffræðileg gögn til auðkenningar"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"stjórna fingrafarabúnaði"</string>
@@ -1083,7 +1087,7 @@
     <string name="deleteText" msgid="6979668428458199034">"Eyða"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Innsláttaraðferð"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Textaaðgerðir"</string>
-    <string name="email" msgid="4560673117055050403">"Tölvupóstur"</string>
+    <string name="email" msgid="4560673117055050403">"Senda tölvupóst"</string>
     <string name="email_desc" msgid="3638665569546416795">"Senda á valið netfang"</string>
     <string name="dial" msgid="1253998302767701559">"Símtal"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Hringja í valið símanúmer"</string>
@@ -1091,7 +1095,7 @@
     <string name="map_desc" msgid="1836995341943772348">"Finna valið heimilisfang"</string>
     <string name="browse" msgid="1245903488306147205">"Opna"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Opna valda vefslóð"</string>
-    <string name="sms" msgid="4560537514610063430">"Skilaboð"</string>
+    <string name="sms" msgid="4560537514610063430">"Senda skilaboð"</string>
     <string name="sms_desc" msgid="7526588350969638809">"Senda skilaboð í valið símanúmer"</string>
     <string name="add_contact" msgid="7867066569670597203">"Bæta við"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Bæta við tengiliði"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Uppfærir spjaldtölvu…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Uppfærir tæki…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Síminn kveikir á sér…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android er að ræsast…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Spjaldtölvan kveikir á sér…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Tækið kveikir á sér…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Fínstillir geymslu."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Ýttu til að sjá öll netkerfi"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Tengjast"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Öll netkerfi"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Tengt við Wi-Fi net sem <xliff:g id="NAME">%s</xliff:g> stakk upp á"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Viltu leyfa <xliff:g id="NAME">%s</xliff:g> að stinga upp á neti fyrir þig?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Já"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nei"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Kveikt verður sjálfkrafa á Wi‑Fi"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Þegar þú ert nálægt vistuðu hágæðaneti"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ekki kveikja aftur"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 4463e11..c9c8f0c 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Consente all\'applicazione di accedere alle funzioni telefoniche del dispositivo. Questa autorizzazione consente all\'applicazione di determinare il numero di telefono e gli ID dei dispositivi, se una chiamata è attiva e il numero remoto connesso da una chiamata."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"indirizzamento delle chiamate tramite il sistema"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Consente all\'app di indirizzare le proprie chiamate tramite il sistema al fine di migliorare l\'esperienza di chiamata."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"visualizzazione e controllo delle chiamate tramite il sistema."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Consente all\'app di visualizzare e controllare le chiamate in corso sul dispositivo. Sono incluse informazioni quali i numeri e lo stato relativi alle chiamate."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"Continuazione di una chiamata da un\'altra app"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Consente all\'app di continuare una chiamata che è stata iniziata in un\'altra app."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"lettura dei numeri di telefono"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Consente all\'applicazione di comunicare con tag, schede e lettori NFC (Near Field Communication)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"disattivazione blocco schermo"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Consente all\'applicazione di disattivare il blocco tastiera ed eventuali protezioni tramite password associate. Ad esempio, il telefono disattiva il blocco tastiera quando riceve una telefonata in arrivo e lo riattiva al termine della chiamata."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"ricezione e richiesta della complessità del blocco schermo"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Consente all\'app di conoscere il livello di complessità del blocco schermo (alto, medio, basso o nessuno), che indica l\'intervallo di caratteri possibile e il tipo di blocco schermo. L\'app può inoltre suggerire agli utenti di aggiornare il blocco schermo a un livello specifico di complessità, ma gli utenti possono ignorare liberamente il suggerimento e uscire. Tieni presente che il blocco schermo non viene memorizzato come testo non crittografato, quindi l\'app non conosce la password esatta."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"Utilizzo di hardware biometrico"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Consente all\'app di utilizzare hardware biometrico per eseguire l\'autenticazione"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"gestisci hardware per il riconoscimento delle impronte digitali"</string>
@@ -1083,7 +1087,7 @@
     <string name="deleteText" msgid="6979668428458199034">"Elimina"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Metodo inserimento"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Azioni testo"</string>
-    <string name="email" msgid="4560673117055050403">"Invia una email"</string>
+    <string name="email" msgid="4560673117055050403">"Invia email"</string>
     <string name="email_desc" msgid="3638665569546416795">"Invia un\'email all\'indirizzo selezionato"</string>
     <string name="dial" msgid="1253998302767701559">"Chiama"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Chiama il numero di telefono selezionato"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Aggiornamento del tablet…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Aggiornamento del dispositivo…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Avvio del telefono…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Avvio di Android…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Avvio del tablet…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Avvio del dispositivo…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Ottimizzazione archiviazione."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tocca per vedere tutte le reti"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Connetti"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Tutte le reti"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"È stata stabilita la connessione alla rete Wi‑Fi proposta da <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Vuoi consentire all\'app <xliff:g id="NAME">%s</xliff:g> di proporti reti?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Sì"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"No"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Il Wi‑Fi verrà attivato automaticamente"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Quando ti trovi nell\'area di una rete salvata di alta qualità"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Non riattivare"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 3f0c0b8..26f6988f 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -453,6 +453,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"מאפשר לאפליקציה לגשת לתכונות הטלפון של המכשיר. אישור זה מתיר לאפליקציה לגלות את מספר הטלפון ואת זיהויי המכשיר, האם שיחה פעילה ואת המספר המרוחק המחובר באמצעות שיחה."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ניתוב שיחות דרך המערכת"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"מאפשרת לאפליקציה לנתב את השיחות דרך המערכת כדי לשפר את חוויית השיחה."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ניתן להציג שיחות ולשלוט בהן באמצעות המערכת."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"מאפשר לאפליקציה להציג שיחות נוכחיות ולשלוט בהן במכשיר. זה כולל פרטים כמו מספרי שיחה של שיחות ומצב השיחה."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"המשך שיחה מאפליקציה אחרת"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"הרשאה זו מתירה לאפליקציה להמשיך שיחה שהחלה באפליקציה אחרת."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"גישה למספרי הטלפון"</string>
@@ -513,6 +515,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"מאפשר לאפליקציה נהל תקשורת עם תגים, כרטיסים וקוראים מסוג \'תקשורת מטווח קצר\'."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"ביטול נעילת המסך שלך"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"מאפשר לאפליקציה להשבית את נעילת המקשים וכל אמצעי אבטחה משויך המבוסס על סיסמה. לדוגמה, הטלפון משבית את נעילת המקשים בעת קבלה של שיחת טלפון נכנסת, ולאחר מכן מפעיל מחדש את נעילת המקשים עם סיום השיחה."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"קבלה ובקשה של מורכבות לנעילת מסך"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"‏מאפשרת לאפליקציה ללמוד את רמת המורכבות של נעילת המסך (גבוהה, בינונית, נמוכה או ללא). רמה זו מציינת את הטווח האפשרי של אורך וסוג של נעילת המסך. האפליקציה יכולה גם להציע למשתמשים שיעדכנו את נעילת המסך ברמה מסוימת, אבל המשתמשים יכולים להתעלם מההצעה ולנווט לפריט אחר כרצונם. יש לשים לב שנעילת המסך לא מאוחסנת ב-plaintext, ולכן האפליקציה לא יודעת מה הסיסמה המדויקת."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"שימוש בחומרה ביומטרית"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"מאפשרת לאפליקציה להשתמש בחומרה ביומטרית לצורך אימות"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"ניהול חומרה של טביעות אצבעות"</string>
@@ -1123,28 +1127,28 @@
     <string name="deleteText" msgid="6979668428458199034">"מחיקה"</string>
     <string name="inputMethod" msgid="1653630062304567879">"שיטת קלט"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"פעולות טקסט"</string>
-    <string name="email" msgid="4560673117055050403">"אימייל"</string>
+    <string name="email" msgid="4560673117055050403">"התכתבות באימייל"</string>
     <string name="email_desc" msgid="3638665569546416795">"שליחת אימייל לכתובת שנבחרה"</string>
-    <string name="dial" msgid="1253998302767701559">"שיחה"</string>
+    <string name="dial" msgid="1253998302767701559">"ביצוע שיחה"</string>
     <string name="dial_desc" msgid="6573723404985517250">"התקשרות למספר הטלפון שנבחר"</string>
-    <string name="map" msgid="5441053548030107189">"מפה"</string>
-    <string name="map_desc" msgid="1836995341943772348">"מיקום הכתובת שנבחרה"</string>
+    <string name="map" msgid="5441053548030107189">"צפייה במפה"</string>
+    <string name="map_desc" msgid="1836995341943772348">"איתור הכתובת שנבחרה"</string>
     <string name="browse" msgid="1245903488306147205">"פתיחה"</string>
     <string name="browse_desc" msgid="8220976549618935044">"פתיחה של כתובת האתר שנבחרה"</string>
-    <string name="sms" msgid="4560537514610063430">"הודעה"</string>
+    <string name="sms" msgid="4560537514610063430">"התכתבות בהודעות"</string>
     <string name="sms_desc" msgid="7526588350969638809">"שליחת הודעה למספר הטלפון שנבחר"</string>
     <string name="add_contact" msgid="7867066569670597203">"הוספה"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"הוספה לאנשי הקשר"</string>
-    <string name="view_calendar" msgid="979609872939597838">"תצוגה"</string>
-    <string name="view_calendar_desc" msgid="5828320291870344584">"הצגת הזמן שנבחר ביומן"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"לוח זמנים"</string>
-    <string name="add_calendar_event_desc" msgid="4326891793260687388">"תזמון אירוע לזמן שנבחר"</string>
+    <string name="view_calendar" msgid="979609872939597838">"צפייה"</string>
+    <string name="view_calendar_desc" msgid="5828320291870344584">"הצגת התאריך שנבחר ביומן"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"קביעת מועד"</string>
+    <string name="add_calendar_event_desc" msgid="4326891793260687388">"קביעת מועד לאירוע בתאריך שנבחר"</string>
     <string name="view_flight" msgid="7691640491425680214">"מעקב"</string>
-    <string name="view_flight_desc" msgid="3876322502674253506">"ניהול מעקב אחר הטיסה שנבחרה"</string>
+    <string name="view_flight_desc" msgid="3876322502674253506">"מעקב אחרי הטיסה שנבחרה"</string>
     <string name="translate" msgid="9218619809342576858">"תרגום"</string>
     <string name="translate_desc" msgid="4502367770068777202">"תרגום הטקסט שנבחר"</string>
-    <string name="define" msgid="7394820043869954211">"הגדרה"</string>
-    <string name="define_desc" msgid="7910883642444919726">"הגדרת הטקסט שנבחר"</string>
+    <string name="define" msgid="7394820043869954211">"מציאת הפירוש"</string>
+    <string name="define_desc" msgid="7910883642444919726">"פירוש הטקסט שנבחר"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"שטח האחסון אוזל"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"ייתכן שפונקציות מערכת מסוימות לא יפעלו"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"‏אין מספיק שטח אחסון עבור המערכת. ודא שיש לך שטח פנוי בגודל 250MB התחל שוב."</string>
@@ -1221,6 +1225,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"הטאבלט מתעדכן…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"הפעלת המכשיר מתחילה…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"הפעלת הטלפון מתחילה…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"‏הפעלת Android מתחילה…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"הפעלת הטאבלט מתחילה…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"הפעלת המכשיר מתחילה…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"מתבצעת אופטימיזציה של האחסון."</string>
@@ -1283,6 +1288,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"יש להקיש כדי לראות את כל הרשתות"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"התחבר"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"כל הרשתות"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"‏יש חיבור לרשת Wi‑Fi שהוצעה על ידי <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"האם ברצונך לקבל הצעות לרשתות מ-<xliff:g id="NAME">%s</xliff:g>?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"כן"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"לא"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"‏ה-Wi-Fi יופעל אוטומטית"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"כשתימצאו בקרבת רשת באיכות גבוהה ששמרתם"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"אל תפעיל שוב"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index e069e33..45587cb 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"端末の電話機能へのアクセスをアプリに許可します。これにより、電話番号、端末ID、通話中かどうか、通話相手の電話番号をアプリから特定できるようになります。"</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"システム経由での通話転送"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"通話環境の改善のために、システム経由での通話転送をアプリに許可します。"</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"システム経由の通話の表示と操作。"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"デバイスで進行中の通話の表示と操作をアプリに許可します。通話の電話番号や状態などの情報も含まれます。"</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"別のアプリでの通話の続行"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"別のアプリで通話を続行することをこのアプリに許可します。"</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"電話番号の読み取り"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"NFCタグ、カード、リーダーとの通信をアプリに許可します。"</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"画面ロックの無効化"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"キーロックとキーロックに関連付けられたパスワードのセキュリティを無効にすることをアプリに許可します。たとえば、かかってきた電話を受ける際にキーロックを無効にし、通話が終了したらキーロックを再度有効にする場合などに使用します。"</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"画面ロックの複雑さの入手とリクエスト"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"このアプリに画面ロックの複雑さレベル(高、中、低、なし)を認識することを許可します。複雑さレベルは、画面ロックの文字数の範囲やタイプを示すものです。アプリはユーザーに一定レベルまで画面ロックを更新するようすすめることもできますが、ユーザーは無視したり別の操作を行ったりできます。画面ロックは平文で保存されないため、アプリが正確なパスワードを知ることはありません。"</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"生体認証ハードウェアの使用"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"生体認証ハードウェアを認証に使用することをアプリに許可します"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"指紋ハードウェアの管理"</string>
@@ -1097,9 +1101,9 @@
     <string name="add_contact_desc" msgid="4830217847004590345">"連絡先に追加します"</string>
     <string name="view_calendar" msgid="979609872939597838">"表示"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"カレンダーで選択した日時を表示します"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"スケジュール"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"予定を作成"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"選択した日時に予定を設定します"</string>
-    <string name="view_flight" msgid="7691640491425680214">"トラック"</string>
+    <string name="view_flight" msgid="7691640491425680214">"フライト"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"選択したフライトをチェックします"</string>
     <string name="translate" msgid="9218619809342576858">"翻訳"</string>
     <string name="translate_desc" msgid="4502367770068777202">"選択したテキストを翻訳します"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"タブレットの更新中…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"端末の更新中…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"スマートフォンの起動中…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Androidの起動中…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"タブレットの起動中…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"端末の起動中…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"ストレージを最適化しています。"</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"すべてのネットワークを表示するにはタップします"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"接続"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"すべてのネットワーク"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> で推奨される Wi-Fi ネットワークに接続"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"ネットワークを推奨することを <xliff:g id="NAME">%s</xliff:g> に許可しますか?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"はい"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"いいえ"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi は自動的にオンになります"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"高品質の保存済みネットワークの検出時"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"再度オンにしない"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 3be3093..07df9fc 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"აპს შეეძლება ჰქონდეს წვდომა მოწყობილობის სატელეფონო ფუნქციებზე. აპმა მსგავსი უფლებით შეძლებს დაადგინოს ტელეფონის ნომერი, მისი სერიული გამოცემა, აქტიური ზარი, დაკავშირებული ნომერი და მსგავსი."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ზარების სისტემის მეშვეობით მარშრუტიზაცია"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"დარეკვის ხარისხის გაუმჯობესების მიზნით, აპს ზარების სისტემის მეშვეობით მარშრუტიზაციის საშუალებას აძლევს."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ზარების ნახვა და გაკონტროლება სისტემის მეშვეობით."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"საშუალებას აძლევს აპს, იხილოს და გააკონტროლოს მიმდინარე ზარები მოწყობილობაზე. აღნიშნული მოიცავს ისეთ ინფორმაციას, როგორიცაა ზარებთან დაკავშირებული აბონენტების ნომრები და ზარების მდგომარეობა."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"ზარის სხვა აპიდან გაგრძელება"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"ნებას რთავს აპს, გააგრძელოს ზარი, რომელიც სხვა აპშია წამოწყებული."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ტელეფონის ნომრების წაკითხვა"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"აპს შეეძლება ახლო მოქმედების რადიოკავშირის (NFC) მეშვეობით ტეგების, ბარათებისა და წამკითხველების შემცველი მონაცემების მიმოცვლა."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"თქვენი ეკრანის ბლოკის გათიშვა"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"შეეძლება კლავიატურის დაბლოკვისა და პაროლით უზრუნველყოფილი ნებისმიერი უსაფრთხოების ფუნქციის დეაქტივაცია. მაგალითად, ტელეფონი შემომავალი ზარის დროს აუქმებს კლავიატურის დაბლოკვას და კვლავ ააქტიურებს მას, როგორც კი ზარი დასრულდება."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"ეკრანის დაბლოკვის მეთოდის სირთულის მიღება და მოთხოვნა"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"საშუალებას აძლევს აპს, შეიტყოს ეკრანის დაბლოკვის მეთოდის სირთულე (მაღალი, საშუალო, დაბალი ან არანაირი), რისი მეშვეობითაც შესაძლებელია ეკრანის დაბლოკვის მეთოდის სიგრძის შესაძლო დიაპაზონისა და ტიპის განსაზღვრა. გარდა ამისა, აპს შეუძლია მომხმარებლებისთვის ეკრანის დაბლოკვის მეთოდის გარკვეულ დონემდე გაძლიერების შეთავაზება, თუმცა მომხმარებლებს შეეძლებათ აღნიშნული შეტყობინების უგულებელყოფა და სხვა ეკრანზე გადასვლა. გაითვალისწინეთ, რომ ეკრანის დაბლოკვის მეთოდი არ ინახება ჩვეულებრივი ტექსტის სახით, ამიტომ აპს არ ეცოდინება ზუსტი პაროლი."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"ბიომეტრიული აპარატის გამოყენება"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"საშუალებას აძლევს აპს, ავტორიზაციისთვის გამოიყენოს ბიომეტრიული აპარატი"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"თითის ანაბეჭდის აპარატის მართვა"</string>
@@ -1083,11 +1087,11 @@
     <string name="deleteText" msgid="6979668428458199034">"წაშლა"</string>
     <string name="inputMethod" msgid="1653630062304567879">"შეყვანის მეთოდი"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"ქმედებები ტექსტზე"</string>
-    <string name="email" msgid="4560673117055050403">"ელფოსტა"</string>
+    <string name="email" msgid="4560673117055050403">"ელფოსტის გაგზავნა"</string>
     <string name="email_desc" msgid="3638665569546416795">"არჩეულ მისამართზე ელფოსტის გაგზავნა"</string>
     <string name="dial" msgid="1253998302767701559">"ზარი"</string>
     <string name="dial_desc" msgid="6573723404985517250">"არჩეულ ტელეფონის ნომერზე დარეკვა"</string>
-    <string name="map" msgid="5441053548030107189">"რუკა"</string>
+    <string name="map" msgid="5441053548030107189">"რუკის გახსნა"</string>
     <string name="map_desc" msgid="1836995341943772348">"არჩეული მისამართის მდებარეობის დადგენა"</string>
     <string name="browse" msgid="1245903488306147205">"გახსნა"</string>
     <string name="browse_desc" msgid="8220976549618935044">"არჩეული URL-ის გახსნა"</string>
@@ -1097,9 +1101,9 @@
     <string name="add_contact_desc" msgid="4830217847004590345">"დამატება"</string>
     <string name="view_calendar" msgid="979609872939597838">"ნახვა"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"არჩეული დროის კალენდარში ნახვა"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"განრიგი"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"დაგეგმვა"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"მოვლენის არჩეული დროისთვის დაგეგმვა"</string>
-    <string name="view_flight" msgid="7691640491425680214">"ჩანაწერი"</string>
+    <string name="view_flight" msgid="7691640491425680214">"მიდევნება"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"არჩეული ფრენისთვის თვალის მიდევნება"</string>
     <string name="translate" msgid="9218619809342576858">"თარგმნა"</string>
     <string name="translate_desc" msgid="4502367770068777202">"არჩეული ტექსტის თარგმნა"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"მიმდინარეობს ტაბლეტის განახლება…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"მიმდინარეობს მოწყობილობის განახლება…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"მიმდინარეობს ტელეფონის ჩართვა…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android იწყება…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"მიმდინარეობს ტაბლეტის ჩართვა…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"მიმდინარეობს მოწყობილობის ჩართვა…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"მეხსიერების ოპტიმიზირება."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"შეეხეთ ყველა ქსელის სანახავად"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"დაკავშირება"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"ყველა ქსელი"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"დაკავშირებულია <xliff:g id="NAME">%s</xliff:g>-ის მიერ შემოთავაზებულ Wi‑Fi ქსელთან"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"გსურთ, საშუალება მისცეთ <xliff:g id="NAME">%s</xliff:g>-ს, შემოგთავაზოთ ქსელები?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"დიახ"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"არა"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi ავტომატურად ჩაირთვება"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"როცა შენახულ მაღალხარისხიან ქსელებთან ახლოს იმყოფებით"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"ხელახლა ნუ ჩართავ"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 0b4ff1c..c622094 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Қолданбаға құрылғыдағы телефон функцияларына кіру мүмкіндігін береді. Бұл рұқсат қолданбаға телефон нөмірі, құрылғы жеке анықтағышы, қоңырау белсенділігі және сол қоңырауға байланысты қашықтағы нөмірді анықтау мүмкіндігін береді."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"қоңырауларды жүйе арқылы бағыттау"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Қоңырау шалу тәжірибесін жақсарту үшін қолданба қоңырауларды жүйе арқылы бағыттай алады."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"жүйе арқылы қоңырауларды көру және басқару."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Қолданбаға құрылғыдағы қазіргі қоңырауларды көруге және басқаруға мүмкіндік береді. Бұл – қоңырау шалу нөмірлері және қоңыраулардың күйі сияқты ақпаратқа қатысты."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"басқа қолданбадағы қоңырауды жалғастыру"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Қолданбаға басқа қолданбадағы қоңырауды жалғастыруға рұқсат береді."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"телефон нөмірлерін оқу"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Қолданбаға NFC белгілерімен, карталармен және оқу құралдарымен байланысуға рұқсат береді."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"экран бекітпесін істен шығару"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Қолданбаларға кілтперне және басқа кілтсөзге қатысты қауіпсіздік шараларын өшіру мүмкіндігін береді. Мысалы, телефон кіріс қоңырауларын алғанда кілтпернені өшіреді және қоңырау аяқталғанда қайта қосады."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"экранды құлыптау күрделілігі туралы дерек алу және оны сұрау"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Қолданбаға экранды құлыптау күрделілігінің деңгейін (жоғары, орташа, төмен немесе жоқ), соның ішінде ұзақтығы мен түрін көрсетеді. Сонымен қатар қолданба пайдаланушыларға экранды құлыптауды белгілі бір деңгейге жаңартуды ұсынады. Бірақ бұл ұсыныстарды елемеуге болады. Экранды құлпы қарапайым мәтін түрінде сақталмайтынын және қолданбаға белгісіз болатынын ескеріңіз."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"биометрикалық жабдықты пайдалану"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Аутентификациялау үшін қолданбаға биометрикалық жабдықты пайдалануға рұқсат береді"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"саусақ ізі жабдығын басқару"</string>
@@ -1083,23 +1087,23 @@
     <string name="deleteText" msgid="6979668428458199034">"Жою"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Енгізу әдісі"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Мәтін әрекеттері"</string>
-    <string name="email" msgid="4560673117055050403">"Электрондық пошта"</string>
-    <string name="email_desc" msgid="3638665569546416795">"Таңдалған электрондық пошта мекенжайына хабар жіберу"</string>
+    <string name="email" msgid="4560673117055050403">"Эл. поштаны ашу"</string>
+    <string name="email_desc" msgid="3638665569546416795">"Таңдалған мекенжайға хабар жіберу"</string>
     <string name="dial" msgid="1253998302767701559">"Қоңырау шалу"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Таңдалған телефон нөміріне қоңырау шалу"</string>
-    <string name="map" msgid="5441053548030107189">"Map"</string>
-    <string name="map_desc" msgid="1836995341943772348">"Таңдалған мекенжайдың орналасқан жерін анықтау"</string>
+    <string name="map" msgid="5441053548030107189">"Картаны ашу"</string>
+    <string name="map_desc" msgid="1836995341943772348">"Таңдалған мекенжайды табу"</string>
     <string name="browse" msgid="1245903488306147205">"Ашу"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Таңдалған URL мекенжайын ашу"</string>
-    <string name="sms" msgid="4560537514610063430">"Хабар"</string>
+    <string name="sms" msgid="4560537514610063430">"Хабар жазу"</string>
     <string name="sms_desc" msgid="7526588350969638809">"Таңдалған телефон нөміріне хабар жіберу"</string>
     <string name="add_contact" msgid="7867066569670597203">"Енгізу"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Контактілер тізіміне енгізу"</string>
     <string name="view_calendar" msgid="979609872939597838">"Көру"</string>
-    <string name="view_calendar_desc" msgid="5828320291870344584">"Күнтізбеде таңдалған уақытты көру"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"Кесте"</string>
-    <string name="add_calendar_event_desc" msgid="4326891793260687388">"Таңдалған уақытқа іс-шараны жоспарлау"</string>
-    <string name="view_flight" msgid="7691640491425680214">"Бақылау"</string>
+    <string name="view_calendar_desc" msgid="5828320291870344584">"Таңдалған уақытты күнтізбеден көру"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"Жоспарлау"</string>
+    <string name="add_calendar_event_desc" msgid="4326891793260687388">"Таңдалған уақытқа іс-шара жоспарлау"</string>
+    <string name="view_flight" msgid="7691640491425680214">"Қадағалау"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"Таңдалған ұшу рейсін қадағалау"</string>
     <string name="translate" msgid="9218619809342576858">"Аудару"</string>
     <string name="translate_desc" msgid="4502367770068777202">"Таңдалған мәтінді аудару"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Планшет жаңартылуда…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Құрылғы жаңартылуда…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Телефон іске қосылуда…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android іске қосылуда…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Планшет іске қосылуда…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Құрылғы іске қосылуда…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Қойманы оңтайландыру."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Барлық желілерді көру үшін түртіңіз"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Қосылу"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Барлық желілер"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> ұсынған Wi‑Fi желісіне жалғанды"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"<xliff:g id="NAME">%s</xliff:g> сізге желі ұсынғанын қалайсыз ба?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Иә"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Жоқ"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi автоматты түрде қосылады"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Сақталған жоғары сапалы желіге жақын болғанда"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Қайта қоспау"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index cd2af7b..ccc243d 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"ឲ្យ​កម្មវិធី​ចូល​ដំណើរការ​លក្ខណៈ​ទូរស័ព្ទ​នៃ​ឧបករណ៍។ សិទ្ធិ​នេះ​​ឲ្យ​កម្មវិធី​កំណត់​លេខ​ទូរស័ព្ទ និង​លេខ​សម្គាល់​ឧបករណ៍ ថា​តើ​ការ​ហៅ​សកម្ម និង​លេខ​ពី​ចម្ងាយ​បាន​ភ្ជាប់​ដោយ​ការ​ហៅ។"</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"បញ្ជូន​ការ​ហៅ​ទូរសព្ទ​តាមរយៈ​ប្រព័ន្ធ"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"អនុញ្ញាត​ឲ្យ​កម្មវិធី​នេះ​បញ្ជូន​ការ​ហៅ​ទូរសព្ទ​របស់វា​តាមរយៈ​ប្រព័ន្ធ ​ដើម្បី​ធ្វើ​ឲ្យ​ការ​ហៅ​ទូរសព្ទ​ប្រសើរ​ជាង​មុន។"</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"មើល និង​គ្រប់គ្រង​ការហៅទូរសព្ទ​តាមរយៈប្រព័ន្ធ។"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"អនុញ្ញាត​ឱ្យកម្មវិធី​មើល និងគ្រប់គ្រង​ការហៅទូរសព្ទ​ដែល​កំពុង​ដំណើរការ​នៅលើ​ឧបករណ៍។ សកម្មភាព​នេះរួមមាន​ព័ត៌មាន​ដូចជា លេខទូរសព្ទ​សម្រាប់ការ​ហៅទូរសព្ទ និង​ស្ថានភាព​នៃការហៅទូរសព្ទជាដើម។"</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"បន្ត​ការ​ហៅ​ទូរសព្ទ​ពី​កម្មវិធី​ផ្សេង​"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"អនុញ្ញាត​ឱ្យ​កម្មវិធី​បន្ត​ការ​ហៅ​ទូរសព្ទ​ ដែល​បាន​ចាប់ផ្តើម​នៅក្នុង​កម្មវិធី​ផ្សេង​។"</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"អាន​លេខ​ទូរសព្ទ"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"ឲ្យ​កម្មវិធី​ទាក់ទង​ជា​មួយ​ស្លាក (NFC) កាត និង​កម្មវិធី​អាន។"</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"បិទ​ការ​ចាក់​សោ​អេក្រង់​របស់​អ្នក"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"ឲ្យ​កម្មវិធី​បិទ​ការ​ចាក់សោ​សុវត្ថិភាព​ពាក្យ​សម្ងាត់​ដែល​បាន​ភ្ជាប់​ណា​មួយ។ ​ឧទាហរណ៍​ត្រឹមត្រូវ​​​នៃ​ការ​បិទ​ទូរស័ព្ទ​ពេល​ទទួលការ​ហៅ​ចូល បន្ទាប់​ម​បើក​សោ​ពេល​ការ​ហៅ​បាន​បញ្ចប់។"</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"ទទួល និងស្នើ​សុំភាព​ស្មុគស្មាញ​នៃការចាក់សោអេក្រង់"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"អនុញ្ញាតឱ្យ​កម្មវិធី​រៀនអំពី​កម្រិតស្មុគស្មាញ​ការនៃការចាក់សោអេក្រង់ (ខ្ពស់ មធ្យម​ ទាប ឬគ្មាន) ដែល​បញ្ជាក់អំពី​​ប្រវែង និងប្រភេទ​នៃការចាក់សោអេក្រង់។ កម្មវិធី​នេះ​ក៏​អាច​ណែនាំឱ្យ​អ្នកប្រើប្រាស់​ធ្វើបច្ចុប្បន្នភាព​ការចាក់សោ​អេក្រង់​ទៅកម្រិតជាក់លាក់​ផងដែរ ប៉ុន្តែ​អ្នកប្រើប្រាស់​អាច​មិនអើពើនឹង​ការណែនាំនេះ​ដោយសេរី។ សូម​ចំណាំថា ការចាក់សោអេក្រង់​មិន​ត្រូវបាន​រក្សាទុក​ជាអត្ថបទ​ធម្មតាទេ ដូច្នេះ​កម្មវិធីនេះ​មិន​ស្គាល់​ពាក្យសម្ងាត់​ពិតប្រាកដ​ឡើយ។"</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"ប្រើ​ឧបករណ៍​ស្កេន​ស្នាមម្រាមដៃ"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"អនុញ្ញាត​ឱ្យ​កម្មវិធី​ប្រើ​ឧបករណ៍​ស្កេន​ស្នាមម្រាមដៃ​សម្រាប់​ការផ្ទៀងផ្ទាត់"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"គ្រប់គ្រងផ្នែករឹងស្នាមម្រាមដៃ"</string>
@@ -1183,6 +1187,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ថេប្លេត​កំពុង​ដំឡើងជំនាន់…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"ឧបករណ៍​កំពុង​ដំឡើងជំនាន់…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"ទូរសព្ទ​កំពុង​ចាប់ផ្ដើម…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android កំពុង​ចាប់ផ្ដើម…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"ថេប្លេត​កំពុង​ចាប់ផ្ដើម…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"ឧបករណ៍​កំពុង​ចាប់ផ្ដើម…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"កំពុងធ្វើឲ្យឧបករណ៍ផ្ទុកមានប្រសិទ្ធភាព។"</string>
@@ -1241,6 +1246,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"ចុចដើម្បីមើលបណ្តាញទាំងអស់"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"ភ្ជាប់"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"បណ្ដាញ​ទាំងអស់"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"បានភ្ជាប់​បណ្ដាញ Wi‑Fi ដែលបាន​ណែនាំដោយ <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"តើអ្នក​ចង់អនុញ្ញាតឱ្យ <xliff:g id="NAME">%s</xliff:g> ណែនាំ​បណ្ដាញ​សម្រាប់អ្នកដែរទេ?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"បាទ/ចាស"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"ទេ"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi នឹង​បើក​ដោយ​ស្វ័យប្រវត្តិ"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"នៅពេល​ដែល​អ្នក​នៅ​ជិត​បណ្តាញ​គុណភាព​ខ្ពស់​ដែល​បាន​រក្សាទុក"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"កុំ​បើក​ឡើង​វិញ"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 44010d2..835070a 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"ಸಾಧನದ ಫೋನ್ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಪ್ರವೇಶಿಸಲು ಅಪ್ಲಿಕೇಶನ್‍‍ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ. ಈ ಅನುಮತಿಯು ಫೋನ್ ಸಂಖ್ಯೆ ಮತ್ತು ಸಾಧನದ ID ಗಳನ್ನು ನಿರ್ಧರಿಸಲು, ಕರೆಯು ಸಕ್ರಿಯವಾಗಿದೆಯೇ ಮತ್ತು ಕರೆಯ ಮೂಲಕ ರಿಮೋಟ್ ಸಂಖ್ಯೆಯು ಸಂಪರ್ಕಗೊಂಡಿವೆಯೇ ಎಂಬುದನ್ನೂ ನಿರ್ಧರಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅವಕಾಶ ಕಲ್ಪಿಸುತ್ತದೆ."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ಕರೆಗಳನ್ನು ಸಿಸ್ಟಂ ಮೂಲಕ ರವಾನಿಸಿ"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"ಕರೆಯ ಅನುಭವವನ್ನು ಸುಧಾರಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ನ ಕರೆಗಳನ್ನು ಸಿಸ್ಟಂ ಮೂಲಕ ರವಾನಿಸಲು ಅನುಮತಿಸುತ್ತದೆ."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ಸಿಸ್ಟಂ ಮೂಲಕ ಕರೆಗಳನ್ನು ವೀಕ್ಷಿಸಿ ಮತ್ತು ನಿಯಂತ್ರಿಸಿ."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"ಸಾಧನದಲ್ಲಿನ ಚಾಲ್ತಿಯಲ್ಲಿರುವ ಕರೆಗಳನ್ನು ವೀಕ್ಷಿಸಲು ಮತ್ತು ನಿಯಂತ್ರಿಸಲು ಆ್ಯಪ್ ಅನುಮತಿಸುತ್ತದೆ. ಕರೆಗಳಿಗೆ ಸಂಬಂಧಿಸಿದ ಕರೆ ಸಂಖ್ಯೆಗಳು ಮತ್ತು ಕರೆ ಮಾಡಿದ ರಾಜ್ಯದಂತಹ ಮಾಹಿತಿಯನ್ನು ಇದು ಒಳಗೊಂಡಿರುತ್ತದೆ."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"ಮತ್ತೊಂದು ಅಪ್ಲಿಕೇಶನ್‌ ಮೂಲಕ ಕರೆಯನ್ನು ಮುಂದುವರಿಸಿ"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"ಮತ್ತೊಂದು ಅಪ್ಲಿಕೇಶನ್‌ನಲ್ಲಿ ಪ್ರಾರಂಭವಾದ ಕರೆಯನ್ನು ಮುಂದುವರಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡಿ."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ಫೋನ್‌ ಸಂಖ್ಯೆಗಳನ್ನು ಓದಿ"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"ಸಮೀಪದ ಕ್ಷೇತ್ರ ಸಂವಹನ (NFC) ಟ್ಯಾಗ್‌ಗಳು, ಕಾರ್ಡ್‌ಗಳು, ಮತ್ತು ಓದುಗರನ್ನು ಅಪ್ಲಿಕೇಶನ್‌ ಅನುಮತಿಸುತ್ತದೆ."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"ಕೀಲಾಕ್ ಮತ್ತು ಯಾವುದೇ ಸಂಬಂಧಿತ ಭದ್ರತಾ ಪಾಸ್‍‍ವರ್ಡ್ ಭದ್ರತೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಅಪ್ಲಿಕೇಶನ್‍‍ಗೆ ಅನುಮತಿ ನೀಡುತ್ತದೆ. ಉದಾಹರಣೆಗೆ, ಒಳಬರುವ ಕರೆಯನ್ನು ಸ್ವೀಕರಿಸುವಾಗ ಕೀಲಾಕ್ ಅನ್ನು ಫೋನ್ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ, ನಂತರ ಕರೆಯು ಅಂತ್ಯಗೊಂಡಾಗ ಕೀಲಾಕ್ ಅನ್ನು ಮರು ಸಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಸಂಕೀರ್ಣತೆಯನ್ನು ಪಡೆದುಕೊಳ್ಳಿ ಮತ್ತು ವಿನಂತಿಸಿ"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಸಂಕೀರ್ಣತೆ ಮಟ್ಟವನ್ನು ತಿಳಿದುಕೊಳ್ಳಲು ಅನುಮತಿಸುತ್ತದೆ (ಹೆಚ್ಚು, ಮಧ್ಯಮ, ಅಥವಾ ಕಡಿಮೆ ಯಾವುದೂ ಅಲ್ಲ), ಇದು ಉದ್ದದ ಸಂಭವನೀಯ ಶ್ರೇಣಿ ಮತ್ತು ಸ್ಕ್ರೀನ್ ಲಾಕ್‌ನ ವಿಧವನ್ನು ಸೂಚಿಸುತ್ತದೆ. ಬಳಕೆದಾರರು ನಿರ್ದಿಷ್ಟ ಮಟ್ಟದವರೆಗೆ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ಅಪ್‌ಡೇಟ್ ಮಾಡಬಹುದು ಎಂಬುದಾಗಿ ಕೂಡ ಆ್ಯಪ್‌ ಬಳಕೆದಾರರಿಗೆ ಸಲಹೆ ಮಾಡುತ್ತದೆ ಆದರೆ ಬಳಕೆದಾರರು ಮುಕ್ತವಾಗಿ ತಿರಸ್ಕರಿಸಬಹುದು ಮತ್ತು ನ್ಯಾವಿಗೇಟ್ ಮಾಡಬಹುದು. ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ಖಾಲಿಪಠ್ಯದಲ್ಲಿ ಸಂಗ್ರಹಿಸಿಲ್ಲ ಎಂಬುದನ್ನು ಗಮನಿಸಿ ಇದರಿಂದ ಆ್ಯಪ್‌ಗೆ ಸರಿಯಾದ ಪಾಸ್‌ವರ್ಡ್ ಗೊತ್ತಿರುವುದಿಲ್ಲ."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"ಬಯೋಮೆಟ್ರಿಕ್ ಹಾರ್ಡ್‌ವೇರ್‌ ಬಳಸಿ"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"ಪ್ರಮಾಣೀಕರಣಕ್ಕಾಗಿ ಬಯೋಮೆಟ್ರಿಕ್ ಹಾರ್ಡ್‌ವೇರ್ ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"ಬೆರಳಚ್ಚು ಹಾರ್ಡ್‌ವೇರ್ ನಿರ್ವಹಿಸಿ"</string>
@@ -1083,25 +1087,25 @@
     <string name="deleteText" msgid="6979668428458199034">"ಅಳಿಸಿ"</string>
     <string name="inputMethod" msgid="1653630062304567879">"ಇನ್‌ಪುಟ್ ವಿಧಾನ"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"ಪಠ್ಯದ ಕ್ರಮಗಳು"</string>
-    <string name="email" msgid="4560673117055050403">"ಇಮೇಲ್"</string>
+    <string name="email" msgid="4560673117055050403">"ಇಮೇಲ್ ಮಾಡಿ"</string>
     <string name="email_desc" msgid="3638665569546416795">"ಆಯ್ಕೆಮಾಡಿದ ವಿಳಾಸಕ್ಕೆ ಇಮೇಲ್‌ ಮಾಡಿ"</string>
-    <string name="dial" msgid="1253998302767701559">"ಕರೆ"</string>
+    <string name="dial" msgid="1253998302767701559">"ಕರೆ ಮಾಡಿ"</string>
     <string name="dial_desc" msgid="6573723404985517250">"ಆಯ್ಕೆಮಾಡಿದ ಫೋನ್ ಸಂಖ್ಯೆಗೆ ಕರೆ ಮಾಡಿ"</string>
     <string name="map" msgid="5441053548030107189">"ನಕ್ಷೆ"</string>
     <string name="map_desc" msgid="1836995341943772348">"ಆಯ್ಕೆ ಮಾಡಿದ ವಿಳಾಸವನ್ನು ಗುರುತಿಸಿ"</string>
-    <string name="browse" msgid="1245903488306147205">"ತೆರೆ"</string>
+    <string name="browse" msgid="1245903488306147205">"ತೆರೆಯಿರಿ"</string>
     <string name="browse_desc" msgid="8220976549618935044">"ಆಯ್ಕೆ ಮಾಡಿದ URL ತೆರೆಯಿರಿ"</string>
-    <string name="sms" msgid="4560537514610063430">"ಸಂದೇಶ"</string>
+    <string name="sms" msgid="4560537514610063430">"ಸಂದೇಶ ಕಳುಹಿಸಿ"</string>
     <string name="sms_desc" msgid="7526588350969638809">"ಆಯ್ಕೆಮಾಡಿದ ಫೋನ್ ಸಂಖ್ಯೆಗೆ ಸಂದೇಶ ಕಳುಹಿಸಿ"</string>
     <string name="add_contact" msgid="7867066569670597203">"ಸೇರಿಸಿ"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"ಸಂಪರ್ಕಗಳಿಗೆ ಸೇರಿಸಿ"</string>
     <string name="view_calendar" msgid="979609872939597838">"ವೀಕ್ಷಿಸಿ"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"ಕ್ಯಾಲೆಂಡರ್‌ನಲ್ಲಿ ಆಯ್ಕೆಮಾಡಿದ ಸಮಯವನ್ನು ವೀಕ್ಷಿಸಿ"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"ಅವಧಿ"</string>
-    <string name="add_calendar_event_desc" msgid="4326891793260687388">"ಆಯ್ಕೆ ಮಾಡಿದ ಸಮಯಕ್ಕೆ ಈವೆಂಟ್ ನಿಗದಿಪಡಿಸಿ"</string>
-    <string name="view_flight" msgid="7691640491425680214">"ಟ್ರ್ಯಾಕ್"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"ಸಮಯ ನಿಗದಿಗೊಳಿಸಿ"</string>
+    <string name="add_calendar_event_desc" msgid="4326891793260687388">"ಆಯ್ಕೆ ಮಾಡಿದ ಸಮಯಕ್ಕೆ ಈವೆಂಟ್ ನಿಗದಿಗೊಳಿಸಿ"</string>
+    <string name="view_flight" msgid="7691640491425680214">"ಟ್ರ್ಯಾಕ್ ಮಾಡಿ"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"ಆಯ್ಕೆಮಾಡಿದ ವಿಮಾನವನ್ನು ಟ್ರ್ಯಾಕ್‌ ಮಾಡಿ"</string>
-    <string name="translate" msgid="9218619809342576858">"ಅನುವಾದ"</string>
+    <string name="translate" msgid="9218619809342576858">"ಅನುವಾದ ಮಾಡಿ"</string>
     <string name="translate_desc" msgid="4502367770068777202">"ಆಯ್ಕೆಮಾಡಿದ ಪಠ್ಯವನ್ನು ಅನುವಾದಿಸಿ"</string>
     <string name="define" msgid="7394820043869954211">"ವಿವರಿಸಿ"</string>
     <string name="define_desc" msgid="7910883642444919726">"ಆಯ್ಕೆಮಾಡಿದ ಪಠ್ಯವನ್ನು ವಿವರಿಸಿ"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ನು ಅಪ್‌ಡೇಟ್ ಮಾಡಲಾಗುತ್ತಿದೆ…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"ಸಾಧನವನ್ನು ಅಪ್‌ಡೇಟ್ ಮಾಡಲಾಗುತ್ತಿದೆ…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"ಫೋನ್ ಅನ್ನು ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"ಟ್ಯಾಬ್ಲೆಟ್ ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"ಸಾಧನವನ್ನು ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"ಸಂಗ್ರಹಣೆಯನ್ನು ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗುತ್ತಿದೆ."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"ಎಲ್ಲಾ ನೆಟ್‌ವರ್ಕ್‌ಗಳನ್ನು ನೋಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"ಸಂಪರ್ಕಿಸಿ"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"ಎಲ್ಲಾ ನೆಟ್‌ವರ್ಕ್‌ಗಳು"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> ಅವರಿಂದ ಸೂಚಿತ ವೈ-ಫೈ ನೆಟ್‌ವರ್ಕ್‌ಗೆ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"<xliff:g id="NAME">%s</xliff:g> ಅವರು ನಿಮಗಾಗಿ ನೆಟ್‌ವರ್ಕ್‌ಗಳನ್ನು ಪ್ರಸ್ತಾಪಿಸುವುದೇ?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"ಹೌದು"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"ಇಲ್ಲ"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"ವೈ‑ಫೈ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಆನ್ ಆಗುತ್ತದೆ"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"ನೀವು ಉಳಿಸಿದ ಅಧಿಕ ಗುಣಮಟ್ಟದ ನೆಟ್‌ವರ್ಕ್‌ ಸಮೀಪದಲ್ಲಿದ್ದಾಗ"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"ಮತ್ತೆ ಆನ್ ಮಾಡಲು ಹಿಂತಿರುಗಬೇಡಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index bdc50bf..8886815 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"앱이 기기의 휴대전화 기능에 접근할 수 있도록 허용합니다. 이 권한을 사용하면 앱이 전화번호 및 기기의 ID, 활성 통화인지 여부, 통화가 연결된 원격 번호 등을 확인할 수 있습니다."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"시스템을 통해 통화 연결"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"통화 환경을 개선하기 위해 앱이 시스템을 통해 통화를 연결하도록 허용합니다."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"시스템을 통해 통화 확인 및 제어"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"앱이 기기에서 진행 중인 통화를 확인 및 제어하도록 허용합니다. 여기에는 통화에 사용된 전화번호 및 통화 상태 등의 정보가 포함됩니다."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"다른 앱에서 전화 받기"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"다른 앱에서 수신한 전화를 계속하려면 앱을 허용합니다."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"전화번호 읽기"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"앱이 NFC(근거리 무선 통신) 태그, 카드 및 리더와 통신할 수 있도록 허용합니다."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"화면 잠금 사용 중지"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"앱이 키 잠금 및 관련 비밀번호 보안을 사용중지할 수 있도록 허용합니다. 예를 들어, 휴대전화가 수신전화를 받을 때 키 잠금을 사용중지했다가 통화가 끝나면 키 잠금을 다시 사용할 수 있습니다."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"화면 잠금 복잡도 확인 및 요청"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"앱이 화면 잠금 길이와 유형의 가능한 범위를 나타내는 잠금 화면 복잡도 수준(높음, 보통, 낮음 또는 없음)을 파악하도록 허용합니다. 앱이 사용자에게 화면 잠금을 특정 수준으로 업데이트할 것을 제안할 수도 있지만, 사용자는 자유롭게 이를 무시하고 다른 곳으로 이동할 수 있습니다. 화면 잠금은 일반 텍스트로 저장되지 않으므로 앱에서 정확한 비밀번호를 알 수 없습니다."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"생체 인식 하드웨어 사용"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"앱에서 생체 인식 하드웨어를 인증에 사용하도록 허용합니다."</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"지문 하드웨어 관리"</string>
@@ -1096,9 +1100,9 @@
     <string name="add_contact" msgid="7867066569670597203">"추가"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"연락처에 추가"</string>
     <string name="view_calendar" msgid="979609872939597838">"보기"</string>
-    <string name="view_calendar_desc" msgid="5828320291870344584">"캘린더에서 선택한 시간 보기"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"일정"</string>
-    <string name="add_calendar_event_desc" msgid="4326891793260687388">"선택한 시간의 일정 예약"</string>
+    <string name="view_calendar_desc" msgid="5828320291870344584">"선택한 시간 캘린더에서 보기"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"일정 만들기"</string>
+    <string name="add_calendar_event_desc" msgid="4326891793260687388">"선택한 시간으로 일정 만들기"</string>
     <string name="view_flight" msgid="7691640491425680214">"추적"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"선택한 항공편 추적"</string>
     <string name="translate" msgid="9218619809342576858">"번역"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"태블릿을 업데이트하는 중…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"기기를 업데이트하는 중…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"휴대전화를 시작하는 중…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android가 시작되는 중…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"태블릿을 시작하는 중…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"기기를 시작하는 중…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"저장소 최적화 중"</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"모든 네트워크를 보려면 탭하세요."</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"연결"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"모든 네트워크"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g>에서 제안한 Wi‑Fi 네트워크에 연결됨"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"<xliff:g id="NAME">%s</xliff:g>에서 네트워크를 제안하도록 허용하시겠습니까?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"예"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"아니요"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi가 자동으로 사용 설정됨"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"저장된 고품질 네트워크가 가까이 있는 경우"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"다시 사용 설정하지 않음"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index f6e9b72..de49741 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Колдонмого түзмөктүн чалуу мүмкүнчүлүктөрүнө жетки алуу уруксатын берет. Бул уруксат колдонмого, телефондун номурун, түзмөктүн ID-син, чалуунун абалын жана байланышта чыккан номурду аныктоого жол берет."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"чалууларды тутум аркылуу өткөрүү"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Чалуунун сапатын жакшыртуу максатында колдонмого чалууларын тутум аркылуу өткөрүүгө уруксат берет."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"тутум аркылуу чалууларды көрүп, көзөмөлдөө."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Колдонмого түзмөктөгү аткарылып жаткан чалууларды көрүп, көзөмөлдөөгө уруксат берет. Буга чалуулардын саны жана абалы сыяктуу маалымат кирет."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"чалууну башка колдонмодон улантуу"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Башка колдонмодон аткарылган чалууну бул колдонмодо улантууга уруксат берүү."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"телефон номерлерин окуу"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Колдонмого Жакынкы аралыкта байланышуу (NFC) белгилери, карталары жана окугучтары менен байланышуу мүмкүнчүлүгүн берет."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"экранды бөгөттөөнү өчүрүү"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Колдонмого экрандын бөгөттөөчү жана ага байланыштуу сырсөз коргоосун өчүрүү уруксатын берет. Мисалы, чалуу келгенде экрандын бөгөтүн алып салат, чалуу бүткөндө кайрадан орнотот."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"экранды бөгөттөө канчалык татаал экенин сурап, маалымат алуу"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Колдонмого экранды бөгөттөөнүн татаалдыгын (татаал, орточо, оңой же такыр жок) үйрөнүүгө мүмкүнчүлүк берет. Татаалдык деңгээли сырсөздүн узундугу жана экранды бөгөттөөнүн түрү боюнча айырмаланат. Колдонмо экранды бөгөттөөнү белгилүү деңгээлге тууралоону колдонуучуларга сунуштай да алат, бирок колдонуучулар ага көңүл бурбай койсо болот. Сырсөздү колдонмо билбеши үчүн, экранды бөгөттөө сырсөзүн кадимки текстте сактоого болбойт."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"биометрикалык аппаратты колдонуу"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Колдонмого аныктыгын текшерүү үчүн биометрикалык аппаратты пайдалануу мүмкүндүгүн берет"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"манжа изинин аппараттык камсыздоосун башкаруу"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Планшет жаңыртылууда…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Түзмөк жаңыртылууда…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Телефон күйгүзүлүүдө…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android жүргүзүлүүдө…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Планшет күйгүзүлүүдө…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Түзмөк күйүгүзүлүүдө…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Сактагыч ыңгайлаштырылууда."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Бардык тармактарды көрүү үчүн басыңыз"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Туташуу"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Бардык тармактар"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> сунуштаган Wi‑Fi тармагына туташтырылды"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"<xliff:g id="NAME">%s</xliff:g> сизге тармактарды сунуштай берсинби?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ооба"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Жок"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi автоматтык түрдө күйөт"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Байланыш сигналы күчтүү тармактарга жакындаганда"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Өзү кайра күйбөйт"</string>
diff --git a/core/res/res/values-land/dimens_permission_controller.xml b/core/res/res/values-land/dimens_permission_controller.xml
deleted file mode 100644
index 2146241..0000000
--- a/core/res/res/values-land/dimens_permission_controller.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2018 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<!-- Landscape dimensions for the permission grant dialog. -->
-<resources>
-    <!-- Assuming the dimension of a sailfish, this yields 95% width in splitscreen and 65% in
-         landscape -->
-    <dimen name="permissionGrantDialogWeight">8.6</dimen>
-    <dimen name="permissionGrantDialogWidth">334dp</dimen>
-</resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index e925d92..9356302 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"ອະນຸຍາດໃຫ້ແອັບຯ ເຂົ້າເຖິງຄວາມສາມາດການໂທລະສັບຂອງອຸປະກອນ. ການກຳນົດສິດນີ້ເຮັດໃຫ້ແອັບຯສາມາດກວດສອບເບີໂທລະສັບ ແລະ ID ຂອງອຸປະກອນ, ບໍ່ວ່າການໂທຈະຍັງດຳເນີນຢູ່ ແລະເບີປາຍທາງເຊື່ອມຕໍ່ຢູ່ຫຼືບໍ່ກໍຕາມ."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"route calls through the system"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ເຫັນ ແລະ ຄວບຄຸມການໂທຜ່ານລະບົບ."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"ອະນຸຍາດໃຫ້ແອັບເຫັນ ແລະ ຄວບຄຸມການໂທທີ່ກຳລັງດຳເນີນຢູ່ອຸປະກອນ. ນີ້ຮວມເຖິງຂໍ້ມູນ ເຊັ່ນ: ເບີໂທສຳລັບການໂທ ແລະ ສະຖານະຂອງການໂທນຳ."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"ສືບຕໍ່ການໂທຈາກແອັບອື່ນ"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"ອະນຸຍາດໃຫ້ແອັບສືບຕໍ່ການໂທເຊິ່ງອາດຖືກເລີ່ມຕົ້ນໃນແອັບອື່ນ."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ອ່ານເບີໂທລະສັບ"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"ອະນຸຍາດໃຫ້ແອັບຯຕິດຕໍ່ສື່ສານກັບປ້າຍກຳກັບ, ບັດ ແລະໂຕອ່ານຂອງການສື່ສານໄລຍະສັ້ນ (NFC)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"ປິດການລັອກໜ້າຈໍ"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"ອະນຸຍາດໃຫ້ແອັບຯປິດການເຮັດວຽກຂອງປຸ່ມລັອກ ແລະລະບົບຄວາມປອດໄພຂອງລະຫັດຜ່ານທີ່ເຊື່ອມໂຍງກັນ. ໂຕຢ່າງ: ໂທລະສັບຈະປິດການເຮັດວຽກຂອງປຸ່ມລັອກເມື່ອມີສາຍໂທເຂົ້າ ຈາກນັ້ນຈຶ່ງເປີດໃຊ້ໄດ້ອີກເມື່ອວາງສາຍແລ້ວ."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"ຮັບ ແລະ ຂໍຄວາມຊັບຊ້ອນການລັອກໜ້າຈໍ"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"ອະນຸຍາດໃຫ້ແອັບສຶກສາລະດັບຄວາມຊັບຊ້ອນຂອງໜ້າຈໍລັອກ (ສູງ, ກາງ ຫຼື ບໍ່ມີ), ເຊິ່ງລະບຸຂອບເຂດຄວາມເປັນໄປໄດ້ຂອງຄວາມຍາວ ແລະ ປະເພດຂອງການລັອກໜ້າຈໍ. ແອັບນີ້ສາມາດແນະນຳຜູ້ໃຊ້ວ່າເຂົາເຈົ້າສາມາດອັບເດດໜ້າຈໍລັອກເປັນລະດັບໃດໜຶ່ງເປັນການສະເພາະໄດ້, ແຕ່ຜູ້ໃຊ້ສາມາດທີ່ຈະບໍ່ສົນໃຈ ຫຼື ເປີດໄປອັນອື່ນໄດ້. ກະລຸນາຮັບຊາບວ່າການລັອກໜ້າຈໍບໍ່ໄດ້ບັນທຶກໃນແບບຂໍ້ຄວາມທຳມະດາ, ດັ່ງນັ້ນແອັບຈະບໍ່ຮູ້ລະຫັດຜ່ານທີ່ແນ່ນອນ."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"ໃຊ້ຮາດແວຊີວະມິຕິ"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"ອະນຸຍາດໃຫ້ແອັບນຳໃຊ້ຮາດແວຊີວະມິຕິສຳລັບການພິສູດຢືນຢັນ"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"ຈັດ​ການ​ຮາດ​ແວ​ລາຍ​ນີ້ວ​ມື"</string>
@@ -1097,9 +1101,9 @@
     <string name="add_contact_desc" msgid="4830217847004590345">"ເພີ່ມໃສ່ລາຍຊື່ຜູ້ຕິດຕໍ່"</string>
     <string name="view_calendar" msgid="979609872939597838">"ເບິ່ງ"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"ເບິ່ງເວລາທີ່ເລືອກໃນປະຕິທິນ"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"ຕັ້ງເວລາ"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"ກຳນົດເວລາ"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"ກຳນົດເວລາສຳລັບເວລາທີ່ເລືອກ"</string>
-    <string name="view_flight" msgid="7691640491425680214">"ແທຣັກ"</string>
+    <string name="view_flight" msgid="7691640491425680214">"ຕິດຕາມ"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"ຕິດຕາມຖ້ຽວບິນທີ່ເລືອກ"</string>
     <string name="translate" msgid="9218619809342576858">"ແປພາສາ"</string>
     <string name="translate_desc" msgid="4502367770068777202">"ແປຂໍ້ຄວາມທີ່ເລືອກ"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ກຳລັງອັບເດດແທັບເລັດ…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"ກຳລັງອັບເດດອຸປະກອນ…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"ກຳລັງເລີ່ມໂທລະສັບ…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"ກຳ​ລັງ​ເລີ່ມລະ​ບົບ​ Android …"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"ກຳລັງເລີ່ມແທັບເລັດ…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"ກຳລັງເລີ່ມອຸປະກອນ…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"ການ​ປັບ​ບ່ອນ​ເກັບ​ຂໍ້​ມູນ​ໃຫ້​ເໝາະ​ສົມ."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"ແຕະເພື່ອເບິ່ງເຄືອຂ່າຍທັງໝົດ"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"ເຊື່ອມ​ຕໍ່"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"ເຄືອຂ່າຍທັງໝົດ"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"ເຊື່ອມຕໍ່ຫາເຄືອຂ່າຍ Wi‑Fi ທີ່ສະເໜີໂດຍ <xliff:g id="NAME">%s</xliff:g> ແລ້ວ"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"ທ່ານຕ້ອງການໃຫ້ <xliff:g id="NAME">%s</xliff:g> ສະເໜີເຄືອຂ່າຍໃຫ້ທ່ານບໍ່?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"ແມ່ນ"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"ບໍ່ແມ່ນ"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"ຈະມີການເປີດໃຊ້ Wi‑Fi ອັດຕະໂນມັດ"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"ເມື່ອທ່ານຢູ່ໃກ້ເຄືອຂ່າຍຄຸນນະພາບສູງທີ່ບັນທຶກໄວ້"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"ບໍ່ຕ້ອງເປີດໃຊ້ຄືນໃໝ່"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index ada983f..8870ebb 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -453,6 +453,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Leidžiama programai pasiekti telefono funkcijas įrenginyje. Šis leidimas suteikia teisę programai nustatyti telefono numerį ir įrenginio ID, tai, ar skambutis aktyvus, ir skambučiu prijungtą nuotolinį numerį."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"nukreipti skambučius per sistemą"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Programai leidžiama nukreipti jos skambučius per sistemą siekiant pagerinti skambinimo paslaugas."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"matyti ir valdyti skambučius per sistemą."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Programai leidžiama matyti ir valdyti vykstančius skambučius įrenginyje. Tai apima tokią informaciją kaip skambučių telefono numeriai ir skambučių būsena."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"tęsti skambutį naudojant kitą programą"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Programai leidžiama tęsti skambutį, kuris buvo pradėtas naudojant kitą programą."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"skaityti telefonų numerius"</string>
@@ -513,6 +515,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Leidžiama programai perduoti artimojo lauko ryšių technologijos (ALR) žymas, korteles ir skaitymo programas."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"išjungti ekrano užraktą"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Leidžiama programai neleisti klavišo užrakto ir visos susijusios slaptažodžio apsaugos. Pvz., telefonas neleidžia klavišo užrakto priimant gaunamąjį skambutį ir pakartotinai jį įgalina, kai skambutis baigiamas."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"gauti ir pateikti ekrano užrakto sudėtingumo užklausą"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Leidžiama programai sužinoti ekrano užrakto sudėtingumo lygį (aukštas, vidutinis, žemas arba nėra), nurodantį galimą ekrano užrakto trukmės diapazoną ir tipą. Be to, programa gali pasiūlyti naudotojams atnaujinti ekrano užraktą į tam tikrą lygį, bet naudotojai gali laisvai nepaisyti ir išeiti. Atminkite, kad ekrano užraktas nesaugomas kaip grynasis tekstas, todėl programa nežino tikslaus slaptažodžio."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"naudoti biometrinę aparatinę įrangą"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Leidžiama programai naudoti biometrinę aparatinę įrangą tapatybei nustatyti"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"tvarkyti piršto antspaudo aparatinę įrangą"</string>
@@ -1131,7 +1135,7 @@
     <string name="map_desc" msgid="1836995341943772348">"Rasti vietą pasirinktu adresu"</string>
     <string name="browse" msgid="1245903488306147205">"Atidaryti"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Atidaryti pasirinktą URL"</string>
-    <string name="sms" msgid="4560537514610063430">"Pranešimas"</string>
+    <string name="sms" msgid="4560537514610063430">"Rašyti pranešimą"</string>
     <string name="sms_desc" msgid="7526588350969638809">"Siųsti pranešimą pasirinktu telefono numeriu"</string>
     <string name="add_contact" msgid="7867066569670597203">"Pridėti"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Pridėti prie kontaktų"</string>
@@ -1221,6 +1225,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Planšetinis kompiuteris atnaujinamas…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Įrenginys atnaujinamas…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Telefonas paleidžiamas…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Paleidžiama „Android“…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Planšetinis kompiuteris paleidžiamas…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Įrenginys paleidžiamas…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimizuojama saugykla."</string>
@@ -1283,6 +1288,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Palieskite, jei norite matyti visus tinklus"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Prisijungti"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Visi tinklai"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Prisijungta prie „<xliff:g id="NAME">%s</xliff:g>“ pasiūlyto „Wi‑Fi“ tinklo"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Ar norite leisti „<xliff:g id="NAME">%s</xliff:g>“ siūlyti jums tinklus?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Taip"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Ne"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"„Wi‑Fi“ bus įjungtas automatiškai"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Kai būsite netoli išsaugoto aukštos kokybės tinklo"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Neįjunkite vėl"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index dd4b125..5a1432b 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -450,6 +450,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Ļauj lietotnei piekļūt ierīces tālruņa funkcijām. Ar šo atļauju lietotne var noteikt tālruņa numuru un ierīču ID, zvana statusu un attālo numuru, ar ko ir izveidots savienojums, veicot zvanu."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"maršrutēt zvanus sistēmā"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Ļauj lietotnei maršrutēt tās zvanus sistēmā, lai uzlabotu zvanīšanas pieredzi."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"skatīt un kontrolēt zvanus sistēmā."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Ļauj lietotnei ierīcē skatīt un kontrolēt aktīvos zvanus. Šeit ir ietverta tāda informācija kā zvanu tālruņa numuri un zvanu statuss."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"turpināt zvanu no citas lietotnes"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Ļauj lietotnei turpināt zvanu, kas tika sākts citā lietotnē."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"lasīt tālruņa numurus"</string>
@@ -510,6 +512,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Ļauj lietotnei sazināties ar tuva darbības lauka sakaru (Near Field Communication — NFC) atzīmēm, kartēm un lasītājiem."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"atspējot ekrāna bloķēšanu"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Ļauj lietotnei atspējot taustiņslēgu un visu saistīto paroļu drošību. Piemēram, tālrunis atspējo taustiņslēgu, saņemot ienākošu zvanu, un pēc zvana pabeigšanas atkārtoti iespējo taustiņslēgu."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"saņemt un pieprasīt informāciju par ekrāna bloķēšanas sarežģītību"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Atļauj lietotnei saglabāt informāciju par ekrāna bloķēšanas sarežģītības pakāpi (augsta, vidēja, zema, nav), kas apzīmē iespējamo garumu un ekrāna bloķēšanas veidus. Lietotnē lietotājiem var tikt rādīts arī ieteikums ekrāna bloķēšanai iestatīt citu līmeni, taču šo ieteikumu var ignorēt un aizvērt. Ņemiet vērā, ka ekrāna bloķēšanas informācija netiek glabāta vienkārša teksta formātā, tāpēc lietotnei nav piekļuves precīzai parolei."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"izmantot biometrisko datu aparatūru"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Atļauj lietotnei izmantot biometrisko datu aparatūru autentificēšanai"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"pārvaldīt pirkstu nospiedumu aparatūru"</string>
@@ -1105,7 +1109,7 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Teksta darbības"</string>
     <string name="email" msgid="4560673117055050403">"E-pasts"</string>
     <string name="email_desc" msgid="3638665569546416795">"Nosūtīt e-pasta ziņojumu uz atlasīto adresi"</string>
-    <string name="dial" msgid="1253998302767701559">"Zvanīt"</string>
+    <string name="dial" msgid="1253998302767701559">"Zvans"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Zvanīt uz atlasīto tālruņa numuru"</string>
     <string name="map" msgid="5441053548030107189">"Karte"</string>
     <string name="map_desc" msgid="1836995341943772348">"Atrast atlasīto adresi"</string>
@@ -1201,6 +1205,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Notiek planšetdatora atjaunināšana…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Notiek ierīces atjaunināšana…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Notiek tālruņa palaišana…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Notiek Android palaišana…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Notiek planšetdatora palaišana…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Notiek ierīces palaišana…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Notiek krātuves optimizēšana."</string>
@@ -1261,6 +1266,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Pieskarieties, lai skatītu visus tīklus"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Izveidot savienojumu"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Visi tīkli"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Izveidots savienojums ar Wi-Fi tīklu, ko ieteica lietotne <xliff:g id="NAME">%s</xliff:g>."</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Vai vēlaties, lai lietotne <xliff:g id="NAME">%s</xliff:g> iesaka tīklus?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Jā"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nē"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi tiks automātiski ieslēgts"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Kad atrodaties saglabāta augstas kvalitātes tīkla tuvumā"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Neieslēgt atkārtoti"</string>
diff --git a/core/res/res/values-mcc302-mnc220/config.xml b/core/res/res/values-mcc302-mnc220/config.xml
index c26bebe..0ae2b4d 100644
--- a/core/res/res/values-mcc302-mnc220/config.xml
+++ b/core/res/res/values-mcc302-mnc220/config.xml
@@ -23,17 +23,4 @@
 
     <integer name="config_mobile_mtu">1410</integer>
 
-    <!-- Values for GPS configuration (Telus) -->
-    <string-array translatable="false" name="config_gpsParameters">
-        <item>SUPL_HOST=supl.google.com</item>
-        <item>SUPL_PORT=7275</item>
-        <item>SUPL_VER=0x20000</item>
-        <item>SUPL_MODE=1</item>
-        <item>SUPL_ES=1</item>
-        <item>LPP_PROFILE=3</item>
-        <item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
-        <item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
-        <item>GPS_LOCK=3</item>
-    </string-array>
-
 </resources>
diff --git a/core/res/res/values-mcc302-mnc221/config.xml b/core/res/res/values-mcc302-mnc221/config.xml
deleted file mode 100644
index 96338b58..0000000
--- a/core/res/res/values-mcc302-mnc221/config.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Values for GPS configuration (Telus) -->
-    <string-array translatable="false" name="config_gpsParameters">
-        <item>SUPL_HOST=supl.google.com</item>
-        <item>SUPL_PORT=7275</item>
-        <item>SUPL_VER=0x20000</item>
-        <item>SUPL_MODE=1</item>
-        <item>SUPL_ES=1</item>
-        <item>LPP_PROFILE=3</item>
-        <item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
-        <item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
-        <item>GPS_LOCK=3</item>
-    </string-array>
-
-</resources>
diff --git a/core/res/res/values-mcc302-mnc370/config.xml b/core/res/res/values-mcc302-mnc370/config.xml
index 79f4bb6..ef32acd 100644
--- a/core/res/res/values-mcc302-mnc370/config.xml
+++ b/core/res/res/values-mcc302-mnc370/config.xml
@@ -24,17 +24,4 @@
     -->
     <integer name="config_mobile_mtu">1410</integer>
 
-  <!-- Values for GPS configuration (Rogers) -->
-    <string-array translatable="false" name="config_gpsParameters">
-        <item>SUPL_HOST=supl.google.com</item>
-        <item>SUPL_PORT=7275</item>
-        <item>SUPL_VER=0x20000</item>
-        <item>SUPL_MODE=1</item>
-        <item>SUPL_ES=1</item>
-        <item>LPP_PROFILE=2</item>
-        <item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
-        <item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
-        <item>GPS_LOCK=3</item>
-    </string-array>
-
 </resources>
diff --git a/core/res/res/values-mcc302-mnc610/config.xml b/core/res/res/values-mcc302-mnc610/config.xml
index 10b007e..2b78f55 100644
--- a/core/res/res/values-mcc302-mnc610/config.xml
+++ b/core/res/res/values-mcc302-mnc610/config.xml
@@ -22,17 +22,4 @@
     -->
     <integer name="config_mobile_mtu">1428</integer>
 
-    <!-- Values for GPS configuration (Bell) -->
-    <string-array translatable="false" name="config_gpsParameters">
-        <item>SUPL_HOST=supl.google.com</item>
-        <item>SUPL_PORT=7275</item>
-        <item>SUPL_VER=0x20000</item>
-        <item>SUPL_MODE=1</item>
-        <item>SUPL_ES=1</item>
-        <item>LPP_PROFILE=2</item>
-        <item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
-        <item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
-        <item>GPS_LOCK=3</item>
-    </string-array>
-
 </resources>
diff --git a/core/res/res/values-mcc302-mnc640/config.xml b/core/res/res/values-mcc302-mnc640/config.xml
deleted file mode 100644
index 657d1e7..0000000
--- a/core/res/res/values-mcc302-mnc640/config.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Values for GPS configuration (Bell) -->
-    <string-array translatable="false" name="config_gpsParameters">
-        <item>SUPL_HOST=supl.google.com</item>
-        <item>SUPL_PORT=7275</item>
-        <item>SUPL_VER=0x20000</item>
-        <item>SUPL_MODE=1</item>
-        <item>SUPL_ES=1</item>
-        <item>LPP_PROFILE=2</item>
-        <item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
-        <item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
-        <item>GPS_LOCK=3</item>
-    </string-array>
-
-</resources>
diff --git a/core/res/res/values-mcc302-mnc720/config.xml b/core/res/res/values-mcc302-mnc720/config.xml
index ba8c75a..7be9a1c 100644
--- a/core/res/res/values-mcc302-mnc720/config.xml
+++ b/core/res/res/values-mcc302-mnc720/config.xml
@@ -24,17 +24,4 @@
     -->
     <integer name="config_mobile_mtu">1430</integer>
 
-  <!-- Values for GPS configuration (Rogers) -->
-    <string-array translatable="false" name="config_gpsParameters">
-        <item>SUPL_HOST=supl.google.com</item>
-        <item>SUPL_PORT=7275</item>
-        <item>SUPL_VER=0x20000</item>
-        <item>SUPL_MODE=1</item>
-        <item>SUPL_ES=1</item>
-        <item>LPP_PROFILE=2</item>
-        <item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
-        <item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
-        <item>GPS_LOCK=3</item>
-    </string-array>
-
 </resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 8b73b99..769ac43 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Овозможува апликацијата да пристапи кон карактеристиките на телефонот на уредот. Оваа дозвола овозможува апликацијата да ги утврди телефонскиот број и ID на уредот, дали повикот е активен и далечинскиот број поврзан со повикот."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"пренасочи повици преку системот"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Дозволете ѝ на апликацијата да ги пренасочи повиците преку системот за да го подобри искуството при јавувањето."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"гледање и контролирање повици преку системот."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Дозволува апликацијата да гледа и контролира тековни повици на уредот. Ова вклучува информации како телефонски броеви за повици и состојбата на повиците."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"продолжување повик од друга апликација"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Овозможува апликацијата да продолжи повик започнат на друга апликација."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"да чита телефонски броеви"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Дозволува апликацијата да комуницира со ознаки, картички и читачи за Комуникација при непосредна близина (NFC)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"оневозможи заклучување на екран"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Овозможува апликацијата да го оневозможи заклучувањето и каква било безбедност поврзана со лозинка. На пример, телефонот го оневозможува заклучувањето при прием на телефонски повик, а потоа повторно го овозможува заклучувањето кога повикот ќе заврши."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"добива и бара комплексност на заклучување екран"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Ѝ дозволува на апликацијата да го научи нивото на комплексност за заклучувањето на екранот (високо, средно, ниско или нема), коешто ги означува можниот опсег на должина и типот на заклувањето на екранот. Апликацијата може да им дава предлози на корисниците да го ажурираат заклучувањето на екранот на одредено ниво, но корисниците можат да го игнорираат и да продолжат понатаму. Имајте предвид дека заклучувањето на екранот не се складира како обичен текст па апликацијата не ја знае точната лозинка."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"користи биометриски хардвер"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Дозволува апликацијата да користи биометриски хардвер за проверка"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"управувај хардвер за отпечатоци"</string>
@@ -1083,23 +1087,23 @@
     <string name="deleteText" msgid="6979668428458199034">"Избриши"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Метод на внес"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Дејства со текст"</string>
-    <string name="email" msgid="4560673117055050403">"E-пошта"</string>
+    <string name="email" msgid="4560673117055050403">"Испрати е-пошта"</string>
     <string name="email_desc" msgid="3638665569546416795">"Испраќа е-порака на избраната адреса"</string>
     <string name="dial" msgid="1253998302767701559">"Повикај"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Го повикува избраниот телефонски број"</string>
-    <string name="map" msgid="5441053548030107189">"Карта"</string>
+    <string name="map" msgid="5441053548030107189">"Отвори карта"</string>
     <string name="map_desc" msgid="1836995341943772348">"Лоцирај ја избраната адреса"</string>
     <string name="browse" msgid="1245903488306147205">"Отвори"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Ја отвора избраната URL"</string>
-    <string name="sms" msgid="4560537514610063430">"Порака"</string>
+    <string name="sms" msgid="4560537514610063430">"Испрати порака"</string>
     <string name="sms_desc" msgid="7526588350969638809">"Испраќа порака на избраниот телефонски број"</string>
     <string name="add_contact" msgid="7867066569670597203">"Додај"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Додава во контакти"</string>
     <string name="view_calendar" msgid="979609872939597838">"Прикажи"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"Го гледа избраното време во календарот"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"Распоред"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"Закажи"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"Закажува настан за избраното време"</string>
-    <string name="view_flight" msgid="7691640491425680214">"Песна"</string>
+    <string name="view_flight" msgid="7691640491425680214">"Следи го"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"Следи избран лет"</string>
     <string name="translate" msgid="9218619809342576858">"Преведи"</string>
     <string name="translate_desc" msgid="4502367770068777202">"Преведете го избраниот текст"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Таблетот се ажурира…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Уредот се ажурира…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Телефонот стартува…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android стартува…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Таблетот стартува…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Уредот стартува…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Оптимизирање на складирањето."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Допрете за да ги видите сите мрежи"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Поврзете се"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Сите мрежи"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Поврзано на Wi‑Fi мрежа обезбедена од <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Дали сакате да дозволите <xliff:g id="NAME">%s</xliff:g> да ви предлага мрежи?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Да"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Не"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi ќе се вклучи автоматски"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Кога сте во близина на зачувана мрежа со висок квалитет"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Не вклучувај повторно"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 8c451a2..620718d 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"ഉപകരണത്തിന്റെ ഫോൺ സവിശേഷതകൾ ആക്‌സസ്സുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഈ അനുമതി ഫോൺ നമ്പർ, ഉപകരണ ഐഡികൾ, ഒരു കോൾ സജീവമാണോയെന്നത്, ഒരു കോൾ കണക്റ്റുചെയ്‌ത വിദൂര നമ്പർ എന്നിവ നിർണ്ണയിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"കോളുകൾ സിസ്റ്റത്തിലൂടെ വിടുക"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"കോളിംഗ് അനുഭവം ‌മെച്ചപ്പെടുത്തുന്നതിനായി തങ്ങളുടെ ‌കോളുകൾ സിസ്റ്റത്തിലേയ്ക്ക് വഴിതിരിച്ചുവിടാൻ ആപ്പുകളെ അനുവദിക്കുന്നു."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"സിസ്‌റ്റത്തിലൂടെ കോളുകൾ കാണുകയും നിയന്ത്രിക്കുകയും ചെയ്യുക."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"ഉപകരണത്തിൽ നിലവിലുള്ള കോളുകൾ കാണാനും നിയന്തിക്കാനും ആപ്പിനെ അനുവദിക്കുന്നു. കോളുകൾക്കുള്ള നമ്പറുകളും അവയുടെ നിലയും പോലെയുള്ള വിവരങ്ങൾ ഇതിൽ ഉൾപ്പെടുന്നു."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"മറ്റൊരു ആപ്പിൽ നിന്നുള്ള കോൾ തുടരുക"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"മറ്റൊരു ആപ്പിൽ ആരംഭിച്ച കോൾ തുടരാൻ ആപ്പിനെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ഫോൺ നമ്പറുകൾ റീഡുചെയ്യൽ"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"നിയർ ഫീൽഡ് കമ്മ്യൂണിക്കേഷൻ (NFC) ടാഗുകളുമായും കാർഡുകളുമായും റീഡറുകളുമായുള്ള ആശയവിനിമയത്തിന് അപ്ലിക്കേഷനുകളെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"നിങ്ങളുടെ സ്‌ക്രീൻ ലോക്ക് പ്രവർത്തനരഹിതമാക്കുക"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"കീലോക്കും ഏതെങ്കിലും അനുബന്ധ പാസ്‌വേഡ് സുരക്ഷയും പ്രവർത്തനരഹിതമാക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഉദാഹരണത്തിന്, ഒരു ഇൻകമിംഗ് കോൾ സ്വീകരിക്കുമ്പോൾ ഫോൺ കീലോക്ക് പ്രവർത്തനരഹിതമാക്കുന്നു, കോൾ അവസാനിക്കുമ്പോൾ കീലോക്ക് വീണ്ടും പ്രവർത്തനക്ഷമമാകുന്നു."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"സ്ക്രീൻ ലോക്ക് സങ്കീർണ്ണത അഭ്യർത്ഥിച്ച്, നേടുക"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"സ്ക്രീൻ ലോക്കിന്റെ സാധ്യമായ നീളവും തരവും സൂചിപ്പിക്കുന്ന, അതിന്റെ സങ്കീർണ്ണതാ നില (ഉയർന്നത്, ഇടത്തരം, കുറഞ്ഞത് അല്ലെങ്കിൽ ഒന്നുമില്ല) മനസിലാക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. സ്‌ക്രീൻ ലോക്ക് ഒരു പ്രത്യേക തലത്തിലേക്ക് അപ്ഡേറ്റ് ചെയ്യുന്ന ഉപയോക്താക്കൾക്ക് നിർദ്ദേശിക്കാനും ആപ്പിനാവും, മാത്രമല്ല ഉപയോക്താക്കൾക്ക് എളുപ്പത്തിൽ അവഗണിക്കാനും മറ്റൊന്നിലേക്ക് നാവിഗേറ്റ് ചെയ്യാനുമാവും. പ്ലെയിൻടെക്‌സ്‌റ്റിൽ സ്ക്രീൻ ലോക്ക് സംഭരിക്കപ്പെട്ടിട്ടില്ലെന്ന കാര്യം ശ്രദ്ധിക്കുക, അതിനാൽ ആപ്പിന് കൃത്യമായ പാസ്‌വേഡ് അറിയില്ല."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"ബയോമെട്രിക് ഹാർ‌ഡ്‌വെയർ ഉപയോഗിക്കുക"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"പരിശോധിച്ചുറപ്പിക്കുന്നതിനായി, ബയോമെട്രിക് ഹാർഡ്‌വെയർ ഉപയോഗിക്കാൻ ആപ്പിനെ അനുവദിക്കുക"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"ഫിംഗർപ്രിന്റ് ഹാർഡ്‌വെയർ നിയന്ത്രിക്കുക"</string>
@@ -1083,23 +1087,23 @@
     <string name="deleteText" msgid="6979668428458199034">"ഇല്ലാതാക്കുക"</string>
     <string name="inputMethod" msgid="1653630062304567879">"ടൈപ്പുചെയ്യൽ രീതി"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"ടെക്‌സ്‌റ്റ് പ്രവർത്തനങ്ങൾ"</string>
-    <string name="email" msgid="4560673117055050403">"ഇമെയിൽ"</string>
+    <string name="email" msgid="4560673117055050403">"ഇമെയിൽ അയയ്ക്കൂക"</string>
     <string name="email_desc" msgid="3638665569546416795">"തിരഞ്ഞെടുത്ത വിലാസത്തിലേക്ക് ഇമെയിൽ അയയ്ക്കുക"</string>
     <string name="dial" msgid="1253998302767701559">"വിളിക്കുക"</string>
-    <string name="dial_desc" msgid="6573723404985517250">"തിരഞ്ഞെടുത്ത നമ്പറിലേക്ക് കോൾ ചെയ്യുക"</string>
-    <string name="map" msgid="5441053548030107189">"മാപ്പ്"</string>
+    <string name="dial_desc" msgid="6573723404985517250">"തിരഞ്ഞെടുത്ത നമ്പറിലേക്ക് വിളിക്കുക"</string>
+    <string name="map" msgid="5441053548030107189">"മാപ്പ് തുറക്കുക"</string>
     <string name="map_desc" msgid="1836995341943772348">"തിരഞ്ഞെടുത്ത വിലാസം കണ്ടെത്തുക"</string>
     <string name="browse" msgid="1245903488306147205">"തുറക്കുക"</string>
-    <string name="browse_desc" msgid="8220976549618935044">"തിരഞ്ഞെടുത്ത URL ഓപ്പൺ ചെയ്യുക"</string>
-    <string name="sms" msgid="4560537514610063430">"സന്ദേശം"</string>
+    <string name="browse_desc" msgid="8220976549618935044">"തിരഞ്ഞെടുത്ത URL തുറക്കുക"</string>
+    <string name="sms" msgid="4560537514610063430">"സന്ദേശം അയയ്ക്കുക"</string>
     <string name="sms_desc" msgid="7526588350969638809">"തിരഞ്ഞെടുത്ത നമ്പറിലേക്ക് സന്ദേശം അയയ്ക്കുക"</string>
     <string name="add_contact" msgid="7867066569670597203">"ചേർക്കുക"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"കോൺടാക്‌റ്റുകളിലേക്ക് ചേർക്കുക"</string>
     <string name="view_calendar" msgid="979609872939597838">"കാണുക"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"കലണ്ടറിൽ തിരഞ്ഞെടുത്ത സമയം കാണുക"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"ഷെഡ്യൂള്‍‌"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"ഷെഡ്യൂള്‍‌ ചെയ്യുക"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"തിരഞ്ഞെടുത്ത സമയത്തേക്ക് ഇവന്റ് ഷെഡ്യൂൾ ചെയ്യുക"</string>
-    <string name="view_flight" msgid="7691640491425680214">"ട്രാക്ക്"</string>
+    <string name="view_flight" msgid="7691640491425680214">"ട്രാക്ക് ചെയ്യുക"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"തിരഞ്ഞെടുത്ത ഫ്ലൈറ്റ് ട്രാക്ക് ചെയ്യുക"</string>
     <string name="translate" msgid="9218619809342576858">"വിവർത്തനം ചെയ്യുക"</string>
     <string name="translate_desc" msgid="4502367770068777202">"തിരഞ്ഞെടുത്ത ടെക്‌സ്‌റ്റ് വിവർത്തനം ചെയ്യുക"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ടാബ്‌ലെറ്റ് അപ്‌ഡേറ്റ് ചെയ്യുന്നു…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"ഉപകരണം അപ്‌ഡേറ്റ് ചെയ്യുന്നു…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"ഫോൺ ആരംഭിക്കുന്നു…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android ആരംഭിക്കുന്നു…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"ടാബ്‌ലെറ്റ് ആരംഭിക്കുന്നു…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"ഉപകരണം ആരംഭിക്കുന്നു…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"സ്റ്റോറേജ്  ഒപ്‌റ്റിമൈസ് ചെയ്യുന്നു."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"എല്ലാ നെറ്റ്‌വർക്കുകളും കാണാൻ ടാപ്പുചെയ്യുക"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"കണക്റ്റുചെയ്യുക"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"എല്ലാ നെറ്റ്‌വർക്കുകളും"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> നിർദേശിച്ച വൈഫൈ നെറ്റ്‌വർക്കിൽ കണക്‌റ്റ് ചെയ്‌തു"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"നിങ്ങൾക്കായി <xliff:g id="NAME">%s</xliff:g>-നെ നെറ്റ്‌വർക്കുകൾ നിർദ്ദേശിക്കാൻ അനുവദിക്കണോ?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"ഉവ്വ്"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"ഇല്ല"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"വൈഫൈ സ്വമേധയാ ഓണാകും"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"നിങ്ങൾ ഉയർന്ന നിലവാരമുള്ള സംരക്ഷിക്കപ്പെട്ട നെറ്റ്‌വർക്കിനരികിലെത്തുമ്പോൾ"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"തിരികെ ഓണാക്കരുത്"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 93f7e37..c0b23fc 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Апп нь төхөөрөмжийн утасны функцд хандах боломжтой. Энэ зөвшөөрөл нь апп-д утасны дугаар болон төхөөрөмжийн ID-г, дуудлага идэвхтэй эсэх, холын дугаар дуудлагаар холбогдсон байгаа эсэхийг тогтоох боломжийг олгоно,"</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"системээр дамжуулах дуудлага"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Дуудлагыг сайжруулахын тулд дуудлагаа системээр дамжуулах зөвшөөрлийг апп-д олгодог."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"систем дэх дуудлагыг харах болон хянах."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Аппад төхөөрөмж дээр хийж буй дуудлагыг харах болон хянахыг зөвшөөрдөг. Үүнд дуудлагын дугаар болон дуудлагын төлөв зэрэг мэдээллийг агуулдаг."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"дуудлагыг өөр аппаас үргэлжлүүлэх"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Аппад өөр аппад эхлүүлсэн дуудлагыг үргэлжлүүлэхийг зөвшөөрдөг."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"утасны дугаарыг унших"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Апп нь Ойролцоо Талбарын Холболт(NFC) таг, карт, болон уншигчтай холбогдох боломжтой."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"дэлгэцний түгжээг идэвхгүй болгох"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Апп нь түгжээ болон бусад холбоотой нууц үгийн аюулгүй байдлыг идэвхгүй болгох боломжтой. Жишээ нь бол утас нь дуудлага ирэх үед түгжээг идэвхгүй болгох ба дуудлага дуусахад буцаан идэвхтэй болгодог."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"дэлгэцийн түгжээний төвөгтэй байдлыг авах болон хүсэх"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Аппад дэлгэцийн түгжээний боломжит уртын хэмжээ болон төрлийг заадаг дэлгэцийн түгжээний төвөгтэй байдлын түвшнийг (өндөр, дундаж, бага эсвэл байхгүй) мэдэж авахыг зөвшөөрдөг. Түүнчлэн, апп хэрэглэгчдэд дэлгэцийн түгжээг тодорхой түвшинд шинэчлэхийг санал болгодог хэдий ч хэрэглэгч үүнийг чөлөөтэй үл хэрэгсэж, орхих боломжтой. Дэлгэцийн түгжээг ил бичвэрээр хадгалдаггүй тул апп тодорхой нууц үгийг мэддэггүй болохыг анхаарна уу."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"биометрийн техник хангамжийг ашиглах"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Aппад биометрийн техник хангамжийг баталгаажуулалтад ашиглахыг зөвшөөрдөг"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"хурууны хээний програм хангамжийг удирдах"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Таблетыг шинэчилж байна…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Төхөөрөмжийг шинэчилж байна…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Утсыг эхлүүлж байна…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Андройд эхэлж байна..."</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Таблетыг эхлүүлж байна…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Төхөөрөмжийг эхлүүлж байна…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Хадгалалтыг сайжруулж байна."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Бүх сүлжээг харахын тулд товшино уу"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Холбогдох"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Бүх сүлжээ"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g>-с санал болгосон Wi-Fi сүлжээнд холбогдсон"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Та <xliff:g id="NAME">%s</xliff:g>-г танд сүлжээ санал болгохыг хүсэж байна уу?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Тийм"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Үгүй"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi автоматаар асна"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Таныг хадгалсан, өндөр чанартай сүлжээний ойролцоо байх үед"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Буцааж асаахгүй"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 902380b..f3d6b4b 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -317,18 +317,18 @@
     <string name="permgrouplab_visual" msgid="8030190588123857921">"फोटो आणि व्हिडिओ"</string>
     <string name="permgroupdesc_visual" msgid="3415827902566663546">"तुमचे फोटो आणि व्हिडिओ अॅक्सेस करा"</string>
     <string name="permgrouprequest_visual" msgid="6907523945030290376">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला तुमचे फोटो आणि व्हिडिओ अॅक्सेस करू द्यायचे का?"</string>
-    <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"विंडो सामग्री पुनर्प्राप्त करा"</string>
-    <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"तुम्ही परस्‍परसंवाद करीत असलेल्‍या विंडोची सामग्री तपासा."</string>
+    <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"विंडोमधील आशय पुन्हा मिळवा"</string>
+    <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"तुम्ही वापरत असलेल्‍या विंडोमधील आशय तपासा."</string>
     <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"स्पर्श करून अन्वेषण चालू करा"</string>
     <string name="capability_desc_canRequestTouchExploration" msgid="7543249041581408313">"टॅप केलेले आयटम मोठ्‍याने बोलले जातील आणि जेश्चरचा वापर करून स्क्रीन एक्सप्लोर केली जाऊ शकते."</string>
     <string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"तुम्ही टाइप करता त्या मजकुराचे निरीक्षण करा"</string>
-    <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"क्रेडिट कार्ड नंबर आणि संकेतशब्‍द यासारखा वैयक्तिक डेटा समाविष्‍ट करते."</string>
-    <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"डिस्प्ले मॅग्निफिकेशन नियंत्रित करा"</string>
-    <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"प्रदर्शनाचा झूम स्तर आणि स्थिती निर्धारण नियंत्रित करा."</string>
+    <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"यामध्ये क्रेडिट कार्ड नंबर आणि पासवर्ड यासारखा वैयक्तिक डेटा समाविष्‍ट आहे."</string>
+    <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"डिस्प्ले मोठा करण्यावर नियंत्रण ठेवा"</string>
+    <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"डिस्प्लेची झूम पातळी आणि पोझिशनिंग नियंत्रित करा."</string>
     <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"जेश्चर करा"</string>
     <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"टॅप, स्वाइप, पिंच आणि इतर जेश्चर करू शकते."</string>
     <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"फिंगरप्रिंट जेश्चर"</string>
-    <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"डिव्‍हाइसच्‍या फिंगरप्रिंट सेंसरवर केलेले जेश्चर कॅप्‍चर करू शकते."</string>
+    <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"डिव्‍हाइसच्‍या फिंगरप्रिंट सेंन्सरवरील जेश्चर कॅप्‍चर करू शकते."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"स्टेटस बार अक्षम करा किंवा सुधारित करा"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"स्टेटस बार अक्षम करण्यासाठी किंवा सिस्टम चिन्हे जोडण्यासाठी आणि काढण्यासाठी अ‍ॅप ला अनुमती देते."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"स्टेटस बार होऊ द्या"</string>
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"डिव्हाइस च्या फोन वैशिष्ट्यांवर अॅक्सेस करण्यास अॅपला अनुमती देते. ही परवानगी कॉल अॅक्टिव्हेट असला किंवा नसला तरीही, फोन नंबर आणि डिव्हाइस आयडी आणि कॉलद्वारे कनेक्ट केलेला रिमोट नंबर निर्धारित करण्यासाठी अॅपला अनुमती देते."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"प्रणालीच्या माध्यमातून कॉल रूट करा"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"कॉल करण्याचा अनुभव सुधारण्यासाठी अॅपला त्याचे कॉल प्रणालीच्या माध्यमातून रूट करू देते."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"सिस्टम वापरून कॉल पाहा आणि नियंत्रण ठेवा."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"डिव्हाइसवर येणार कॉल पाहण्यासाठी आणि नियंत्रित करण्यासाठी अॅपला अनुमती देते. यामध्ये कॉल करण्यासाठी कॉलचा नंबर आणि कॉलची स्थिती यासारख्या माहितीचा समावेश असतो."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"दुसऱ्या अॅपवरून कॉल करणे सुरू ठेवा"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"दुसऱ्या अॅपमध्ये सुरू झालेल्या कॉलला पुढे सुरू ठेवण्याची अॅपला अनुमती देते."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"फोन नंबर वाचा"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"फील्ड जवळील कम्युनिकेशन (NFC) टॅग, कार्डे आणि वाचक यांच्यासह संवाद करण्यासाठी अॅपला अनुमती देते."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"तुमचे स्क्रीन लॉक अक्षम करा"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"कीलॉक आणि कोणतीही संबद्ध पासवर्ड सुरक्षितता अक्षम करण्यासाठी अ‍ॅप ला अनुमती देते. उदाहरणार्थ, येणारा फोन कॉल प्राप्त करताना फोन कीलॉक अक्षम करतो, नंतर जेव्हा कॉल समाप्त होतो तेव्हा तो कीलॉक पुन्हा-सक्षम करतो."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"स्क्रीन लॉक जटिलता मिळवा आणि त्यासाठी विनंती करा"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"अ‍ॅपला स्क्रीन लॉक जटिलता पातळी (उच्च, मध्यम, खालची किंवा काहीही नाही) जाणून घेऊ देते, जी लांबीची संभाव्य श्रेणी आणि स्क्रीन लॉकचा प्रकार सूचित करते. अ‍ॅप वापरकर्त्यांना हेदेखील सुचवू शकते की त्यांनी स्क्रीन लॉक ठराविक पातळीपर्यंत अपडेट करावे परंतु वापरकर्ते मुक्तपणे ते दुर्लक्षित करू शकतात आणि तेथून नेव्हिगेट करू शकतात. स्क्रीन लॉक प्लेनटेक्स्टमध्ये स्टोअर केले जात नसल्यामुळे अ‍ॅपला नेमका पासवर्ड माहीत नसतो याची नोंद घ्या."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"बायोमेट्रिक हार्डवेअर वापरा"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"ऑथेंटिकेशनसाठी बायोमेट्रिक हार्डवेअरचा वापर करण्याची अॅपला अनुमती देते"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"फिंगरप्रिंट हार्डवेअर व्यवस्थापित करा"</string>
@@ -1110,7 +1114,7 @@
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"सिस्टीमसाठी पुरेसे संचयन नाही. आपल्याकडे 250MB मोकळे स्थान असल्याचे सुनिश्चित करा आणि रीस्टार्ट करा."</string>
     <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> चालत आहे"</string>
     <string name="app_running_notification_text" msgid="1197581823314971177">"अधिक माहितीसाठी किंवा अ‍ॅप थांबविण्यासाठी टॅप करा."</string>
-    <string name="ok" msgid="5970060430562524910">"ठीक"</string>
+    <string name="ok" msgid="5970060430562524910">"ठीक आहे"</string>
     <string name="cancel" msgid="6442560571259935130">"रद्द करा"</string>
     <string name="yes" msgid="5362982303337969312">"ठीक"</string>
     <string name="no" msgid="5141531044935541497">"रद्द करा"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"टॅबलेट अपडेट होत आहे…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"डिव्‍हाइस अपडेट होत आहे…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"फोन सुरू होत आहे…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android प्रारंभ करत आहे…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"टॅबलेट सुरू होत आहे…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"डिव्‍हाइस सुरू होत आहे…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"संचयन ऑप्टिमाइझ करत आहे."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"सर्व नेटवर्क पाहण्यासाठी टॅप करा"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"कनेक्ट करा"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"सर्व नेटवर्क"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> ने सुचवलेल्या वाय-फाय नेटवर्कशी कनेक्ट केले"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"तुम्हाला <xliff:g id="NAME">%s</xliff:g> ने तुमच्यासाठी नेटवर्क सुचवायला हवे आहे का?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"होय"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"नाही"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"वाय-फाय आपोआप चालू होईल"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"तुम्ही जेव्हा सेव्ह केलेल्या उच्च दर्जाच्या नेटवर्कजवळ असाल तेव्हा"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"पुन्हा चालू करू नका"</string>
@@ -1857,7 +1866,7 @@
     <string name="language_selection_title" msgid="2680677278159281088">"एक भाषा जोडा"</string>
     <string name="country_selection_title" msgid="2954859441620215513">"प्रदेश प्राधान्य"</string>
     <string name="search_language_hint" msgid="7042102592055108574">"भाषा नाव टाइप करा"</string>
-    <string name="language_picker_section_suggested" msgid="8414489646861640885">"सूचित केलेले"</string>
+    <string name="language_picker_section_suggested" msgid="8414489646861640885">"सुचवलेल्या भाषा"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"सर्व भाषा"</string>
     <string name="region_picker_section_all" msgid="8966316787153001779">"सर्व प्रदेश"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"शोध"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 6492f48..0eb022a 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Membenarkan apl mengakses ciri telefon pada peranti. Kebenaran ini membolehkan apl menentukan nombor telefon dan ID peranti, sama ada panggilan aktif dan nombor jauh yang dihubungkan dengan panggilan."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"halakan panggilan menerusi sistem"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Membenarkan apl menghalakan panggilan menerusi sistem untuk meningkatkan pengalaman panggilan."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"lihat dan kawal panggilan melalui sistem."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Membenarkan apl melihat dan mengawal panggilan yang sedang berlangsung pada peranti. Ini termasuklah maklumat seperti nombor panggilan untuk panggilan dan keadaan panggilan tersebut."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"teruskan panggilan daripada apl lain"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Membenarkan apl meneruskan panggilan yang dimulakan dalam apl lain."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"baca nombor telefon"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Membenarkan apl berkomunikasi dengan teg, kad dan pembaca Komunikasi Medan Dekat (NFC)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"lumpuhkan kunci skrin anda"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Membenarkan apl melumpuhkan kunci kekunci dan sebarang keselamatan kata laluan yang berkaitan. Sebagai contoh, telefon melumpuhkan kunci kekunci apabila menerima panggilan telefon masuk kemudian mendayakan semula kunci kekunci apabila panggilan selesai."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"dapatkan dan minta kekompleksan kunci skrin"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Benarkan apl mengetahui tahap kekompleksan kunci skrin (tinggi, sederhana, rendah atau tiada) yang menunjukkan kemungkinan julat panjang dan jenis kunci skrin. Apl juga boleh mencadangkan kepada pengguna supaya mengemas kini kunci skrin pada tahap tertentu namun pengguna boleh mengabaikan dan menavigasi keluar dengan bebas. Sila ambil perhatian bahawa kunci skrin tidak disimpan dalam teks biasa, maka apl tidak mengetahui kata laluan yang tepat."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"gunakan perkakasan biometrik"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Membenarkan apl menggunakan perkakasan biometrik untuk pengesahan"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"urus perkakasan cap jari"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet sedang mengemas kini…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Peranti sedang mengemas kini…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Telefon sedang dimulakan…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android sedang dimulakan…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet sedang dimulakan…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Peranti sedang dimulakan…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Mengoptimumkan storan."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Ketik untuk melihat semua rangkaian"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Sambung"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Semua rangkaian"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Dihubungkan ke rangkaian Wi‑Fi yang dicadangkan oleh <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Adakah anda ingin <xliff:g id="NAME">%s</xliff:g> mencadangkan rangkaian kepada anda?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ya"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Tidak"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi akan dihidupkan secara automatik"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Apabila anda berada berdekatan dengan rangkaian disimpan yang berkualiti tinggi"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Jangan hidupkan kembali"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index a3da75e..1c5f0c7 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"အပလီကေးရှင်းအား ဖုန်းရဲ့ စွမ်းဆောင်ချက်များအား သုံးခွင့်ပြုပါ။ အပလီကေးရှင်းအနေဖြင့် ဖုန်းနံပါတ်၊ စက်နံပါတ်၊ ဖုန်းခေါ်နေမှု ရှိမရှိနှင့် တဖက်မှ ဖုန်းနံပါတ် များအား သိရှိနိုင်ပါသည်"</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ခေါ်ဆိုမှုများကို စနစ်မှတစ်ဆင့် ဖြတ်သန်းခွင့်ပြုပါ"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"ခေါ်ဆိုမှု အတွေ့အကြုံ ပိုမိုကောင်းမွန်လာစေရန်အတွက် အက်ပ်၏ ခေါ်ဆိုမှုအား စနစ်မှတစ်ဆင့် ဖြတ်သန်းရန် ခွင့်ပြုပါသည်။"</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"စနစ်မှတစ်ဆင့် ခေါ်ဆိုမှုများကို ကြည့်ရှုထိန်းချုပ်ပါ။"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"စက်ပစ္စည်းပေါ်ရှိ လက်ရှိခေါ်ဆိုမှုများကို အက်ပ်အား ကြည့်ရှုထိန်းချုပ်ခွင့်ပြုသည်။ ၎င်းတွင် ခေါ်ဆိုမှုနံပါတ်များနှင့် ခေါ်ဆိုမှုအခြေအနေများကဲ့သို့သော အခြေအနေများ ပါဝင်သည်။"</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"အခြားအက်ပ်မှ ဖုန်းခေါ်ဆိုမှုကို ဆက်လက်ပြုလုပ်ပါ"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"အခြားအက်ပ်တွင် စတင်ထားသည့် ဖုန်းခေါ်ဆိုမှုကို ဆက်လက်ပြုလုပ်ရန် ဤအက်ပ်ကို ခွင့်ပြုသည်။"</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ဖုန်းနံပါတ်များကို ဖတ်ရန်"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"အက်ပ်အား တာတို စက်ကွင်း ဆက်သွယ်ရေး (NFC) တဲဂ်များ၊ ကဒ်များ နှင့် ဖတ်ကြသူတို့နှင့် ဆက်သွယ်ပြောဆိုခွင့် ပြုသည်။"</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"ဖန်သားပြင် သော့ချခြင်းအား မလုပ်နိုင်အောင် ပိတ်ရန်"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"အပလီကေးရှင်းအား သော့ချခြင်းနှင့် သက်ဆိုင်ရာ စကားဝှက်သတ်မှတ်ခြင်းများအား မသုံးနိုင်အောင် ပိတ်ခြင်းကို ခွင့်ပြုရန်။ ဥပမာ ဖုန်းလာလျှင် သော့ပိတ်ခြင်း ပယ်ဖျက်ခြင်း၊ ဖုန်းပြောပြီးလျှင် သော့ကို အလိုအလျောက် ပြန်ပိတ်ခြင်း"</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"ဖုန်းမျက်နှာပြင်လော့ခ် ရှုပ်ထွေးမှုကို ရယူခြင်းနှင့် တောင်းခံခြင်း"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"ဖုန်းမျက်နှာပြင်လော့ခ်၏ ရှုပ်ထွေးမှုအဆင့် (မြင့်၊ အလယ်အလတ်၊ နိမ့် သို့မဟုတ် မရှိ) အား လေ့လာရန် အက်ပ်ကို ခွင့်ပြုသည်။ ၎င်းက သတ်မှတ်ထားနိုင်သော ဖုန်းမျက်နှာပြင်လော့ခ်၏ စာလုံးရေနှင့် အမျိုးအစားကို ညွှန်ပြပေးသည်။ အသုံးပြုသူများအနေနှင့် ဖုန်းမျက်နှာပြင်လော့ခ်ကို အတိုင်းအတာတစ်ခုအထိ အဆင့်မြှင့်ရန် အက်ပ်က အကြံပြုနိုင်သည်။ သို့သော်လည်း အသုံးပြုသူများက ၎င်းကို ဂရုပြုမနေဘဲ လွတ်လပ်စွာ ကြည့်ရှုနိုင်ပါသည်။ ဖုန်းမျက်နှာပြင်လော့ခ်ကို စာသားအတိုင်း သိမ်းမထားသဖြင့် အက်ပ်သည် စကားဝှက်အစစ်ကို မသိနိုင်ကြောင်း သတိပြုပါ။"</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"ဇီဝဗေဒဆိုင်ရာ အချက်အလက်သုံး ကွန်ပျူတာဆိုင်ရာ စက်ပစ္စည်းကို အသုံးပြုရန်"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"အထောက်အထားစိစစ်ခြင်းအတွက် ဇီဝဗေဒဆိုင်ရာ သတင်းအချက်အလက်များသုံးသည့် ကွန်ပျူတာဆိုင်ရာ စက်ပစ္စည်းကို အသုံးပြုရန် အက်ပ်ကို ခွင့်ပြုသည်"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"လက်ဗွေရာပစ္စည်းကို စီမံမည်"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"တက်ဘလက်ကို အပ်ဒိတ်လုပ်နေသည်…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"စက်ပစ္စည်းကို အပ်ဒိတ်လုပ်နေသည်…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"ဖုန်း စတင်နေသည်…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android စတင်နေ…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"တက်ဘလက် စတင်နေသည်…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"စက်ပစ္စည်း စတင်နေသည်…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"သိုလှောင်မှုအား ပြုပြင်ခြင်း။"</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"ကွန်ရက်အားလုံးကို ကြည့်ရန် တို့ပါ"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"ချိတ်ဆက်ရန်"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"ကွန်ရက်အားလုံး"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> အကြံပြုထားသော Wi‑Fi ကွန် ရက်သို့ ချိတ်ဆက်ထားသည်"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"သင့်အတွက် ကွန်ရက်များ အကြံပြုရန် <xliff:g id="NAME">%s</xliff:g> ကို ခွင့်ပြုလိုပါသလား။"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Yes"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"No"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi ကို အလိုအလျောက်​ ပြန်ဖွင့်ပေးလိမ့်ပါမည်"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"သိမ်းဆည်းထားသည့် အရည်အသွေးမြင့်ကွန်ရက်များအနီးသို့ ရောက်ရှိသည့်အခါ"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"ပြန်မဖွင့်ပါနှင့်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 30cd600..233b98a 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Lar appen bruke enhetens telefonfunksjoner. Med denne tillatelsen kan appen finne telefonnummer og enhets-ID-er, registrere om en samtale pågår, og se det eksterne nummeret det opprettes en forbindelse med via oppringing."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"send anrop gjennom systemet"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Lar appen sende anrop gjennom systemet for å forbedre anropsopplevelsen."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"se og kontrollere anrop i systemet."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Lar appen se og kontrollere anrop som pågår på enheten. Dette inkluderer informasjon som anropsnumre og tilstanden til anropene."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"fortsette et anrop fra en annen app"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Lar appen fortsette et anrop som ble startet i en annen app."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"les telefonnumre"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Lar appen kommunisere med etiketter, kort og lesere som benytter NFC-teknologi."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"deaktivere skjermlåsen"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Lar appen deaktivere tastelåsen og eventuell tilknyttet passordsikkerhet. Et eksempel er at telefonen deaktiverer tastelåsen når du mottar et innkommende anrop, og deretter aktiverer tastelåsen igjen når samtalen er ferdig."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"hente og be om kompleksitet for skjermlås"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Lar appen lære skjermlåsens kompleksitetsnivå (høy, middels, lav eller ingen), som indikerer det mulige området for lengde og type skjermlås. Appen kan foreslå at brukeren oppdaterer skjermlåsen til et bestemt nivå, men brukere kan velge å ignorere dette og navigere bort. Vær oppmerksom på at skjermlåsen ikke er lagret klartekst, så appen kan ikke se det nøyaktige passordet."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"bruke biometrisk maskinvare"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Lar appen bruke biometrisk maskinvare til godkjenning"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"administrere fingeravtrykkmaskinvare"</string>
@@ -1083,21 +1087,21 @@
     <string name="deleteText" msgid="6979668428458199034">"Slett"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Inndatametode"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Teksthandlinger"</string>
-    <string name="email" msgid="4560673117055050403">"E-post"</string>
+    <string name="email" msgid="4560673117055050403">"Send e-post"</string>
     <string name="email_desc" msgid="3638665569546416795">"Send e-post til den valgte adressen"</string>
     <string name="dial" msgid="1253998302767701559">"Ring"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Ring det valgte telefonnummeret"</string>
-    <string name="map" msgid="5441053548030107189">"Kart"</string>
+    <string name="map" msgid="5441053548030107189">"Se Kart"</string>
     <string name="map_desc" msgid="1836995341943772348">"Finn den valgte adressen"</string>
     <string name="browse" msgid="1245903488306147205">"Åpne"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Åpne den valgte nettadressen"</string>
-    <string name="sms" msgid="4560537514610063430">"Melding"</string>
+    <string name="sms" msgid="4560537514610063430">"Send melding"</string>
     <string name="sms_desc" msgid="7526588350969638809">"Send melding til det valgte telefonnummeret"</string>
     <string name="add_contact" msgid="7867066569670597203">"Legg til"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Legg til i kontakter"</string>
     <string name="view_calendar" msgid="979609872939597838">"Se"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"Se det valgte klokkeslettet i kalenderen"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"Oversikt"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"Planlegg"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"Planlegg aktivitet for valgt klokkeslett"</string>
     <string name="view_flight" msgid="7691640491425680214">"Spor"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"Spor den valgte flyvningen"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Nettbrettet oppdateres …"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Enheten oppdateres …"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Telefonen starter …"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android starter …"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Nettbrettet starter …"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Enheten starter …"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimaliser lagring."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Trykk for å se alle nettverkene"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Koble til"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Alle nettverk"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Koblet til et Wi-Fi-nettverk som ble foreslått av <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Vil du la <xliff:g id="NAME">%s</xliff:g> foreslå nettverk for deg?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ja"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nei"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi slås på automatisk"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Når du er i nærheten av et lagret nettverk av høy kvalitet"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ikke slå på igjen"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 188ce8b..0b2362a 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"उपकरणको फोन विशेषताहरूको पहुँच गर्न अनुप्रयोगलाई अनुमति दिन्छ। यस अनुमतिले फोन नम्बर र उपकरणको IDs, कल सक्षम छ कि छैन र कलद्वारा जोडिएको टाढाको नम्बर निर्धारण गर्न अनुमति दिन्छ।"</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"प्रणाली मार्फत कल गर्न दिनुहोस्‌"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"कल गर्दाको अनुभवलाई सुधार्न यस अनुप्रयोगलाई प्रणाली मार्फत कलहरू गर्न अनुमति दिन्छ।"</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"प्रणालीमार्फत कलहरू हेर्नुका साथै तिनीहरूलाई नियन्त्रण गर्नुहोस्‌।"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"अनुप्रयोगलाई यन्त्रमा जारी रहेका कलहरू हेर्नुका साथै तिनीहरूलाई गर्ने अनुमति दिनुहोस्‌। यसमा गरिएका कलहरूको सङ्ख्या र कलहरूको अवस्था जस्ता जानकारी समावेश हुन्छन्‌।"</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"अर्को अनुप्रयोगमा सुरु गरिएको कल जारी राख्नुहोस्"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"यस अनुप्रयोगलाई अर्को अनुप्रयोगमा सुरु गरिएको कल जारी राख्ने अनुमति दिन्छ।"</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"फोन नम्बरहरू पढ्ने"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"अनुप्रयोगलाई नयाँ क्षेत्र संचार (NFC) ट्यागहरू, कार्डहरू र पाठकहरूसँग अन्तर्क्रिया गर्न अनुमति दिन्छ।"</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"स्क्रिन लक असक्षम पार्नुहोस्"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"कुनै सम्बन्धित पासवर्ड सुरक्षा र किलकलाई असक्षम पार्न अनुप्रयोगलाई अनुमति दिन्छ। उदाहरणको लागि, अन्तर्गमन फोन कल प्राप्त गर्दा फोनले किलकलाई असक्षम पार्छ, त्यसपछि कल सकिएको बेला किलक पुनःसक्षम पार्छ।"</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"स्क्रिन लकको जटिलतासम्बन्धी जानकारी प्राप्त गर्न र त्यसका लागि अनुरोध गर्न"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"यसले अनुप्रयोगलाई स्क्रिन लकको जटिलताको स्तर (उच्च, मध्यम, न्यून वा कुनै पनि होइन) थाहा पाउने अनुमति दिन्छ जसले स्क्रिन लकको लम्बाइको सम्भावित दायरा र त्यसको प्रकारलाई जनाउँछ। यसै गरी, यो अनुप्रयोगले प्रयोगकर्ताहरूलाई स्क्रिन लक अद्यावधिक गर्ने सुझाव पनि दिन सक्छ तर प्रयोगकर्ताहरू उक्त सुझावको बेवास्ता गरी बाहिर निस्कन सक्छन्। स्क्रिन लकलाई सादा पाठको ढाँचामा भण्डारण नगरिने हुँदा यो अनुप्रयोगले वास्तविक पासवर्ड थाहा पाउँदैन भन्ने कुरा याद राख्नुहोस्।"</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"बायोमेट्रिक हार्डवेयर प्रयोग गर्नुहोस्‌"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"अनुप्रयोगलाई प्रमाणीकरणका लागि बायोमेट्रिक हार्डवेयर प्रयोग गर्न अनुमति दिन्छ"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"औठाछाप हार्डवेयर व्यवस्थापन गर्नुहोस्"</string>
@@ -1083,28 +1087,28 @@
     <string name="deleteText" msgid="6979668428458199034">"मेट्नुहोस्"</string>
     <string name="inputMethod" msgid="1653630062304567879">"निवेश विधि"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"पाठ कार्यहरू"</string>
-    <string name="email" msgid="4560673117055050403">"इमेल"</string>
+    <string name="email" msgid="4560673117055050403">"इमेल गर्नुहोस्"</string>
     <string name="email_desc" msgid="3638665569546416795">"चयन गरिएको ठेगानामा इमेल पठाउनुहोस्"</string>
-    <string name="dial" msgid="1253998302767701559">"कल"</string>
+    <string name="dial" msgid="1253998302767701559">"कल गर्नुहोस्"</string>
     <string name="dial_desc" msgid="6573723404985517250">"चयन गरिएको फोन नम्बरमा कल गर्नुहोस्"</string>
-    <string name="map" msgid="5441053548030107189">"नक्सा"</string>
+    <string name="map" msgid="5441053548030107189">"नक्सा अनुप्रयोग खोल्नुहोस्"</string>
     <string name="map_desc" msgid="1836995341943772348">"चयन गरिएको ठेगाना पत्ता लगाउनुहोस्"</string>
     <string name="browse" msgid="1245903488306147205">"खोल्नुहोस्"</string>
     <string name="browse_desc" msgid="8220976549618935044">"चयन गरिएको URL खोल्नुहोस्"</string>
-    <string name="sms" msgid="4560537514610063430">"सन्देश"</string>
+    <string name="sms" msgid="4560537514610063430">"सन्देश पठाउनुहोस्"</string>
     <string name="sms_desc" msgid="7526588350969638809">"चयन गरिएको फोन नम्बरमा सन्देश पठाउनुहोस्‌"</string>
     <string name="add_contact" msgid="7867066569670597203">"थप्नुहोस्"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"सम्पर्क सूचीमा थप्नुहोस्"</string>
     <string name="view_calendar" msgid="979609872939597838">"हेर्नुहोस्"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"पात्रोमा चयन गरिएको समय हेर्नुहोस्"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"समयतालिका"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"समयतालिका बनाउनुहोस्"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"चयन गरिएको समयका लागि कार्यक्रमको समयतालिका बनाउनुहोस्‌"</string>
     <string name="view_flight" msgid="7691640491425680214">"ट्र्याक गर्नुहोस्"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"चयन गरिएको उडान ट्रयाक गर्नुहोस्"</string>
     <string name="translate" msgid="9218619809342576858">"अनुवाद गर्नुहोस्"</string>
-    <string name="translate_desc" msgid="4502367770068777202">"चयन गरिएको पाठ अनुवाद गर्नुहोस्"</string>
-    <string name="define" msgid="7394820043869954211">"परिभाषा गर्नुहोस्"</string>
-    <string name="define_desc" msgid="7910883642444919726">"चयन गरिएको पाठ परिभाषा गर्नुहोस्‌"</string>
+    <string name="translate_desc" msgid="4502367770068777202">"चयन गरिएको पाठको अनुवाद गर्नुहोस्"</string>
+    <string name="define" msgid="7394820043869954211">"परिभाषा दिनुहोस्"</string>
+    <string name="define_desc" msgid="7910883642444919726">"चयन गरिएको पाठको परिभाषा दिनुहोस्"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"भण्डारण ठाउँ सकिँदै छ"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"सायद केही प्रणाली कार्यक्रमहरूले काम गर्दैनन्"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"प्रणालीको लागि पर्याप्त भण्डारण छैन। तपाईँसँग २५० मेगा बाइट ठाउँ खाली भएको निश्चित गर्नुहोस् र फेरि सुरु गर्नुहोस्।"</string>
@@ -1187,6 +1191,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ट्याब्लेटको अद्यावधिक गरिँदै छ…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"यन्त्रको अद्यावधिक गरिँदै छ…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"फोन सुरु हुँदै छ…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android शुरू हुँदैछ..."</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"ट्याब्लेट सुरु हुँदै छ…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"यन्त्र सुरु हुँदै छ…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"भण्डारण आफू अनुकूल गर्दै।"</string>
@@ -1245,6 +1250,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"सबै नेटवर्कहरू हेर्न ट्याप गर्नुहोस्"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"जडान गर्नुहोस्"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"सबै नेटवर्कहरू"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> ले प्रस्ताव गरेको Wi‑Fi नेटवर्कमा जोडियो"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"तपाईं <xliff:g id="NAME">%s</xliff:g> लाई आफ्ना लागि नेटवर्कहरूको प्रस्ताव गर्ने अनुमति दिन चाहनुहुन्छ?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"हो"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"होइन"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi स्वतः सक्रिय हुनेछ"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"तपाईं कुनै सुरक्षित गरिएको उच्च गुणस्तरीय नेटवर्कको नजिक हुनुभएको अवस्थामा"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"फेरि सक्रिय नगर्नुहोला"</string>
@@ -1743,7 +1752,7 @@
     <string name="reason_service_unavailable" msgid="7824008732243903268">"प्रिन्ट सेवा सक्षम गरिएको छैन"</string>
     <string name="print_service_installed_title" msgid="2246317169444081628">"<xliff:g id="NAME">%s</xliff:g> सेवा स्थापित भयो"</string>
     <string name="print_service_installed_message" msgid="5897362931070459152">"सक्षम पार्न ट्याप गर्नुहोस्"</string>
-    <string name="restr_pin_enter_admin_pin" msgid="8641662909467236832">"प्रशासकको PIN प्रविष्ट गर्नुहोस्"</string>
+    <string name="restr_pin_enter_admin_pin" msgid="8641662909467236832">"प्रशासकको PIN प्रविष्टि गर्नुहोस्"</string>
     <string name="restr_pin_enter_pin" msgid="3395953421368476103">"PIN प्रविष्टि गर्नुहोस्"</string>
     <string name="restr_pin_incorrect" msgid="8571512003955077924">"गलत"</string>
     <string name="restr_pin_enter_old_pin" msgid="1462206225512910757">"वर्तमान PIN"</string>
@@ -1905,7 +1914,7 @@
     <string name="time_picker_hour_label" msgid="2979075098868106450">"घन्टा"</string>
     <string name="time_picker_minute_label" msgid="5168864173796598399">"मिनेट"</string>
     <string name="time_picker_header_text" msgid="143536825321922567">"समय सेट गर्नुहोस्"</string>
-    <string name="time_picker_input_error" msgid="7574999942502513765">"मान्य समय प्रविष्ट गर्नुहोस्"</string>
+    <string name="time_picker_input_error" msgid="7574999942502513765">"मान्य समय प्रविष्टि गर्नुहोस्"</string>
     <string name="time_picker_prompt_label" msgid="7588093983899966783">"समय टाइप गर्नुहोस्‌"</string>
     <string name="time_picker_text_input_mode_description" msgid="4148166758173708199">"समय इनपुट गर्न पाठ इनपुट मोडमा स्विच गर्नुहोस्।"</string>
     <string name="time_picker_radial_mode_description" msgid="4953403779779557198">"समय इनपुट गर्न घडी मोडमा स्विच गर्नुहोस्।"</string>
diff --git a/core/res/res/values-night/themes_device_defaults.xml b/core/res/res/values-night/themes_device_defaults.xml
index 84c6446..43b3552 100644
--- a/core/res/res/values-night/themes_device_defaults.xml
+++ b/core/res/res/values-night/themes_device_defaults.xml
@@ -59,4 +59,6 @@
 
     <!-- Theme for the dialog shown when an app crashes or ANRs. -->
     <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.DeviceDefault.Dialog.Alert" />
-</resources>
+
+    <style name="Theme.DeviceDefault.DayNight" parent="Theme.DeviceDefault" />
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values-night/themes_permission_controller.xml b/core/res/res/values-night/themes_permission_controller.xml
deleted file mode 100644
index 0ad2bdc..0000000
--- a/core/res/res/values-night/themes_permission_controller.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2018 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<!-- themes for the permission grant dialog. -->
-<resources>
-    <style name="Theme.DeviceDefault.PermissionGrantApp"
-           parent="@style/Theme.DeviceDefault.Panel">
-        <item name="windowIsFloating">false</item>
-        <item name="windowTranslucentStatus">true</item>
-        <item name="backgroundDimEnabled">true</item>
-        <item name="windowAnimationStyle">@style/Animation.Material.Dialog</item>
-    </style>
-
-    <style name="Theme.DeviceDefault.PermissionGrant"
-           parent="@style/Theme.DeviceDefault.Dialog">
-        <item name="titleTextStyle">@style/PermissionGrantTitleMessage</item>
-        <item name="radioButtonStyle">@style/PermissionGrantRadioButton</item>
-        <item name="checkboxStyle">@style/PermissionGrantCheckbox</item>
-        <item name="buttonBarStyle">@style/PermissionGrantButtonBar</item>
-    </style>
-</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 2ff9fde..996b700 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Hiermee kan de app toegang krijgen tot de telefoonfuncties van het apparaat, Met deze toestemming kan de app het telefoonnummer en de apparaat-ID\'s bepalen, of een gesprek actief is, en het andere telefoonnummer waarmee wordt gebeld."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"gesprekken doorschakelen via het systeem"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Hiermee kan de app de bijbehorende gesprekken doorschakelen via het systeem om de belfunctionaliteit te verbeteren."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"gesprekken via het systeem bekijken en beheren"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Hiermee kan de app actieve gesprekken op het apparaat bekijken en beheren. Dit omvat informatie zoals nummers voor gesprekken en de status van de gesprekken."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"een gesprek voortzetten vanuit een andere app"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Hiermee kan de app een gesprek voortzetten dat is gestart in een andere app."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"telefoonnummers lezen"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Hiermee kan de app communiceren met NFC-tags (Near Field Communication), kaarten en lezers."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"je schermvergrendeling uitschakelen"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Hiermee kan de app de toetsenblokkering en bijbehorende wachtwoordbeveiliging uitschakelen. Zo kan de telefoon de toetsenblokkering uitschakelen wanneer je wordt gebeld en de toetsenblokkering weer inschakelen als het gesprek is beëindigd."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"complexiteit van schermbeveiliging ophalen en aanvragen"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Hiermee krijgt de app toestemming om het complexiteitsniveau van de schermvergrendeling te achterhalen (hoog, midden, laag of geen). Dat geeft een indicatie van het mogelijke lengtebereik en type van de schermvergrendeling. De app kan gebruikers ook voorstellen de schermvergrendeling naar een bepaald niveau te updaten, maar gebruikers kunnen dit altijd negeren en de app verlaten. De schermvergrendeling wordt niet opgeslagen in platte tekst, zodat de app het precieze wachtwoord niet weet."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"biometrische hardware gebruiken"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Hiermee kan de app biometrische hardware gebruiken voor verificatie"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"Vingerafdrukhardware beheren"</string>
@@ -1083,15 +1087,15 @@
     <string name="deleteText" msgid="6979668428458199034">"Verwijderen"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Invoermethode"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Tekstacties"</string>
-    <string name="email" msgid="4560673117055050403">"E-mail"</string>
+    <string name="email" msgid="4560673117055050403">"E-mailen"</string>
     <string name="email_desc" msgid="3638665569546416795">"E-mail sturen aan geselecteerd e-mailadres"</string>
     <string name="dial" msgid="1253998302767701559">"Bellen"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Geselecteerd telefoonnummer bellen"</string>
-    <string name="map" msgid="5441053548030107189">"Kaart"</string>
+    <string name="map" msgid="5441053548030107189">"Kaart openen"</string>
     <string name="map_desc" msgid="1836995341943772348">"Geselecteerd adres zoeken"</string>
     <string name="browse" msgid="1245903488306147205">"Openen"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Geselecteerde URL openen"</string>
-    <string name="sms" msgid="4560537514610063430">"Bericht"</string>
+    <string name="sms" msgid="4560537514610063430">"Bericht verzenden"</string>
     <string name="sms_desc" msgid="7526588350969638809">"Sms sturen aan geselecteerd telefoonnummer"</string>
     <string name="add_contact" msgid="7867066569670597203">"Toevoegen"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Toevoegen aan contacten"</string>
@@ -1100,7 +1104,7 @@
     <string name="add_calendar_event" msgid="1953664627192056206">"Plannen"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"Evenement plannen voor geselecteerde tijd"</string>
     <string name="view_flight" msgid="7691640491425680214">"Volgen"</string>
-    <string name="view_flight_desc" msgid="3876322502674253506">"Geselecteerde vlucht tonen"</string>
+    <string name="view_flight_desc" msgid="3876322502674253506">"Geselecteerde vlucht volgen"</string>
     <string name="translate" msgid="9218619809342576858">"Vertalen"</string>
     <string name="translate_desc" msgid="4502367770068777202">"Geselecteerde tekst vertalen"</string>
     <string name="define" msgid="7394820043869954211">"Definiëren"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet wordt geüpdatet…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Apparaat wordt geüpdatet…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Telefoon wordt gestart…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android wordt gestart…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet wordt gestart…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Apparaat wordt gestart…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Opslagruimte wordt geoptimaliseerd."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tik om alle netwerken te bekijken"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Verbinding maken"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Alle netwerken"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Verbonden met wifi-netwerk dat is voorgesteld door <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Wil je dat <xliff:g id="NAME">%s</xliff:g> je netwerken kan voorstellen?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ja"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nee"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wifi wordt automatisch ingeschakeld"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Wanneer je in de buurt van een opgeslagen netwerk van hoge kwaliteit bent"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Niet weer inschakelen"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index f1c7baf..27b7d5d 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"ଆପ୍‌କୁ ଡିଭାଇସ୍‌ର ଫୋନ୍‌ ବୈଶିଷ୍ଟ୍ୟ ଆକ୍ସେସ୍‍ କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ। ଏହି ଅନୁମତି ଆପ୍‌କୁ ଫୋନ୍‌ ନମ୍ବର୍ ଓ ଡିଭାଇସ୍‌ IDଗୁଡ଼ିକୁ ନିର୍ଦ୍ଧାରଣ କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ, କଲ୍ ସକ୍ରିୟ ଥିଲେ ବି ଏବଂ କଲ୍ ଦ୍ୱାରା ସଂଯୋଗ ଥିବା ରିମୋଟ୍‌ ନମ୍ବର୍‌କୁ ମଧ୍ୟ।"</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ସିଷ୍ଟମ୍‍ ଜରିଆରେ କଲ୍‌ର ମାର୍ଗ ବଦଳାଇପାରେ"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"କଲ୍‍ କରିବାର ଅନୁଭୂତି ବଢ଼ାଇବାକୁ ସିଷ୍ଟମ୍‍ ଜରିଆରେ ଆପର କଲ୍‍ଗୁଡ଼ିକୁ ରୁଟ୍‍ କରିବାକୁ ଏହାକୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ସିଷ୍ଟମ୍‍ ମାଧ୍ୟମରେ କଲ୍‍ଗୁଡ଼ିକୁ ଦେଖିଥାଏ ଏବଂ ନିୟନ୍ତ୍ରଣ କରିଥାଏ।"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"ଆପ୍‍କୁ ଡିଭାଇସ୍‍‍ରେ ଚାଲୁଥିବା କଲ୍‍ଗୁଡ଼ିକୁ ଦେଖିବାକୁ ଏବଂ ନିୟନ୍ତ୍ରଣ କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ। ଏଥିରେ କଲ୍‍ଗୁଡ଼ିକ ପାଇଁ କଲ୍‍ ନମ୍ବର୍‍ ଏବଂ କଲ୍‍ଗୁଡ଼ିକର ସ୍ଥିତି ପରି ସୂଚନା ଅନ୍ତର୍ଭୁକ୍ତ ଅଛି।"</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"ଅନ୍ୟ ଏକ ଆପ୍‌ରୁ କଲ୍‌କୁ ଜାରି ରଖନ୍ତୁ"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"ଅନ୍ୟ ଆପ୍‌ରେ ଆରମ୍ଭ ହୋଇଥିବା ଗୋଟିଏ କଲ୍‌କୁ ଜାରି ରଖିବା ପାଇଁ ଆପ୍‌କୁ ଅନୁମତି ଦିଅନ୍ତୁ।"</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ଫୋନ୍‍ ନମ୍ବର ପଢ଼େ"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"ନିଅର୍‍ ଫିଲ୍ଡ କମ୍ୟୁନିକେସନ୍‍ନ (NFC) ଟାଗ୍‍, କାର୍ଡ ଓ ରିଡରଗୁଡ଼ିକ ସହ ଯୋଗାଯୋଗ କରିବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"ଆପଣଙ୍କ ସ୍କ୍ରୀନ୍‍ ଲକ୍‍ ଅକ୍ଷମ କରନ୍ତୁ"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"ଆପ୍‌କୁ କୀ\'ଲକ୍ କିମ୍ବା ସେଥିରେ ଥିବା କୌଣସି ପାସ୍‌ୱର୍ଡ ସୁରକ୍ଷାକୁ ଅକ୍ଷମ କରିବା ପାଇଁ ଅନୁମତି ଦିଏ, ଉଦାହରଣସ୍ୱରୂପ, ଇନ୍‌କମିଙ୍ଗ ଫୋନ୍‌ କଲ୍ ପ୍ରାପ୍ତ କରିବା ସମୟରେ ଫୋନ୍‌ଟି କୀ\'ଲକ୍‌କୁ ଅକ୍ଷମ କରିଦିଏ, ତା’ପରେ କଲ୍ ସମାପ୍ତ ହେବାପରେ ପୁଣି କୀ\'ଲକ୍‌କୁ ସକ୍ଷମ କରିଥାଏ।"</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"ସ୍କ୍ରିନ୍‌ ଲକ୍‌ର ଜଟିଳତା ପ୍ରାପ୍ତ କରନ୍ତୁ କିମ୍ବା ଅନୁରୋଧ କରନ୍ତୁ"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"ସ୍କ୍ରିନ୍‌ ଲକ୍‌ର ଜଟିଳତା ସ୍ତର (ଉଚ୍ଚ, ମଧ୍ୟମ, ନିମ୍ନ କିମ୍ବା କିଛିନୁହେଁ), ଜାଣିବାକୁ ଆପ୍‌କୁ ଅନୁମତି ଦିଅନ୍ତୁ, ଯାହା ସ୍କ୍ରିନ୍‌ ଲକ୍‌ର ସମ୍ଭାବ୍ୟ ପରିସୀମାର ଲମ୍ବ ଏବଂ ପ୍ରକାର ସୂଚୀତ କରେ। ଆପ୍‌ ଏହା ମଧ୍ୟ ଉପଯୋଗକର୍ତ୍ତାମାନଙ୍କୁ ପରାମର୍ଶ ଦେଇପାରେ ଯେ ସେମାନେ ସ୍କ୍ରିନ୍‌ ଲକ୍‌କୁ ଏକ ନିର୍ଦ୍ଧିଷ୍ଟ ସ୍ତର ପର୍ଯ୍ୟନ୍ତ ଅପ୍‌ଡେଟ୍‌ କରିପାରନ୍ତି, କିନ୍ତୁ ଉପଯୋଗକର୍ତ୍ତାମାନେ ନିଜ ଇଚ୍ଛାରେ ଏହାକୁ ଉପେକ୍ଷା ଏବଂ ନାଭିଗେଟ୍ କରିପାରିବେ। ଏହା ଧ୍ୟାନ ଦିଅନ୍ତୁ ଯେ ସ୍କ୍ରିନ୍‌ ଲକ୍‌ ସରଳ ଟେକ୍ସଟ୍‌ରେ ଷ୍ଟୋର୍‌ କରାଯାଇନଥାଏ ଯେପରି ଆପ୍‌ ସଠିକ୍‌ ପାସ୍‌‍ୱର୍ଡ ଜାଣିପାରିନଥାଏ।"</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"ବାୟୋମେଟ୍ରିକ୍‌ ହାର୍ଡୱେର୍‌ ବ୍ୟବହାର କରନ୍ତୁ"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"ସ୍ୱୀକୃତି ପାଇଁ ବାୟୋମେଟ୍ରିକ୍‌ ହାର୍ଡୱେର୍‌ ବ୍ୟବହାର କରିବାକୁ ଅନୁମତି ଦିଏ"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ହାର୍ଡୱେର୍‍ ପରିଚାଳନା କରନ୍ତୁ"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ଟାବଲେଟ୍ ଅପଡେଟ୍ ହେଉଛି…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"ଡିଭାଇସ୍ ଅପଡେଟ୍ ହେଉଛି…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"ଫୋନ୍ ଆରମ୍ଭ ହେଉଛି…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"ଆଣ୍ଡ୍ରଏଡ୍ ଆରମ୍ଭ ହେଉଛି…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"ଟାବଲେଟ୍ ଆରମ୍ଭ ହେଉଛି…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"ଡିଭାଇସ୍ ଆରମ୍ଭ ହେଉଛି…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"ଷ୍ଟୋରେଜ୍‍ ବଢ଼ାଯାଉଛି"</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"ସମସ୍ତ ନେଟ୍‌ୱର୍କ ଦେଖିବାକୁ ଟାପ୍‍ କରନ୍ତୁ"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"କନେକ୍ଟ କରନ୍ତୁ"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"ସମସ୍ତ ନେଟ୍‌ୱର୍କ"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g>ଙ୍କ ଦ୍ୱାରା ପ୍ରସ୍ତାବିତ ୱାଇ-ଫାଇ ନେଟ୍‌ୱର୍କ ସହ ସଂଯୁକ୍ତ ହୋଇଛି"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"ଆପଣ କ’ଣ ଚାହୁଁଛନ୍ତି <xliff:g id="NAME">%s</xliff:g>, ଆପଣଙ୍କ ପାଇଁ ନେଟ୍‌ୱର୍କର ପ୍ରସ୍ତାବ ଦିଅନ୍ତୁ?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"ହଁ"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"ନାହିଁ"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"ୱାଇ-ଫାଇ ସ୍ୱଚାଳିତ ଭାବେ ଅନ୍ ହେବ"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"ଆପଣ ଏକ ଉଚ୍ଚ-କ୍ୱାଲିଟୀ ବିଶିଷ୍ଟ ସେଭ୍‌ କରାଯାଇଥିବା ନେଟ୍‌ୱର୍କ ପାଖରେ ଥିବା ସମୟରେ"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"ପୁଣି ଅନ୍‍ କରନ୍ତୁ ନାହିଁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index e3fe527..eacf006 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -318,7 +318,7 @@
     <string name="permgroupdesc_visual" msgid="3415827902566663546">"ਤੁਹਾਡੀਆਂ ਫ਼ੋਟੋਆਂ ਅਤੇ ਵੀਡੀਓ ਤੱਕ ਪਹੁੰਚ"</string>
     <string name="permgrouprequest_visual" msgid="6907523945030290376">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਤੁਹਾਡੀਆਂ ਫ਼ੋਟੋਆਂ ਅਤੇ ਵੀਡੀਓ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੇਣੀ ਹੈ?"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"ਵਿੰਡੋ ਸਮੱਗਰੀ ਮੁੜ ਪ੍ਰਾਪਤ ਕਰਨਾ"</string>
-    <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"ਇੱਕ ਵਿੰਡੋ ਦੀ ਸਮੱਗਰੀ ਦੀ ਜਾਂਚ ਕਰੋ, ਜਿਸ ਨਾਲ ਤੁਸੀਂ ਅੰਤਰਕਿਰਿਆ ਕਰ ਰਹੇ ਹੋ"</string>
+    <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"ਉਸ ਵਿੰਡੋ ਸਮੱਗਰੀ ਦੀ ਜਾਂਚ ਕਰੋ, ਜਿਸ ਨਾਲ ਤੁਸੀਂ ਅੰਤਰਕਿਰਿਆ ਕਰ ਰਹੇ ਹੋ"</string>
     <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"\'ਸਪੱਰਸ਼ ਰਾਹੀਂ ਪੜਚੋਲ ਕਰੋ\' ਚਾਲੂ ਕਰਨਾ"</string>
     <string name="capability_desc_canRequestTouchExploration" msgid="7543249041581408313">"ਟੈਪ ਕੀਤੀਆਂ ਆਈਟਮਾਂ ਨੂੰ ਉੱਚੀ ਆਵਾਜ਼ ਵਿੱਚ ਬੋਲਿਆ ਜਾਵੇਗਾ ਅਤੇ ਸਕ੍ਰੀਨ ਦੀ ਸੰਕੇਤਾਂ ਦੀ ਵਰਤੋਂ ਨਾਲ ਪੜਚੋਲ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ।"</string>
     <string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਟਾਈਪ ਕੀਤੀ ਲਿਖਤ ਦਾ ਨਿਰੀਖਣ ਕਰਨਾ"</string>
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"ਐਪ ਨੂੰ ਡੀਵਾਈਸ ਦੀਆਂ ਫ਼ੋਨ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਤੱਕ ਪਹੁੰਚ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਇਜਾਜ਼ਤ ਐਪ ਨੂੰ ਫ਼ੋਨ ਨੰਬਰ ਅਤੇ ਡੀਵਾਈਸ ਆਈ.ਡੀ. ਨਿਰਧਾਰਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦੀ ਹੈ, ਇੱਕ ਕਾਲ ਕਿਰਿਆਸ਼ੀਲ ਹੈ ਜਾਂ ਨਹੀਂ ਅਤੇ ਰਿਮੋਟ ਨੰਬਰ ਇੱਕ ਕਾਲ ਨਾਲ ਕਨੈਕਟ ਹੈ ਜਾਂ ਨਹੀਂ।"</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ਸਿਸਟਮ ਰਾਹੀਂ ਕਾਲਾਂ ਰੂਟ ਕਰੋ"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"ਕਾਲ ਕਰਨ ਦੇ ਅਨੁਭਵ ਨੂੰ ਬਿਹਤਰ ਬਣਾਉਣ ਲਈ ਐਪ ਨੂੰ ਇਸਦੀਆਂ ਕਾਲਾਂ ਨੂੰ ਸਿਸਟਮ ਰਾਹੀਂ ਰੂਟ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦੀ ਹੈ।"</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ਸਿਸਟਮ ਰਾਹੀਂ ਕਾਲਾਂ ਦੇਖੋ ਅਤੇ ਕੰਟਰੋਲ ਕਰੋ।"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"ਐਪ ਨੂੰ ਡੀਵਾਈਸ \'ਤੇ ਜਾਰੀ ਕਾਲਾਂ ਦੇਖਣ ਅਤੇ ਕੰਟਰੋਲ ਕਰਨ ਦਿਓ। ਇਸ ਵਿੱਚ ਕਾਲਾਂ ਦੇ ਨੰਬਰਾਂ ਅਤੇ ਉਹਨਾਂ ਦੀ ਸਥਿਤੀ ਬਾਰੇ ਜਾਣਕਾਰੀ ਸ਼ਾਮਲ ਹੈ।"</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"ਕਿਸੇ ਹੋਰ ਐਪ ਤੋਂ ਕਾਲ ਜਾਰੀ ਰੱਖੋ"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"ਐਪ ਨੂੰ ਉਹ ਕਾਲ ਜਾਰੀ ਰੱਖਣ ਦਿਓ ਜਿਸਨੂੰ ਹੋਰ ਐਪ ਤੋਂ ਚਾਲੂ ਕੀਤਾ ਗਿਆ ਸੀ।"</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ਫ਼ੋਨ ਨੰਬਰ ਪੜ੍ਹੋ"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"ਐਪ ਨੂੰ ਨਜ਼ਦੀਕੀ ਖੇਤਰ ਸੰਚਾਰ (NFC) ਟੈਗਾਂ, ਕਾਰਡਾਂ ਅਤੇ ਰੀਡਰਾਂ ਨਾਲ ਸੰਚਾਰ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"ਆਪਣਾ ਸਕ੍ਰੀਨ  ਲਾਕ  ਅਸਮਰੱਥ ਬਣਾਓ"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"ਐਪ ਨੂੰ ਕੀਲਾਕ ਅਤੇ ਕਿਸੇ ਵੀ ਸੰਬੰਧਿਤ ਪਾਸਵਰਡ ਸੁਰੱਖਿਆ ਨੂੰ ਬੰਦ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਉਦਾਹਰਨ ਲਈ, ਫ਼ੋਨ ਇੱਕ ਇਨਕਮਿੰਗ ਫ਼ੋਨ ਕਾਲ ਪ੍ਰਾਪਤ ਕਰਨ ਵੇਲੇ ਬੰਦ ਕਰਦਾ ਹੈ, ਫਿਰ ਜਦੋਂ ਕਾਲ ਖਤਮ ਹੁੰਦੀ ਹੈ ਤਾਂ ਕੀਲਾਕ ਨੂੰ ਮੁੜ-ਚਾਲੂ ਕਰਦਾ ਹੈ।"</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਜਟਿਲਤਾ ਪ੍ਰਾਪਤ ਕਰੋ ਅਤੇ ਬੇਨਤੀ ਕਰੋ"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"ਐਪ ਨੂੰ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਜਟਿਲਤਾ ਦੇ ਪੱਧਰ ਬਾਰੇ ਜਾਣਨ ਦਿਓ (ਉੱਚ, ਮੱਧਮ, ਘੱਟ ਜਾਂ ਕੋਈ ਨਹੀਂ), ਜੋ ਲੰਬਾਈ ਦੀ ਸੰਭਵ ਰੇਂਜ ਅਤੇ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਕਿਸਮ ਦਾ ਸੰਕੇਤ ਦਿੰਦਾ ਹੈ। ਐਪ ਵਰਤੋਂਕਾਰਾਂ ਨੂੰ ਇੱਕ ਖਾਸ ਪੱਧਰ ਤੱਕ ਸਕ੍ਰੀਨ ਲਾਕ ਅੱਪਡੇਟ ਕਰਨ ਲਈ ਸੁਝਾਅ ਵੀ ਦੇ ਸਕਦੀ ਹੈ ਪਰ ਵਰਤੋਂਕਾਰ ਇਸਨੂੰ ਅਣਡਿੱਠ ਕਰ ਸਕਦੇ ਹਨ। ਨੋਟ ਕਰੋ ਕਿ ਸਕ੍ਰੀਨ ਲਾਕ ਸਧਾਰਨ ਲਿਖਤ ਵਿੱਚ ਸਟੋਰ ਨਹੀਂ ਕੀਤਾ ਜਾਂਦਾ ਹੈ ਇਸ ਲਈ ਐਪ ਸਹੀ ਪਾਸਵਰਡ ਬਾਰੇ ਨਹੀਂ ਜਾਣਦੀ ਹੈ।"</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"ਬਾਇਓਮੈਟ੍ਰਿਕ ਹਾਰਡਵੇਅਰ ਵਰਤੋ"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"ਐਪ ਨੂੰ ਪ੍ਰਮਾਣੀਕਰਨ ਲਈ ਬਾਇਓਮੈਟ੍ਰਿਕ ਹਾਰਡਵੇਅਰ ਵਰਤਣ ਦਿੰਦਾ ਹੈ"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਹਾਰਡਵੇਅਰ ਵਿਵਸਥਿਤ ਕਰੋ"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ਟੈਬਲੈੱਟ ਅੱਪਡੇਟ ਹੋ ਰਿਹਾ ਹੈ…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"ਡੀਵਾਈਸ ਅੱਪਡੇਟ ਹੋ ਰਿਹਾ ਹੈ…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"ਫ਼ੋਨ ਚਾਲੂ ਹੋ ਰਿਹਾ ਹੈ…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android ਚਾਲੂ ਕਰ ਰਿਹਾ ਹੈ…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"ਟੈਬਲੈੱਟ ਚਾਲੂ ਹੋ ਰਿਹਾ ਹੈ…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"ਡੀਵਾਈਸ ਚਾਲੂ ਹੋ ਰਿਹਾ ਹੈ…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"ਸਟੋਰੇਜ ਅਨੁਕੂਲ ਹੋ ਰਹੀ ਹੈ।"</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"ਸਾਰੇ ਨੈੱਟਵਰਕਾਂ ਨੂੰ ਦੇਖਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"ਕਨੈਕਟ ਕਰੋ"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"ਸਾਰੇ ਨੈੱਟਵਰਕ"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> ਵੱਲੋਂ ਸੁਝਾਏ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਹੈ"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"ਕੀ ਤੁਸੀਂ ਚਾਹੁੰਦੇ ਹੋ ਕਿ <xliff:g id="NAME">%s</xliff:g> ਤੁਹਾਨੂੰ ਨੈੱਟਵਰਕ ਸੁਝਾਏ?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"ਹਾਂ"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"ਨਹੀਂ"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"ਵਾਈ‑ਫਾਈ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਚੱਲ ਪਵੇਗਾ"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"ਜਦੋਂ ਤੁਸੀਂ ਕਿਸੇ ਰੱਖਿਅਤ ਕੀਤੇ ਉੱਚ-ਗੁਣਵੱਤਾ ਵਾਲੇ ਨੈੱਟਵਰਕ ਦੇ ਨੇੜੇ ਹੋਵੋ"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"ਵਾਪਸ ਚਾਲੂ ਨਾ ਕਰੋ"</string>
@@ -1513,7 +1522,7 @@
     <string name="description_target_unlock_tablet" msgid="3833195335629795055">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਸਵਾਈਪ ਕਰੋ।"</string>
     <string name="action_bar_home_description" msgid="5293600496601490216">"ਹੋਮ \'ਤੇ ਜਾਓ"</string>
     <string name="action_bar_up_description" msgid="2237496562952152589">"ਉੱਪਰ ਜਾਓ"</string>
-    <string name="action_menu_overflow_description" msgid="2295659037509008453">"ਹੋਰ ਚੋਣਾਂ"</string>
+    <string name="action_menu_overflow_description" msgid="2295659037509008453">"ਹੋਰ ਵਿਕਲਪ"</string>
     <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
     <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
     <string name="storage_internal" msgid="3570990907910199483">"ਅੰਦਰੂਨੀ ਸਾਂਝੀ ਕੀਤੀ ਸਟੋਰੇਜ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 85303ee..b2bb7b0 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -453,6 +453,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Pozwala aplikacji na dostęp do funkcji telefonicznych urządzenia. Aplikacja z tym uprawnieniem może odczytać numer telefonu i identyfikator urządzenia, sprawdzić, czy połączenie jest aktywne, oraz poznać numer, z którym jest nawiązane połączenie."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"przekazywanie połączeń przez system"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Zezwala aplikacji na przekazywanie połączeń przez system, by poprawić ich jakość."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"przeglądanie i kontrolowanie połączeń w systemie."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Zezwala aplikacji na przeglądanie i kontrolowanie trwających połączeń na urządzeniu. Dotyczy to informacji takich jak numery, z którymi nawiązane jest połączenie, oraz stan połączeń."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"kontynuuj połączenie w innej aplikacji"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Zezwala na kontynuowanie przez aplikację połączenia rozpoczętego w innej aplikacji."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"odczytywanie numerów telefonów"</string>
@@ -513,6 +515,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Pozwala aplikacji na komunikowanie się z tagami, kartami i czytnikami NFC (Near Field Communication)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"wyłączanie blokady ekranu"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Pozwala aplikacji na wyłączenie blokady klawiatury i wszystkich związanych z tym haseł zabezpieczających. Na przykład telefon wyłącza blokadę klawiatury, gdy odbiera połączenie przychodzące, a następnie włącza ją ponownie po zakończeniu połączenia."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"uzyskiwanie informacji o stopniu trudności blokady ekranu i proszenie te informacje"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Zezwala aplikacji na poznanie stopnia złożoności blokady ekranu (wysoki, średni, niski lub brak), który wskazuje na możliwy zakres długości oraz typ blokady ekranu. Aplikacja może też zasugerować zaktualizowanie blokady ekranu pod kątem określonego stopnia trudności, ale użytkownik może to zignorować i zamknąć komunikat. Pamiętaj, że blokada ekranu nie jest zapisywana przy użyciu zwykłego tekstu, więc aplikacja nie pozna dokładnego hasła."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"używanie sprzętu biometrycznego"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Zezwala aplikacji na używanie sprzętu biometrycznego na potrzeby autoryzacji"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"zarządzanie czytnikiem linii papilarnych"</string>
@@ -1123,11 +1127,11 @@
     <string name="deleteText" msgid="6979668428458199034">"Usuń"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Sposób wprowadzania tekstu"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Działania na tekście"</string>
-    <string name="email" msgid="4560673117055050403">"E-mail"</string>
+    <string name="email" msgid="4560673117055050403">"Wyślij e-maila"</string>
     <string name="email_desc" msgid="3638665569546416795">"Wyślij e-maila na wybrany adres"</string>
     <string name="dial" msgid="1253998302767701559">"Zadzwoń"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Zadzwoń pod wybrany numer telefonu"</string>
-    <string name="map" msgid="5441053548030107189">"Mapa"</string>
+    <string name="map" msgid="5441053548030107189">"Otwórz mapę"</string>
     <string name="map_desc" msgid="1836995341943772348">"Pokaż wybrany adres na mapie"</string>
     <string name="browse" msgid="1245903488306147205">"Otwórz"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Otwórz wybrany adres URL"</string>
@@ -1139,7 +1143,7 @@
     <string name="view_calendar_desc" msgid="5828320291870344584">"Wyświetl wybraną datę w kalendarzu"</string>
     <string name="add_calendar_event" msgid="1953664627192056206">"Zaplanuj"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"Zaplanuj wydarzenie na wybraną godzinę"</string>
-    <string name="view_flight" msgid="7691640491425680214">"Utwór"</string>
+    <string name="view_flight" msgid="7691640491425680214">"Śledź"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"Śledź wybrany lot"</string>
     <string name="translate" msgid="9218619809342576858">"Tłumacz"</string>
     <string name="translate_desc" msgid="4502367770068777202">"Przetłumacz zaznaczony tekst"</string>
@@ -1221,6 +1225,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet się aktualizuje…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Urządzenie się aktualizuje…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Telefon się uruchamia…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android się uruchamia…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet się uruchamia…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Urządzenie się uruchamia…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optymalizacja pamięci."</string>
@@ -1283,6 +1288,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Kliknij, by zobaczyć wszystkie sieci"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Połącz"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Wszystkie sieci"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Połączono z siecią Wi‑Fi zaproponowaną przez aplikację <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Czy chcesz zezwolić aplikacji <xliff:g id="NAME">%s</xliff:g> na proponowanie sieci?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Tak"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nie"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi włączy się automatycznie"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Gdy znajdziesz się w pobliżu zapisanej sieci o mocnym sygnale"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Nie włączaj ponownie"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 1cbcf0f..b1a1d39 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite que o app acesse os recursos de telefonia do dispositivo. Esta permissão autoriza o app a determinar o número de telefone e IDs de dispositivo, quando uma chamada está ativa, e o número remoto conectado a uma chamada."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"encaminhar chamadas pelo sistema"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permite que o app encaminhe suas chamadas por meio do sistema para melhorar a experiência com chamadas."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ver e controlar chamadas pelo sistema."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Permite que o app veja e controle chamadas em andamento no dispositivo. Isso inclui informações como número e estado das chamadas."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"continuar uma chamada de outro app"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permite que o app continue uma chamada que foi iniciada em outro app."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ler números de telefone"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Permite que o app se comunique com leitores, cartões e etiqueta NFC (comunicação a curta distância)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"desativar o bloqueio de tela"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Permite que o app desative o bloqueio de teclas e qualquer segurança por senha associada. Por exemplo, o telefone desativa o bloqueio de telas ao receber uma chamada e o reativa quando a chamada é finalizada."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"receber e solicitar complexidade de bloqueio de tela"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Permite que o app saiba o nível de complexidade do bloqueio de tela (alto, médio, baixo ou nenhum), que indica o intervalo possível de comprimento e o tipo de bloqueio de tela. O app também pode sugerir a atualização do bloqueio de tela até um certo nível, mas os usuários podem ignorar a sugestão. O bloqueio de tela não é armazenado em texto simples, então o app não tem acesso à senha exata."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"Usar hardware de biometria"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Permite que o app use hardware de biometria para autenticação"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"gerenciar hardware de impressão digital"</string>
@@ -1083,7 +1087,7 @@
     <string name="deleteText" msgid="6979668428458199034">"Excluir"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Método de entrada"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Ações de texto"</string>
-    <string name="email" msgid="4560673117055050403">"E-mail"</string>
+    <string name="email" msgid="4560673117055050403">"Mandar e-mail"</string>
     <string name="email_desc" msgid="3638665569546416795">"Enviar e-mail para endereço selecionado"</string>
     <string name="dial" msgid="1253998302767701559">"Ligar"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Ligar para o número de telefone selecionado"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"O tablet está sendo atualizado…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"O dispositivo está sendo atualizado…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"O smartphone está iniciando…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"O Android está iniciando..."</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"O tablet está iniciando…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"O dispositivo está iniciando…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Otimizando o armazenamento."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Toque para ver todas as redes"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Conectar"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Todas as redes"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Conectado à rede Wi-Fi sugerida por <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Permitir que o app <xliff:g id="NAME">%s</xliff:g> faça sugestões de rede?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Sim"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Não"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"O Wi‑Fi será ativado automaticamente"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Quando você estiver perto de uma rede salva de alta qualidade"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Não ativar novamente"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 6ad4ec3..ec4cf8c 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite que a aplicação aceda às funcionalidades de telefone do dispositivo. Esta autorização permite que a aplicação determine o número de telefone e IDs do dispositivo, se alguma chamada está ativa e qual o número remoto ligado por uma chamada."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"encaminhar chamadas através do sistema"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permite que a aplicação encaminhe as respetivas chamadas através do sistema de modo a melhorar a experiência da chamada."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ver e controlar chamadas através do sistema."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Permite à aplicação ver e controlar as chamadas em curso no dispositivo. Isto inclui informações como números de telefone das chamadas e o estado das mesmas."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"continuar uma chamada a partir de outra aplicação"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permite à aplicação continuar uma chamada iniciada noutra aplicação."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ler os números de telefone"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Permite que a aplicação comunique com etiquetas, cartões e leitores Near Field Communication (NFC)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"desativar o bloqueio do ecrã"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Permite que a aplicação desative o bloqueio de teclas e qualquer segurança por palavra-passe associada. Por exemplo, o telemóvel desativa o bloqueio de teclas quando recebe uma chamada e reativa o bloqueio de teclas ao terminar a chamada."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"obter e solicitar a complexidade do bloqueio de ecrã"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Permite que a aplicação aprenda o nível de complexidade do bloqueio de ecrã (elevado, médio, baixo ou nenhum), que indica o intervalo de comprimento e o tipo de bloqueio de ecrã possíveis. A aplicação também pode sugerir aos utilizadores que atualizem o bloqueio de ecrã para um determinado nível, mas estes podem ignorar livremente a sugestão e continuar a navegação. Tenha em atenção que o bloqueio de ecrã não é armazenado em texto simples, pelo que a aplicação desconhece a palavra-passe exata."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"Utilizar hardware biométrico"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Permite que a aplicação utilize hardware biométrico para autenticação."</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"gerir o hardware de impressão digital"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"O tablet está a atualizar…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"O dispositivo está a atualizar…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"O telemóvel está a iniciar…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"O Android está a iniciar…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"O tablet está a iniciar…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"O dispositivo está a iniciar…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"A otimizar o armazenamento."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Toque para ver todas as redes"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Ligar"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Todas as redes"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Ligação a rede Wi‑Fi proposta por <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Pretende permitir que <xliff:g id="NAME">%s</xliff:g> lhe proponha redes?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Sim"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Não"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"O Wi‑Fi será ativado automaticamente"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Quando estiver próximo de uma rede de alta qualidade guardada."</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Não reativar"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 1cbcf0f..b1a1d39 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite que o app acesse os recursos de telefonia do dispositivo. Esta permissão autoriza o app a determinar o número de telefone e IDs de dispositivo, quando uma chamada está ativa, e o número remoto conectado a uma chamada."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"encaminhar chamadas pelo sistema"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permite que o app encaminhe suas chamadas por meio do sistema para melhorar a experiência com chamadas."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ver e controlar chamadas pelo sistema."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Permite que o app veja e controle chamadas em andamento no dispositivo. Isso inclui informações como número e estado das chamadas."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"continuar uma chamada de outro app"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permite que o app continue uma chamada que foi iniciada em outro app."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ler números de telefone"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Permite que o app se comunique com leitores, cartões e etiqueta NFC (comunicação a curta distância)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"desativar o bloqueio de tela"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Permite que o app desative o bloqueio de teclas e qualquer segurança por senha associada. Por exemplo, o telefone desativa o bloqueio de telas ao receber uma chamada e o reativa quando a chamada é finalizada."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"receber e solicitar complexidade de bloqueio de tela"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Permite que o app saiba o nível de complexidade do bloqueio de tela (alto, médio, baixo ou nenhum), que indica o intervalo possível de comprimento e o tipo de bloqueio de tela. O app também pode sugerir a atualização do bloqueio de tela até um certo nível, mas os usuários podem ignorar a sugestão. O bloqueio de tela não é armazenado em texto simples, então o app não tem acesso à senha exata."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"Usar hardware de biometria"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Permite que o app use hardware de biometria para autenticação"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"gerenciar hardware de impressão digital"</string>
@@ -1083,7 +1087,7 @@
     <string name="deleteText" msgid="6979668428458199034">"Excluir"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Método de entrada"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Ações de texto"</string>
-    <string name="email" msgid="4560673117055050403">"E-mail"</string>
+    <string name="email" msgid="4560673117055050403">"Mandar e-mail"</string>
     <string name="email_desc" msgid="3638665569546416795">"Enviar e-mail para endereço selecionado"</string>
     <string name="dial" msgid="1253998302767701559">"Ligar"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Ligar para o número de telefone selecionado"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"O tablet está sendo atualizado…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"O dispositivo está sendo atualizado…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"O smartphone está iniciando…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"O Android está iniciando..."</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"O tablet está iniciando…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"O dispositivo está iniciando…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Otimizando o armazenamento."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Toque para ver todas as redes"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Conectar"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Todas as redes"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Conectado à rede Wi-Fi sugerida por <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Permitir que o app <xliff:g id="NAME">%s</xliff:g> faça sugestões de rede?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Sim"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Não"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"O Wi‑Fi será ativado automaticamente"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Quando você estiver perto de uma rede salva de alta qualidade"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Não ativar novamente"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index dca0510..5b36b9a 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -450,6 +450,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite aplicației să acceseze funcțiile de telefon ale dispozitivului. Cu această permisiune aplicația stabilește numărul de telefon și ID-urile de dispozitiv, dacă un apel este activ, precum și numărul de la distanță conectat printr-un apel."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"să direcționeze apelurile prin intermediul sistemului"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permiteți aplicației să direcționeze apelurile prin intermediul sistemului pentru a îmbunătăți calitatea apelurilor."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"Vedeți și controlați apelurile prin intermediul sistemului."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Permite aplicației să vadă și să controleze apelurile în desfășurare pe dispozitiv. Aceasta include informații ca numerele pentru apeluri și starea apelurilor."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"să continue un apel dintr-o altă aplicație"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permite aplicației să continue un apel care a fost inițiat dintr-o altă aplicație."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"să citească numerele de telefon"</string>
@@ -510,6 +512,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Permite aplicației să comunice cu etichetele, cardurile și cititoarele NFC (Near Field Communication)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"dezactivează blocarea ecranului"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Permite aplicației să dezactiveze blocarea tastelor și orice modalitate asociată de securizare prin parolă. De exemplu, telefonul dezactivează blocarea tastelor când se primește un apel telefonic și reactivează blocarea tastelor la terminarea apelului."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"primește și solicită complexitatea blocării ecranului"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Permite aplicației să învețe nivelul de complexitate al blocării ecranului (ridicat, mediu, scăzut sau fără) ce indică intervalul posibil de lungime a parolei și tipul de blocare a ecranului. Aplicația le poate sugera utilizatorilor să își actualizeze blocarea ecranului la un anumit nivel, dar utilizatorii pot ignora sugestia și pot naviga în continuare. Rețineți că blocarea ecranului nu este stocată ca text simplu, astfel încât aplicația să nu cunoască parola exactă."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"utilizați hardware biometric"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Permite aplicației să folosească hardware biometric pentru autentificare"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"gestionează hardware-ul pentru amprentă"</string>
@@ -1104,21 +1108,21 @@
     <string name="inputMethod" msgid="1653630062304567879">"Metodă de intrare"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Acțiuni pentru text"</string>
     <string name="email" msgid="4560673117055050403">"E-mail"</string>
-    <string name="email_desc" msgid="3638665569546416795">"Trimiteți e-mail la adresa selectată"</string>
-    <string name="dial" msgid="1253998302767701559">"Apelați"</string>
-    <string name="dial_desc" msgid="6573723404985517250">"Apelați numărul de telefon selectat"</string>
+    <string name="email_desc" msgid="3638665569546416795">"Trimiteți un e-mail la adresa selectată"</string>
+    <string name="dial" msgid="1253998302767701559">"Sunați"</string>
+    <string name="dial_desc" msgid="6573723404985517250">"Sunați la numărul de telefon selectat"</string>
     <string name="map" msgid="5441053548030107189">"Hartă"</string>
     <string name="map_desc" msgid="1836995341943772348">"Localizați adresa selectată"</string>
     <string name="browse" msgid="1245903488306147205">"Deschideți"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Deschideți adresa URL selectată"</string>
-    <string name="sms" msgid="4560537514610063430">"Mesaj"</string>
+    <string name="sms" msgid="4560537514610063430">"Trimiteți mesaj"</string>
     <string name="sms_desc" msgid="7526588350969638809">"Trimiteți un mesaj la numărul de telefon selectat"</string>
     <string name="add_contact" msgid="7867066569670597203">"Adăugați"</string>
-    <string name="add_contact_desc" msgid="4830217847004590345">"Adăugați la persoanele din Agendă"</string>
+    <string name="add_contact_desc" msgid="4830217847004590345">"Adăugați în agendă"</string>
     <string name="view_calendar" msgid="979609872939597838">"Afișați"</string>
-    <string name="view_calendar_desc" msgid="5828320291870344584">"Afișați ora selectată în calendar"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"Program"</string>
-    <string name="add_calendar_event_desc" msgid="4326891793260687388">"Programați evenimentul pentru momentul selectat"</string>
+    <string name="view_calendar_desc" msgid="5828320291870344584">"Afișați data selectată în calendar"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"Programați"</string>
+    <string name="add_calendar_event_desc" msgid="4326891793260687388">"Programați evenimentul pentru data selectată"</string>
     <string name="view_flight" msgid="7691640491425680214">"Urmăriți"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"Urmăriți zborul selectat"</string>
     <string name="translate" msgid="9218619809342576858">"Traduceți"</string>
@@ -1201,6 +1205,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Se actualizează tableta…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Se actualizează dispozitivul.…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Pornește telefonul…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android pornește..."</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Pornește tableta…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Pornește dispozitivul…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Se optimizează stocarea."</string>
@@ -1261,6 +1266,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Atingeți pentru a vedea toate rețelele"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Conectați-vă"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Toate rețelele"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Conectat la rețeaua Wi-Fi propusă de <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Permiteți aplicației <xliff:g id="NAME">%s</xliff:g> să vă sugereze rețele?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Da"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nu"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi se va activa automat"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Când vă aflați lângă o rețea salvată, de înaltă calitate"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Nu reactivați"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 8278c28..ac7908c 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -453,6 +453,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Приложение получит доступ к функциям телефона на устройстве. Кроме того, оно сможет определять номера телефонов и серийные номера моделей, состояние активности вызова, а также удаленные номера, с которыми установлено соединение."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"перенаправлять звонки в системе"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Приложение сможет перенаправлять звонки в системе с целью улучшения качества связи."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"Управление вызовами через систему"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Приложение сможет управлять текущими вызовами на устройстве, а также получит доступ к сведениям о них, в том числе к номерам телефонов и статусам звонков."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"Продолжить вызов, начатый в другом приложении"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Разрешает приложению продолжить вызов, начатый в другом приложении."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"чтение номеров телефонов"</string>
@@ -513,6 +515,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Приложение сможет обмениваться данными с NFC-метками, картами и устройствами считывания, используя NFC."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"Отключение функции блокировки экрана"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Приложение сможет отключать блокировку экрана и другие функции защиты. Например, блокировка экрана будет отключаться при получении входящего вызова и включаться после завершения разговора."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"Доступ к информации об уровне сложности блокировки экрана и предоставление рекомендаций по его повышению"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Приложение получит доступ к сведениям об уровне сложности блокировки экрана (высокий, средний, низкий или не задан), в том числе о типе блокировки и длине пароля. Кроме того, оно сможет предлагать пользователям повысить уровень сложности блокировки. Эти рекомендации необязательны. Обратите внимание, что пароль не хранится в виде открытого текста и недоступен приложению."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"Использование биометрического оборудования"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Приложение сможет использовать биометрическое оборудование для аутентификации"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"управление сканером отпечатков"</string>
@@ -1123,7 +1127,7 @@
     <string name="deleteText" msgid="6979668428458199034">"Удалить"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Способ ввода"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Операции с текстом"</string>
-    <string name="email" msgid="4560673117055050403">"Письмо"</string>
+    <string name="email" msgid="4560673117055050403">"Написать письмо"</string>
     <string name="email_desc" msgid="3638665569546416795">"Отправить письмо выбранному адресату"</string>
     <string name="dial" msgid="1253998302767701559">"Позвонить"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Позвонить по выбранному номеру"</string>
@@ -1137,7 +1141,7 @@
     <string name="add_contact_desc" msgid="4830217847004590345">"Добавить в контакты"</string>
     <string name="view_calendar" msgid="979609872939597838">"Открыть"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"Посмотреть выбранный день в календаре"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"Расписание"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"Запланировать"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"Запланировать событие на выбранный день"</string>
     <string name="view_flight" msgid="7691640491425680214">"Отследить"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"Отслеживать выбранный рейс"</string>
@@ -1221,6 +1225,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Обновление планшета…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Обновление устройства…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Запуск телефона…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Запуск Android…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Запуск планшета…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Запуск устройства…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Оптимизация хранилища…"</string>
@@ -1283,6 +1288,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Нажмите, чтобы увидеть список сетей"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Подключиться"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Все сети"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Установлено подключение к сети Wi‑Fi, найденной приложением \'<xliff:g id="NAME">%s</xliff:g>\'"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Разрешить приложению \'<xliff:g id="NAME">%s</xliff:g>\' предлагать вам сети?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Да"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Нет"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi включится автоматически"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Когда вы будете в зоне действия сохраненной сети с хорошим сигналом."</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Не включать снова"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 7a43ecd..6486f3a 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"උපාංගයේ දුරකථන විශේෂාංග වෙත ප්‍රවේශයට යෙදුමට ඉඩ දෙයි.  ඇමතුම සක්‍රිය වුවත්, සහ ඇමතුමකින් දුරස්ථ අංකය සම්බන්ධ වුවත් දුරකථන අංකය සහ උපාංග ID හඳුනා ගැනීමට මෙම අවසරය යෙදුමට ඉඩ දෙයි."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"පද්ධතිය හරහා ඇමතුම් මාර්ගගත කරන්න"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"ඇමතුම් අත්දැකීම වැඩිදියුණු කිරීම සඳහා යෙදුමට පද්ධතිය හරහා එහි ඇමතුම් මාර්ගගත කිරීමට ඉඩ දෙයි."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"පද්ධතිය හරහා ඇමතුම් බලන්න සහ පාලනය කරන්න."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"යෙදුමට උපාංගයෙහි පවතින ඇමතුම් බැලීමට සහ පාලනය කිරීමට ඉඩ දෙයි මෙහි ඇමතුම් සඳහා ඇමතුම් අංක සහ ඇමතුම්වල තත්ත්වය වැනි තොරතුරු ඇතුළත් වේ."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"වෙනත් යෙදුමක් වෙතින් වන ඇමතුමක් දිගටම කරගෙන යන්න"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"වෙනත් යෙදුමක ආරම්භ කරන ලද ඇමතුමක් දිගටම කරගෙන යාමට යෙදුමට ඉඩ දෙයි."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"දුරකථන අංක කියවන්න"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"ආසන්න ක්ෂේත්‍ර සන්නිවේදන (NFC) ටැග්, පත්, සහ කියවන්නන් සමඟ සන්නිවේදනය කිරීමට යෙදුමට අවසර දෙන්න."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"ඔබගේ තිරයේ අගුල අබල කරන්න"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"යතුරු අගුල සහ ඕනෑම සම්බන්ධිත මුරපද ආරක්ෂාවක් අබල කිරීමට යෙදුමට අවසර දෙන්න. මෙහි උදාහරණයක් වන්නේ පැමිණෙන ඇමතුමක් ලැබෙද්දී, දුරකථනය අක්‍රිය වන අතර ඇමතුම අවසාන වන විට යතුරු අගුල නැවත සක්‍රිය වෙයි."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"තිර අගුලු සංකිර්ණය ලබා ගන්න සහ ඉල්ලන්න"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"තිර අගුලෙහි තිබිය හැකි දිගෙහි පරාසය හා වර්ගයේ පිළිබිඹු කරන, තිර අගුලු සංකීර්ණතා මට්ටම (ඉහළ, මධ්‍යම, අඩු හෝ රහිත) දැන ගැනීමට යෙදුමට ඉඩ දෙයි. තිරයේ අගුල නිශ්චිත මට්ටමක් වෙත යාවත්කාලීන කරන බවද මෙම යෙදුම පරිශීලකයින්ට යෝජනා කළ හැකිය. නමුත් පරිශීලකයන්ට නිදහසේ නොසලකා හැර සංචාලනය කළ හැකිය. තිර අලුල සරල පෙළ තුළ ගබඩා කර නැති බව සලකන්න එම නිසා යෙදුම නිවැරදි මුරපදය නොදනී."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"ජීවමිතික දෘඪාංග භාවිත කරන්න"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"සත්‍යාපනය සඳහා ජීවමිතික දෘඪාංග භාවිත කිරීමට යෙදුමට ඉඩ දෙයි"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"ඇඟිලි සලකුණු දෘඩාංග කළමනාකරණය කිරීම."</string>
@@ -1183,6 +1187,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ටැබ්ලට් පරිගණකය යාවත්කාලීන කරමින්..."</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"උපාංගය යාවත්කාලීන කරමින්..."</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"දුරකථනය ආරම්භ කරමින්…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android ආරම්භ කරමින්…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"ටැබ්ලට් පරිගණකය ආරම්භ කරමින්…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"උපාංගය ආරම්භ කරමින්…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"ආචයනය ප්‍රශස්තිකරණය කිරීම."</string>
@@ -1241,6 +1246,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"සියලු ජාල බැලීමට තට්ටු කරන්න"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"සම්බන්ධ කරන්න"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"සියලු ජාල"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> විසින් යෝජිත Wi-Fi ජාලයට සම්බන්ධ වුණි"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"<xliff:g id="NAME">%s</xliff:g>ඔබට ඔබ සඳහා ජාල යෝජනා කිරීමට අවශ්‍යද?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"ඔව්"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"නැත"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi ස්වයංක්‍රියව ක්‍රියාත්මක වනු ඇත"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"ඔබ උසස් තත්ත්වයේ සුරැකි ජාලයක් අවට සිටින විට"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"නැවත ක්‍රියාත්මක නොකරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 7e158c2..66d6ef6 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -453,6 +453,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Umožňuje aplikácii pristupovať k telefónnym funkciám zariadenia. Aplikácia s týmto povolením môže určiť telefónne číslo a ID zariadenia, či práve prebieha hovor, a vzdialené číslo, s ktorým je prostredníctvom hovoru nadviazané spojenie."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"presmerovanie hovorov cez systém"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Umožňuje aplikácii presmerovať hovory cez systém na účely zlepšenia kvality hovorov."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"zobrazenie a kontrola hovorov prostredníctvom systému."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Umožňuje aplikácii zobrazovať a kontrolovať prebiehajúce hovory v zariadení. Táto možnosť zahŕňa údaje, akými sú napríklad čísla jednotlivých hovorov alebo stav hovorov."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"pokračovať v hovore z inej aplikácie"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Umožňuje aplikácii pokračovať v hovore začatom v inej aplikácii."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"čítanie telefónnych čísel"</string>
@@ -513,6 +515,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Umožňuje aplikácii komunikovať so značkami, kartami a čítačkami s podporou technológie NFC."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"deaktivácia zámky obrazovky"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Umožňuje aplikácii zakázať uzamknutie klávesnice a akékoľvek súvisiace zabezpečenie heslom. Príkladom je zakázanie uzamknutia klávesnice pri prichádzajúcom telefonickom hovore a jeho opätovné povolenie po skončení hovoru."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"získavať a vyžadovať zložitosť zámky obrazovky"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Umožňuje aplikácii zapamätať si úroveň zložitosti zámky obrazovky (vysoká, stredná, nízka alebo žiadna), ktorá udáva pravdepodobný rozsah dĺžky a typu zámky obrazovky. Aplikácia tiež navrhuje používateľom aktualizáciu zámky obrazovky na určitú úroveň, používatelia sa však môžu na základe vlastného uváženia rozhodnúť tento návrh ignorovať a prejsť inam. Upozorňujeme, že zámka obrazovky nie je uložená vo forme obyčajného textu, takže aplikácia nepozná presné heslo."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"používať biometrický hardvér"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Umožňuje aplikácii používať na overenie totožnosti biometrický hardvér"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"spravovať hardvér na snímanie odtlačkov prstov"</string>
@@ -1123,15 +1127,15 @@
     <string name="deleteText" msgid="6979668428458199034">"Odstrániť"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Metóda vstupu"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Operácie s textom"</string>
-    <string name="email" msgid="4560673117055050403">"E-mail"</string>
+    <string name="email" msgid="4560673117055050403">"Poslať e-mail"</string>
     <string name="email_desc" msgid="3638665569546416795">"Napísať na vybratú e-mailovú adresu"</string>
     <string name="dial" msgid="1253998302767701559">"Volať"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Zavolať na vybraté telefónne číslo"</string>
-    <string name="map" msgid="5441053548030107189">"Mapa"</string>
+    <string name="map" msgid="5441053548030107189">"Otvoriť mapu"</string>
     <string name="map_desc" msgid="1836995341943772348">"Nájsť vybranú adresu"</string>
     <string name="browse" msgid="1245903488306147205">"Otvoriť"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Otvoriť vybratú webovú adresu"</string>
-    <string name="sms" msgid="4560537514610063430">"Správa"</string>
+    <string name="sms" msgid="4560537514610063430">"Poslať správu"</string>
     <string name="sms_desc" msgid="7526588350969638809">"Napísať SMS na vybraté telefónne číslo"</string>
     <string name="add_contact" msgid="7867066569670597203">"Pridať"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Pridať medzi kontakty"</string>
@@ -1221,6 +1225,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet sa aktualizuje…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Zariadenie sa aktualizuje…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Telefón sa spúšťa…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Systém Android sa spúšťa…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet sa spúšťa…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Zariadenie sa spúšťa…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimalizuje sa úložisko"</string>
@@ -1283,6 +1288,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Klepnutím zobrazíte všetky siete"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Pripojiť"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Všetky siete"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Pripojené k sieti Wi‑Fi navrhnutej používateľom <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Chcete umožniť používateľovi <xliff:g id="NAME">%s</xliff:g> navrhovať siete za vás?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Áno"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nie"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi sa zapne automaticky"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Keď budete v blízkosti kvalitnej uloženej siete"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Znova nezapínať"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 0790e90..dcaa4f5 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -453,6 +453,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Aplikaciji omogoča dostop do funkcij telefona v napravi. S tem dovoljenjem lahko aplikacija določi telefonsko številko in ID-je naprave, določi lahko tudi, ali je klic aktiven, in oddaljeno številko, s katero je klic povezan."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"Usmeri klice prek sistema"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Dovoli, da aplikacija usmeri klice prek sistema, da se tako izboljša izkušnja klicanja."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ogled in nadzor klicev prek sistema."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Aplikaciji dovoljuje, da si ogleda in nadzoruje aktivne klice v napravi. To vključuje informacije, kot so telefonske številke klicev in stanje klicev."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"Nadaljevanje klica iz druge aplikacije"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Aplikaciji dovoljuje nadaljevanje klica, ki se je začel v drugi aplikaciji."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"branje telefonskih številk"</string>
@@ -513,6 +515,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Podpira komunikacijo med računalnikom in oznakami, karticami in bralniki komunikacije s tehnologijo bližnjega polja."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"onemogočanje zaklepanja zaslona"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Aplikaciji dovoljuje, da onemogoči zaklep tipkovnice in morebitno povezano varnostno geslo. Telefon na primer onemogoči zaklep tipkovnice pri dohodnem klicu ter vnovič omogoči zaklep, ko je klic končan."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"pridobi in zahtevaj zapletenost zaklepanja zaslona"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Aplikaciji dovoljuje, da pridobi raven zapletenosti zaklepanja zaslona (visoka, srednja, nizka ali brez), ki označuje možen obseg dolžine in vrsto zaklepanja zaslona. Aplikacija lahko tudi predlaga uporabnikom, da posodobijo zaklepanje zaslona na določeno raven, vendar lahko uporabniki to opozorilo prezrejo in ga zaprejo tako, da se pomaknejo stran. Upoštevajte, da zaklepanje zaslona ni shranjeno v navadnem besedilu, tako da aplikacija ne pozna točnega gesla."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"uporaba strojne opreme za biometrične podatke"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Aplikaciji omogoča uporabo strojne opreme za biometrične podatke za preverjanje pristnosti"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"upravljanje strojne opreme za prstne odtise"</string>
@@ -1221,6 +1225,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablični računalnik se posodablja …"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Naprava se posodablja …"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Telefon se zaganja …"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android se zaganja …"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablični računalnik se zaganja …"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Naprava se zaganja …"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimiziranje shrambe."</string>
@@ -1283,6 +1288,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Dotaknite se, če si želite ogledati vsa omrežja"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Vzpostavi povezavo"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Vsa omrežja"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Vzpostavljena povezava z omrežjem Wi‑Fi na predlog aplikacije <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Ali želite aplikaciji <xliff:g id="NAME">%s</xliff:g> dovoliti predlaganje omrežij?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Da"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Ne"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Povezava Wi‑Fi se bo samodejno vklopila"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Ko ste v bližini zanesljivega shranjenega omrežja"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ne vklopi znova"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 5c001ed..413ff8a 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Lejon aplikacionin të hyjë në funksionet telefonike të pajisjes. Kjo leje i mundëson aplikacionit të përcaktojë numrin e telefonit dhe ID-të e pajisjes, nëse një telefonatë është aktive apo nëse numri në distancë është i lidhur me një telefonatë."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"kalon telefonatat përmes sistemit"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Lejon që aplikacioni të kalojë telefonatat përmes sistemit për të përmirësuar përvojën e telefonatës."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"Shiko dhe kontrollo telefonatat nëpërmjet sistemit."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Lejon që aplikacioni të shikojë dhe të kontrollojë telefonatat në vazhdim në pajisje. Kjo përfshin informacione si p.sh. numrat e telefonatave për telefonatat dhe gjendjen e telefonatave."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"vazhdo një telefonatë nga një aplikacion tjetër"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Lejon që aplikacioni të vazhdojë një telefonatë që është nisur në një aplikacion tjetër."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"lexo numrat e telefonit"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Lejon aplikacionin të komunikojë me etiketimet e \"Komunikimit të fushës së afërt (NFC)\", kartat dhe lexuesit."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"çaktivizo kyçjen e ekranit"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Lejon aplikacionin të çaktivizojë kyçjen e tasteve dhe çdo mbrojtje të lidhur me fjalëkalimin. Për shembull, telefoni çaktivizon kyçjen e tasteve kur merr një telefonatë hyrëse, pastaj riaktivizon kyçjen e tasteve kur mbaron telefonata."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"merr dhe kërko kompleksitetin e kyçjes së ekranit"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Lejo që aplikacioni të mësojë nivelin e kompleksitetit të kyçjes së ekranit (i lartë, i mesëm, i ulët ose asnjë), që tregon gamën e mundshme të gjatësisë dhe llojin e kyçjes së ekranit. Aplikacioni mund t\'u sugjerojë gjithashtu përdoruesve që të përditësojnë kyçjen e ekranit në një nivel të caktuar, por përdoruesit mund ta shpërfillin lirshëm dhe të navigojnë më tej. Ki parasysh se kyçja e ekranit nuk ruhet në tekst të thjeshtë, prandaj aplikacioni nuk e di fjalëkalimin e saktë."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"përdor harduerin biometrik"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"E lejon aplikacionin që të përdorë harduerin biometrik për vërtetimin"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"të menaxhojë harduerin e gjurmës së gishtit"</string>
@@ -1087,19 +1091,19 @@
     <string name="email_desc" msgid="3638665569546416795">"Dërgo email tek adresa e zgjedhur"</string>
     <string name="dial" msgid="1253998302767701559">"Telefono"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Telefono në numrin e zgjedhur të telefonit"</string>
-    <string name="map" msgid="5441053548030107189">"Harta"</string>
+    <string name="map" msgid="5441053548030107189">"Hap hartën"</string>
     <string name="map_desc" msgid="1836995341943772348">"Gjej adresën e zgjedhur"</string>
     <string name="browse" msgid="1245903488306147205">"Hap"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Hap URL-në e zgjedhur"</string>
-    <string name="sms" msgid="4560537514610063430">"Mesazh"</string>
+    <string name="sms" msgid="4560537514610063430">"Dërgo mesazh"</string>
     <string name="sms_desc" msgid="7526588350969638809">"Dërgo mesazh te numri i zgjedhur i telefonit"</string>
     <string name="add_contact" msgid="7867066569670597203">"Shto"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Shto te kontaktet"</string>
     <string name="view_calendar" msgid="979609872939597838">"Shiko"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"Shiko kohën e zgjedhur në kalendar"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"Orari"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"Planifiko"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"Planifiko ngjarjen për kohën e zgjedhur"</string>
-    <string name="view_flight" msgid="7691640491425680214">"Pjesë muzikore"</string>
+    <string name="view_flight" msgid="7691640491425680214">"Monitoro"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"Monitoro fluturimin e zgjedhur"</string>
     <string name="translate" msgid="9218619809342576858">"Përkthe"</string>
     <string name="translate_desc" msgid="4502367770068777202">"Përkthe tekstin e zgjedhur"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tableti po përditësohet…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Pajisja po përditësohet…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Telefoni po niset…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"\"Androidi\" po fillon…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tableti po niset…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Pajisja po niset…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Po përshtat ruajtjen."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Trokit për të parë të gjitha rrjetet"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Lidhu"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Të gjitha rrjetet"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Lidhur me rrjetin Wi‑Fi të propozuar nga <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Dëshiron të lejosh që <xliff:g id="NAME">%s</xliff:g> të propozojë rrjetet për ty?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Po"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Jo"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi do të aktivizohet automatikisht"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Kur ndodhesh pranë një rrjeti të ruajtur me cilësi të lartë"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Mos e aktivizo përsëri"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index b64f6fe..8f8fe0e 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -450,6 +450,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Дозвољава апликацији да приступа функцијама телефона на уређају. Ова дозвола омогућава апликацији да утврди број телефона и ИД-ове уређаја, затим да ли је позив активан, као и број даљинског уређаја са којим је успостављен позив."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"преусмеравање позива преко система"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Дозвољава апликацији да преусмерава позиве преко система да би побољшала доживљај позивања."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"преглед и контрола позива преко система."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Дозвољава апликацији да прегледа и контролише тренутне позиве на уређају. То обухвата информације попут бројева телефона и статуса позива."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"настави позив у другој апликацији"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Дозвољава апликацији да настави позив који је започет у другој апликацији."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"читање бројева телефона"</string>
@@ -510,6 +512,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Дозвољава апликацији да комуницира са ознакама, картицама и читачима комуникације кратког домета (NFC)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"онемогућавање закључавања екрана"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Дозвољава апликацији да онемогући закључавање тастатуре и све повезане безбедносне мере са лозинкама. На пример, телефон онемогућава закључавање тастатуре при пријему долазног телефонског позива, а затим га поново омогућава по завршетку позива."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"добијање и тражење нивоа сложености закључавања екрана"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Дозвољава апликацији да сазна ниво сложености закључавања екрана (висока, средња, ниска или ниједна), што указује на могући опсег трајања и тип закључавања екрана. Апликација може и да предлаже корисницима да ажурирају закључавање екрана на одређени ниво, али корисници слободно могу да занемаре то и да иду на друге странице. Имајте на уму да се подаци за закључавање екрана не чувају као обичан текст, па апликација не зна тачну лозинку."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"користи биометријски хардвер"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Дозвољава апликацији да користи биометријски хардвер за потврду идентитета"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"управљај хардвером за отиске прстију"</string>
@@ -1107,7 +1111,7 @@
     <string name="email_desc" msgid="3638665569546416795">"Пошаљите имејл на изабрану адресу"</string>
     <string name="dial" msgid="1253998302767701559">"Позови"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Позовите изабрани број телефона"</string>
-    <string name="map" msgid="5441053548030107189">"Мапа"</string>
+    <string name="map" msgid="5441053548030107189">"Прикажи на мапи"</string>
     <string name="map_desc" msgid="1836995341943772348">"Пронађите изабрану адресу"</string>
     <string name="browse" msgid="1245903488306147205">"Отвори"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Отворите изабрани URL"</string>
@@ -1201,6 +1205,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Таблет се ажурира…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Уређај се ажурира…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Телефон се покреће…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android се покреће…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Таблет се покреће…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Уређај се покреће…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Меморија се оптимизује."</string>
@@ -1261,6 +1266,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Додирните да бисте видели све мреже"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Повежи"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Све мреже"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Повезани сте са Wi‑Fi мрежом коју предлаже <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Желите ли да дозволите да вам <xliff:g id="NAME">%s</xliff:g> предлаже мреже?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Да"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Не"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi ће се аутоматски укључити"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Када сте у близини сачуване мреже високог квалитета"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Не укључуј поново"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 18c66ed..cd5580d 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Tillåter att appen kommer åt enhetens telefonfunktioner. Med den här behörigheten tillåts appen att identifiera mobilens telefonnummer och enhets-ID, om ett samtal pågår och vilket nummer samtalet är kopplat till."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"dirigera samtal via systemet"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Appen tillåts att dirigera samtal via systemet för att förbättra samtalsupplevelsen."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"visa och styra samtal via systemet."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Tillåter att appen kan visa och styra pågående samtal på enheten. Detta omfattar information som telefonnummer för samtal och samtalens status."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"fortsätta ett samtal från en annan app"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Tillåter att appen fortsätter ett samtal som har startats i en annan app."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"läsa telefonnummer"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Tillåter att appen kommunicerar med etiketter, kort och läsare för närfältskommunikation (NFC)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"inaktivera skärmlåset"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Tillåter att appen inaktiverar tangentlåset och tillhörande lösenordsskydd. Ett exempel kan vara att tangentlåset inaktiveras vid inkommande samtal och aktiveras igen när samtalet är avslutat."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"hämta och begär komplexitet för låsskärmen"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Tillåter att appen får reda på komplexitetsnivån för skärmlåset (hög, mellan, låg eller ingen). Detta ger en indikation på skärmlåsets möjliga längd och typ. Appen kan även föreslå att skärmlåset uppdateras till en viss nivå, men användare kan ignorera förslaget och fortsätta navigera. Observera att skärmlåset inte lagras i vanlig text så appen får inte reda på det exakta lösenordet."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"använd biometrisk maskinvara"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Tillåter att appen använder biometrisk maskinvara vid autentisering"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"hantera maskinvara för fingeravtryck"</string>
@@ -1087,7 +1091,7 @@
     <string name="email_desc" msgid="3638665569546416795">"Skicka e-post till vald adress"</string>
     <string name="dial" msgid="1253998302767701559">"Ring"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Ring valt telefonnummer"</string>
-    <string name="map" msgid="5441053548030107189">"Karta"</string>
+    <string name="map" msgid="5441053548030107189">"Öppna karta"</string>
     <string name="map_desc" msgid="1836995341943772348">"Hitta den valda adressen"</string>
     <string name="browse" msgid="1245903488306147205">"Öppna"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Öppna vald webbadress"</string>
@@ -1097,9 +1101,9 @@
     <string name="add_contact_desc" msgid="4830217847004590345">"Lägg till i Kontakter"</string>
     <string name="view_calendar" msgid="979609872939597838">"Visa"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"Visa vald tid i kalendern"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"Schema"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"Schemalägg"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"Schemalägg händelse på den valda tiden"</string>
-    <string name="view_flight" msgid="7691640491425680214">"Spår"</string>
+    <string name="view_flight" msgid="7691640491425680214">"Spåra"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"Spåra valt flyg"</string>
     <string name="translate" msgid="9218619809342576858">"Översätt"</string>
     <string name="translate_desc" msgid="4502367770068777202">"Översätt markerad text"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Surfplattan uppdateras …"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Enheten uppdateras …"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Mobilen startar …"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android startar …"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Surfplattan startar …"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Enheten startar …"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Lagringsutrymmet optimeras."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tryck för att visa alla nätverk"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Anslut"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Alla nätverk"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Ansluten till Wi-Fi-nätverket som föreslogs i <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Vill du tillåta att <xliff:g id="NAME">%s</xliff:g> föreslår nätverk till dig?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ja"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nej"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi aktiveras automatiskt"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"När du är i närheten av ett sparat nätverk av hög kvalitet"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Återaktivera inte"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 51ac513..bc3e4ad 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Huruhusu programu kufikia vipengele vya simu vilivyo kwenye kifaa. Idhini hii inaruhusu programu kutambua nambari ya simu na kifaa, kama kuna simu inayopigwa, na nambari ya mbali iliyounganishwa kwenye simu."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"elekeza simu kupitia mfumo"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Huruhusu programu kuelekeza simu zake kupitia mfumo ili kuboresha hali ya kupiga simu."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"kuona na kudhibiti simu kupitia mfumo."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Inaruhusu programu ione na kudhibiti simu zinazoendelea kupigwa kwenye kifaa. Hii inajumuisha maelezo kama vile nambari za simu zinazopigwa na hali ya simu."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"endelea na simu kutoka programu nyingine"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Huruhusu programu kuendelea na simu ambayo ilianzishwa katika programu nyingine."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"kusoma nambari za simu"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Inaruhusu programu kuwasiliana na lebo, kadi na wasomaji wa Near Field Communication (NFC)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"zima kufuli la skrini yako"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Inaruhusu programu kulemaza ufunguo wa vitufe na usalama mwingine ambata wa nenosiri. Kwa mfano, simu inalemaza ufunguo wa viitufe inapopokea simu inayoingia, kisha inawezesha upya ufunguo wa vitufe wakati simu inapokamilika."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"kupata na kuomba kiwango cha uchangamano wa kufunga skrini"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Huruhusu programu kupata maelezo kuhusu kiwango cha uchangamano wa kufunga skrini (juu, wastani, chini au hakuna), ambacho huashiria urefu unaowezekana na aina ya kufunga skrini. Programu pia inaweza kupendekeza kuwa watumiaji wasasishe kipengele cha kufunga skrini kiwe cha kiwango fulani lakini watumiaji wanaweza kupuuza kwa hiari na waende kwingine. Kumbuka kuwa maelezo ya kufunga skrini hayahifadhiwi kama maandishi, hivyo programu haitambui nenosiri mahususi."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"tumia maunzi ya kibiolojia"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Huruhusu programu itumie maunzi ya kibiolojia katika uthibitishaji"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"dhibiti maunzi ya kitambulisho"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Kompyuta kibao inasasishwa…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Kifaa kinasasishwa…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Simu inawaka…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Inaanzisha Android..."</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Kompyuta kibao inawaka…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Kifaa kiwaka…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Inaboresha hifadhi."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Gusa ili uone mitandao yote"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Unganisha"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Mitandao yote"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Imeunganishwa na mtandao wa Wi‑Fi uliopendekezwa na <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Je, ungependa kuruhusu <xliff:g id="NAME">%s</xliff:g> akupendekezee mitandao?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ndiyo"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Hapana"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi itawashwa kiotomatiki"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Unapokuwa karibu na mtandao uliohifadhiwa wenye ubora wa juu"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Usiwashe tena"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 1f2c6a6..22da7b0 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"சாதனத்தின் மொபைல் அம்சங்களை அணுகப் பயன்பாட்டை அனுமதிக்கிறது. மொபைல் மற்றும் சாதன ஐடிகள், அழைப்பு செயலில் உள்ளதா மற்றும் அழைப்பு மூலம் இணைக்கப்பட்ட தொலைக் கட்டுப்பாட்டு எண் ஆகியவற்றைத் தீர்மானிக்க இந்த அனுமதி பயன்பாட்டை அனுமதிக்கிறது."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"சிஸ்டம் மூலம் அழைப்புகளை ரூட் செய்தல்"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"அழைக்கும் அனுபவத்தை மேம்படுத்தும் பொருட்டு, சிஸ்டம் மூலம் தனது அழைப்புகளை ரூட் செய்ய பயன்பாட்டை அனுமதிக்கும்."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"சிஸ்டம் மூலமாக அழைப்புகளைப் பார்த்தலும் கட்டுப்படுத்துதலும்."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"செயலில் உள்ள அழைப்புகளைச் சாதனத்தில் பார்க்கவும் கட்டுப்படுத்தவும் ஆப்ஸை அனுமதிக்கிறது. அழைப்பு எண்கள் மற்றும் அழைப்புகளின் நிலை போன்ற தகவல்களும் இதில் அடங்கும்."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"மற்றொரு பயன்பாட்டிலிருந்து அழைப்பைத் தொடருதல்"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"மற்றொரு பயன்பாட்டில் தொடங்கப்பட்ட அழைப்பைத் தொடருவதற்கு, பயன்பாட்டை அனுமதிக்கிறது."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ஃபோன் எண்களைப் படித்தல்"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"குறுகிய இடைவெளி தகவல்பரிமாற்றம் (NFC), குறிகள், கார்டுகள் மற்றும் ரீடர்கள் ஆகியவற்றுடன் தொடர்புகொள்ள, பயன்பாட்டை அனுமதிக்கிறது."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"உங்கள் திரைப் பூட்டை முடக்குதல்"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"விசைப்பூட்டையும், தொடர்புடைய கடவுச்சொல் பாதுகாப்பையும் முடக்கப் பயன்பாட்டை அனுமதிக்கிறது. எடுத்துக்காட்டாக, உள்வரும் மொபைல் அழைப்பைப் பெறும்போது மொபைல் விசைப்பூட்டை முடக்குகிறது, பிறகு அழைப்பு முடிந்தவுடன் விசைப்பூட்டை மீண்டும் இயக்குகிறது."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"திரைப் பூட்டு தொடர்பான சிக்கலைத் தீர்க்க அனுமதி பெறுதல்"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"திரைப் பூட்டு தொடர்பான சிக்கலின் தன்மையைப் பற்றி (அதிகம், நடுத்தரம், குறைவு அல்லது ஏதுமில்லை) அறிந்துகொள்ள ஆப்ஸை அனுமதிக்கிறது. இதன் மூலம் திரைப் பூட்டின் திறனையும் வகையையும் பற்றி அறிந்துகொள்ள முடிகிறது. மேலும் திரைப் பூட்டு தொடர்பான சிக்கலின் தன்மையை குறிப்பிட்ட நிலைக்குப் புதுப்பித்துக்கொள்ளலாம் என்பதையும் ஆப்ஸ் பயனர்களுக்குப் பரிந்துரைக்கலாம். ஆனால் தங்கள் விருப்பப்படி அவற்றைப் பயனர்கள் ஏற்கவோ நிராகரிக்கவோ செய்யலாம். கவனத்திற்கு: திரைப் பூட்டு எளிய உரையிலான கடவுச்சொல்லால் சேமிக்கப்படுவதில்லை என்பதால் சரியான கடவுச்சொல்லை ஆப்ஸால் அறிய இயலாது."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"பயோமெட்ரிக் வன்பொருளைப் பயன்படுத்து"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"பயோமெட்ரிக் வன்பொருளைப் பயன்படுத்தி அங்கீகரிப்பதற்கு, பயன்பாட்டை அனுமதிக்கும்"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"கைரேகை வன்பொருளை நிர்வகி"</string>
@@ -1083,7 +1087,7 @@
     <string name="deleteText" msgid="6979668428458199034">"நீக்கு"</string>
     <string name="inputMethod" msgid="1653630062304567879">"உள்ளீட்டு முறை"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"உரை நடவடிக்கைகள்"</string>
-    <string name="email" msgid="4560673117055050403">"மின்னஞ்சல்"</string>
+    <string name="email" msgid="4560673117055050403">"மின்னஞ்சல் அனுப்பு"</string>
     <string name="email_desc" msgid="3638665569546416795">"தேர்ந்தெடுத்த முகவரிக்கு மின்னஞ்சல் அனுப்பும்"</string>
     <string name="dial" msgid="1253998302767701559">"அழை"</string>
     <string name="dial_desc" msgid="6573723404985517250">"தேர்ந்தெடுத்த ஃபோன் எண்ணை அழைக்கும்"</string>
@@ -1091,13 +1095,13 @@
     <string name="map_desc" msgid="1836995341943772348">"தேர்ந்தெடுத்த முகவரிக்கான வரைபடத்தைத் திறக்கும்"</string>
     <string name="browse" msgid="1245903488306147205">"திற"</string>
     <string name="browse_desc" msgid="8220976549618935044">"தேர்ந்தெடுத்த URLஐத் திறக்கும்"</string>
-    <string name="sms" msgid="4560537514610063430">"செய்தி"</string>
+    <string name="sms" msgid="4560537514610063430">"செய்தி அனுப்பு"</string>
     <string name="sms_desc" msgid="7526588350969638809">"தேர்ந்தெடுத்த ஃபோன் எண்ணிற்குச் செய்தி அனுப்பும்"</string>
     <string name="add_contact" msgid="7867066569670597203">"சேர்"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"தொடர்புகளில் சேர்க்கும்"</string>
     <string name="view_calendar" msgid="979609872939597838">"காண்பி"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"கேலெண்டரில் தேர்ந்தெடுத்த நேரத்தைக் காட்டும்"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"திட்டமிடுதல்"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"திட்டமிடு"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"தேர்ந்தெடுத்த நேரத்திற்கு நிகழ்வைத் திட்டமிடும்"</string>
     <string name="view_flight" msgid="7691640491425680214">"கண்கானி"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"தேர்ந்தெடுத்த விமானத்தைக் கண்காணிக்கும்"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"டேப்லெட் புதுப்பிக்கப்படுகிறது…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"சாதனம் புதுப்பிக்கப்படுகிறது…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"மொபைல் தொடங்குகிறது…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android துவங்குகிறது..."</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"டேப்லெட் தொடங்குகிறது…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"சாதனம் தொடங்குகிறது…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"சேமிப்பகத்தை உகந்ததாக்குகிறது."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"எல்லா நெட்வொர்க்குகளையும் பார்க்க, தட்டவும்"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"இணை"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"எல்லா நெட்வொர்க்குகளும்"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> இன் வைஃபை நெட்வொர்க்குடன் இணைக்கப்பட்டது"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"உங்களுக்கு நெட்வொர்க்குகளைக் காட்ட <xliff:g id="NAME">%s</xliff:g>ஐ அனுமதிக்கவா?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"சரி"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"வேண்டாம்"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"வைஃபை தானாக ஆன் ஆகும்"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"சேமித்த, உயர்தர நெட்வொர்க்கிற்கு அருகில் இருக்கும்போது"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"மீண்டும் ஆன் செய்யாதே"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index fd853fb..1cc0274 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"పరికరం యొక్క ఫోన్ ఫీచర్‌లను యాక్సెస్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఈ అనుమతి ఫోన్ నంబర్ మరియు పరికరం IDలను, కాల్ సక్రియంగా ఉందా లేదా అనే విషయాన్ని మరియు కాల్ ద్వారా కనెక్ట్ చేయబడిన రిమోట్ నంబర్‌ను కనుగొనడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"కాల్‌లను సిస్టమ్ ద్వారా వెళ్లేలా చేయి"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"కాలింగ్ అనుభవాన్ని మెరుగుపరచడం కోసం తన కాల్‌లను సిస్టమ్ ద్వారా వెళ్లేలా చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"సిస్టమ్ ద్వారా కాల్‌లను చూసి, నియంత్రించండి."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"పరికరంలో కొనసాగుతున్న కాల్‌లను చూడడానికి మరియు నియంత్రించడానికి యాప్‌ను అనుమతిస్తుంది. ఇందులో కాల్ కోసం కాల్‌ల నంబర్‌లు మరియు రాష్ట్ర కాల్ వంటి సమాచారం ఉంటుంది."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"మరో యాప్ నుండి కాల్‌ని కొనసాగించండి"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"మరో యాప్‌లో ప్రారంభించిన కాల్‌ని కొనసాగించడానికి యాప్‌ని అనుమతిస్తుంది."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ఫోన్ నంబర్‌లను చదువు"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"సమీప ఫీల్డ్ కమ్యూనికేషన్ (NFC) ట్యాగ్‌లు, కార్డులు మరియు రీడర్‌లతో కమ్యూనికేట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"మీ స్క్రీన్ లాక్‌ను నిలిపివేయడం"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"కీలాక్ మరియు ఏదైనా అనుబంధించబడిన పాస్‌వర్డ్ భద్రతను నిలిపివేయడానికి యాప్‌ను అనుమతిస్తుంది. ఉదాహరణకు, ఇన్‌కమింగ్ ఫోన్ కాల్ వస్తున్నప్పుడు ఫోన్ కీలాక్‌ను నిలిపివేస్తుంది, ఆపై కాల్ ముగిసిన తర్వాత కీలాక్‌ను మళ్లీ ప్రారంభిస్తుంది."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"స్క్రీన్ లాక్ పాస్‌వర్డ్ బల స్థాయిని పొందడం మరియు అభ్యర్ధించడం"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"ఇది మీ స్క్రీన్ లాక్ పాస్‌వర్డ్‌ బల స్థాయి (తీవ్రంగా ఉండాలా లేదా ఓ మోస్తరుగా ఉండాలా లేదా తక్కువ తీవ్రంగా గానీ అసలు ఏదీ కాకుండా ఉండాలా) అనే విషయాన్ని గుర్తుంచుకోవడానికి యాప్‌కి అనుమతిస్తుంది, అలాగే పాస్‌‌వర్డ్ పొడుగు ఎంత ఉండాలీ, ఏ రకమైన స్క్రీన్ లాక్ పధ్ధతి అనుసరించాలో సూచిస్తుంది. యాప్ ఇంకా స్క్రీన్ లాక్‌ పాస్‌వర్డ్‌ బల స్థాయిని ఏ స్థాయిలో సెట్ చేసుకుంటే బాగుంటుందో వినియోగదారులకు తెలపగలదు కానీ వినియోగదారులు దాని సూచనలను పట్టించుకోకుండా వారి ఇష్టం మేరకు చక్కగా సెట్ చేసుకోవచ్చు. మీకో ముఖ్యమైన మాట స్క్రీన్ లాక్‌ పాస్‌వర్డ్‌ అనేది సాదా వచన రూపంలో నిల్వ చేయబడదు, కనుక అసలు మీరు పాస్‌వర్డ్‌ ఏం పెట్టారనే విషయం యాప్‌కి తెలియదు."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"బయోమెట్రిక్ హార్డ్‌వేర్‌ని ఉపయోగించు"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"ప్రమాణీకరణ కోసం బయోమెట్రిక్ హార్డ్‌వేర్‌ను ఉపయోగించడానికి యాప్‌ని అనుమతిస్తుంది"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"వేలిముద్ర హార్డ్‌వేర్‌ని నిర్వహించడానికి అనుమతి"</string>
@@ -1083,27 +1087,27 @@
     <string name="deleteText" msgid="6979668428458199034">"తొలగించు"</string>
     <string name="inputMethod" msgid="1653630062304567879">"ఇన్‌పుట్ పద్ధతి"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"వచనానికి సంబంధించిన చర్యలు"</string>
-    <string name="email" msgid="4560673117055050403">"ఇమెయిల్"</string>
+    <string name="email" msgid="4560673117055050403">"ఇమెయిల్ చేయి"</string>
     <string name="email_desc" msgid="3638665569546416795">"ఎంచుకున్న చిరునామాకు ఇమెయిల్‌ను పంపుతుంది"</string>
-    <string name="dial" msgid="1253998302767701559">"కాల్ చేయండి"</string>
+    <string name="dial" msgid="1253998302767701559">"కాల్ చేయి"</string>
     <string name="dial_desc" msgid="6573723404985517250">"ఎంచుకున్న ఫోన్ నంబర్‌కు కాల్ చేస్తుంది"</string>
-    <string name="map" msgid="5441053548030107189">"మ్యాప్"</string>
+    <string name="map" msgid="5441053548030107189">"మ్యాప్ తెరువు"</string>
     <string name="map_desc" msgid="1836995341943772348">"ఎంచుకున్న చిరునామాను గుర్తించు"</string>
-    <string name="browse" msgid="1245903488306147205">"తెరవండి"</string>
+    <string name="browse" msgid="1245903488306147205">"తెరువు"</string>
     <string name="browse_desc" msgid="8220976549618935044">"ఎంచుకున్న URLని తెరుస్తుంది"</string>
-    <string name="sms" msgid="4560537514610063430">"సందేశం"</string>
+    <string name="sms" msgid="4560537514610063430">"సందేశం పంపు"</string>
     <string name="sms_desc" msgid="7526588350969638809">"ఎంచుకున్న ఫోన్ నంబర్‌కి సందేశం పంపుతుంది"</string>
-    <string name="add_contact" msgid="7867066569670597203">"జోడించండి"</string>
+    <string name="add_contact" msgid="7867066569670597203">"జోడించు"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"పరిచయాలకు జోడిస్తుంది"</string>
-    <string name="view_calendar" msgid="979609872939597838">"చూడండి"</string>
+    <string name="view_calendar" msgid="979609872939597838">"చూడు"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"ఎంచుకున్న సమయాన్ని క్యాలెండర్‌లో వీక్షించండి"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"షెడ్యూల్"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"షెడ్యూల్ చేయి"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"ఎంచుకున్న సమయానికి ఈవెంట్‌ను షెడ్యూల్ చేస్తుంది"</string>
-    <string name="view_flight" msgid="7691640491425680214">"ట్రాక్ చేయండి"</string>
+    <string name="view_flight" msgid="7691640491425680214">"ట్రాక్ చేయి"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"ఎంచుకున్న విమానాన్ని ట్రాక్ చేస్తుంది"</string>
-    <string name="translate" msgid="9218619809342576858">"అనువదించండి"</string>
+    <string name="translate" msgid="9218619809342576858">"అనువదించు"</string>
     <string name="translate_desc" msgid="4502367770068777202">"ఎంచుకున్న వచనాన్ని అనువదించండి"</string>
-    <string name="define" msgid="7394820043869954211">"నిర్వచించండి"</string>
+    <string name="define" msgid="7394820043869954211">"నిర్వహించు"</string>
     <string name="define_desc" msgid="7910883642444919726">"ఎంచుకున్న వచనాన్ని నిర్వచించండి"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"నిల్వ ఖాళీ అయిపోతోంది"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"కొన్ని సిస్టమ్ కార్యాచరణలు పని చేయకపోవచ్చు"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"టాబ్లెట్ అప్‌డేట్‌ అవుతోంది…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"పరికరం అప్‌డేట్‌ అవుతోంది…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"ఫోన్ ప్రారంభమవుతోంది…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android ప్రారంభమవుతోంది…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"టాబ్లెట్ ప్రారంభమవుతోంది…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"పరికరం ప్రారంభమవుతోంది…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"నిల్వను అనుకూలపరుస్తోంది."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"అన్ని నెట్‌వర్క్‌లు చూడటానికి నొక్కండి"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"కనెక్ట్ చేయి"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"అన్ని నెట్‌వర్క్‌లు"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> సూచన మేరకు Wi‑Fi నెట్‌వర్క్‌కు కనెక్ట్ చేయబడింది"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"మీరు <xliff:g id="NAME">%s</xliff:g> మీకోసం సూచించే నెట్‌వర్క్‌లకు అనుమతిస్తారా?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"అవును"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"లేదు"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi స్వయంచాలకంగా ఆన్ అవుతుంది"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"మీరు అధిక నాణ్యత గల సేవ్ చేసిన నెట్‌వర్క్‌కు సమీపంగా ఉన్నప్పుడు"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"తిరిగి ఆన్ చేయవద్దు"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 9ce2930..2edf57c 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"อนุญาตให้แอปพลิเคชันเข้าถึงฟีเจอร์โทรศัพท์ของอุปกรณ์ การอนุญาตนี้ทำให้แอปพลิเคชันสามารถตรวจสอบหมายเลขโทรศัพท์และรหัสอุปกรณ์ ตรวจสอบว่ามีการโทรที่ทำงานอยู่หรือไม่ และตรวจสอบหมายเลขระยะไกลที่เชื่อมต่อด้วยการโทร"</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"กำหนดเส้นทางการโทรผ่านระบบ"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"อนุญาตให้แอปกำหนดเส้นทางการโทรของแอปผ่านระบบเพื่อปรับปรุงประสบการณ์ในการโทร"</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ดูและจัดการการติดต่อผ่านระบบ"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"อนุญาตให้แอปดูและจัดการการติดต่อในอุปกรณ์ ซึ่งรวมถึงข้อมูลอย่างเช่น หมายเลขที่ใช้ในการติดต่อและสถานะการติดต่อ"</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"ต่อสายจากแอปอื่น"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"อนุญาตให้แอปต่อสายที่เริ่มจากแอปอื่น"</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"อ่านหมายเลขโทรศัพท์"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"อนุญาตให้แอปพลิเคชันสื่อสารกับแท็ก Near Field Communication (NFC) การ์ด และโปรแกรมอ่าน"</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"ปิดใช้งานการล็อกหน้าจอของคุณ"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"อนุญาตให้แอปพลิเคชันปิดใช้งานการล็อกปุ่มกดและการรักษาความปลอดภัยด้วยรหัสผ่านใดๆ ที่เกี่ยวข้อง ตัวอย่างเช่น โทรศัพท์ปิดใช้งานการล็อกปุ่มกดเมื่อรับสายเรียกเข้า จากนั้นจึงเปิดใช้งานการล็อกปุ่มกดใหม่หลังจากวางสาย"</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"ใช้และขอดูความซับซ้อนของ \"ล็อกหน้าจอ\""</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"อนุญาตให้แอปเรียนรู้ระดับความซับซ้อนของการล็อกหน้าจอ (สูง ปานกลาง ต่ำ หรือไม่มี) ซึ่งแสดงให้เห็นช่วงความยาวและประเภทของการล็อกหน้าจอที่เป็นไปได้ นอกจากนี้แอปยังแนะนำให้ผู้ใช้อัปเดตการล็อกหน้าจอเป็นระดับหนึ่งๆ ได้ด้วย แต่ผู้ใช้จะปฏิเสธและไปยังส่วนต่างๆ ต่อได้ โปรดทราบว่าระบบไม่ได้จัดเก็บการล็อกหน้าจอไว้เป็นข้อความธรรมดา เพื่อให้แอปไม่รู้รหัสผ่าน"</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"ใช้ฮาร์ดแวร์ชีวมิติ"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"อนุญาตให้แอปใช้ฮาร์ดแวร์ชีวมิติเพื่อตรวจสอบสิทธิ์"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"จัดการฮาร์ดแวร์ลายนิ้วมือ"</string>
@@ -1097,14 +1101,14 @@
     <string name="add_contact_desc" msgid="4830217847004590345">"เพิ่มในรายชื่อติดต่อ"</string>
     <string name="view_calendar" msgid="979609872939597838">"ดู"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"ดูเวลาที่เลือกในปฏิทิน"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"กำหนดการ"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"ตั้งเวลา"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"ตั้งเวลากิจกรรมสำหรับเวลาที่เลือก"</string>
-    <string name="view_flight" msgid="7691640491425680214">"แทร็ก"</string>
+    <string name="view_flight" msgid="7691640491425680214">"ติดตาม"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"ติดตามเที่ยวบินที่เลือก"</string>
     <string name="translate" msgid="9218619809342576858">"แปล"</string>
     <string name="translate_desc" msgid="4502367770068777202">"แปลข้อความที่เลือก"</string>
     <string name="define" msgid="7394820043869954211">"หาความหมาย"</string>
-    <string name="define_desc" msgid="7910883642444919726">"ให้คำจำกัดความข้อความที่เลือก"</string>
+    <string name="define_desc" msgid="7910883642444919726">"หาความหมายข้อความที่เลือก"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"พื้นที่จัดเก็บเหลือน้อย"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"บางฟังก์ชันระบบอาจไม่ทำงาน"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"พื้นที่เก็บข้อมูลไม่เพียงพอสำหรับระบบ โปรดตรวจสอบว่าคุณมีพื้นที่ว่าง 250 MB แล้วรีสตาร์ท"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"แท็บเล็ตกำลังอัปเดต…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"อุปกรณ์กำลังอัปเดต…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"โทรศัพท์กำลังเริ่มต้น…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android กำลังเริ่มต้น…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"แท็บเล็ตกำลังเริ่มต้น…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"อุปกรณ์กำลังเริ่มต้น…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"กำลังเพิ่มประสิทธิภาพพื้นที่จัดเก็บข้อมูล"</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"แตะเพื่อดูเครือข่ายทั้งหมด"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"เชื่อมต่อ"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"เครือข่ายทั้งหมด"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"เชื่อมต่ออยู่กับเครือข่าย Wi‑Fi ที่ <xliff:g id="NAME">%s</xliff:g> แนะนำ"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"ต้องการให้ <xliff:g id="NAME">%s</xliff:g> แนะนำเครือข่ายให้ไหม"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"ใช่"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"ไม่"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi จะเปิดโดยอัตโนมัติ"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"เมื่อคุณอยู่ใกล้เครือข่ายคุณภาพสูงที่บันทึกไว้"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"ไม่ต้องเปิดอีกครั้ง"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 1193625..f7995f7 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Pinapayagan ang app na i-access ang mga tampok ng telepono ng device. Pinapayagan ng pahintulot na ito ang app na tukuyin ang numero ng telepono at  mga ID ng device, kung aktibo man ang isang tawag, at ang malayuang numerong ikinonekta ng isang tawag."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"iruta ang mga tawag sa pamamagitan ng system"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Pinapayagan ang app na iruta ang mga tawag nito sa pamamagitan ng system upang mapahusay ang karanasan sa pagtawag."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"tingnan at kontrolin ang mga tawag sa pamamagitan ng system."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Binibigyang-daan ang app na makita at makontrol ang mga kasalukuyang tawag sa device. Kabilang dito ang impormasyon gaya ng mga numero ng tawag para sa mga tawag at ang status ng mga tawag."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"ipagpatuloy ang isang tawag mula sa ibang app"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Pinapayagan ang app na ipagpatuloy ang isang tawag na sinimulan sa ibang app."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"basahin ang mga numero ng telepono"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Pinapayagan ang app na makipag-ugnay sa Near Field Communication (NFC) na mga tag, card, at reader."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"i-disable ang iyong screen lock"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Pinapayagan ang app na i-disable ang keylock at anumang nauugnay na seguridad sa password. Halimbawa, hindi pinapagana ng telepono ang keylock kapag nakakatanggap ng papasok na tawag sa telepono, pagkatapos ay muling pinapagana ang keylock kapag tapos na ang tawag."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"kunin at humiling ng pagiging kumplikado ng lock ng screen"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Pinapayagan ang app na malaman ang antas ng pagiging kumplikado ng lock ng screen (mataas, katamtaman, mababa, o wala), na nagsasaad ng posibleng hanay ng haba at uri ng lock ng screen. Maaari ding magmungkahi ang app sa mga user na i-update nila ang lock ng screen sa isang partikular na antas ngunit malaya ang mga user na balewalain ito at mag-navigate palayo. Tandaang hindi naka-store bilang plaintext ang lock ng screen kaya hindi alam ng app ang eksaktong password."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"gumamit ng biometric hardware"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Pinapayagan ang app na gumamit ng biometric hardware para sa pag-authenticate"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"pamahalaan ang hardware ng fingerprint"</string>
@@ -1085,13 +1089,13 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Pagkilos ng teksto"</string>
     <string name="email" msgid="4560673117055050403">"Mag-email"</string>
     <string name="email_desc" msgid="3638665569546416795">"Mag-email sa mga piniling address"</string>
-    <string name="dial" msgid="1253998302767701559">"Tawagan"</string>
+    <string name="dial" msgid="1253998302767701559">"Tumawag"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Tawagan ang piniling numero ng telepono"</string>
-    <string name="map" msgid="5441053548030107189">"Mapa"</string>
+    <string name="map" msgid="5441053548030107189">"Mag-mapa"</string>
     <string name="map_desc" msgid="1836995341943772348">"Hanapin ang piniling address"</string>
     <string name="browse" msgid="1245903488306147205">"Buksan"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Buksan ang piniling URL"</string>
-    <string name="sms" msgid="4560537514610063430">"Padalhan ng Mensahe"</string>
+    <string name="sms" msgid="4560537514610063430">"Magmensahe"</string>
     <string name="sms_desc" msgid="7526588350969638809">"Padalhan ng mensahe ang piniling numero ng telepono"</string>
     <string name="add_contact" msgid="7867066569670597203">"Magdagdag"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Idagdag sa mga contact"</string>
@@ -1101,8 +1105,8 @@
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"Mag-iskedyul ng event para sa piniling oras"</string>
     <string name="view_flight" msgid="7691640491425680214">"Subaybayan"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"I-track ang piniling flight"</string>
-    <string name="translate" msgid="9218619809342576858">"Isalin"</string>
-    <string name="translate_desc" msgid="4502367770068777202">"Isalin ang napiling text"</string>
+    <string name="translate" msgid="9218619809342576858">"I-translate"</string>
+    <string name="translate_desc" msgid="4502367770068777202">"I-translate ang piniling text"</string>
     <string name="define" msgid="7394820043869954211">"Ilarawan"</string>
     <string name="define_desc" msgid="7910883642444919726">"Ilarawan ang piniling text"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nauubusan na ang puwang ng storage"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Nag-a-update ang tablet…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Nag-a-update ang device…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Nagsisimula ang telepono…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Nagsisimula ang Android…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Nagsisimula ang tablet…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Nagsisimula ang device…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Ino-optimize ang storage."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"I-tap upang makita ang lahat ng network"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Kumonekta"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Lahat ng network"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Nakakonekta sa Wi‑Fi network na iminungkahi ng <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Gusto mo bang hayaan ang <xliff:g id="NAME">%s</xliff:g> na magmungkahi ng mga network para sa iyo?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Oo"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Hindi"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Awtomatikong mag-o-on ang Wi‑Fi"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Kapag malapit ka sa naka-save na network na mataas ang kalidad"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Huwag i-on muli"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 521da3f..761eba6 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Uygulamaya cihazdaki telefon özelliklerine erişme izni verir. Bu izin, uygulamanın telefon numarasını ve cihaz kimliğini, etkin bir çağrı olup olmadığını ve çağrıda bağlanılan karşı tarafın numarasını öğrenmesine olanak sağlar."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"çağrıları sistem üzerinden yönlendir"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Uygulamanın, çağrı deneyimini iyileştirmek için çağrılarını sistem üzerinden yönlendirmesine olanak tanır."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"aramaları sistemde görüp denetleme."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Uygulamanın cihazda devam eden aramaları görmesini ve denetlemesini sağlar. Bu bilgiler arasında aramaların yapıldığı numaralar ve aramaların durumu gibi bilgiler yer alır."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"başka bir uygulamadaki çağrıya devam etme"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Uygulamanın, başka bir uygulamada başlatılan çağrıya devam etmesine izin verir."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"telefon numaralarını oku"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Uygulamaya, Near Field Communication (NFC) etiketleri, kartlar ve okuyucular ile iletişim kurma izni verir."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"ekran kilidimi devre dışı bırak"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Uygulamaya, tuş kilidini ve ilişkili tüm şifreli güvenlik önlemlerini devre dışı bırakma izni verir. Örneğin, telefon, çağrı alındığında tuş kilidinin devre dışı bırakır ve sonra, görüşme bittiğinde kilidi yeniden etkinleştirir."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"ekran kilidi karmaşıklığını edinme ve isteme"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Uygulamanın, ekran kilidi karmaşıklık seviyesini (yüksek, orta, düşük veya yok) öğrenmesini sağlar. Ekran kilidi karmaşıklık seviyesi, ekran kilidinin olası uzunluk aralığını ve türünü gösterir. Uygulama, kullanıcılara ekran kilidini belirli bir seviyeye güncellemelerini de önerebilir, ancak kullanıcılar bunu istedikleri gibi yoksayabilir ve geçebilirler. Ekran kilidi şifrelenmemiş metin olarak saklanmadığı için uygulamanın şifreyi tam olarak bilmediğini unutmayın."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"biyometrik donanım kullan"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Uygulamanın kimlik doğrulama için biyometrik donanım kullanmasına izin verir"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"parmak izi donanımını yönetme"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet güncelleniyor…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Cihaz güncelleniyor…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Telefon başlatılıyor…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android başlatılıyor…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet başlatılıyor…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Cihaz başlatılıyor…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Depolama optimize ediliyor."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tüm ağları görmek için dokunun"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Bağlan"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Tüm ağlar"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> tarafından önerilen kablosuz ağa bağlanıldı"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"<xliff:g id="NAME">%s</xliff:g> uygulamasının sizin için ağ önerisinde bulunmasına izin vermek istiyor musunuz?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Evet"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Hayır"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Kablosuz özelliği otomatik olarak açılacak"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Daha önce kaydedilmiş yüksek kaliteli bir ağın yakınında olduğunuzda"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Tekrar açılmasın"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 2bac0c3..e7e99ae 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -453,6 +453,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Дозволяє програмі отримувати доступ до телефонних функцій пристрою. Такий дозвіл дає програмі змогу визначати номер телефону й ідентифікатори пристрою, активність виклику, а також віддалений номер, на який здійснюється виклик."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"маршрутизувати виклики через систему"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Дозволяє додатку маршрутизувати виклики через систему, щоб було зручніше телефонувати."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"Переглядати виклики через систему й керувати ними."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Додаток може переглядати поточні виклики на пристрої та керувати ними. Це стосується такої інформації, як номери та стан викликів."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"продовжувати виклик з іншого додатка"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Додаток може продовжувати виклик, початий в іншому додатку."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"переглядати номери телефону"</string>
@@ -513,6 +515,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Дозволяє програмі обмінюватися даними з тегами, картками та читачами екрана Near Field Communication (NFC)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"вимикати блокування екрана"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Дозволяє програмі вимикати блокування клавіатури та будь-який пов’язаний паролем захист. Наприклад: телефон вимикає блокування клавіатури під час отримання вхідного дзвінка, після закінчення якого блокування клавіатури відновлюється."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"отримувати надсилати запити щодо рівня складності пароля для блокування екрана"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Дає змогу додатку визначати рівень складності пароля для блокування екрана (високий, середній, низький або нульовий), що вказує на можливий діапазон його довжини й тип. Додаток також може пропонувати користувачам підвищити рівень складності пароля для блокування екрана, але цю пропозицію можна ігнорувати. Примітка: оскільки пароль зашифрований, додаток його не знає."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"використовувати біометричне апаратне забезпечення"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Додаток може використовувати біометричне апаратне забезпечення для автентифікації"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"керувати апаратним забезпеченням для цифрових відбитків"</string>
@@ -1123,15 +1127,15 @@
     <string name="deleteText" msgid="6979668428458199034">"Видалити"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Метод введення"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Дії з текстом"</string>
-    <string name="email" msgid="4560673117055050403">"Електронна пошта"</string>
+    <string name="email" msgid="4560673117055050403">"Написати лист"</string>
     <string name="email_desc" msgid="3638665569546416795">"Надіслати електронний лист на вибрану адресу"</string>
-    <string name="dial" msgid="1253998302767701559">"Зателефонувати"</string>
-    <string name="dial_desc" msgid="6573723404985517250">"Набрати вибраний номер телефону"</string>
-    <string name="map" msgid="5441053548030107189">"Карта"</string>
+    <string name="dial" msgid="1253998302767701559">"Телефонувати"</string>
+    <string name="dial_desc" msgid="6573723404985517250">"Телефонувати за вибраним номером"</string>
+    <string name="map" msgid="5441053548030107189">"Відкрити карту"</string>
     <string name="map_desc" msgid="1836995341943772348">"Знайти вибрану адресу"</string>
     <string name="browse" msgid="1245903488306147205">"Відкрити"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Відкрити вибрану URL-адресу"</string>
-    <string name="sms" msgid="4560537514610063430">"Повідомлення"</string>
+    <string name="sms" msgid="4560537514610063430">"Написати SMS"</string>
     <string name="sms_desc" msgid="7526588350969638809">"Надіслати повідомлення за вибраним номером телефону"</string>
     <string name="add_contact" msgid="7867066569670597203">"Додати"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Додати в контакти"</string>
@@ -1221,6 +1225,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Планшет оновлюється…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Пристрій оновлюється…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Телефон запускається…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Запуск ОС Android…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Планшет запускається…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Пристрій запускається…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Оптимізація пам’яті."</string>
@@ -1283,6 +1288,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Торкніться, щоб побачити всі мережі"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Під’єднатися"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Усі мережі"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Під’єднано до мережі Wi-Fi, яку запропонував додаток <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Дозволити додатку <xliff:g id="NAME">%s</xliff:g> пропонувати вам мережі?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Так"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Ні"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi вмикатиметься автоматично"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Коли ви поблизу збереженої мережі високої якості"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Не вмикати знову"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 2266221..e33f3f2 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"‏ایپ کو آلے کی فون والی خصوصیات تک رسائی حاصل کرنے کی اجازت دیتا ہے۔ یہ اجازت ایپ کو فون نمبر اور آلے کے IDs کا تعین کرنے، آیا کوئی کال فعال ہے، اور کال کے ذریعہ مربوط ریموٹ نمبر کا تعین کرنے دیتی ہے۔"</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"سسٹم کے ذریعہ کالز روٹ کریں"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"کالںگ کا تجربہ بہتر بنانے کے لیے سسٹم کے ذریعہ ایپ کو کالز روٹ کرنے کی اجازت دیتا ہے۔"</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"سسٹم کے ذریعے کالز دیکھیں اور کنٹرول کریں۔"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"ایپ کو آلہ پر جاری کالز دیکھنے اور کنٹرول کرنے کی اجازت دیں۔ اس میں کالز کیلئے کال نمبرز اور کالز کی حالت جیسی معلومات شامل ہیں۔"</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"دوسری ایپ کی کال جاری رکھیں"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"ایپ کو دوسری ایپ میں شروع کردہ کال کو جاری رکھنے کی اجازت ملتی ہے۔"</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"فون نمبرز پڑھیں"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"‏ایپ کو Near Field Communication (NFC)‎ ٹیگز، کارڈز اور ریڈرز کے ساتھ مواصلت کرنے کی اجازت دیٹا ہے۔"</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"اپنے اسکرین لاک کو غیر فعال کریں"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"ایپ کو کلیدی لاک اور کسی بھی متعلقہ پاس ورڈ سیکیورٹی کو غیر فعال کرنے کی اجازت دیتا ہے۔ مثلاً، کوئی آنے والی فون کال موصول ہونے کے وقت فون کلیدی لاک کو غیر فعال کرتا ہے، پھر کال پوری ہوجانے پر کلیدی لاک کو دوبارہ فعال کردیتا ہے۔"</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"موصول کریں اور اسکرین لاک کی پیچیدگی کے لیے درخواست کریں"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"اپپ کو اسکرین لاک کی پیچیدگی (مُشکل، درمیانی، آسان یا کوئی نہیں) کو جاننے کی اجازت دیتا ہے، جو لمبائی کی ممکنہ حد اور اسکرین لاک کے قسم کو بتاتا ہے۔ اپپ صارفین کو مشوره بھی دے سکتی ہے کہ وہ اسکرین لاک کو مخصوس لیول تک اپ ڈیٹ کرتے ہیں لیکن صارفین آزادانہ طور پر نظر انداز اور نیویگیٹ کر سکتے ہیں۔ یاد رکھیں کہ اسکرین لاک پلین ٹیکسٹ میں اسٹور کیا ہوا نہیں ہے اس لیے اپپ صحیح پاس ورڈ نہیں جانتی ہے۔"</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"بایومیٹرک ہارڈ ویئر استعمال کریں"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"ایپ کو توثیق کے لیے بایومیٹرک ہارڈ ویئر استعمال کرنے کی اجازت دیتا ہے"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"فنگر پرنٹ ہارڈ ویئر کا نظم کریں"</string>
@@ -1083,15 +1087,15 @@
     <string name="deleteText" msgid="6979668428458199034">"حذف کریں"</string>
     <string name="inputMethod" msgid="1653630062304567879">"اندراج کا طریقہ"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"متن کی کارروائیاں"</string>
-    <string name="email" msgid="4560673117055050403">"ای میل"</string>
+    <string name="email" msgid="4560673117055050403">"ای میل بھیجیں"</string>
     <string name="email_desc" msgid="3638665569546416795">"منتخب کردہ پتہ پر ای میل کریں"</string>
     <string name="dial" msgid="1253998302767701559">"کال کریں"</string>
     <string name="dial_desc" msgid="6573723404985517250">"منتخب کردہ فون نمبر پر کال کریں"</string>
-    <string name="map" msgid="5441053548030107189">"نقشہ"</string>
+    <string name="map" msgid="5441053548030107189">"نقشہ کی ایپ کھولیں"</string>
     <string name="map_desc" msgid="1836995341943772348">"منتخب کردہ پتہ تلاش کریں"</string>
     <string name="browse" msgid="1245903488306147205">"کھولیں"</string>
     <string name="browse_desc" msgid="8220976549618935044">"‏منتخب کردہ URL کھولیں"</string>
-    <string name="sms" msgid="4560537514610063430">"پیغام"</string>
+    <string name="sms" msgid="4560537514610063430">"پیغام بھیجیں"</string>
     <string name="sms_desc" msgid="7526588350969638809">"منتخب کردہ فون نمبر پر پیغام بھیجیں"</string>
     <string name="add_contact" msgid="7867066569670597203">"شامل کریں"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"رابطوں میں شامل کریں"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ٹیبلٹ اپ ڈیٹ ہو رہا ہے…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"آلہ اپ ڈیٹ ہو رہا ہے…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"فون شروع ہو رہا ہے…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"‏Android شروع ہو رہا ہے…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"ٹیبلیٹ شروع ہو رہا ہے…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"آلہ شروع ہو رہا ہے…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"اسٹوریج کو بہترین بنایا جا رہا ہے۔"</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"تمام نیٹ ورکس دیکھنے کیلئے تھپتھپائيں"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"منسلک کریں"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"سبھی نیٹ ورکس"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"‏<xliff:g id="NAME">%s</xliff:g> کے ذریعے تجویز کردہ Wi-Fi نیٹ ورک سے منسلک کر دیا گیا"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"کیا آپ <xliff:g id="NAME">%s</xliff:g> کو آپ کے لیے نیٹ ورکس تجویز کرنے کی اجازت دینا چاہتے ہیں؟"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"ہاں"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"نہیں"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"‏Wi‑Fi از خود آن ہو جائے گا"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"جب آپ اعلی معیار کے محفوظ کردہ نیٹ ورک کے قریب ہوں"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"دوبارہ آن نہ کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 204df4e..7742c3e 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Ilovaga qurilmangizdagi telefon xususiyatlariga kirishga ruxsat beradi. Bu ruxsat ilovaga telefon raqami va qurilma nomlari, qo‘ng‘iroq faol yoki faolsizligi va masofadagi raqam qo‘ng‘rioq orqali bog‘langanligini aniqlashga imkon beradi."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"chaqiruvlarni tizim orqali yo‘naltirish"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Ilova aloqa sifatini yaxshilash maqsadida chaqiruvlarni tizim orqali yo‘naltirishi mumkin."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"tizim orqali chaqiruvlarni tekshirish va boshqarish."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Ilovaga qurilmadagi amaldagi chaqiruvlarni tekshirish va boshqarish imkonini beradi. Shuningdek, chaqiruvlar holati va kiruvchi-chiquvchi chaqiruv raqamlari axborotiga ham ruxsat beradi."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"chaqiruvni boshqa ilovada davom ettirish"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Ilovaga boshqa ilovada boshlangan chaqiruvni davom ettirish imkon beradi"</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"telefon raqamlarini o‘qish"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Ilova qisqa masofali aloqa (NFC) texnologiyasi yordamida NFC yorliqlari, kartalar va o‘qish moslamalari bilan ma’lumot almashishi mumkin."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"ekran qulfini o‘chirib qo‘yish"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Ilovaga ekran qulfini va har qanday parol  yordamidagi xavfsizlik himoyalarini o‘chirishga ruxsat beradi. Masalan, kirish qo‘ng‘irog‘ida telefon ekran qulfini o‘chiradi va qo‘ng‘iroq tugashi bilan qulfni yoqadi."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"Ekran qulfining qiyinlik darajasini oshirish haqidagi axborotga ruxsat va tavsiyalar berish"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Ilova ekran qulfining qiyinlik darajasi (yuqori, oʻrta, past yoki hech qanday), jumladan, qulf turi va parol uzunligi haqida axborotga ruxsat oladi. Bundan tashqari, foydalanuvchilarga qulflash qiyinligi darajasini oshirish taklif etiladi. Bu tavsiyalar majburiy emas. Parollar ochiq matn sifatida saqlanmaydi va ilova uni ocha olmaydi."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"biometrik sensordan foydalanish"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Haqiqiylikni tekshirish uchun biometrik sensordan foydalanish imkonini beradi"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"barmoq izi skanerini boshqarish"</string>
@@ -1084,22 +1088,22 @@
     <string name="inputMethod" msgid="1653630062304567879">"Kiritish uslubi"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Matn yozish"</string>
     <string name="email" msgid="4560673117055050403">"E-pochta"</string>
-    <string name="email_desc" msgid="3638665569546416795">"Belgilangan manzilga xat yuborish"</string>
+    <string name="email_desc" msgid="3638665569546416795">"Belgilangan e-pochta manziliga xat yuborish"</string>
     <string name="dial" msgid="1253998302767701559">"Chaqiruv"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Belgilangan raqamga telefon qilish"</string>
     <string name="map" msgid="5441053548030107189">"Xarita"</string>
     <string name="map_desc" msgid="1836995341943772348">"Belgilangan manzilni xaritadan topish"</string>
     <string name="browse" msgid="1245903488306147205">"Ochish"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Belgilangan URL manzilini ochish"</string>
-    <string name="sms" msgid="4560537514610063430">"Xabar"</string>
+    <string name="sms" msgid="4560537514610063430">"SMS yozish"</string>
     <string name="sms_desc" msgid="7526588350969638809">"Belgilangan telefon raqamiga SMS yuborish"</string>
-    <string name="add_contact" msgid="7867066569670597203">"Qo‘shish"</string>
+    <string name="add_contact" msgid="7867066569670597203">"Saqlab olish"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Kontaktlarga saqlash"</string>
     <string name="view_calendar" msgid="979609872939597838">"Ochish"</string>
-    <string name="view_calendar_desc" msgid="5828320291870344584">"Taqvimda belgilangan vaqtni ko‘rish"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"Jadval"</string>
-    <string name="add_calendar_event_desc" msgid="4326891793260687388">"Belgilangan vaqt uchun tadbirni rejalashtirish"</string>
-    <string name="view_flight" msgid="7691640491425680214">"Trek"</string>
+    <string name="view_calendar_desc" msgid="5828320291870344584">"Belgilangan vaqtni taqvimda ochish"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"Rejalashtirish"</string>
+    <string name="add_calendar_event_desc" msgid="4326891793260687388">"Belgilangan vaqt uchun tadbir rejalashtirish"</string>
+    <string name="view_flight" msgid="7691640491425680214">"Kuzatish"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"Belgilangan parvozni kuzatish"</string>
     <string name="translate" msgid="9218619809342576858">"Tarjima qilish"</string>
     <string name="translate_desc" msgid="4502367770068777202">"Belgilangan matnni tarjima qilish"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Planshet yangilanmoqda…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Qurilma yangilanmoqda…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Telefon ishga tushirilmoqda…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android ishga tushmoqda…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Planshet ishga tushirilmoqda…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Qurilma ishga tushirilmoqda…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Xotira optimallashtirilmoqda."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Barcha tarmoqlarni ko‘rish uchun bosing"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Ulanish"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Barcha tarmoqlar"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> ilovasi topgan Wi-Fi tarmoqqa ulanildi"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"<xliff:g id="NAME">%s</xliff:g> ilovasi sizga tarmoq taklif etishi uchun unga ruxsat berilsinmi?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ha"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Yoʻq"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi avtomatik ravishda yoqiladi"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Saqlangan tarmoqlar ichidan signali yaxshisi hududida ekaningizda"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Qayta yoqilmasin"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 70f3161..86bb532 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Cho phép ứng dụng truy cập vào các tính năng điện thoại của thiết bị. Quyền này cho phép ứng dụng xác định số điện thoại và ID thiết bị, cho dù cuộc gọi có hiện hoạt hay không và số từ xa có được kết nối bằng một cuộc gọi hay không."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"định tuyến cuộc gọi thông qua hệ thống"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Cho phép ứng dụng định tuyến cuộc gọi thông qua hệ thống nhằm cải thiện trải nghiệm gọi điện."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"xem và kiểm soát cuộc gọi thông qua hệ thống."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Cho phép ứng dụng xem và kiểm soát cuộc gọi đến trên thiết bị. Ứng dụng sẽ xem và kiểm soát các thông tin như số điện thoại của các cuộc gọi và trạng thái cuộc gọi."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"tiếp tục cuộc gọi từ một ứng dụng khác"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Cho phép ứng dụng tiếp tục cuộc gọi được bắt đầu trong một ứng dụng khác."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"đọc số điện thoại"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Cho phép ứng dụng giao tiếp với thẻ Giao tiếp trường gần (NFC), thẻ và trình đọc."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"vô hiệu hóa khóa màn hình của bạn"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Cho phép ứng dụng tắt khóa phím và bất kỳ bảo mật mật khẩu được liên kết nào. Ví dụ: điện thoại tắt khóa phím khi nhận được cuộc gọi đến, sau đó bật lại khóa phím khi cuộc gọi kết thúc."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"nhận và yêu cầu độ phức tạp của khóa màn hình"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Cho phép ứng dụng nắm được mức độ phức tạp của khóa màn hình (cao, trung bình, thấp hoặc không có), cho biết khoảng độ dài và loại khóa màn hình có thể có. Ứng dụng cũng có thể đề xuất cho người dùng rằng họ nên cập nhật khóa màn hình lên một mức độ nhất định nhưng người dùng có thể tùy ý bỏ qua và chuyển sang phần khác. Xin lưu ý rằng khóa màn hình không được lưu trữ dưới dạng văn bản thuần túy, do đó, ứng dụng sẽ không biết mật khẩu chính xác."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"sử dụng phần cứng sinh trắc học"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Cho phép ứng dụng dùng phần cứng sinh trắc học để xác thực"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"quản lý phần cứng vân tay"</string>
@@ -1083,22 +1087,22 @@
     <string name="deleteText" msgid="6979668428458199034">"Xóa"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Phương thức nhập"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Tác vụ văn bản"</string>
-    <string name="email" msgid="4560673117055050403">"Email"</string>
+    <string name="email" msgid="4560673117055050403">"Gửi email"</string>
     <string name="email_desc" msgid="3638665569546416795">"Gửi email đến địa chỉ đã chọn"</string>
-    <string name="dial" msgid="1253998302767701559">"Gọi"</string>
+    <string name="dial" msgid="1253998302767701559">"Gọi điện"</string>
     <string name="dial_desc" msgid="6573723404985517250">"Gọi đến số điện thoại đã chọn"</string>
-    <string name="map" msgid="5441053548030107189">"Bản đồ"</string>
+    <string name="map" msgid="5441053548030107189">"Xem bản đồ"</string>
     <string name="map_desc" msgid="1836995341943772348">"Tìm địa chỉ đã chọn"</string>
     <string name="browse" msgid="1245903488306147205">"Mở"</string>
     <string name="browse_desc" msgid="8220976549618935044">"Mở URL đã chọn"</string>
-    <string name="sms" msgid="4560537514610063430">"Gửi tin nhắn"</string>
+    <string name="sms" msgid="4560537514610063430">"Nhắn tin"</string>
     <string name="sms_desc" msgid="7526588350969638809">"Nhắn tin đến số điện thoại đã chọn"</string>
     <string name="add_contact" msgid="7867066569670597203">"Thêm"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"Thêm vào danh bạ"</string>
     <string name="view_calendar" msgid="979609872939597838">"Xem"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"Xem thời gian đã chọn trong lịch"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"Lịch biểu"</string>
-    <string name="add_calendar_event_desc" msgid="4326891793260687388">"Lên lịch biểu sự kiện cho thời gian đã chọn"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"Lên lịch"</string>
+    <string name="add_calendar_event_desc" msgid="4326891793260687388">"Lên lịch sự kiện cho thời gian đã chọn"</string>
     <string name="view_flight" msgid="7691640491425680214">"Theo dõi"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"Theo dõi chuyến bay đã chọn"</string>
     <string name="translate" msgid="9218619809342576858">"Dịch"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Máy tính bảng đang cập nhật…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Thiết bị đang cập nhật…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Điện thoại đang khởi động…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android đang khởi động..."</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Máy tính bảng đang khởi động…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Thiết bị đang khởi động…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Tối ưu hóa lưu trữ."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Nhấn để xem tất cả các mạng"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Kết nối"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Tất cả các mạng"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Đã kết nối với mạng Wi‑Fi do <xliff:g id="NAME">%s</xliff:g> đề xuất"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Bạn có muốn cho phép <xliff:g id="NAME">%s</xliff:g> đề xuất mạng cho bạn không?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Có"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Không"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi sẽ tự động bật"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Khi bạn ở gần mạng đã lưu chất lượng cao"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Không bật lại"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index c38406f..dbb7834 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"允许该应用访问设备的电话功能。此权限可让该应用确定本机号码和设备 ID、是否正处于通话状态以及拨打的号码。"</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"通过系统转接来电"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"允许该应用通过系统转接来电,以改善通话体验。"</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"查看并控制通过系统拨打的电话。"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"允许应用查看并控制设备上正在进行的通话,其中包括通话号码和通话状态等信息。"</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"继续进行来自其他应用的通话"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"允许该应用继续进行在其他应用中发起的通话。"</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"读取电话号码"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"允许应用与近距离无线通信(NFC)标签、卡和读取器通信。"</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"停用屏幕锁定"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"允许该应用停用键锁以及任何关联的密码安全措施。例如,让手机在接听来电时停用键锁,在通话结束后重新启用键锁。"</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"获取和请求屏幕锁定复杂度"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"允许应用了解屏幕锁定复杂度(高、中、低或无),即屏幕锁定的可能长度范围和类型。应用还可以建议用户将屏幕锁定更新为特定复杂度,但用户可以随意选择忽略该建议并离开。请注意,系统不会以纯文字形式存储屏幕锁定选项的内容,因此应用不会知道确切密码。"</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"使用生物特征硬件"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"允许该应用使用生物特征硬件进行身份验证"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"管理指纹硬件"</string>
@@ -1083,11 +1087,11 @@
     <string name="deleteText" msgid="6979668428458199034">"删除"</string>
     <string name="inputMethod" msgid="1653630062304567879">"输入法"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"文字操作"</string>
-    <string name="email" msgid="4560673117055050403">"电子邮件"</string>
+    <string name="email" msgid="4560673117055050403">"发送电子邮件"</string>
     <string name="email_desc" msgid="3638665569546416795">"将电子邮件发送至所选地址"</string>
     <string name="dial" msgid="1253998302767701559">"拨打电话"</string>
     <string name="dial_desc" msgid="6573723404985517250">"拨打所选电话号码"</string>
-    <string name="map" msgid="5441053548030107189">"地图"</string>
+    <string name="map" msgid="5441053548030107189">"查看地图"</string>
     <string name="map_desc" msgid="1836995341943772348">"找到所选地址"</string>
     <string name="browse" msgid="1245903488306147205">"打开"</string>
     <string name="browse_desc" msgid="8220976549618935044">"打开所选网址"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"平板电脑正在更新…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"设备正在更新…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"手机正在启动…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android 正在启动…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"平板电脑正在启动…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"设备正在启动…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"正在优化存储空间。"</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"点按即可查看所有网络"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"连接"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"所有网络"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"已连接到<xliff:g id="NAME">%s</xliff:g>建议的 WLAN 网络"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"要让<xliff:g id="NAME">%s</xliff:g>为您建议网络吗?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"是"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"否"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"WLAN 将自动开启"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"当您位于已保存的高品质网络信号范围内时"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"不要重新开启"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index f59cfcb..6248db5 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"允許應用程式使用裝置的電話功能。這項權限允許應用程式確定手機號碼和裝置編號、是否正在通話中,以及所撥打的對方號碼。"</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"透過系統轉接來電"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"允許應用程式透過系統轉接來電,以改善通話體驗。"</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"透過系統查看和控制通話。"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"允許應用程式查看和控制裝置上正在進行的通話,當中包括通話號碼和通話狀態等資訊。"</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"繼續進行來自其他應用程式的通話"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"允許應用程式繼續進行在其他應用程式中開始的通話。"</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"讀取電話號碼"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"允許應用程式使用近距離無線通訊 (NFC) 標記、卡片及讀取程式進行通訊。"</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"停用螢幕上鎖"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"允許應用程式停用按鍵鎖定以及其他相關的密碼安全措施。例如:手機收到來電時停用按鍵鎖定,通話結束後重新啟用按鍵鎖定。"</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"獲取並要求螢幕鎖定複雜程度"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"允許應用程式瞭解螢幕鎖定的複雜程度 (高、中、低或無),這項資料說明了螢幕鎖定的可能長度範圍和類型。應用程式亦能建議使用者將螢幕鎖定更新至某個複雜程度,但使用者可以隨意忽略並離開。請注意,螢幕鎖定並非以純文字儲存,因此應用程式不知道確切的密碼。"</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"使用生物識別硬件"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"允許應用程式使用生物識別硬件驗證"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"管理指紋硬件"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"正在更新平板電腦…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"正在更新裝置…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"手機正在啟動…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android 正在啟動…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"平板電腦正在啟動…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"裝置正在啟動…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"正在優化儲存空間。"</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"輕按即可查看所有網絡"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"連線"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"所有網絡"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"已連接至「<xliff:g id="NAME">%s</xliff:g>」建議的 Wi‑Fi 網絡"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"您希望「<xliff:g id="NAME">%s</xliff:g>」為您建議網絡嗎?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"是"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"否"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi 將會自動開啟"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"當您位於已儲存的高品質網絡信號範圍內時"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"不要重新開啟"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 489dfea..74da200 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"允許應用程式使用裝置的電話功能。這項權限可讓應用程式判讀手機號碼和裝置 ID、是否正在通話中,以及所撥打的對方號碼。"</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"透過系統接通來電"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"允許應用程式透過系統接通來電,以改善通話品質。"</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"查看及控管透過系統撥打的電話。"</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"允許應用程式查看及控管在裝置上撥出的電話,包括撥打的電話號碼和通話狀態等資訊。"</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"繼續進行來自其他應用程式的通話"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"允許應用程式繼續進行在其他應用程式中發起的通話。"</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"讀取電話號碼"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"允許應用程式與近距離無線通訊 (NFC) 電子感應標籤、卡片及感應器進行通訊。"</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"停用螢幕鎖定"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"允許應用程式停用按鍵鎖定以及其他相關的密碼安全性功能。例如:手機收到來電時停用按鍵鎖定,通話結束後重新啟用按鍵鎖定。"</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"取得及要求螢幕鎖定複雜度"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"允許應用程式記住螢幕鎖定的複雜度 (高、中、低或無),即螢幕鎖定的可能長度範圍和類型。這樣一來,應用程式還能建議使用者將螢幕鎖定更新為特定複雜度,但使用者可選擇忽略建議並離開應用程式。請注意,系統不會以純文字格式儲存螢幕鎖定選項的內容,因此應用程式不會知道確切密碼。"</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"使用生物特徵硬體"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"允許應用程式使用生物特徵硬體進行驗證"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"管理指紋硬體"</string>
@@ -1083,28 +1087,28 @@
     <string name="deleteText" msgid="6979668428458199034">"刪除"</string>
     <string name="inputMethod" msgid="1653630062304567879">"輸入法"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"文字動作"</string>
-    <string name="email" msgid="4560673117055050403">"電子郵件"</string>
-    <string name="email_desc" msgid="3638665569546416795">"將電子郵件寄到所選地址"</string>
-    <string name="dial" msgid="1253998302767701559">"通話"</string>
-    <string name="dial_desc" msgid="6573723404985517250">"撥打所選電話號碼"</string>
-    <string name="map" msgid="5441053548030107189">"地圖"</string>
-    <string name="map_desc" msgid="1836995341943772348">"尋找所選地址"</string>
+    <string name="email" msgid="4560673117055050403">"發送電子郵件"</string>
+    <string name="email_desc" msgid="3638665569546416795">"將電子郵件寄到選取的地址"</string>
+    <string name="dial" msgid="1253998302767701559">"撥號通話"</string>
+    <string name="dial_desc" msgid="6573723404985517250">"撥打選取的電話號碼"</string>
+    <string name="map" msgid="5441053548030107189">"查看地圖"</string>
+    <string name="map_desc" msgid="1836995341943772348">"尋找選取的地址"</string>
     <string name="browse" msgid="1245903488306147205">"開啟"</string>
-    <string name="browse_desc" msgid="8220976549618935044">"開啟所選網址"</string>
-    <string name="sms" msgid="4560537514610063430">"訊息"</string>
-    <string name="sms_desc" msgid="7526588350969638809">"將訊息傳送到所選電話號碼"</string>
+    <string name="browse_desc" msgid="8220976549618935044">"開啟選取的網址"</string>
+    <string name="sms" msgid="4560537514610063430">"發送訊息"</string>
+    <string name="sms_desc" msgid="7526588350969638809">"將訊息傳送到選取的電話號碼"</string>
     <string name="add_contact" msgid="7867066569670597203">"新增"</string>
     <string name="add_contact_desc" msgid="4830217847004590345">"新增至聯絡人"</string>
     <string name="view_calendar" msgid="979609872939597838">"查看"</string>
-    <string name="view_calendar_desc" msgid="5828320291870344584">"在日曆中查看所選時間"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"時間表"</string>
-    <string name="add_calendar_event_desc" msgid="4326891793260687388">"將活動安排在所選時間"</string>
+    <string name="view_calendar_desc" msgid="5828320291870344584">"在日曆中查看選取的時間"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"加入日曆"</string>
+    <string name="add_calendar_event_desc" msgid="4326891793260687388">"將活動安排在選取的時間"</string>
     <string name="view_flight" msgid="7691640491425680214">"追蹤"</string>
-    <string name="view_flight_desc" msgid="3876322502674253506">"追蹤所選航班"</string>
+    <string name="view_flight_desc" msgid="3876322502674253506">"追蹤選取的航班"</string>
     <string name="translate" msgid="9218619809342576858">"翻譯"</string>
-    <string name="translate_desc" msgid="4502367770068777202">"翻譯所選文字"</string>
-    <string name="define" msgid="7394820043869954211">"定義"</string>
-    <string name="define_desc" msgid="7910883642444919726">"定義所選文字"</string>
+    <string name="translate_desc" msgid="4502367770068777202">"翻譯選取的文字"</string>
+    <string name="define" msgid="7394820043869954211">"查看定義"</string>
+    <string name="define_desc" msgid="7910883642444919726">"定義選取的文字"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"儲存空間即將用盡"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"部分系統功能可能無法運作"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"系統儲存空間不足。請確定你已釋出 250MB 的可用空間,然後重新啟動。"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"平板電腦正在更新…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"裝置正在更新…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"手機正在啟動…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android 正在啟動…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"平板電腦正在啟動…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"裝置正在啟動…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"正在對儲存空間進行最佳化處理。"</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"輕觸即可查看所有網路"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"連線"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"所有網路"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"已連上「<xliff:g id="NAME">%s</xliff:g>」建議使用的 Wi-Fi 網路"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"是否要允許「<xliff:g id="NAME">%s</xliff:g>」為你建議網路?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"是"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"否"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi 將自動開啟"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"當你位於已儲存的高品質網路範圍內時"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"不要重新開啟"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 18a3db4..93b2ae6 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -447,6 +447,8 @@
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Ivumela uhlelo lokusebenza ukufinyelela izici zefoni zedivayisi. Le mvume ivumela uhlelo lokusebenza ukucacisa inombolo yefoni nobunikazi bedivayisi, ukuthi noma ikholi iyasebenza, futhi nenombolo yesilawuli kude zixhunywe ngekholi."</string>
     <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"yanza imizila yamakholi ngesistimu"</string>
     <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Ivumela uhlelo lokusebenza ukwenza imizila yamakholi ngesistimu ukuze ithuthukise umuzwa wokushaya."</string>
+    <string name="permlab_callCompanionApp" msgid="3599252979411970473">"bona futhi ulawule amakholi ngesistimu."</string>
+    <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Ivumela uhlelo lokusebenza ukubona nokulawula amakholi aqhubekayo kudivayisi. Lokhu kubandakanya ulwazi olufana nezinombolo zamakholi nesimo samakholi."</string>
     <string name="permlab_acceptHandover" msgid="2661534649736022409">"qhuba ikholi kusukela kolunye uhlelo lokusebenza"</string>
     <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Ivumela uhlelo lokusebenza ukuze luqhube ikholi eqalwe kolunye uhlelo lokusebenza."</string>
     <string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"funda izinombolo zefoni"</string>
@@ -507,6 +509,8 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Ivuela uhlelo lokusebenza ukuthi ixhumane ne-Near Field Communication (NFC) amathegi, amakhadi kanye nezinhlelo zokufunda."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"khubaza ukukhiya kwakho iskrini"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Ivumela uhlelo lokusebenza ukukhubaza ukuvala ukhiye nanoma yikuphi ukuphepha kwephasiwedi okuhlobene. Isibonelo, ifoni ikhubaza ukuvala ukhiye lapho ithola ikholi yefoni engenayo, bese inike amandla kabusha ukuvala ukhiye lapho ikholi isiqedile."</string>
+    <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"thola futhi ucele ukukhiywa kwesikrini"</string>
+    <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Ivumela uhlelo lokusebenza ukufunda ileveli yokukhiywa kwesikrini (phezulu, phakathi, phansi noma lutho), okubonisa ibanga elinokwenzeka lobubanzi nohlobo lokukhiywa kwesikrini. Uhlelo lokusebenza futhi lingaphakamisa kubasebenzisi ukuthi babuyekeze ukukhiywa kwesikrini kuya kwenye ileveli kodwa abasebenzisi bangaziba kalula bese bazule. Qaphela ukuthi ukhiywa kwesikrini akulondoloziwe embalweni ocacile ukuze uhlelo lokusebenza lingazi iphasiwedi."</string>
     <string name="permlab_useBiometric" msgid="8837753668509919318">"sebenzisa izingxenyekazi zekhompyutha ze-biometric"</string>
     <string name="permdesc_useBiometric" msgid="8389855232721612926">"Ivumela uhlelo lokusebenza ukuthi lusebenzise izingxenyekazi zekhompyutha ze-biometric ukuze kuqinisekiswe"</string>
     <string name="permlab_manageFingerprint" msgid="5640858826254575638">"phatha izingxenyekazi zekhompyutha zezigxivizo zeminwe"</string>
@@ -1097,7 +1101,7 @@
     <string name="add_contact_desc" msgid="4830217847004590345">"Engeza koxhumana nabo"</string>
     <string name="view_calendar" msgid="979609872939597838">"Buka"</string>
     <string name="view_calendar_desc" msgid="5828320291870344584">"Buka isikhathi esikhethiwe kwikhalenda"</string>
-    <string name="add_calendar_event" msgid="1953664627192056206">"Ishejuli"</string>
+    <string name="add_calendar_event" msgid="1953664627192056206">"Shejula"</string>
     <string name="add_calendar_event_desc" msgid="4326891793260687388">"Shejula imicimbi yesikhathi esikhethiwe"</string>
     <string name="view_flight" msgid="7691640491425680214">"Landelela"</string>
     <string name="view_flight_desc" msgid="3876322502674253506">"Ithrekhi ikhethe indiza"</string>
@@ -1181,6 +1185,7 @@
     <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Ithebulethi iyabuyekeza…"</string>
     <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Idivayisi iyabuyekeza…"</string>
     <string name="android_start_title" product="default" msgid="4536778526365907780">"Ifoni iyaqala…"</string>
+    <string name="android_start_title" product="automotive" msgid="8418054686415318207">"I-Android iyaqala…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"Ithebulethi iyaqala…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"Idivayisi iyaqala…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Ikhulisa isitoreji."</string>
@@ -1239,6 +1244,10 @@
     <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Thepha ukuze ubone onke amanethiwekhi"</string>
     <string name="wifi_available_action_connect" msgid="2635699628459488788">"Xhuma"</string>
     <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Onke amanethiwekhi"</string>
+    <string name="wifi_suggestion_title" msgid="1524719325616630906">"Kuxhumeke kunethiwekhi ye-Wi‑Fi ephakanyiswe ngu-<xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="wifi_suggestion_content" msgid="4505530604935334589">"Ingabe ufuna ukuvumela i-<xliff:g id="NAME">%s</xliff:g> iphakamisa amanethiwkehi kuwe?"</string>
+    <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Yebo"</string>
+    <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Cha"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"I-Wi-Fi izovuleka ngokuzenzakalela"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Uma useduze kwenethiwekhi yekhwalithi ephezulu elondoloziwe"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ungaphindi uvule"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8dbea38..fa3a549 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3660,7 +3660,12 @@
              the settings for this service. This setting cannot be changed at runtime. -->
         <attr name="settingsActivity" />
         <!-- Attribute whether the accessibility service wants to be able to retrieve the
-             active window content. This setting cannot be changed at runtime. -->
+             active window content. This setting cannot be changed at runtime.
+             <p>
+             Required to allow setting the {@link android.accessibilityservice
+             #AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} flag.
+             </p>
+         -->
         <attr name="canRetrieveWindowContent" format="boolean" />
         <!-- Attribute whether the accessibility service wants to be able to request touch
              exploration mode in which touched items are spoken aloud and the UI can be
@@ -3755,6 +3760,8 @@
         <!-- Component name of an activity that allows the user to modify
              the settings for this service. -->
         <attr name="settingsActivity"/>
+        <!-- Secure Element which the AIDs should be routed to -->
+        <attr name="secureElementName"/>
     </declare-styleable>
 
     <!-- Specify one or more <code>aid-group</code> elements inside a
@@ -7632,103 +7639,143 @@
     <declare-styleable name="VoiceInteractionSession">
     </declare-styleable>
 
+    <!-- {@deprecated Copy this definition into your own application project.} -->
     <declare-styleable name="KeyboardView">
-        <!-- Default KeyboardView style. -->
+        <!-- Default KeyboardView style.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="keyboardViewStyle" format="reference" />
 
         <!-- Image for the key. This image needs to be a StateListDrawable, with the following
              possible states: normal, pressed, checkable, checkable+pressed, checkable+checked,
-             checkable+checked+pressed. -->
+             checkable+checked+pressed.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="keyBackground" format="reference" />
 
-        <!-- Size of the text for character keys. -->
+        <!-- Size of the text for character keys.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="keyTextSize" format="dimension" />
 
-        <!-- Size of the text for custom keys with some text and no icon. -->
+        <!-- Size of the text for custom keys with some text and no icon.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="labelTextSize" format="dimension" />
 
-        <!-- Color to use for the label in a key. -->
+        <!-- Color to use for the label in a key.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="keyTextColor" format="color" />
 
-        <!-- Layout resource for key press feedback.-->
+        <!-- Layout resource for key press feedback.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="keyPreviewLayout" format="reference" />
 
-        <!-- Vertical offset of the key press feedback from the key. -->
+        <!-- Vertical offset of the key press feedback from the key.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="keyPreviewOffset" format="dimension" />
 
-        <!-- Height of the key press feedback popup. -->
+        <!-- Height of the key press feedback popup.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="keyPreviewHeight" format="dimension" />
 
-        <!-- Amount to offset the touch Y coordinate by, for bias correction. -->
+        <!-- Amount to offset the touch Y coordinate by, for bias correction.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="verticalCorrection" format="dimension" />
 
-        <!-- Layout resource for popup keyboards. -->
+        <!-- Layout resource for popup keyboards.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="popupLayout" format="reference" />
 
+        <!-- {@deprecated Copy this definition into your own application project.} -->
         <attr name="shadowColor" />
+        <!-- {@deprecated Copy this definition into your own application project.} -->
         <attr name="shadowRadius" />
     </declare-styleable>
 
+    <!-- {@deprecated Copy this definition into your own application project.} -->
     <declare-styleable name="KeyboardViewPreviewState">
         <!-- State for {@link android.inputmethodservice.KeyboardView KeyboardView}
-                key preview background. -->
+                key preview background.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="state_long_pressable" format="boolean" />
     </declare-styleable>
 
+    <!-- {@deprecated Copy this definition into your own application project.} -->
     <declare-styleable name="Keyboard">
-        <!-- Default width of a key, in pixels or percentage of display width. -->
+        <!-- Default width of a key, in pixels or percentage of display width.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="keyWidth" format="dimension|fraction" />
-        <!-- Default height of a key, in pixels or percentage of display width. -->
+        <!-- Default height of a key, in pixels or percentage of display width.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="keyHeight" format="dimension|fraction" />
-        <!-- Default horizontal gap between keys. -->
+        <!-- Default horizontal gap between keys.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="horizontalGap" format="dimension|fraction" />
-        <!-- Default vertical gap between rows of keys. -->
+        <!-- Default vertical gap between rows of keys.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="verticalGap" format="dimension|fraction" />
     </declare-styleable>
 
+    <!-- {@deprecated Copy this definition into your own application project.} -->
     <declare-styleable name="Keyboard_Row">
-        <!-- Row edge flags. -->
+        <!-- Row edge flags.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="rowEdgeFlags">
-            <!-- Row is anchored to the top of the keyboard. -->
+            <!-- Row is anchored to the top of the keyboard.
+             {@deprecated Copy this definition into your own application project.} -->
             <flag name="top" value="4" />
-            <!-- Row is anchored to the bottom of the keyboard. -->
+            <!-- Row is anchored to the bottom of the keyboard.
+             {@deprecated Copy this definition into your own application project.} -->
             <flag name="bottom" value="8" />
         </attr>
         <!-- Mode of the keyboard. If the mode doesn't match the
-             requested keyboard mode, the row will be skipped. -->
+             requested keyboard mode, the row will be skipped.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="keyboardMode" format="reference" />
     </declare-styleable>
 
+    <!-- {@deprecated Copy this definition into your own application project.} -->
     <declare-styleable name="Keyboard_Key">
-        <!-- The unicode value or comma-separated values that this key outputs. -->
+        <!-- The unicode value or comma-separated values that this key outputs.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="codes" format="integer|string" />
-        <!-- The XML keyboard layout of any popup keyboard. -->
+        <!-- The XML keyboard layout of any popup keyboard.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="popupKeyboard" format="reference" />
-        <!-- The characters to display in the popup keyboard. -->
+        <!-- The characters to display in the popup keyboard.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="popupCharacters" format="string" />
-        <!-- Key edge flags. -->
+        <!-- Key edge flags.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="keyEdgeFlags">
-            <!-- Key is anchored to the left of the keyboard. -->
+            <!-- Key is anchored to the left of the keyboard.
+                 {@deprecated Copy this definition into your own application project.} -->
             <flag name="left" value="1" />
-            <!-- Key is anchored to the right of the keyboard. -->
+            <!-- Key is anchored to the right of the keyboard.
+                 {@deprecated Copy this definition into your own application project.} -->
             <flag name="right" value="2" />
         </attr>
-        <!-- Whether this is a modifier key such as Alt or Shift. -->
+        <!-- Whether this is a modifier key such as Alt or Shift.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="isModifier" format="boolean" />
-        <!-- Whether this is a toggle key. -->
+        <!-- Whether this is a toggle key.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="isSticky" format="boolean" />
-        <!-- Whether long-pressing on this key will make it repeat. -->
+        <!-- Whether long-pressing on this key will make it repeat.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="isRepeatable" format="boolean" />
-        <!-- The icon to show in the popup preview. -->
+        <!-- The icon to show in the popup preview.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="iconPreview" format="reference" />
-        <!-- The string of characters to output when this key is pressed. -->
+        <!-- The string of characters to output when this key is pressed.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="keyOutputText" format="string" />
-        <!-- The label to display on the key. -->
+        <!-- The label to display on the key.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="keyLabel" format="string" />
-        <!-- The icon to display on the key instead of the label. -->
+        <!-- The icon to display on the key instead of the label.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="keyIcon" format="reference" />
         <!-- Mode of the keyboard. If the mode doesn't match the
-             requested keyboard mode, the key will be skipped. -->
+             requested keyboard mode, the key will be skipped.
+             {@deprecated Copy this definition into your own application project.} -->
         <attr name="keyboardMode" />
     </declare-styleable>
 
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 35263a3..54f6c63 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1112,6 +1112,11 @@
          resource] to be present in order to function. Default value is false. -->
     <attr name="isSplitRequired" format="boolean" />
 
+    <!-- Flag to specify if this app prioritizes code integrity. The system may choose
+         to run with better integrity guarantee in various components if possible based on the app's
+         <code>targetSdkVersion</code>. -->
+    <attr name="preferCodeIntegrity" format="boolean" />
+
     <!-- Extra options for an activity's UI. Applies to either the {@code <activity>} or
          {@code <application>} tag. If specified on the {@code <application>}
          tag these will be considered defaults for all activities in the
@@ -1294,6 +1299,20 @@
          supports any size. -->
     <attr name="maxAspectRatio" format="float" />
 
+    <!-- This value indicates the minimum aspect ratio the activity supports. If the app runs on a
+         device with a narrower aspect ratio, the system automatically letterboxes the app, leaving
+         portions of the screen unused so the app can run at its specified minimum aspect ratio.
+         <p>
+         Minimum aspect ratio, expressed as (longer dimension / shorter dimension) in decimal
+         form. For example, if the minimum aspect ratio is 4:3, set value to 1.33.
+         <p>
+         Value needs to be greater or equal to 1.0, otherwise it is ignored.
+         <p>
+         NOTE: This attribute is ignored if the activity has
+         {@link android.R.attr#resizeableActivity} set to true, since that means your activity
+         supports any size. -->
+    <attr name="minAspectRatio" format="float" />
+
     <!-- This value indicates how tasks rooted at this activity will behave in lockTask mode.
          While in lockTask mode the system will not launch non-permitted tasks until
          lockTask mode is disabled.
@@ -1566,11 +1585,13 @@
              to honor this flag as well. -->
         <attr name="usesCleartextTraffic" />
         <attr name="multiArch" />
+        <attr name="preferCodeIntegrity" />
         <attr name="extractNativeLibs" />
         <attr name="defaultToDeviceProtectedStorage" format="boolean" />
         <attr name="directBootAware" />
         <attr name="resizeableActivity" />
         <attr name="maxAspectRatio" />
+        <attr name="minAspectRatio" />
         <attr name="networkSecurityConfig" />
         <!-- Declare the category of this app. Categories are used to cluster multiple apps
              together into meaningful groups, such as when summarizing battery, network, or
@@ -2386,6 +2407,7 @@
         <attr name="resizeableActivity" />
         <attr name="supportsPictureInPicture" />
         <attr name="maxAspectRatio" />
+        <attr name="minAspectRatio" />
         <attr name="lockTaskMode" />
         <attr name="showForAllUsers" />
 
diff --git a/core/res/res/values/colors_car.xml b/core/res/res/values/colors_car.xml
index ea7c009..f4aeff7 100644
--- a/core/res/res/values/colors_car.xml
+++ b/core/res/res/values/colors_car.xml
@@ -50,7 +50,7 @@
     <color name="car_headline4_dark">@android:color/black</color>
     <color name="car_headline4">@color/car_headline4_light</color>
 
-    <color name="car_body1_light">@color/car_grey_100</color>
+    <color name="car_body1_light">@color/car_grey_50</color>
     <color name="car_body1_dark">@color/car_grey_900</color>
     <color name="car_body1">@color/car_body1_light</color>
 
@@ -58,7 +58,7 @@
     <color name="car_body2_dark">@color/car_grey_700</color>
     <color name="car_body2">@color/car_body2_light</color>
 
-    <color name="car_body3_light">@android:color/white</color>
+    <color name="car_body3_light">@color/car_grey_400</color>
     <color name="car_body3_dark">@android:color/black</color>
     <color name="car_body3">@color/car_body3_light</color>
 
@@ -137,7 +137,7 @@
     <color name="car_toast_background">#E6282a2d</color>
 
     <!-- Misc colors -->
-    <color name="car_highlight_light">@color/car_teal_700</color>
+    <color name="car_highlight_light">@color/car_teal_200</color>
     <color name="car_highlight_dark">@color/car_teal_200</color>
     <color name="car_highlight">@color/car_highlight_dark</color>
     <color name="car_accent_light">@color/car_highlight_light</color>
@@ -148,6 +148,7 @@
     <color name="car_user_switcher_user_image_fgcolor">@color/car_grey_900</color>
 
     <!-- Color palette for cars -->
+    <color name="car_grey_972">#ff090A0C</color>
     <color name="car_grey_958">#ff0e1013</color>
     <color name="car_grey_928">#ff17181b</color>
     <color name="car_grey_900">#ff202124</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 97a21a5..8735557 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -929,6 +929,9 @@
          in hardware. -->
     <bool name="config_setColorTransformAccelerated">false</bool>
 
+    <!-- Boolean indicating whether display white balance is supported. -->
+    <bool name="config_displayWhiteBalanceAvailable">false</bool>
+
     <!-- Control whether Night display is available. This should only be enabled on devices
          that have a HWC implementation that can apply the matrix passed to setColorTransform
          without impacting power, performance, and app compatibility (e.g. protected content). -->
@@ -1165,6 +1168,10 @@
     <!-- The default suggested battery % at which we enable battery saver automatically.  -->
     <integer name="config_lowBatteryAutoTriggerDefaultLevel">15</integer>
 
+    <!-- The app which will handle routine based automatic battery saver, if empty the UI for
+         routine based battery saver will be hidden -->
+    <string name="config_batterySaverScheduleProvider"></string>
+
     <!-- Close low battery warning when battery level reaches the lowBatteryWarningLevel
          plus this -->
     <integer name="config_lowBatteryCloseWarningBump">5</integer>
@@ -1437,26 +1444,6 @@
     <integer-array name="config_autoBrightnessKeyboardBacklightValues">
     </integer-array>
 
-    <!-- Array of hysteresis constraint values for brightening, represented as tenths of a
-         percent. The length of this array is assumed to be one greater than
-         config_dynamicHysteresisLuxLevels. The brightening threshold is calculated as
-         lux * (1.0f + CONSTRAINT_VALUE). When the current lux is higher than this threshold,
-         the screen brightness is recalculated. See the config_dynamicHysteresisLuxLevels
-         description for how the constraint value is chosen. -->
-    <integer-array name="config_dynamicHysteresisBrightLevels">
-        <item>100</item>
-    </integer-array>
-
-    <!-- Array of hysteresis constraint values for darkening, represented as tenths of a
-         percent. The length of this array is assumed to be one greater than
-         config_dynamicHysteresisLuxLevels. The darkening threshold is calculated as
-         lux * (1.0f - CONSTRAINT_VALUE). When the current lux is lower than this threshold,
-         the screen brightness is recalculated. See the config_dynamicHysteresisLuxLevels
-         description for how the constraint value is chosen. -->
-    <integer-array name="config_dynamicHysteresisDarkLevels">
-        <item>200</item>
-    </integer-array>
-
     <!-- An array describing the screen's backlight values corresponding to the brightness
          values in the config_screenBrightnessNits array.
 
@@ -1474,19 +1461,73 @@
     <array name="config_screenBrightnessNits">
     </array>
 
-
     <!-- Array of ambient lux threshold values. This is used for determining hysteresis constraint
          values by calculating the index to use for lookup and then setting the constraint value
          to the corresponding value of the array. The new brightening hysteresis constraint value
-         is the n-th element of config_dynamicHysteresisBrightLevels, and the new darkening
-         hysteresis constraint value is the n-th element of config_dynamicHysteresisDarkLevels.
+         is the n-th element of config_ambientBrighteningThresholds, and the new darkening
+         hysteresis constraint value is the n-th element of config_ambientDarkeningThresholds.
 
          The (zero-based) index is calculated as follows: (MAX is the largest index of the array)
-         condition                      calculated index
-         value < lux[0]                 0
-         lux[n] <= value < lux[n+1]     n+1
-         lux[MAX] <= value              MAX+1 -->
-    <integer-array name="config_dynamicHysteresisLuxLevels">
+         condition                       calculated index
+         value < level[0]                0
+         level[n] <= value < level[n+1]  n+1
+         level[MAX] <= value             MAX+1 -->
+    <integer-array name="config_ambientThresholdLevels">
+    </integer-array>
+
+    <!-- Array of hysteresis constraint values for brightening, represented as tenths of a
+         percent. The length of this array is assumed to be one greater than
+         config_ambientThresholdLevels. The brightening threshold is calculated as
+         lux * (1.0f + CONSTRAINT_VALUE). When the current lux is higher than this threshold,
+         the screen brightness is recalculated. See the config_ambientThresholdLevels
+         description for how the constraint value is chosen. -->
+    <integer-array name="config_ambientBrighteningThresholds">
+        <item>100</item>
+    </integer-array>
+
+    <!-- Array of hysteresis constraint values for darkening, represented as tenths of a
+         percent. The length of this array is assumed to be one greater than
+         config_ambientThresholdLevels. The darkening threshold is calculated as
+         lux * (1.0f - CONSTRAINT_VALUE). When the current lux is lower than this threshold,
+         the screen brightness is recalculated. See the config_ambientThresholdLevels
+         description for how the constraint value is chosen. -->
+    <integer-array name="config_ambientDarkeningThresholds">
+        <item>200</item>
+    </integer-array>
+
+    <!-- Array of screen brightness threshold values. This is used for determining hysteresis
+         constraint values by calculating the index to use for lookup and then setting the
+         constraint value to the corresponding value of the array. The new brightening hysteresis
+         constraint value is the n-th element of config_screenBrighteningThresholds, and the new
+         darkening hysteresis constraint value is the n-th element of
+         config_screenDarkeningThresholds.
+
+         The (zero-based) index is calculated as follows: (MAX is the largest index of the array)
+         condition                       calculated index
+         value < level[0]                0
+         level[n] <= value < level[n+1]  n+1
+         level[MAX] <= value             MAX+1 -->
+    <integer-array name="config_screenThresholdLevels">
+    </integer-array>
+
+    <!-- Array of hysteresis constraint values for brightening, represented as tenths of a
+         percent. The length of this array is assumed to be one greater than
+         config_screenThresholdLevels. The brightening threshold is calculated as
+         screenBrightness * (1.0f + CONSTRAINT_VALUE). When the new screen brightness is higher
+         than this threshold, it is applied. See the config_screenThresholdLevels description for
+         how the constraint value is chosen. -->
+    <integer-array name="config_screenBrighteningThresholds">
+        <item>100</item>
+    </integer-array>
+ 
+    <!-- Array of hysteresis constraint values for darkening, represented as tenths of a
+         percent. The length of this array is assumed to be one greater than
+         config_screenThresholdLevels. The darkening threshold is calculated as
+         screenBrightness * (1.0f - CONSTRAINT_VALUE). When the new screen brightness is lower than
+         this threshold, it is applied. See the config_screenThresholdLevels description for how
+         the constraint value is chosen. -->
+    <integer-array name="config_screenDarkeningThresholds">
+        <item>200</item>
     </integer-array>
 
     <!-- Amount of time it takes for the light sensor to warm up in milliseconds.
@@ -2019,6 +2060,10 @@
          This is intended to allow packaging drivers or tools for installation on a PC. -->
     <string translatable="false" name="config_isoImagePath"></string>
 
+    <!-- Whether the system enables per-display focus. If the system has the input method for each
+         display, this value should be true. -->
+    <bool name="config_perDisplayFocusEnabled">false</bool>
+
     <!-- Whether a software navigation bar should be shown. NOTE: in the future this may be
          autodetected from the Configuration. -->
     <bool name="config_showNavigationBar">false</bool>
@@ -2118,9 +2163,6 @@
     <!-- If the sensor that wakes up the lock screen is available or not. -->
     <bool name="config_dozeWakeLockScreenSensorAvailable">false</bool>
 
-    <!-- Type of the wake up sensor. Empty if not supported. -->
-    <string name="config_dozeWakeScreenSensorType" translatable="false"></string>
-
     <!-- Control whether the always on display mode is available. This should only be enabled on
          devices where the display has been tuned to be power efficient in DOZE and/or DOZE_SUSPEND
          states. -->
@@ -2573,6 +2615,11 @@
     <!-- Package name for default network scorer app; overridden by product overlays. -->
     <string name="config_defaultNetworkScorerPackageName"></string>
 
+    <!-- Feature flag to enable memory efficient task snapshots that are used in recents optimized
+         for low memory devices and replace the app transition starting window with the splash
+         screen. -->
+    <bool name="config_lowRamTaskSnapshotsAndRecents">false</bool>
+
     <!-- Determines whether recent tasks are provided to the user. Default device has recents
          property. If this is false, then the following recents config flags are ignored. -->
     <bool name="config_hasRecents">true</bool>
@@ -2798,19 +2845,6 @@
 
     <bool name="config_auto_attach_data_on_creation">true</bool>
 
-    <!-- Values for GPS configuration -->
-    <string-array translatable="false" name="config_gpsParameters">
-        <item>SUPL_HOST=supl.google.com</item>
-        <item>SUPL_PORT=7275</item>
-        <item>SUPL_VER=0x20000</item>
-        <item>SUPL_MODE=1</item>
-        <item>SUPL_ES=1</item>
-        <item>LPP_PROFILE=0</item>
-        <item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
-        <item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
-        <item>GPS_LOCK=3</item>
-    </string-array>
-
     <!-- Sprint need a 70 ms delay for 3way call -->
     <integer name="config_cdma_3waycall_flash_delay">0</integer>
 
@@ -3450,8 +3484,6 @@
 
     <!-- Name of a font family to use for headlines. If empty, falls back to platform default -->
     <string name="config_headlineFontFamily" translatable="false"></string>
-    <!-- Name of a font family to use for headlines. Defaults to sans-serif-light -->
-    <string name="config_headlineFontFamilyLight" translatable="false">sans-serif-light</string>
     <!-- Allows setting custom fontFeatureSettings on specific text. -->
     <string name="config_headlineFontFeatureSettings" translatable="false"></string>
 
@@ -3518,8 +3550,6 @@
     <string name="config_headlineFontFamilyMedium" translateable="false">@string/font_family_button_material</string>
     <!-- Name of a font family to use for body text. -->
     <string name="config_bodyFontFamily" translatable="false">sans-serif</string>
-    <!-- Name of a font family to use for light body text. -->
-    <string name="config_bodyFontFamilyLight" translatable="false">sans-serif-light</string>
     <!-- Name of a font family to use for medium body text. -->
     <string name="config_bodyFontFamilyMedium" translatable="false">sans-serif-medium</string>
 
@@ -3602,4 +3632,13 @@
          (android.view.InputEventCompatProcessor). -->
     <string name="config_inputEventCompatProcessorOverrideClassName" translatable="false"></string>
 
+    <!-- Component name for the default module metadata provider on this device -->
+    <string name="config_defaultModuleMetadataProvider">com.android.modulemetadata</string>
+
+    <!-- This is the default launcher component to use on secondary displays that support system
+         decorations.
+         This launcher activity must support multiple instances and have corresponding launch mode
+         set in AndroidManifest.
+         {@see android.view.Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} -->
+    <string name="config_secondaryHomeComponent" translatable="false">com.android.launcher3/com.android.launcher3.SecondaryDisplayLauncher</string>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index eac8b48..799d9d8 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1067,7 +1067,8 @@
   <public type="id" name="inputExtractEditText" id="0x01020025" />
 
   <!-- View ID of the {@link android.inputmethodservice.KeyboardView} within
-        an input method's input area. -->
+       an input method's input area.
+       {@deprecated Use Copy this definition into your own application project.} -->
   <public type="id" name="keyboardView" id="0x01020026" />
   <!-- View ID of a {@link android.view.View} to close a popup keyboard -->
   <public type="id" name="closeButton" id="0x01020027" />
@@ -1082,6 +1083,7 @@
   <public type="style" name="Theme.InputMethod" id="0x01030054" />
   <public type="style" name="Theme.NoDisplay" id="0x01030055" />
   <public type="style" name="Animation.InputMethod" id="0x01030056" />
+  <!-- {@deprecated Use Copy this definition into your own application project.} -->
   <public type="style" name="Widget.KeyboardView" id="0x01030057" />
   <public type="style" name="ButtonBar" id="0x01030058" />
   <public type="style" name="Theme.Panel" id="0x01030059" />
@@ -2931,6 +2933,7 @@
         <public name="selectionDividerHeight" />
         <public name="foregroundServiceType" />
         <public name="hasFragileUserData" />
+        <public name="minAspectRatio" />
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b4">
@@ -2975,6 +2978,11 @@
         <public name="config_mediaMetadataBitmapMaxSize" />
     </public-group>
 
+    <public-group type="color" first-id="0x0106001c">
+        <!-- @hide @SystemApi -->
+        <public name="system_notification_accent_color" />
+    </public-group>
+
   <!-- ===============================================================
        DO NOT ADD UN-GROUPED ITEMS HERE
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index cab01f9..0878562 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3066,6 +3066,14 @@
     <string name="whichViewApplicationNamed">Open with %1$s</string>
     <!-- Label for a link to a intent resolver dialog to view something -->
     <string name="whichViewApplicationLabel">Open</string>
+    <!-- Title of intent resolver dialog when selecting a viewer application that opens URI
+         [CHAR LIMIT=128]. -->
+    <string name="whichGiveAccessToApplication">Give access to open <xliff:g id="host" example="mail.google.com">%1$s</xliff:g> links with</string>
+    <!-- Title of intent resolver dialog when selecting a viewer application that opens URI
+         and a previously used application is known [CHAR LIMIT=128]. -->
+    <string name="whichGiveAccessToApplicationNamed">Give access to open <xliff:g id="host" example="mail.google.com">%1$s</xliff:g> links with <xliff:g id="application" example="Gmail">%2$s</xliff:g></string>
+    <!-- Label for a link to an intent resolver dialog to open URI [CHAR LIMIT=18] -->
+    <string name="whichGiveAccessToApplicationLabel">Give access</string>
     <!-- Title of intent resolver dialog when selecting an editor application to run. -->
     <string name="whichEditApplication">Edit with</string>
     <!-- Title of intent resolver dialog when selecting an editor application to run
@@ -3200,6 +3208,8 @@
     <!-- [CHAR LIMIT=40] Title of dialog that is shown when system is starting. -->
     <string name="android_start_title" product="default">Phone is starting\u2026</string>
     <!-- [CHAR LIMIT=40] Title of dialog that is shown when system is starting. -->
+    <string name="android_start_title" product="automotive">Android is starting\u2026</string>
+    <!-- [CHAR LIMIT=40] Title of dialog that is shown when system is starting. -->
     <string name="android_start_title" product="tablet">Tablet is starting\u2026</string>
     <!-- [CHAR LIMIT=40] Title of dialog that is shown when system is starting. -->
     <string name="android_start_title" product="device">Device is starting\u2026</string>
@@ -4154,6 +4164,10 @@
          from the activity resolver to use just this once. [CHAR LIMIT=25] -->
     <string name="activity_resolver_use_once">Just once</string>
 
+    <!-- Title for a button to choose to go to
+          'Open by Default' app settings. [CHAR LIMIT=25] -->
+    <string name="activity_resolver_app_settings">Settings</string>
+
     <!-- Text for the toast that is shown when the user clicks on a launcher that
          doesn't support the work profile. [CHAR LIMIT=100] -->
     <string name="activity_resolver_work_profiles_support">%1$s doesn\'t support work profile</string>
@@ -4422,7 +4436,8 @@
         <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> off</string>
 
     <!-- Text spoken when accessibility shortcut warning dialog is shown. [CHAR LIMIT=none] -->
-    <string name="accessibility_shortcut_spoken_feedback">Use Accessibility Shortcut again to start the current accessibility feature</string>
+    <string name="accessibility_shortcut_spoken_feedback">Press and hold both volume keys for three seconds to use
+        <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g></string>
 
     <!-- Text appearing in a prompt at the top of UI allowing the user to select a target service or feature to be assigned to the Accessibility button in the navigation bar. -->
     <string name="accessibility_button_prompt_text">Choose a feature to use when you tap the Accessibility button:</string>
@@ -5223,6 +5238,15 @@
     <!-- Content description of the overlay icon in the notification. [CHAR LIMIT=NONE] -->
     <string name="notification_appops_overlay_active">displaying over other apps on your screen</string>
 
+    <!-- Dynamic mode battery saver strings -->
+    <!-- The user visible name of the notification channel for the routine mode battery saver fyi notification [CHAR_LIMIT=80]-->
+    <string name="dynamic_mode_notification_channel_name">Routine Mode info notification</string>
+    <!-- Title of notification letting users know why battery saver was turned on automatically [CHAR_LIMIT=NONE]-->
+    <string name="dynamic_mode_notification_title">Battery may run out before usual charge</string>
+    <!-- Summary of notification letting users know why battery saver was turned on automatically [CHAR_LIMIT=NONE]-->
+    <string name="dynamic_mode_notification_summary">Battery Saver activated to extend battery life</string>
+
+
     <!-- Strings for car -->
     <!-- String displayed when loading a user in the car [CHAR LIMIT=30] -->
     <string name="car_loading_profile">Loading</string>
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 2c04ec8..200ef2f 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -41,7 +41,9 @@
         <item name="textAppearance">?attr/textAppearanceButton</item>
         <item name="textColor">@color/btn_colored_text_material</item>
     </style>
-    <style name="Widget.DeviceDefault.TextView" parent="Widget.Material.TextView"/>
+    <style name="Widget.DeviceDefault.TextView" parent="Widget.Material.TextView">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
     <style name="Widget.DeviceDefault.CheckedTextView" parent="Widget.Material.CheckedTextView"/>
     <style name="Widget.DeviceDefault.AutoCompleteTextView" parent="Widget.Material.AutoCompleteTextView"/>
     <style name="Widget.DeviceDefault.CompoundButton.CheckBox" parent="Widget.Material.CompoundButton.CheckBox"/>
@@ -266,6 +268,12 @@
     <style name="TextAppearance.DeviceDefault.Notification.Reply" parent="TextAppearance.Material.Notification.Reply">
         <item name="fontFamily">@string/config_bodyFontFamily</item>
     </style>
+    <style name="TextAppearance.DeviceDefault.Notification.Info" parent="TextAppearance.Material.Notification.Info">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Notification.Info.Ambient" parent="TextAppearance.Material.Notification.Info.Ambient">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
     <style name="TextAppearance.DeviceDefault.Widget" parent="TextAppearance.Material.Widget">
         <item name="fontFamily">@string/config_bodyFontFamily</item>
     </style>
@@ -347,6 +355,30 @@
         <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
     </style>
     <style name="TextAppearance.DeviceDefault.Widget.Toolbar.Title" parent="TextAppearance.DeviceDefault.Widget.ActionBar.Title"/>
+    <style name="TextAppearance.DeviceDefault.Widget.Toolbar.Subtitle" parent="TextAppearance.DeviceDefault.Widget.ActionBar.Subtitle"/>
+    <style name="TextAppearance.DeviceDefault.Body1" parent="TextAppearance.Material.Body1">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Body2" parent="TextAppearance.Material.Body2">
+        <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Subhead" parent="TextAppearance.Material.Subhead">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Headline" parent="TextAppearance.Material.Headline">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Display1" parent="TextAppearance.Material.Display1">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Title" parent="TextAppearance.Material.Title">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Caption" parent="TextAppearance.Material.Caption">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.ListItem" parent="TextAppearance.DeviceDefault.Subhead"/>
+    <style name="TextAppearance.DeviceDefault.ListItemSecondary" parent="TextAppearance.DeviceDefault.Body1"/>
 
     <!-- Preference Styles -->
     <style name="Preference.DeviceDefault" parent="Preference.Material"/>
diff --git a/core/res/res/values/styles_permission_controller.xml b/core/res/res/values/styles_permission_controller.xml
deleted file mode 100644
index e6e0de3..0000000
--- a/core/res/res/values/styles_permission_controller.xml
+++ /dev/null
@@ -1,131 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2018 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<resources>
-    <!-- styles for the permission grant dialog. -->
-    <style name="PermissionGrantDialog">
-        <item name="background">?attr/windowBackground</item>
-        <item name="elevation">?attr/windowElevation</item>
-        <item name="layout_weight">@dimen/permissionGrantDialogWeight</item>
-        <item name="layout_width">@dimen/permissionGrantDialogWidth</item>
-    </style>
-
-    <style name="PermissionGrantTitleIcon">
-        <item name="layout_width">36dp</item>
-        <item name="layout_height">36dp</item>
-        <item name="tint">?attr/colorAccent</item>
-        <item name="scaleType">fitCenter</item>
-    </style>
-
-    <style name="PermissionGrantTitleMessage"
-           parent="@style/TextAppearance.DeviceDefault">
-        <item name="paddingStart">22dp</item>
-        <item name="textSize">20sp</item>
-        <item name="textColor">?attr/textColorPrimary</item>
-    </style>
-
-    <style name="PermissionGrantIndex"
-           parent="@style/TextAppearance.DeviceDefault">
-        <item name="paddingEnd">12dp</item>
-        <item name="singleLine">true</item>
-        <item name="textColor">?attr/textColorSecondary</item>
-    </style>
-
-    <style name="PermissionGrantDescription">
-        <item name="layout_marginTop">20dp</item>
-        <item name="layout_marginStart">24dp</item>
-        <item name="layout_marginBottom">16dp</item>
-        <item name="layout_marginEnd">24dp</item>
-    </style>
-
-    <style name="PermissionGrantContent">
-        <item name="layout_marginStart">16dp</item>
-        <item name="layout_marginEnd">24dp</item>
-    </style>
-
-    <style name="PermissionGrantRadioGroup">
-        <item name="layout_marginStart">8dp</item>
-        <item name="layout_marginTop">-4dp</item>
-    </style>
-
-    <style name="PermissionGrantRadioButton"
-           parent="@style/Widget.DeviceDefault.CompoundButton.RadioButton">
-        <item name="paddingStart">16dp</item>
-        <item name="paddingTop">8dp</item>
-        <item name="paddingBottom">8dp</item>
-        <item name="textSize">16sp</item>
-    </style>
-
-    <style name="PermissionGrantDetailMessage"
-           parent="@style/TextAppearance.DeviceDefault">
-        <item name="layout_marginStart">8dp</item>
-        <item name="layout_marginBottom">4dp</item>
-        <item name="textColor">?attr/textColorPrimary</item>
-        <item name="textSize">16sp</item>
-    </style>
-
-    <style name="PermissionGrantDetailMessageSpace">
-        <item name="layout_height">8dp</item>
-    </style>
-
-    <style name="PermissionGrantCheckbox"
-           parent="@style/Widget.DeviceDefault.CompoundButton.CheckBox">
-        <item name="paddingStart">16dp</item>
-        <item name="textColor">?attr/textColorSecondary</item>
-        <item name="buttonTint">?attr/textColorSecondary</item>
-        <item name="textSize">16sp</item>
-    </style>
-
-    <style name="PermissionGrantButtonBar">
-        <item name="layout_marginStart">24dp</item>
-        <item name="layout_marginEnd">16dp</item>
-        <item name="layout_marginBottom">4dp</item>
-    </style>
-
-    <!-- styles for the permission review screen. -->
-    <style name="PermissionReviewDescription">
-        <item name="layout_marginTop">20dp</item>
-        <item name="layout_marginStart">24dp</item>
-        <item name="layout_marginBottom">16dp</item>
-        <item name="layout_marginEnd">24dp</item>
-    </style>
-
-    <style name="PermissionReviewTitleIcon">
-        <item name="layout_marginTop">4dp</item>
-        <item name="layout_width">36dp</item>
-        <item name="layout_height">36dp</item>
-        <item name="scaleType">fitCenter</item>
-    </style>
-
-    <style name="PermissionReviewTitleMessage"
-           parent="@style/TextAppearance.DeviceDefault">
-        <item name="paddingStart">22dp</item>
-        <item name="textSize">20sp</item>
-        <item name="textColor">?attr/textColorPrimary</item>
-    </style>
-
-    <style name="PermissionReviewSettings">
-        <item name="layout_marginStart">8dp</item>
-        <item name="layout_marginEnd">8dp</item>
-    </style>
-
-    <style name="PermissionReviewButtonBar">
-        <item name="layout_marginStart">24dp</item>
-        <item name="layout_marginEnd">16dp</item>
-        <item name="layout_marginBottom">4dp</item>
-    </style>
-</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 161e416..c2d97bc 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
 /* Copyright 2012, The Android Open Source Project
 **
@@ -330,6 +329,7 @@
   <java-symbol type="bool" name="config_enableMultiUserUI"/>
   <java-symbol type="bool" name="config_enableNewAutoSelectNetworkUI"/>
   <java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
+  <java-symbol type="bool" name="config_lowRamTaskSnapshotsAndRecents" />
   <java-symbol type="bool" name="config_hasRecents" />
   <java-symbol type="string" name="config_recentsComponentName" />
   <java-symbol type="integer" name="config_minNumVisibleRecentTasks_lowRam" />
@@ -1269,7 +1269,6 @@
   <java-symbol type="array" name="config_telephonyEuiccDeviceCapabilities" />
   <java-symbol type="array" name="config_telephonyHardware" />
   <java-symbol type="array" name="config_keySystemUuidMapping" />
-  <java-symbol type="array" name="config_gpsParameters" />
   <java-symbol type="array" name="required_apps_managed_user" />
   <java-symbol type="array" name="required_apps_managed_profile" />
   <java-symbol type="array" name="required_apps_managed_device" />
@@ -1369,15 +1368,6 @@
   <java-symbol type="drawable" name="ic_wifi_signal_2" />
   <java-symbol type="drawable" name="ic_wifi_signal_3" />
   <java-symbol type="drawable" name="ic_wifi_signal_4" />
-  <java-symbol type="drawable" name="ic_signal_wifi_badged_0_bars" />
-  <java-symbol type="drawable" name="ic_signal_wifi_badged_1_bar" />
-  <java-symbol type="drawable" name="ic_signal_wifi_badged_2_bars" />
-  <java-symbol type="drawable" name="ic_signal_wifi_badged_3_bars" />
-  <java-symbol type="drawable" name="ic_signal_wifi_badged_4_bars" />
-  <java-symbol type="drawable" name="ic_signal_wifi_badged_4k" />
-  <java-symbol type="drawable" name="ic_signal_wifi_badged_hd" />
-  <java-symbol type="drawable" name="ic_signal_wifi_badged_sd" />
-  <java-symbol type="drawable" name="ic_signal_wifi_badged_ld" />
   <java-symbol type="drawable" name="stat_notify_rssi_in_range" />
   <java-symbol type="drawable" name="stat_sys_gps_on" />
   <java-symbol type="drawable" name="stat_sys_tether_wifi" />
@@ -1670,6 +1660,7 @@
   <java-symbol type="bool" name="config_lockDayNightMode" />
   <java-symbol type="bool" name="config_lockUiMode" />
   <java-symbol type="bool" name="config_reverseDefaultRotation" />
+  <java-symbol type="bool" name="config_perDisplayFocusEnabled" />
   <java-symbol type="bool" name="config_showNavigationBar" />
   <java-symbol type="bool" name="config_supportAutoRotation" />
   <java-symbol type="bool" name="target_honeycomb_needs_options_menu" />
@@ -1828,9 +1819,12 @@
   <java-symbol type="array" name="config_autoBrightnessKeyboardBacklightValues" />
   <java-symbol type="array" name="config_autoBrightnessLcdBacklightValues" />
   <java-symbol type="array" name="config_autoBrightnessLevels" />
-  <java-symbol type="array" name="config_dynamicHysteresisBrightLevels" />
-  <java-symbol type="array" name="config_dynamicHysteresisDarkLevels" />
-  <java-symbol type="array" name="config_dynamicHysteresisLuxLevels" />
+  <java-symbol type="array" name="config_ambientThresholdLevels" />
+  <java-symbol type="array" name="config_ambientBrighteningThresholds" />
+  <java-symbol type="array" name="config_ambientDarkeningThresholds" />
+  <java-symbol type="array" name="config_screenThresholdLevels" />
+  <java-symbol type="array" name="config_screenBrighteningThresholds" />
+  <java-symbol type="array" name="config_screenDarkeningThresholds" />
   <java-symbol type="array" name="config_minimumBrightnessCurveLux" />
   <java-symbol type="array" name="config_minimumBrightnessCurveNits" />
   <java-symbol type="array" name="config_protectedNetworks" />
@@ -2149,6 +2143,7 @@
   <java-symbol type="id" name="resolver_list" />
   <java-symbol type="id" name="button_once" />
   <java-symbol type="id" name="button_always" />
+  <java-symbol type="id" name="button_app_settings" />
   <java-symbol type="integer" name="config_globalActionsKeyTimeout" />
   <java-symbol type="integer" name="config_maxResolverActivityColumns" />
   <java-symbol type="array" name="config_notificationSignalExtractors" />
@@ -2504,11 +2499,15 @@
   <java-symbol type="bool" name="config_use_voip_mode_for_ims" />
   <java-symbol type="attr" name="touchscreenBlocksFocus" />
   <java-symbol type="layout" name="resolver_list_with_default" />
+  <java-symbol type="string" name="activity_resolver_app_settings" />
   <java-symbol type="string" name="whichApplicationNamed" />
   <java-symbol type="string" name="whichApplicationLabel" />
   <java-symbol type="string" name="whichViewApplication" />
   <java-symbol type="string" name="whichViewApplicationNamed" />
   <java-symbol type="string" name="whichViewApplicationLabel" />
+  <java-symbol type="string" name="whichGiveAccessToApplication" />
+  <java-symbol type="string" name="whichGiveAccessToApplicationNamed" />
+  <java-symbol type="string" name="whichGiveAccessToApplicationLabel" />
   <java-symbol type="string" name="whichEditApplication" />
   <java-symbol type="string" name="whichEditApplicationNamed" />
   <java-symbol type="string" name="whichEditApplicationLabel" />
@@ -3029,6 +3028,7 @@
   <java-symbol type="drawable" name="ic_doc_generic" />
 
   <java-symbol type="bool" name="config_setColorTransformAccelerated" />
+  <java-symbol type="bool" name="config_displayWhiteBalanceAvailable" />
   <java-symbol type="bool" name="config_nightDisplayAvailable" />
   <java-symbol type="bool" name="config_allowDisablingAssistDisclosure" />
   <java-symbol type="integer" name="config_defaultNightDisplayAutoMode" />
@@ -3324,7 +3324,6 @@
   <java-symbol type="bool" name="config_displayBrightnessBucketsInDoze" />
   <java-symbol type="integer" name="config_storageManagerDaystoRetainDefault" />
   <java-symbol type="string" name="config_headlineFontFamily" />
-  <java-symbol type="string" name="config_headlineFontFamilyLight" />
   <java-symbol type="string" name="config_headlineFontFamilyMedium" />
 
   <java-symbol type="drawable" name="stat_sys_vitals" />
@@ -3459,6 +3458,7 @@
   <java-symbol type="integer" name="config_lowBatteryAutoTriggerDefaultLevel" />
   <java-symbol type="bool" name="config_batterySaverStickyBehaviourDisabled" />
   <java-symbol type="integer" name="config_dynamicPowerSavingsDefaultDisableThreshold" />
+  <java-symbol type="string" name="config_batterySaverScheduleProvider" />
 
   <!-- For car devices -->
   <java-symbol type="string" name="car_loading_profile" />
@@ -3499,7 +3499,6 @@
 
   <java-symbol type="integer" name="db_wal_truncate_size" />
   <java-symbol type="integer" name="config_wakeUpDelayDoze" />
-  <java-symbol type="string" name="config_dozeWakeScreenSensorType" />
 
   <!-- For Bluetooth AbsoluteVolume -->
   <java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index1" />
@@ -3515,4 +3514,14 @@
   <java-symbol type="dimen" name="rounded_corner_radius" />
   <java-symbol type="dimen" name="rounded_corner_radius_top" />
   <java-symbol type="dimen" name="rounded_corner_radius_bottom" />
+
+  <java-symbol type="string" name="config_defaultModuleMetadataProvider" />
+
+  <!-- For Secondary Launcher -->
+  <java-symbol type="string" name="config_secondaryHomeComponent" />
+  
+  <java-symbol type="string" name="dynamic_mode_notification_channel_name" />
+  <java-symbol type="string" name="dynamic_mode_notification_title" />
+  <java-symbol type="string" name="dynamic_mode_notification_summary" />
+  <java-symbol type="drawable" name="ic_battery" />
 </resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index fec101a..0f4ca66 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -68,6 +68,10 @@
         <item name="textAppearanceLargePopupMenu">@style/TextAppearance.DeviceDefault.Widget.PopupMenu.Large</item>
         <item name="textAppearanceSmallPopupMenu">@style/TextAppearance.DeviceDefault.Widget.PopupMenu.Small</item>
 
+        <item name="textAppearanceListItem">@style/TextAppearance.DeviceDefault.ListItem</item>
+        <item name="textAppearanceListItemSmall">@style/TextAppearance.DeviceDefault.ListItem</item>
+        <item name="textAppearanceListItemSecondary">@style/TextAppearance.DeviceDefault.ListItemSecondary</item>
+
         <!-- Button styles -->
         <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonStyle">@style/Widget.DeviceDefault.Button</item>
@@ -211,7 +215,7 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
-
+        <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
     </style>
 
     <style name="Theme.DeviceDefault" parent="Theme.DeviceDefaultBase" />
@@ -783,7 +787,6 @@
         <!-- Text styles -->
         <item name="textAppearance">@style/TextAppearance.DeviceDefault</item>
         <item name="textAppearanceInverse">@style/TextAppearance.DeviceDefault.Inverse</item>
-
         <item name="textAppearanceLarge">@style/TextAppearance.DeviceDefault.Large</item>
         <item name="textAppearanceMedium">@style/TextAppearance.DeviceDefault.Medium</item>
         <item name="textAppearanceSmall">@style/TextAppearance.DeviceDefault.Small</item>
@@ -792,11 +795,12 @@
         <item name="textAppearanceSmallInverse">@style/TextAppearance.DeviceDefault.Small.Inverse</item>
         <item name="textAppearanceSearchResultTitle">@style/TextAppearance.DeviceDefault.SearchResult.Title</item>
         <item name="textAppearanceSearchResultSubtitle">@style/TextAppearance.DeviceDefault.SearchResult.Subtitle</item>
-
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
-
         <item name="textAppearanceLargePopupMenu">@style/TextAppearance.DeviceDefault.Widget.PopupMenu.Large</item>
         <item name="textAppearanceSmallPopupMenu">@style/TextAppearance.DeviceDefault.Widget.PopupMenu.Small</item>
+        <item name="textAppearanceListItem">@style/TextAppearance.DeviceDefault.ListItem</item>
+        <item name="textAppearanceListItemSmall">@style/TextAppearance.DeviceDefault.ListItem</item>
+        <item name="textAppearanceListItemSecondary">@style/TextAppearance.DeviceDefault.ListItemSecondary</item>
 
         <!-- Button styles -->
         <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
@@ -936,6 +940,7 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
     </style>
 
     <!-- Variant of the DeviceDefault (light) theme that has a solid (opaque) action bar with an
@@ -1438,19 +1443,23 @@
     </style>
 
     <!-- DeviceDefault theme for a window that should look like the Settings app.  -->
-    <style name="Theme.DeviceDefault.Settings" parent="Theme.Material.Settings">
+    <style name="Theme.DeviceDefault.Settings" parent="Theme.DeviceDefault.Light">
+        <!-- From Theme.Material.Light.LightStatusBar -->
+        <item name="windowLightStatusBar">true</item>
+
+        <!-- From Theme.Material.Settings -->
+        <item name="homeAsUpIndicator">@drawable/ic_ab_back_material_settings</item>
+        <item name="presentationTheme">@style/Theme.Material.Settings.Dialog.Presentation</item>
+        <item name="searchDialogTheme">@style/Theme.Material.Settings.SearchBar</item>
+        <item name="panelMenuListTheme">@style/Theme.Material.Settings.CompactMenu</item>
+
         <!-- action bar -->
-        <item name="actionBarStyle">@style/Widget.DeviceDefault.Light.ActionBar.Solid</item>
-        <item name="actionBarTheme">@style/ThemeOverlay.DeviceDefault.ActionBar.Accent</item>
+        <item name="actionBarTheme">@style/ThemeOverlay.DeviceDefault.ActionBar</item>
         <item name="popupTheme">@style/ThemeOverlay.DeviceDefault.Popup.Light</item>
 
         <!-- Color palette -->
-        <item name="colorBackground">@color/background_device_default_light</item>
-        <item name="colorPrimary">@color/primary_device_default_settings_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings_light</item>
         <item name="colorSecondary">@color/secondary_device_default_settings_light</item>
-        <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorError">@color/error_color_device_default_light</item>
         <item name="colorEdgeEffect">@android:color/black</item>
 
         <!-- Add white nav bar with divider that matches material -->
@@ -1458,24 +1467,9 @@
         <item name="navigationBarColor">@android:color/white</item>
         <item name="windowLightNavigationBar">true</item>
 
-        <!-- Dialog attributes -->
-        <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
-        <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
-
-        <!-- Text styles -->
-        <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
-
         <!-- Button styles -->
-        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
-        <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
-        <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
-
-        <!-- Toolbar attributes -->
-        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
-
         <item name="listDivider">@color/list_divider_color_light</item>
     </style>
 
@@ -1646,8 +1640,10 @@
 
     <style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" />
 
+    <style name="Theme.DeviceDefault.DayNight" parent="Theme.DeviceDefault.Light" />
+
     <!-- Theme used for the intent picker activity. -->
-    <style name="Theme.DeviceDefault.Resolver" parent="Theme.Material.Light">
+    <style name="Theme.DeviceDefault.Resolver" parent="Theme.DeviceDefault.DayNight">
         <item name="windowEnterTransition">@empty</item>
         <item name="windowExitTransition">@empty</item>
         <item name="windowIsTranslucent">true</item>
@@ -1659,30 +1655,8 @@
         <item name="colorControlActivated">?attr/colorControlHighlight</item>
         <item name="listPreferredItemPaddingStart">?attr/dialogPreferredPadding</item>
         <item name="listPreferredItemPaddingEnd">?attr/dialogPreferredPadding</item>
-
-        <!-- Dialog attributes -->
-        <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
-        <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
-
-        <!-- Button styles -->
-        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
-        <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
-
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
-        <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorError">@color/error_color_device_default_light</item>
-
-        <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
-        <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
-
-        <!-- Toolbar attributes -->
-        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
-
     <!-- @hide DeviceDefault themes for the autofill FillUi -->
     <style name="Theme.DeviceDefault.Autofill" />
     <style name="Theme.DeviceDefault.Light.Autofill" />
@@ -1697,11 +1671,8 @@
 
     <style name="ThemeOverlay.DeviceDefault" />
 
-    <!-- @hide Theme overlay that inherits from material actionbar, and use accent color for
-             primary text -->
-    <style name="ThemeOverlay.DeviceDefault.ActionBar.Accent" parent="ThemeOverlay.Material.ActionBar">
-        <item name="textColorPrimary">@color/btn_colored_borderless_text_material</item>
-    </style>
+    <!-- @hide Theme overlay that inherits from material actionbar -->
+    <style name="ThemeOverlay.DeviceDefault.ActionBar" parent="ThemeOverlay.Material.ActionBar" />
 
     <!-- @hide Theme overlay for a light popup in action bar -->
     <style name="ThemeOverlay.DeviceDefault.Popup.Light" parent="@style/ThemeOverlay.Material.Light" />
@@ -1725,9 +1696,11 @@
     </style>
 
     <style name="Theme.DeviceDefault.Notification" parent="@style/Theme.Material.Notification">
+        <item name="notificationHeaderTextAppearance">@style/TextAppearance.DeviceDefault.Notification.Info</item>
     </style>
 
     <style name="Theme.DeviceDefault.Notification.Ambient" parent="@style/Theme.Material.Notification.Ambient">
+        <item name="notificationHeaderTextAppearance">@style/TextAppearance.DeviceDefault.Notification.Info.Ambient</item>
     </style>
 
 </resources>
diff --git a/core/res/res/values/themes_permission_controller.xml b/core/res/res/values/themes_permission_controller.xml
deleted file mode 100644
index 369cee3..0000000
--- a/core/res/res/values/themes_permission_controller.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2018 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<resources>
-    <!-- themes for the permission grant dialog. -->
-    <style name="Theme.DeviceDefault.PermissionGrantApp"
-           parent="@style/Theme.DeviceDefault.Light.Panel">
-        <item name="windowIsFloating">false</item>
-        <item name="windowTranslucentStatus">true</item>
-        <item name="backgroundDimEnabled">true</item>
-        <item name="windowAnimationStyle">@style/Animation.Material.Dialog</item>
-    </style>
-
-    <style name="Theme.DeviceDefault.PermissionGrant"
-           parent="@style/Theme.DeviceDefault.Light.Dialog">
-        <item name="titleTextStyle">@style/PermissionGrantTitleMessage</item>
-        <item name="radioButtonStyle">@style/PermissionGrantRadioButton</item>
-        <item name="checkboxStyle">@style/PermissionGrantCheckbox</item>
-        <item name="buttonBarStyle">@style/PermissionGrantButtonBar</item>
-    </style>
-
-    <!-- themes for the permission review dialog. -->
-    <style name="Theme.DeviceDefault.PermissionReviewApp"
-           parent="@style/Theme.DeviceDefault.Settings">
-        <item name="windowActionBar">false</item>
-        <item name="windowNoTitle">true</item>
-        <item name="titleTextStyle">@style/PermissionReviewTitleMessage</item>
-        <item name="buttonBarStyle">@style/PermissionReviewButtonBar</item>
-    </style>
-</resources>
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 55e21a7..514ea0c 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -517,6 +517,28 @@
     }
 
     @Test
+    public void testMalformedTransate_int() throws Exception {
+        try {
+            // The non-standard Linux access mode 3 should throw
+            // an IllegalArgumentException.
+            translateModePosixToPfd(O_RDWR | O_WRONLY);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testMalformedTransate_string() throws Exception {
+        try {
+            // The non-standard Linux access mode 3 should throw
+            // an IllegalArgumentException.
+            translateModePosixToString(O_RDWR | O_WRONLY);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
     public void testTranslateMode_Invalid() throws Exception {
         try {
             translateModeStringToPosix("rwx");
diff --git a/core/tests/coretests/src/android/os/OsTests.java b/core/tests/coretests/src/android/os/OsTests.java
index 985fa4f..2b84126 100644
--- a/core/tests/coretests/src/android/os/OsTests.java
+++ b/core/tests/coretests/src/android/os/OsTests.java
@@ -33,7 +33,6 @@
         suite.addTestSuite(MessageQueueTest.class);
         suite.addTestSuite(MessengerTest.class);
         suite.addTestSuite(PatternMatcherTest.class);
-        suite.addTestSuite(SystemPropertiesTest.class);
 
         return suite;
     }
diff --git a/core/tests/coretests/src/android/os/SystemPropertiesTest.java b/core/tests/coretests/src/android/os/SystemPropertiesTest.java
deleted file mode 100644
index 25868ce..0000000
--- a/core/tests/coretests/src/android/os/SystemPropertiesTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os;
-
-import static junit.framework.Assert.assertEquals;
-import junit.framework.TestCase;
-
-import android.os.SystemProperties;
-import android.test.suitebuilder.annotation.SmallTest;
-
-public class SystemPropertiesTest extends TestCase {
-    private static final String KEY = "com.android.frameworks.coretests";
-    @SmallTest
-    public void testProperties() throws Exception {
-        if (false) {
-        String value;
-       
-        SystemProperties.set(KEY, "");
-        value = SystemProperties.get(KEY, "default");
-        assertEquals("default", value);
-
-        SystemProperties.set(KEY, "AAA");
-        value = SystemProperties.get(KEY, "default");
-        assertEquals("AAA", value);
-
-        value = SystemProperties.get(KEY);
-        assertEquals("AAA", value);
-
-        SystemProperties.set(KEY, "");
-        value = SystemProperties.get(KEY, "default");
-        assertEquals("default", value);
-
-        value = SystemProperties.get(KEY);
-        assertEquals("", value);
-        }
-    }
-}
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
new file mode 100644
index 0000000..800b864
--- /dev/null
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider;
+
+import static android.provider.DeviceConfig.OnPropertyChangedListener;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.fail;
+
+import android.app.ActivityThread;
+import android.content.ContentResolver;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/** Tests that ensure appropriate settings are backed up. */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DeviceConfigTest {
+    // TODO(b/109919982): Migrate tests to CTS
+    private static final String sNamespace = "namespace1";
+    private static final String sKey = "key1";
+    private static final String sValue = "value1";
+    private static final long WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS = 2000; // 2 sec
+
+    private final Object mLock = new Object();
+
+    @After
+    public void cleanUp() {
+        deleteViaContentProvider(sNamespace, sKey);
+    }
+
+    @Test
+    public void getProperty_empty() {
+        String result = DeviceConfig.getProperty(sNamespace, sKey);
+        assertNull(result);
+    }
+
+    @Test
+    public void setAndGetProperty_sameNamespace() {
+        DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
+        String result = DeviceConfig.getProperty(sNamespace, sKey);
+        assertEquals(sValue, result);
+    }
+
+    @Test
+    public void setAndGetProperty_differentNamespace() {
+        String newNamespace = "namespace2";
+        DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
+        String result = DeviceConfig.getProperty(newNamespace, sKey);
+        assertNull(result);
+    }
+
+    @Test
+    public void setAndGetProperty_multipleNamespaces() {
+        String newNamespace = "namespace2";
+        String newValue = "value2";
+        DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
+        DeviceConfig.setProperty(newNamespace, sKey, newValue, false);
+        String result = DeviceConfig.getProperty(sNamespace, sKey);
+        assertEquals(sValue, result);
+        result = DeviceConfig.getProperty(newNamespace, sKey);
+        assertEquals(newValue, result);
+
+        // clean up
+        deleteViaContentProvider(newNamespace, sKey);
+    }
+
+    @Test
+    public void setAndGetProperty_overrideValue() {
+        String newValue = "value2";
+        DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
+        DeviceConfig.setProperty(sNamespace, sKey, newValue, false);
+        String result = DeviceConfig.getProperty(sNamespace, sKey);
+        assertEquals(newValue, result);
+    }
+
+    @Test
+    public void testListener() {
+        setPropertyAndAssertSuccessfulChange(sNamespace, sKey, sValue);
+    }
+
+    private void setPropertyAndAssertSuccessfulChange(String setNamespace, String setName,
+            String setValue) {
+        final AtomicBoolean success = new AtomicBoolean();
+
+        OnPropertyChangedListener changeListener = new OnPropertyChangedListener() {
+                    @Override
+                    public void onPropertyChanged(String namespace, String name, String value) {
+                        assertEquals(setNamespace, namespace);
+                        assertEquals(setName, name);
+                        assertEquals(setValue, value);
+                        success.set(true);
+
+                        synchronized (mLock) {
+                            mLock.notifyAll();
+                        }
+                    }
+                };
+        Executor executor = ActivityThread.currentApplication().getMainExecutor();
+        DeviceConfig.addOnPropertyChangedListener(setNamespace, executor, changeListener);
+        try {
+            DeviceConfig.setProperty(setNamespace, setName, setValue, false);
+
+            final long startTimeMillis = SystemClock.uptimeMillis();
+            synchronized (mLock) {
+                while (true) {
+                    if (success.get()) {
+                        return;
+                    }
+                    final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+                    if (elapsedTimeMillis >= WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS) {
+                        fail("Could not change setting for "
+                                + WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS + " ms");
+                    }
+                    final long remainingTimeMillis = WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS
+                            - elapsedTimeMillis;
+                    try {
+                        mLock.wait(remainingTimeMillis);
+                    } catch (InterruptedException ie) {
+                        /* ignore */
+                    }
+                }
+            }
+        } finally {
+            DeviceConfig.removeOnPropertyChangedListener(changeListener);
+        }
+    }
+
+    private static boolean deleteViaContentProvider(String namespace, String key) {
+        ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
+        String compositeName = namespace + "/" + key;
+        Bundle result = resolver.call(
+                DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, compositeName, null);
+        assertNotNull(result);
+        return compositeName.equals(result.getString(Settings.NameValueTable.VALUE));
+    }
+
+}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 3a37fb6..68b08f9 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -269,6 +269,7 @@
                     Settings.Global.GNSS_SATELLITE_BLACKLIST,
                     Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS,
                     Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
+                    Settings.Global.HDMI_CONTROL_AUTO_TV_OFF_ENABLED,
                     Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
                     Settings.Global.HDMI_CONTROL_ENABLED,
                     Settings.Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
@@ -384,6 +385,7 @@
                     Settings.Global.PRIV_APP_OOB_LIST,
                     Settings.Global.PRIVATE_DNS_DEFAULT_MODE,
                     Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED,
+                    Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED,
                     Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED,
                     Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED,
                     Settings.Global.PROVISIONING_APN_ALARM_DELAY_IN_MS,
@@ -474,7 +476,8 @@
                     Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE,
                     Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS,
                     Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES,
-                    Settings.Global.UPDATED_GFX_DRIVER_DEV_OPT_IN_APP,
+                    Settings.Global.GUP_DEV_OPT_IN_APPS,
+                    Settings.Global.GUP_BLACK_LIST,
                     Settings.Global.GPU_DEBUG_LAYER_APP,
                     Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
                     Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT,
@@ -543,7 +546,9 @@
                     Settings.Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED,
                     Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS,
                     Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS,
-                    Settings.Global.BACKUP_MULTI_USER_ENABLED);
+                    Settings.Global.BACKUP_MULTI_USER_ENABLED,
+                    Settings.Global.ISOLATED_STORAGE_LOCAL,
+                    Settings.Global.ISOLATED_STORAGE_REMOTE);
     private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
              newHashSet(
                  Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
@@ -584,6 +589,7 @@
                  Settings.Secure.DEFAULT_INPUT_METHOD,
                  Settings.Secure.DEVICE_PAIRED,
                  Settings.Secure.DIALER_DEFAULT_APPLICATION,
+                 Settings.Secure.DISABLE_AIRPLANE_MODE_AFTER_SP_DISABLED,
                  Settings.Secure.DISABLED_PRINT_SERVICES,
                  Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS,
                  Settings.Secure.DISPLAY_DENSITY_FORCED,
@@ -605,6 +611,8 @@
                  Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT, // Candidate?
                  Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
                  Settings.Secure.LOCK_TO_APP_EXIT_LOCKED,
+                 Settings.Secure.MAINTAIN_AIRPLANE_MODE_AFTER_SP_DISABLED,
+                 Settings.Secure.MAINTAIN_LOCATION_AFTER_SP_DISABLED,
                  Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH,
                  Settings.Secure.MULTI_PRESS_TIMEOUT,
                  Settings.Secure.NFC_PAYMENT_FOREGROUND,
@@ -616,6 +624,7 @@
                  Settings.Secure.PARENTAL_CONTROL_LAST_UPDATE,
                  Settings.Secure.PAYMENT_SERVICE_SEARCH_URI,
                  Settings.Secure.PRINT_SERVICE_SEARCH_URI,
+                 Settings.Secure.REENABLE_LOCATION_AFTER_SP_DISABLED,
                  Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT, // Candidate?
                  Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY,
                  Settings.Secure.SEARCH_MAX_RESULTS_PER_SOURCE,
@@ -639,6 +648,7 @@
                  Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
                  Settings.Secure.SELECTED_SPELL_CHECKER,  // Intentionally removed in Q
                  Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE,  // Intentionally removed in Q
+                 Settings.Secure.SENSOR_PRIVACY_SENSOR_STATE,
                  Settings.Secure.SETTINGS_CLASSNAME,
                  Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, // candidate?
                  Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
diff --git a/core/tests/coretests/src/android/provider/SettingsProviderTest.java b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
index 04e8802..cb6f0e6 100644
--- a/core/tests/coretests/src/android/provider/SettingsProviderTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
@@ -44,12 +44,6 @@
 
 /** Unit test for SettingsProvider. */
 public class SettingsProviderTest extends AndroidTestCase {
-    /**
-     * TODO(b/113100523): Move this to DeviceConfig.java when it is added, and expose it as a System
-     *     API.
-     */
-    private static final Uri CONFIG_CONTENT_URI =
-            Uri.parse("content://" + Settings.AUTHORITY + "/config");
 
     @MediumTest
     public void testNameValueCache() {
@@ -406,27 +400,27 @@
         try {
             // value is empty
             Bundle results =
-                    r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
+                    r.call(DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
             assertNull(results.get(Settings.NameValueTable.VALUE));
 
             // save value
-            results = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, name, args);
+            results = r.call(DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, name, args);
             assertNull(results);
 
             // value is no longer empty
-            results = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
+            results = r.call(DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
             assertEquals(value, results.get(Settings.NameValueTable.VALUE));
 
             // save new value
             args.putString(Settings.NameValueTable.VALUE, newValue);
-            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, name, args);
+            r.call(DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, name, args);
 
             // new value is returned
-            results = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
+            results = r.call(DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
             assertEquals(newValue, results.get(Settings.NameValueTable.VALUE));
         } finally {
             // clean up
-            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, name, null);
+            r.call(DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, name, null);
         }
     }
 
@@ -440,22 +434,23 @@
 
         try {
             // save value
-            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, name, args);
+            r.call(DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, name, args);
 
             // get value
             Bundle results =
-                    r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
+                    r.call(DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
             assertEquals(value, results.get(Settings.NameValueTable.VALUE));
 
             // delete value
-            results = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, name, null);
+            results = r.call(DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, name,
+                    null);
 
             // value is empty now
-            results = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
+            results = r.call(DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
             assertNull(results.get(Settings.NameValueTable.VALUE));
         } finally {
             // clean up
-            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, name, null);
+            r.call(DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, name, null);
         }
     }
 
@@ -473,12 +468,12 @@
 
         try {
             // save both values
-            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, name, args);
+            r.call(DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, name, args);
             args.putString(Settings.NameValueTable.VALUE, newValue);
-            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, newName, args);
+            r.call(DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, newName, args);
 
             // list all values
-            Bundle result = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_LIST_CONFIG,
+            Bundle result = r.call(DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_LIST_CONFIG,
                     null, null);
             Map<String, String> keyValueMap =
                     (HashMap) result.getSerializable(Settings.NameValueTable.VALUE);
@@ -488,14 +483,14 @@
 
             // list values for prefix
             args.putString(Settings.CALL_METHOD_PREFIX_KEY, prefix);
-            result = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_LIST_CONFIG, null, args);
+            result = r.call(DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_LIST_CONFIG, null, args);
             keyValueMap = (HashMap) result.getSerializable(Settings.NameValueTable.VALUE);
             assertThat(keyValueMap, aMapWithSize(1));
             assertEquals(value, keyValueMap.get(name));
         } finally {
             // clean up
-            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, name, null);
-            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, newName, null);
+            r.call(DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, name, null);
+            r.call(DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, newName, null);
         }
     }
 }
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
new file mode 100644
index 0000000..d520f15
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view;
+
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static junit.framework.Assert.assertEquals;
+import static org.mockito.Mockito.verify;
+
+import android.graphics.Insets;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseArray;
+import android.view.SurfaceControl.Transaction;
+import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@RunWith(AndroidJUnit4.class)
+public class InsetsAnimationControlImplTest {
+
+    private InsetsAnimationControlImpl mController;
+
+    private SurfaceSession mSession = new SurfaceSession();
+    private SurfaceControl mTopLeash;
+    private SurfaceControl mNavLeash;
+
+    @Mock Transaction mMockTransaction;
+    @Mock InsetsController mMockController;
+    @Mock WindowInsetsAnimationControlListener mMockListener;
+    @Mock SyncRtSurfaceTransactionApplier mMockTransactionApplier;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mTopLeash = new SurfaceControl.Builder(mSession)
+                .setName("testSurface")
+                .build();
+        mNavLeash = new SurfaceControl.Builder(mSession)
+                .setName("testSurface")
+                .build();
+        InsetsState state = new InsetsState();
+        state.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 500, 100));
+        state.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(400, 0, 500, 500));
+        InsetsSourceConsumer topConsumer = new InsetsSourceConsumer(TYPE_TOP_BAR, state,
+                () -> mMockTransaction, mMockController);
+        topConsumer.setControl(new InsetsSourceControl(TYPE_TOP_BAR, mTopLeash));
+
+        InsetsSourceConsumer navConsumer = new InsetsSourceConsumer(TYPE_NAVIGATION_BAR, state,
+                () -> mMockTransaction, mMockController);
+        navConsumer.hide();
+        navConsumer.setControl(new InsetsSourceControl(TYPE_NAVIGATION_BAR, mNavLeash));
+
+        SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>();
+        consumers.put(TYPE_TOP_BAR, topConsumer);
+        consumers.put(TYPE_NAVIGATION_BAR, navConsumer);
+        mController = new InsetsAnimationControlImpl(consumers,
+                new Rect(0, 0, 500, 500), state, mMockListener, WindowInsets.Type.systemBars(),
+                () -> mMockTransactionApplier);
+    }
+
+    @Test
+    public void testGetters() {
+        assertEquals(Insets.of(0, 100, 100, 0), mController.getShownStateInsets());
+        assertEquals(Insets.of(0, 0, 0, 0), mController.getHiddenStateInsets());
+        assertEquals(Insets.of(0, 100, 0, 0), mController.getCurrentInsets());
+        assertEquals(WindowInsets.Type.systemBars(), mController.getTypes());
+    }
+
+    @Test
+    public void testChangeInsets() {
+        mController.changeInsets(Insets.of(0, 30, 40, 0));
+        assertEquals(Insets.of(0, 30, 40, 0), mController.getCurrentInsets());
+
+        ArgumentCaptor<SurfaceParams> captor = ArgumentCaptor.forClass(SurfaceParams.class);
+        verify(mMockTransactionApplier).scheduleApply(captor.capture());
+        List<SurfaceParams> params = captor.getAllValues();
+        assertEquals(2, params.size());
+        SurfaceParams first = params.get(0);
+        SurfaceParams second = params.get(1);
+        SurfaceParams topParams = first.surface == mTopLeash ? first : second;
+        SurfaceParams navParams = first.surface == mNavLeash ? first : second;
+        assertPosition(topParams.matrix, new Rect(0, 0, 500, 100), new Rect(0, -70, 500, 30));
+        assertPosition(navParams.matrix, new Rect(400, 0, 500, 500), new Rect(460, 0, 560, 500));
+    }
+
+    private void assertPosition(Matrix m, Rect original, Rect transformed) {
+        RectF rect = new RectF(original);
+        rect.offsetTo(0, 0);
+        m.mapRect(rect);
+        rect.round(original);
+        assertEquals(original, transformed);
+    }
+}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 2ad6028..d3d274a 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -28,6 +28,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mockito;
 
 @Presubmit
 @FlakyTest(detail = "Promote once confirmed non-flaky")
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 6bb9539..9807f26 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -16,10 +16,17 @@
 
 package android.view;
 
+import static android.view.InsetsState.INSET_SIDE_BOTTOM;
+import static android.view.InsetsState.INSET_SIDE_TOP;
 import static android.view.InsetsState.TYPE_IME;
 import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_SIDE_BAR_1;
+import static android.view.InsetsState.TYPE_SIDE_BAR_2;
+import static android.view.InsetsState.TYPE_SIDE_BAR_3;
 import static android.view.InsetsState.TYPE_TOP_BAR;
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
 import static org.junit.Assert.assertNotEquals;
 
 import android.graphics.Rect;
@@ -27,6 +34,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.FlakyTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseIntArray;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -45,9 +53,12 @@
         mState.getSource(TYPE_TOP_BAR).setVisible(true);
         mState.getSource(TYPE_IME).setFrame(new Rect(0, 200, 100, 300));
         mState.getSource(TYPE_IME).setVisible(true);
+        SparseIntArray typeSideMap = new SparseIntArray();
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
-                DisplayCutout.NO_CUTOUT);
+                DisplayCutout.NO_CUTOUT, typeSideMap);
         assertEquals(new Rect(0, 100, 0, 100), insets.getSystemWindowInsets());
+        assertEquals(INSET_SIDE_TOP, typeSideMap.get(TYPE_TOP_BAR));
+        assertEquals(INSET_SIDE_BOTTOM, typeSideMap.get(TYPE_IME));
     }
 
     @Test
@@ -57,7 +68,7 @@
         mState.getSource(TYPE_IME).setFrame(new Rect(0, 100, 100, 300));
         mState.getSource(TYPE_IME).setVisible(true);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
-                DisplayCutout.NO_CUTOUT);
+                DisplayCutout.NO_CUTOUT, null);
         assertEquals(100, insets.getStableInsetBottom());
         assertEquals(new Rect(0, 0, 0, 200), insets.getSystemWindowInsets());
     }
@@ -69,7 +80,7 @@
         mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
         mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
-                DisplayCutout.NO_CUTOUT);
+                DisplayCutout.NO_CUTOUT, null);
         assertEquals(new Rect(0, 100, 20, 0), insets.getSystemWindowInsets());
     }
 
@@ -81,7 +92,7 @@
         mState.getSource(TYPE_IME).setVisible(true);
         mState.removeSource(TYPE_IME);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
-                DisplayCutout.NO_CUTOUT);
+                DisplayCutout.NO_CUTOUT, null);
         assertEquals(0, insets.getSystemWindowInsetBottom());
     }
 
@@ -127,4 +138,13 @@
         p.recycle();
         assertEquals(mState, mState2);
     }
+
+    @Test
+    public void testGetDefaultVisibility() {
+        assertTrue(InsetsState.getDefaultVisibly(TYPE_TOP_BAR));
+        assertTrue(InsetsState.getDefaultVisibly(TYPE_SIDE_BAR_1));
+        assertTrue(InsetsState.getDefaultVisibly(TYPE_SIDE_BAR_2));
+        assertTrue(InsetsState.getDefaultVisibly(TYPE_SIDE_BAR_3));
+        assertFalse(InsetsState.getDefaultVisibly(TYPE_IME));
+    }
 }
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index 81ec85e..82eaf88 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -368,6 +368,7 @@
         assertThat(textLanguage, isTextLanguage("ja"));
     }
 
+    /* DISABLED: b/122467291
     @Test
     public void testSuggestConversationActions_textReplyOnly_maxThree() {
         if (isTextClassifierDisabled()) return;
@@ -395,7 +396,7 @@
             assertThat(conversationAction,
                     isConversationAction(ConversationActions.TYPE_TEXT_REPLY));
         }
-    }
+    }*/
 
     @Test
     public void testSuggestConversationActions_textReplyOnly_noMax() {
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index 97f02cb..dc3a12f 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.Binder;
+import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -637,7 +638,7 @@
 
     @Test
     public void testAddsDebugEntries() {
-        long startTime = System.currentTimeMillis();
+        long startTime = SystemClock.elapsedRealtime();
         TestBinderCallsStats bcs = new TestBinderCallsStats();
         bcs.setAddDebugEntries(true);
         ArrayList<BinderCallsStats.ExportedCallStat> callStats = bcs.getExportedCallStats();
diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
index f26dfad..b65c1e6 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -442,11 +442,13 @@
         LooperStats.ExportedEntry debugEntry1 = entries.get(1);
         assertThat(debugEntry1.handlerClassName).isEqualTo("");
         assertThat(debugEntry1.messageName).isEqualTo("__DEBUG_start_time_millis");
-        assertThat(debugEntry1.totalLatencyMicros).isEqualTo(looperStats.getStartTimeMillis());
+        assertThat(debugEntry1.totalLatencyMicros).isEqualTo(
+                looperStats.getStartElapsedTimeMillis());
         LooperStats.ExportedEntry debugEntry2 = entries.get(2);
         assertThat(debugEntry2.handlerClassName).isEqualTo("");
         assertThat(debugEntry2.messageName).isEqualTo("__DEBUG_end_time_millis");
-        assertThat(debugEntry2.totalLatencyMicros).isAtLeast(looperStats.getStartTimeMillis());
+        assertThat(debugEntry2.totalLatencyMicros).isAtLeast(
+                looperStats.getStartElapsedTimeMillis());
         LooperStats.ExportedEntry debugEntry3 = entries.get(3);
         assertThat(debugEntry3.handlerClassName).isEqualTo("");
         assertThat(debugEntry3.messageName).isEqualTo("__DEBUG_battery_time_millis");
diff --git a/core/tests/featureflagtests/Android.mk b/core/tests/featureflagtests/Android.mk
index 5e518b6..ce7cb18 100644
--- a/core/tests/featureflagtests/Android.mk
+++ b/core/tests/featureflagtests/Android.mk
@@ -9,7 +9,7 @@
     $(call all-java-files-under, src)
 
 LOCAL_DX_FLAGS := --core-library
-LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib androidx.test.rules
 LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
 LOCAL_PACKAGE_NAME := FrameworksCoreFeatureFlagTests
 LOCAL_PRIVATE_PLATFORM_APIS := true
diff --git a/core/tests/featureflagtests/AndroidManifest.xml b/core/tests/featureflagtests/AndroidManifest.xml
index b8ffacb..326374e 100644
--- a/core/tests/featureflagtests/AndroidManifest.xml
+++ b/core/tests/featureflagtests/AndroidManifest.xml
@@ -26,7 +26,7 @@
     </application>
 
     <instrumentation
-        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.frameworks.coretests.featureflagtests"
         android:label="Frameworks FeatureFlagUtils Tests" />
 
diff --git a/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java b/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
index 0b1b333..3160428 100644
--- a/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
+++ b/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
@@ -24,10 +24,11 @@
 import android.content.Context;
 import android.os.SystemProperties;
 import android.provider.Settings;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
index 96ab977..5a86885 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
@@ -278,13 +278,12 @@
             }
         }
 
-        final String no = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, " +
-            "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " +
-            "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip " +
-            "ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit " +
-            "esse cillum dolore eu fugiat nulla pariatur. " +
-            "Excepteur sint occaecat cupidatat non proident, " +
-            "sunt in culpa qui officia deserunt mollit anim id est laborum.";
+        final String no = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
+                + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
+                + "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo "
+                + "consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse "
+                + "cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non "
+                + "proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
         final String so = "Lorem ipsum: single overlay.";
         final String mo = "Lorem ipsum: multiple overlays.";
 
diff --git a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
index 6d5276f..27986cc 100644
--- a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
+++ b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2018 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy
- * of the License at
+ * 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
+ *      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.
+ * 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.om.hosttest;
 
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
index 8656781..c7b2dd1 100644
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
@@ -20,7 +20,7 @@
 LOCAL_PACKAGE_NAME := OverlayHostTests_UpdateOverlay
 LOCAL_SDK_VERSION := current
 LOCAL_COMPATIBILITY_SUITE := general-tests
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
 LOCAL_USE_AAPT2 := true
 LOCAL_AAPT_FLAGS := --no-resource-removal
 include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml b/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml
index 06077a7..8b5fe99 100644
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml
@@ -21,7 +21,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.server.om.hosttest.update_overlay_test"
         android:label="Update Overlay Test"/>
 </manifest>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java b/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
index a174d77..fef6320 100644
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
@@ -1,17 +1,17 @@
 /*
  * Copyright (C) 2018 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy
- * of the License at
+ * 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
+ *      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.
+ * 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.om.hosttest.update_overlay_test;
 
@@ -19,9 +19,10 @@
 
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/packagemanagertests/Android.mk b/core/tests/packagemanagertests/Android.mk
index f95231f..8c00d14 100644
--- a/core/tests/packagemanagertests/Android.mk
+++ b/core/tests/packagemanagertests/Android.mk
@@ -9,7 +9,7 @@
     $(call all-java-files-under, src)
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
+    androidx.test.rules \
     frameworks-base-testutils \
     mockito-target-minus-junit4
 
diff --git a/core/tests/packagemanagertests/AndroidManifest.xml b/core/tests/packagemanagertests/AndroidManifest.xml
index 8f49008..ee4a775 100644
--- a/core/tests/packagemanagertests/AndroidManifest.xml
+++ b/core/tests/packagemanagertests/AndroidManifest.xml
@@ -24,7 +24,7 @@
     </application>
 
     <instrumentation
-            android:name="android.support.test.runner.AndroidJUnitRunner"
+            android:name="androidx.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.frameworks.coretests.packagemanager"
             android:label="Frameworks PackageManager Core Tests" />
 
diff --git a/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java b/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
index 4e0f2a8..c5c9700 100644
--- a/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
+++ b/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
@@ -22,10 +22,11 @@
 import android.os.FileUtils;
 import android.os.ServiceManager;
 import android.os.UserManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/privacytests/Android.mk b/core/tests/privacytests/Android.mk
index 3c1526b..7765977 100644
--- a/core/tests/privacytests/Android.mk
+++ b/core/tests/privacytests/Android.mk
@@ -8,7 +8,7 @@
 LOCAL_SRC_FILES := \
     $(call all-java-files-under, src)
 
-LOCAL_STATIC_JAVA_LIBRARIES := junit rappor-tests android-support-test truth-prebuilt
+LOCAL_STATIC_JAVA_LIBRARIES := junit rappor-tests androidx.test.rules truth-prebuilt
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_PACKAGE_NAME := FrameworksPrivacyLibraryTests
diff --git a/core/tests/privacytests/AndroidManifest.xml b/core/tests/privacytests/AndroidManifest.xml
index a0e5281..3c82614 100644
--- a/core/tests/privacytests/AndroidManifest.xml
+++ b/core/tests/privacytests/AndroidManifest.xml
@@ -22,7 +22,7 @@
     </application>
 
     <instrumentation
-            android:name="android.support.test.runner.AndroidJUnitRunner"
+            android:name="androidx.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.frameworks.coretests.privacy"
             android:label="Frameworks Privacy Library Tests" />
 
diff --git a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
index c88a722..18928eb 100644
--- a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
@@ -17,21 +17,20 @@
 package android.privacy;
 
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.privacy.internal.longitudinalreporting.LongitudinalReportingConfig;
 import android.privacy.internal.longitudinalreporting.LongitudinalReportingEncoder;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
 
diff --git a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
index 71bd8f1..4a35350 100644
--- a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
@@ -22,8 +22,9 @@
 
 import android.privacy.internal.rappor.RapporConfig;
 import android.privacy.internal.rappor.RapporEncoder;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
index 933e54e..928351e 100644
--- a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
+++ b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
@@ -16,13 +16,13 @@
 
 package android.os;
 
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import junit.framework.TestCase;
 
-import android.os.SystemProperties;
-import android.test.suitebuilder.annotation.SmallTest;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 public class SystemPropertiesTest extends TestCase {
     private static final String KEY = "sys.testkey";
@@ -188,4 +188,25 @@
             fail("InterruptedException");
         }
     }
+
+    @SmallTest
+    public void testDigestOf() {
+        final String empty = SystemProperties.digestOf();
+        final String finger = SystemProperties.digestOf("ro.build.fingerprint");
+        final String fingerBrand = SystemProperties.digestOf(
+                "ro.build.fingerprint", "ro.product.brand");
+        final String brandFinger = SystemProperties.digestOf(
+                "ro.product.brand", "ro.build.fingerprint");
+
+        // Shouldn't change over time
+        assertTrue(Objects.equals(finger, SystemProperties.digestOf("ro.build.fingerprint")));
+
+        // Different properties means different results
+        assertFalse(Objects.equals(empty, finger));
+        assertFalse(Objects.equals(empty, fingerBrand));
+        assertFalse(Objects.equals(finger, fingerBrand));
+
+        // Same properties means same result
+        assertTrue(Objects.equals(fingerBrand, brandFinger));
+    }
 }
diff --git a/core/tests/utiltests/Android.mk b/core/tests/utiltests/Android.mk
index 5c60c81..343c07a 100644
--- a/core/tests/utiltests/Android.mk
+++ b/core/tests/utiltests/Android.mk
@@ -15,7 +15,7 @@
 LOCAL_JNI_SHARED_LIBRARIES := libmemoryintarraytest libcutils libc++
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
+    androidx.test.rules \
     frameworks-base-testutils \
     mockito-target-minus-junit4 \
 
diff --git a/core/tests/utiltests/AndroidManifest.xml b/core/tests/utiltests/AndroidManifest.xml
index 8db81ca..4ef4b1f 100644
--- a/core/tests/utiltests/AndroidManifest.xml
+++ b/core/tests/utiltests/AndroidManifest.xml
@@ -51,7 +51,7 @@
     </application>
 
     <instrumentation
-        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.frameworks.utiltests"
         android:label="Frameworks Utility Tests" />
 
diff --git a/core/tests/utiltests/runtests.sh b/core/tests/utiltests/runtests.sh
index 853119f..3b46cbb 100755
--- a/core/tests/utiltests/runtests.sh
+++ b/core/tests/utiltests/runtests.sh
@@ -21,4 +21,4 @@
 
 adb install -r -g "$OUT/data/app/FrameworksUtilTests/FrameworksUtilTests.apk"
 
-adb shell am instrument -w "$@" 'com.android.frameworks.utiltests/android.support.test.runner.AndroidJUnitRunner'
+adb shell am instrument -w "$@" 'com.android.frameworks.utiltests/androidx.test.runner.AndroidJUnitRunner'
diff --git a/core/tests/utiltests/src/android/util/IntArrayTest.java b/core/tests/utiltests/src/android/util/IntArrayTest.java
index a6120a1..a76c640 100644
--- a/core/tests/utiltests/src/android/util/IntArrayTest.java
+++ b/core/tests/utiltests/src/android/util/IntArrayTest.java
@@ -19,8 +19,9 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/utiltests/src/android/util/LongArrayTest.java b/core/tests/utiltests/src/android/util/LongArrayTest.java
index a7afcbd..a9a168b 100644
--- a/core/tests/utiltests/src/android/util/LongArrayTest.java
+++ b/core/tests/utiltests/src/android/util/LongArrayTest.java
@@ -19,8 +19,9 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
index 24b33ef..2daefe7 100644
--- a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
+++ b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
@@ -24,8 +24,11 @@
 
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.runner.AndroidJUnit4;
+
 import libcore.io.IoUtils;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/utiltests/src/android/util/RemoteIntArray.java b/core/tests/utiltests/src/android/util/RemoteIntArray.java
index 11d0888..4a3a01e 100644
--- a/core/tests/utiltests/src/android/util/RemoteIntArray.java
+++ b/core/tests/utiltests/src/android/util/RemoteIntArray.java
@@ -24,7 +24,8 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
+
+import androidx.test.InstrumentationRegistry;
 
 import java.io.Closeable;
 import java.io.IOException;
diff --git a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
index b18ee17..03cf3eb 100644
--- a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
@@ -29,11 +29,12 @@
 import android.content.pm.UserInfo;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
 import android.test.mock.MockContentResolver;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.internal.widget.ILockSettings;
 import com.android.internal.widget.LockPatternUtils;
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
new file mode 100644
index 0000000..25dabad
--- /dev/null
+++ b/data/etc/Android.bp
@@ -0,0 +1,64 @@
+// Copyright (C} 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License"};
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+// Sysconfig files
+
+prebuilt_etc {
+    name: "framework-sysconfig.xml",
+    sub_dir: "sysconfig",
+    src: "framework-sysconfig.xml",
+}
+
+prebuilt_etc {
+    name: "hiddenapi-package-whitelist.xml",
+    sub_dir: "sysconfig",
+    src: "hiddenapi-package-whitelist.xml",
+}
+
+// Privapp permission whitelist files
+
+prebuilt_etc {
+    name: "platform.xml",
+    sub_dir: "permissions",
+    src: "platform.xml",
+}
+
+prebuilt_etc {
+    name: "privapp-permissions-platform.xml",
+    sub_dir: "permissions",
+    src: "privapp-permissions-platform.xml",
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.settings",
+    product_specific: true,
+    sub_dir: "permissions",
+    src: "com.android.settings.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.systemui",
+    product_specific: true,
+    sub_dir: "permissions",
+    src: "com.android.systemui.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "com.android.timezone.updater.xml",
+    sub_dir: "permissions",
+    src: "com.android.timezone.updater.xml",
+}
diff --git a/data/etc/Android.mk b/data/etc/Android.mk
deleted file mode 100644
index d24c140a..0000000
--- a/data/etc/Android.mk
+++ /dev/null
@@ -1,57 +0,0 @@
-#
-# Copyright (C) 2008 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# 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.
-#
-
-LOCAL_PATH := $(my-dir)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := framework-sysconfig.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/sysconfig
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := platform.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := privapp-permissions-platform.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := hiddenapi-package-whitelist.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/sysconfig
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := com.android.timezone.updater.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_RELATIVE_PATH := permissions
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml
new file mode 100644
index 0000000..2110a8f
--- /dev/null
+++ b/data/etc/com.android.settings.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="com.android.settings">
+        <permission name="android.permission.ACCESS_CHECKIN_PROPERTIES"/>
+        <permission name="android.permission.ACCESS_NOTIFICATIONS"/>
+        <permission name="android.permission.BACKUP"/>
+        <permission name="android.permission.BATTERY_STATS"/>
+        <permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
+        <permission name="android.permission.CHANGE_APP_IDLE_STATE"/>
+        <permission name="android.permission.CHANGE_CONFIGURATION"/>
+        <permission name="android.permission.DELETE_PACKAGES"/>
+        <permission name="android.permission.FORCE_STOP_PACKAGES"/>
+        <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
+        <permission name="android.permission.MANAGE_DEBUGGING"/>
+        <permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
+        <permission name="android.permission.MANAGE_FINGERPRINT"/>
+        <permission name="android.permission.MANAGE_USB"/>
+        <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE" />
+        <permission name="android.permission.MASTER_CLEAR"/>
+        <permission name="android.permission.MODIFY_PHONE_STATE"/>
+        <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
+        <permission name="android.permission.MOVE_PACKAGE"/>
+        <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
+        <permission name="android.permission.PACKAGE_USAGE_STATS"/>
+        <permission name="android.permission.READ_SEARCH_INDEXABLES"/>
+        <permission name="android.permission.REBOOT"/>
+        <permission name="android.permission.SET_TIME"/>
+        <permission name="android.permission.STATUS_BAR"/>
+        <permission name="android.permission.TETHER_PRIVILEGED"/>
+        <permission name="android.permission.USE_RESERVED_DISK"/>
+        <permission name="android.permission.USER_ACTIVITY"/>
+        <permission name="android.permission.WRITE_APN_SETTINGS"/>
+        <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+        <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
new file mode 100644
index 0000000..b65bc1d
--- /dev/null
+++ b/data/etc/com.android.systemui.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="com.android.systemui">
+        <permission name="android.permission.BATTERY_STATS"/>
+        <permission name="android.permission.BIND_APPWIDGET"/>
+        <permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
+        <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
+        <permission name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"/>
+        <permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/>
+        <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
+        <permission name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS"/>
+        <permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
+        <permission name="android.permission.CONTROL_VPN"/>
+        <permission name="android.permission.DUMP"/>
+        <permission name="android.permission.GET_APP_OPS_STATS"/>
+        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+        <permission name="android.permission.MANAGE_ACTIVITY_STACKS"/>
+        <permission name="android.permission.MANAGE_DEBUGGING"/>
+        <permission name="android.permission.MANAGE_SENSOR_PRIVACY"/>
+        <permission name="android.permission.MANAGE_USB"/>
+        <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.MASTER_CLEAR"/>
+        <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
+        <permission name="android.permission.MODIFY_PHONE_STATE"/>
+        <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
+        <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
+        <permission name="android.permission.READ_DREAM_STATE"/>
+        <permission name="android.permission.READ_FRAME_BUFFER"/>
+        <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
+        <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+        <permission name="android.permission.REAL_GET_TASKS"/>
+        <permission name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE"/>
+        <permission name="android.permission.START_ACTIVITY_AS_CALLER"/>
+        <permission name="android.permission.START_TASKS_FROM_RECENTS"/>
+        <permission name="android.permission.STATUS_BAR"/>
+        <permission name="android.permission.STOP_APP_SWITCHES"/>
+        <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
+        <permission name="android.permission.TETHER_PRIVILEGED"/>
+        <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
+        <permission name="android.permission.USE_RESERVED_DISK"/>
+        <permission name="android.permission.WRITE_DREAM_STATE"/>
+        <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+        <permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/>
+        <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index dcf95fd..58b57e5 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -33,6 +33,10 @@
         <permission name="android.permission.CRYPT_KEEPER"/>
     </privapp-permissions>
 
+    <privapp-permissions package="com.android.carrierconfig">
+        <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+    </privapp-permissions>
+
     <privapp-permissions package="com.android.cellbroadcastreceiver">
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.MANAGE_USERS"/>
@@ -250,42 +254,6 @@
         <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
     </privapp-permissions>
 
-    <privapp-permissions package="com.android.settings">
-        <permission name="android.permission.ACCESS_CHECKIN_PROPERTIES"/>
-        <permission name="android.permission.ACCESS_NOTIFICATIONS"/>
-        <permission name="android.permission.BACKUP"/>
-        <permission name="android.permission.BATTERY_STATS"/>
-        <permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
-        <permission name="android.permission.CHANGE_APP_IDLE_STATE"/>
-        <permission name="android.permission.CHANGE_CONFIGURATION"/>
-        <permission name="android.permission.DELETE_PACKAGES"/>
-        <permission name="android.permission.FORCE_STOP_PACKAGES"/>
-        <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
-        <permission name="android.permission.MANAGE_DEBUGGING"/>
-        <permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
-        <permission name="android.permission.MANAGE_FINGERPRINT"/>
-        <permission name="android.permission.MANAGE_USB"/>
-        <permission name="android.permission.MANAGE_USERS"/>
-        <permission name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE" />
-        <permission name="android.permission.MASTER_CLEAR"/>
-        <permission name="android.permission.MODIFY_PHONE_STATE"/>
-        <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
-        <permission name="android.permission.MOVE_PACKAGE"/>
-        <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
-        <permission name="android.permission.PACKAGE_USAGE_STATS"/>
-        <permission name="android.permission.READ_SEARCH_INDEXABLES"/>
-        <permission name="android.permission.REBOOT"/>
-        <permission name="android.permission.SET_TIME"/>
-        <permission name="android.permission.STATUS_BAR"/>
-        <permission name="android.permission.TETHER_PRIVILEGED"/>
-        <permission name="android.permission.USE_RESERVED_DISK"/>
-        <permission name="android.permission.USER_ACTIVITY"/>
-        <permission name="android.permission.WRITE_APN_SETTINGS"/>
-        <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
-        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
-        <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
-    </privapp-permissions>
-
     <privapp-permissions package="com.android.settings.intelligence">
         <permission name="android.permission.MANAGE_FINGERPRINT"/>
         <permission name="android.permission.MODIFY_PHONE_STATE"/>
@@ -333,6 +301,8 @@
         <permission name="android.permission.POWER_SAVER" />
         <permission name="android.permission.READ_FRAME_BUFFER"/>
         <permission name="android.permission.READ_LOWPAN_CREDENTIAL"/>
+        <!-- Needed for test only -->
+        <permission name="android.permission.READ_PRECISE_PHONE_STATE" />
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
         <permission name="android.permission.REAL_GET_TASKS"/>
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
@@ -370,50 +340,6 @@
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
     </privapp-permissions>
 
-    <privapp-permissions package="com.android.systemui">
-        <permission name="android.permission.BATTERY_STATS"/>
-        <permission name="android.permission.BIND_APPWIDGET"/>
-        <permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
-        <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
-        <permission name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"/>
-        <permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/>
-        <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
-        <permission name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS"/>
-        <permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
-        <permission name="android.permission.CONTROL_VPN"/>
-        <permission name="android.permission.DUMP"/>
-        <permission name="android.permission.GET_APP_OPS_STATS"/>
-        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
-        <permission name="android.permission.MANAGE_ACTIVITY_STACKS"/>
-        <permission name="android.permission.MANAGE_DEBUGGING"/>
-        <permission name="android.permission.MANAGE_USB"/>
-        <permission name="android.permission.MANAGE_USERS"/>
-        <permission name="android.permission.MASTER_CLEAR"/>
-        <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
-        <permission name="android.permission.MODIFY_PHONE_STATE"/>
-        <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
-        <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
-        <permission name="android.permission.READ_DREAM_STATE"/>
-        <permission name="android.permission.READ_FRAME_BUFFER"/>
-        <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
-        <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
-        <permission name="android.permission.REAL_GET_TASKS"/>
-        <permission name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE"/>
-        <permission name="android.permission.START_ACTIVITY_AS_CALLER"/>
-        <permission name="android.permission.START_TASKS_FROM_RECENTS"/>
-        <permission name="android.permission.STATUS_BAR"/>
-        <permission name="android.permission.STOP_APP_SWITCHES"/>
-        <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
-        <permission name="android.permission.TETHER_PRIVILEGED"/>
-        <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
-        <permission name="android.permission.USE_RESERVED_DISK"/>
-        <permission name="android.permission.WRITE_DREAM_STATE"/>
-        <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
-        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
-        <permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/>
-        <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
-    </privapp-permissions>
-
     <privapp-permissions package="com.android.tv">
         <permission name="android.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE"/>
         <permission name="android.permission.DVB_DEVICE"/>
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index c84c035..04bf804 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -312,16 +312,14 @@
         <font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font>
     </family>
     <family lang="und-Mymr" variant="elegant">
-        <font weight="400" style="normal">NotoSansMyanmar-Regular.otf</font>
-        <font weight="500" style="normal">NotoSansMyanmar-Medium.otf</font>
-        <font weight="700" style="normal">NotoSansMyanmar-Bold.otf</font>
+        <font weight="400" style="normal">NotoSansMyanmar-Regular-ZawDecode.ttf</font>
+        <font weight="700" style="normal">NotoSansMyanmar-Bold-ZawDecode.ttf</font>
         <font weight="400" style="normal" fallbackFor="serif">NotoSerifMyanmar-Regular.otf</font>
         <font weight="700" style="normal" fallbackFor="serif">NotoSerifMyanmar-Bold.otf</font>
     </family>
     <family lang="und-Mymr" variant="compact">
-        <font weight="400" style="normal">NotoSansMyanmarUI-Regular.otf</font>
-        <font weight="500" style="normal">NotoSansMyanmarUI-Medium.otf</font>
-        <font weight="700" style="normal">NotoSansMyanmarUI-Bold.otf</font>
+        <font weight="400" style="normal">NotoSansMyanmarUI-Regular-ZawDecode.ttf</font>
+        <font weight="700" style="normal">NotoSansMyanmarUI-Bold-ZawDecode.ttf</font>
     </family>
     <family lang="und-Thaa">
         <font weight="400" style="normal">NotoSansThaana-Regular.ttf</font>
@@ -365,7 +363,7 @@
         <font weight="400" style="normal">NotoSansCarian-Regular.ttf</font>
     </family>
     <family lang="und-Cakm">
-        <font weight="400" style="normal">NotoSansChakma-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansChakma-Regular.otf</font>
     </family>
     <family lang="und-Cher">
         <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
diff --git a/data/sounds/AllAudio.mk b/data/sounds/AllAudio.mk
index bb8add1..c6c7d3b 100644
--- a/data/sounds/AllAudio.mk
+++ b/data/sounds/AllAudio.mk
@@ -15,227 +15,227 @@
 LOCAL_PATH := frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-    $(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
-    $(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
-    $(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
-    $(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
-    $(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
-    $(LOCAL_PATH)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Argon.ogg:system/media/audio/alarms/Argon.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Barium.ogg:system/media/audio/alarms/Barium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Carbon.ogg:system/media/audio/alarms/Carbon.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Cesium.ogg:system/media/audio/alarms/Cesium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Fermium.ogg:system/media/audio/alarms/Fermium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Hassium.ogg:system/media/audio/alarms/Hassium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Helium.ogg:system/media/audio/alarms/Helium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Krypton.ogg:system/media/audio/alarms/Krypton.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Neon.ogg:system/media/audio/alarms/Neon.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:system/media/audio/alarms/Neptunium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:system/media/audio/alarms/Nobelium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Osmium.ogg:system/media/audio/alarms/Osmium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:system/media/audio/alarms/Oxygen.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:system/media/audio/alarms/Platinum.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:system/media/audio/alarms/Plutonium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Promethium.ogg:system/media/audio/alarms/Promethium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Scandium.ogg:system/media/audio/alarms/Scandium.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
-    $(LOCAL_PATH)/notifications/Aldebaran.ogg:system/media/audio/notifications/Aldebaran.ogg \
-    $(LOCAL_PATH)/notifications/Altair.ogg:system/media/audio/notifications/Altair.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Alya.ogg:system/media/audio/notifications/Alya.ogg \
-    $(LOCAL_PATH)/notifications/Antares.ogg:system/media/audio/notifications/Antares.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Antimony.ogg:system/media/audio/notifications/Antimony.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Argon.ogg:system/media/audio/notifications/Argon.ogg \
-    $(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:system/media/audio/notifications/Bellatrix.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Beryllium.ogg:system/media/audio/notifications/Beryllium.ogg \
-    $(LOCAL_PATH)/notifications/Betelgeuse.ogg:system/media/audio/notifications/Betelgeuse.ogg \
-    $(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg \
-    $(LOCAL_PATH)/notifications/Canopus.ogg:system/media/audio/notifications/Canopus.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-    $(LOCAL_PATH)/notifications/Castor.ogg:system/media/audio/notifications/Castor.ogg \
-    $(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Cobalt.ogg:system/media/audio/notifications/Cobalt.ogg \
-    $(LOCAL_PATH)/notifications/Cricket.ogg:system/media/audio/notifications/Cricket.ogg \
-    $(LOCAL_PATH)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \
-    $(LOCAL_PATH)/notifications/Deneb.ogg:system/media/audio/notifications/Deneb.ogg \
-    $(LOCAL_PATH)/notifications/Doink.ogg:system/media/audio/notifications/Doink.ogg \
-    $(LOCAL_PATH)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \
-    $(LOCAL_PATH)/notifications/Drip.ogg:system/media/audio/notifications/Drip.ogg \
-    $(LOCAL_PATH)/notifications/Electra.ogg:system/media/audio/notifications/Electra.ogg \
-    $(LOCAL_PATH)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \
-    $(LOCAL_PATH)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \
-    $(LOCAL_PATH)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Fluorine.ogg:system/media/audio/notifications/Fluorine.ogg \
-    $(LOCAL_PATH)/notifications/Fomalhaut.ogg:system/media/audio/notifications/Fomalhaut.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Gallium.ogg:system/media/audio/notifications/Gallium.ogg \
-    $(LOCAL_PATH)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Helium.ogg:system/media/audio/notifications/Helium.ogg \
-    $(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Iridium.ogg:system/media/audio/notifications/Iridium.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Krypton.ogg:system/media/audio/notifications/Krypton.ogg \
-    $(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Lalande.ogg:system/media/audio/notifications/Lalande.ogg \
-    $(LOCAL_PATH)/notifications/Merope.ogg:system/media/audio/notifications/Merope.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
-    $(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Palladium.ogg:system/media/audio/notifications/Palladium.ogg \
-    $(LOCAL_PATH)/notifications/Plastic_Pipe.ogg:system/media/audio/notifications/Plastic_Pipe.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Polaris.ogg:system/media/audio/notifications/Polaris.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Proxima.ogg:system/media/audio/notifications/Proxima.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Radon.ogg:system/media/audio/notifications/Radon.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Rubidium.ogg:system/media/audio/notifications/Rubidium.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Selenium.ogg:system/media/audio/notifications/Selenium.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
-    $(LOCAL_PATH)/notifications/Sirrah.ogg:system/media/audio/notifications/Sirrah.ogg \
-    $(LOCAL_PATH)/notifications/SpaceSeed.ogg:system/media/audio/notifications/SpaceSeed.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Strontium.ogg:system/media/audio/notifications/Strontium.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Syrma.ogg:system/media/audio/notifications/Syrma.ogg \
-    $(LOCAL_PATH)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Talitha.ogg:system/media/audio/notifications/Talitha.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Tejat.ogg:system/media/audio/notifications/Tejat.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Thallium.ogg:system/media/audio/notifications/Thallium.ogg \
-    $(LOCAL_PATH)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:system/media/audio/notifications/Upsilon.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
-    $(LOCAL_PATH)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Xenon.ogg:system/media/audio/notifications/Xenon.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Zirconium.ogg:system/media/audio/notifications/Zirconium.ogg \
-    $(LOCAL_PATH)/notifications/arcturus.ogg:system/media/audio/notifications/arcturus.ogg \
-    $(LOCAL_PATH)/notifications/moonbeam.ogg:system/media/audio/notifications/moonbeam.ogg \
-    $(LOCAL_PATH)/notifications/pixiedust.ogg:system/media/audio/notifications/pixiedust.ogg \
-    $(LOCAL_PATH)/notifications/pizzicato.ogg:system/media/audio/notifications/pizzicato.ogg \
-    $(LOCAL_PATH)/notifications/regulus.ogg:system/media/audio/notifications/regulus.ogg \
-    $(LOCAL_PATH)/notifications/sirius.ogg:system/media/audio/notifications/sirius.ogg \
-    $(LOCAL_PATH)/notifications/tweeters.ogg:system/media/audio/notifications/tweeters.ogg \
-    $(LOCAL_PATH)/notifications/vega.ogg:system/media/audio/notifications/vega.ogg \
-    $(LOCAL_PATH)/ringtones/ANDROMEDA.ogg:system/media/audio/ringtones/ANDROMEDA.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Atria.ogg:system/media/audio/ringtones/Atria.ogg \
-    $(LOCAL_PATH)/ringtones/BOOTES.ogg:system/media/audio/ringtones/BOOTES.ogg \
-    $(LOCAL_PATH)/newwavelabs/Backroad.ogg:system/media/audio/ringtones/Backroad.ogg \
-    $(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \
-    $(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \
-    $(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:system/media/audio/ringtones/Big_Easy.ogg \
-    $(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \
-    $(LOCAL_PATH)/newwavelabs/Bollywood.ogg:system/media/audio/ringtones/Bollywood.ogg \
-    $(LOCAL_PATH)/newwavelabs/BussaMove.ogg:system/media/audio/ringtones/BussaMove.ogg \
-    $(LOCAL_PATH)/ringtones/CANISMAJOR.ogg:system/media/audio/ringtones/CANISMAJOR.ogg \
-    $(LOCAL_PATH)/ringtones/CASSIOPEIA.ogg:system/media/audio/ringtones/CASSIOPEIA.ogg \
-    $(LOCAL_PATH)/newwavelabs/Cairo.ogg:system/media/audio/ringtones/Cairo.ogg \
-    $(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:system/media/audio/ringtones/Calypso_Steel.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:system/media/audio/ringtones/CanisMajor.ogg \
-    $(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:system/media/audio/ringtones/CaribbeanIce.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-    $(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:system/media/audio/ringtones/Champagne_Edition.ogg \
-    $(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:system/media/audio/ringtones/Club_Cubano.ogg \
-    $(LOCAL_PATH)/newwavelabs/CrayonRock.ogg:system/media/audio/ringtones/CrayonRock.ogg \
-    $(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:system/media/audio/ringtones/CrazyDream.ogg \
-    $(LOCAL_PATH)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
-    $(LOCAL_PATH)/newwavelabs/DancinFool.ogg:system/media/audio/ringtones/DancinFool.ogg \
-    $(LOCAL_PATH)/newwavelabs/Ding.ogg:system/media/audio/ringtones/Ding.ogg \
-    $(LOCAL_PATH)/newwavelabs/DonMessWivIt.ogg:system/media/audio/ringtones/DonMessWivIt.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
-    $(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:system/media/audio/ringtones/DreamTheme.ogg \
-    $(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:system/media/audio/ringtones/Eastern_Sky.ogg \
-    $(LOCAL_PATH)/newwavelabs/Enter_the_Nexus.ogg:system/media/audio/ringtones/Enter_the_Nexus.ogg \
-    $(LOCAL_PATH)/ringtones/Eridani.ogg:system/media/audio/ringtones/Eridani.ogg \
-    $(LOCAL_PATH)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \
-    $(LOCAL_PATH)/ringtones/FreeFlight.ogg:system/media/audio/ringtones/FreeFlight.ogg \
-    $(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:system/media/audio/ringtones/FriendlyGhost.ogg \
-    $(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:system/media/audio/ringtones/Funk_Yall.ogg \
-    $(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:system/media/audio/ringtones/GameOverGuitar.ogg \
-    $(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:system/media/audio/ringtones/Gimme_Mo_Town.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg \
-    $(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:system/media/audio/ringtones/Glacial_Groove.ogg \
-    $(LOCAL_PATH)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \
-    $(LOCAL_PATH)/newwavelabs/HalfwayHome.ogg:system/media/audio/ringtones/HalfwayHome.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \
-    $(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:system/media/audio/ringtones/InsertCoin.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:system/media/audio/ringtones/Kuma.ogg \
-    $(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \
-    $(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \
-    $(LOCAL_PATH)/ringtones/Lyra.ogg:system/media/audio/ringtones/Lyra.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-    $(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \
-    $(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \
-    $(LOCAL_PATH)/newwavelabs/Nairobi.ogg:system/media/audio/ringtones/Nairobi.ogg \
-    $(LOCAL_PATH)/newwavelabs/Nassau.ogg:system/media/audio/ringtones/Nassau.ogg \
-    $(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:system/media/audio/ringtones/NewPlayer.ogg \
-    $(LOCAL_PATH)/newwavelabs/No_Limits.ogg:system/media/audio/ringtones/No_Limits.ogg \
-    $(LOCAL_PATH)/newwavelabs/Noises1.ogg:system/media/audio/ringtones/Noises1.ogg \
-    $(LOCAL_PATH)/newwavelabs/Noises2.ogg:system/media/audio/ringtones/Noises2.ogg \
-    $(LOCAL_PATH)/newwavelabs/Noises3.ogg:system/media/audio/ringtones/Noises3.ogg \
-    $(LOCAL_PATH)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-    $(LOCAL_PATH)/ringtones/PERSEUS.ogg:system/media/audio/ringtones/PERSEUS.ogg \
-    $(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:system/media/audio/ringtones/Paradise_Island.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:system/media/audio/ringtones/Perseus.ogg \
-    $(LOCAL_PATH)/newwavelabs/Playa.ogg:system/media/audio/ringtones/Playa.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Rasalas.ogg:system/media/audio/ringtones/Rasalas.ogg \
-    $(LOCAL_PATH)/newwavelabs/Revelation.ogg:system/media/audio/ringtones/Revelation.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \
-    $(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \
-    $(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
-    $(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
-    $(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
-    $(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:system/media/audio/ringtones/Road_Trip.ogg \
-    $(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:system/media/audio/ringtones/RomancingTheTone.ogg \
-    $(LOCAL_PATH)/newwavelabs/Safari.ogg:system/media/audio/ringtones/Safari.ogg \
-    $(LOCAL_PATH)/newwavelabs/Savannah.ogg:system/media/audio/ringtones/Savannah.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-    $(LOCAL_PATH)/newwavelabs/Seville.ogg:system/media/audio/ringtones/Seville.ogg \
-    $(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:system/media/audio/ringtones/Shes_All_That.ogg \
-    $(LOCAL_PATH)/newwavelabs/SilkyWay.ogg:system/media/audio/ringtones/SilkyWay.ogg \
-    $(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:system/media/audio/ringtones/SitarVsSitar.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-    $(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:system/media/audio/ringtones/SpringyJalopy.ogg \
-    $(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:system/media/audio/ringtones/Steppin_Out.ogg \
-    $(LOCAL_PATH)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \
-    $(LOCAL_PATH)/ringtones/Testudo.ogg:system/media/audio/ringtones/Testudo.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
-    $(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:system/media/audio/ringtones/Third_Eye.ogg \
-    $(LOCAL_PATH)/newwavelabs/Thunderfoot.ogg:system/media/audio/ringtones/Thunderfoot.ogg \
-    $(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \
-    $(LOCAL_PATH)/ringtones/URSAMINOR.ogg:system/media/audio/ringtones/URSAMINOR.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:system/media/audio/ringtones/UrsaMinor.ogg \
-    $(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:system/media/audio/ringtones/VeryAlarmed.ogg \
-    $(LOCAL_PATH)/ringtones/Vespa.ogg:system/media/audio/ringtones/Vespa.ogg \
-    $(LOCAL_PATH)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:system/media/audio/ringtones/Zeta.ogg \
-    $(LOCAL_PATH)/ringtones/hydra.ogg:system/media/audio/ringtones/hydra.ogg \
-    $(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-    $(LOCAL_PATH)/effects/ogg/Effect_Tick_48k.ogg:system/media/audio/ui/Effect_Tick.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressDelete_120_48k.ogg:system/media/audio/ui/KeypressDelete.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressReturn_120_48k.ogg:system/media/audio/ui/KeypressReturn.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120_48k.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressStandard_120_48k.ogg:system/media/audio/ui/KeypressStandard.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressInvalid_120_48k.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-    $(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
-    $(LOCAL_PATH)/effects/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-    $(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-    $(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-    $(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:system/media/audio/ui/Trusted.ogg \
-    $(LOCAL_PATH)/effects/ogg/VideoRecord_48k.ogg:system/media/audio/ui/VideoRecord.ogg \
-    $(LOCAL_PATH)/effects/ogg/VideoStop_48k.ogg:system/media/audio/ui/VideoStop.ogg \
-    $(LOCAL_PATH)/effects/ogg/WirelessChargingStarted.ogg:system/media/audio/ui/WirelessChargingStarted.ogg \
-    $(LOCAL_PATH)/effects/ogg/camera_click_48k.ogg:system/media/audio/ui/camera_click.ogg \
-    $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-    $(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:system/media/audio/ui/ChargingStarted.ogg \
-    $(LOCAL_PATH)/effects/ogg/InCallNotification.ogg:system/media/audio/ui/InCallNotification.ogg \
-    $(LOCAL_PATH)/effects/ogg/NFCFailure.ogg:system/media/audio/ui/NFCFailure.ogg \
-    $(LOCAL_PATH)/effects/ogg/NFCInitiated.ogg:system/media/audio/ui/NFCInitiated.ogg \
-    $(LOCAL_PATH)/effects/ogg/NFCSuccess.ogg:system/media/audio/ui/NFCSuccess.ogg \
-    $(LOCAL_PATH)/effects/ogg/NFCTransferComplete.ogg:system/media/audio/ui/NFCTransferComplete.ogg \
-    $(LOCAL_PATH)/effects/ogg/NFCTransferInitiated.ogg:system/media/audio/ui/NFCTransferInitiated.ogg \
+    $(LOCAL_PATH)/Alarm_Beep_01.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_01.ogg \
+    $(LOCAL_PATH)/Alarm_Beep_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_02.ogg \
+    $(LOCAL_PATH)/Alarm_Beep_03.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_03.ogg \
+    $(LOCAL_PATH)/Alarm_Buzzer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Buzzer.ogg \
+    $(LOCAL_PATH)/Alarm_Classic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Classic.ogg \
+    $(LOCAL_PATH)/Alarm_Rooster_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Rooster_02.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Argon.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Barium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Barium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Carbon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Carbon.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Cesium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Cesium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Fermium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Fermium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Hassium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Hassium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Helium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Krypton.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Krypton.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Neon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neon.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neptunium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Nobelium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Osmium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Osmium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Oxygen.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Platinum.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Plutonium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Promethium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Promethium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Scandium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Scandium.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
+    $(LOCAL_PATH)/notifications/Aldebaran.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Aldebaran.ogg \
+    $(LOCAL_PATH)/notifications/Altair.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Altair.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Alya.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Alya.ogg \
+    $(LOCAL_PATH)/notifications/Antares.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Antares.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Antimony.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Antimony.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Arcturus.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Argon.ogg \
+    $(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Beat_Box_Android.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Bellatrix.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Beryllium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Beryllium.ogg \
+    $(LOCAL_PATH)/notifications/Betelgeuse.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Betelgeuse.ogg \
+    $(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CaffeineSnake.ogg \
+    $(LOCAL_PATH)/notifications/Canopus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Canopus.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+    $(LOCAL_PATH)/notifications/Castor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Castor.ogg \
+    $(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Cobalt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Cobalt.ogg \
+    $(LOCAL_PATH)/notifications/Cricket.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Cricket.ogg \
+    $(LOCAL_PATH)/newwavelabs/DearDeer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DearDeer.ogg \
+    $(LOCAL_PATH)/notifications/Deneb.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Deneb.ogg \
+    $(LOCAL_PATH)/notifications/Doink.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Doink.ogg \
+    $(LOCAL_PATH)/newwavelabs/DontPanic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DontPanic.ogg \
+    $(LOCAL_PATH)/notifications/Drip.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Drip.ogg \
+    $(LOCAL_PATH)/notifications/Electra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Electra.ogg \
+    $(LOCAL_PATH)/F1_MissedCall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_MissedCall.ogg \
+    $(LOCAL_PATH)/F1_New_MMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_MMS.ogg \
+    $(LOCAL_PATH)/F1_New_SMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_SMS.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Fluorine.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Fluorine.ogg \
+    $(LOCAL_PATH)/notifications/Fomalhaut.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Fomalhaut.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Gallium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Gallium.ogg \
+    $(LOCAL_PATH)/notifications/Heaven.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Heaven.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Helium.ogg \
+    $(LOCAL_PATH)/newwavelabs/Highwire.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Highwire.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Hojus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Hojus.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Iridium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Iridium.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Krypton.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Krypton.ogg \
+    $(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/KzurbSonar.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Lalande.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Lalande.ogg \
+    $(LOCAL_PATH)/notifications/Merope.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Merope.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Mira.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Mira.ogg \
+    $(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/OnTheHunt.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Palladium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Palladium.ogg \
+    $(LOCAL_PATH)/notifications/Plastic_Pipe.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Plastic_Pipe.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Polaris.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Polaris.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Proxima.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Proxima.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Radon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Radon.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Rubidium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Rubidium.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Selenium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Selenium.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Shaula.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Shaula.ogg \
+    $(LOCAL_PATH)/notifications/Sirrah.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Sirrah.ogg \
+    $(LOCAL_PATH)/notifications/SpaceSeed.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/SpaceSeed.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Spica.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Spica.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Strontium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Strontium.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Syrma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Syrma.ogg \
+    $(LOCAL_PATH)/notifications/TaDa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/TaDa.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Talitha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Talitha.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Tejat.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tejat.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Thallium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Thallium.ogg \
+    $(LOCAL_PATH)/notifications/Tinkerbell.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tinkerbell.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Upsilon.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Vega.ogg \
+    $(LOCAL_PATH)/newwavelabs/Voila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Voila.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Xenon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Xenon.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Zirconium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Zirconium.ogg \
+    $(LOCAL_PATH)/notifications/arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/arcturus.ogg \
+    $(LOCAL_PATH)/notifications/moonbeam.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/moonbeam.ogg \
+    $(LOCAL_PATH)/notifications/pixiedust.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pixiedust.ogg \
+    $(LOCAL_PATH)/notifications/pizzicato.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pizzicato.ogg \
+    $(LOCAL_PATH)/notifications/regulus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/regulus.ogg \
+    $(LOCAL_PATH)/notifications/sirius.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/sirius.ogg \
+    $(LOCAL_PATH)/notifications/tweeters.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/tweeters.ogg \
+    $(LOCAL_PATH)/notifications/vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/vega.ogg \
+    $(LOCAL_PATH)/ringtones/ANDROMEDA.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ANDROMEDA.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Andromeda.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Aquila.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ArgoNavis.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Atria.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Atria.ogg \
+    $(LOCAL_PATH)/ringtones/BOOTES.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BOOTES.ogg \
+    $(LOCAL_PATH)/newwavelabs/Backroad.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Backroad.ogg \
+    $(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BeatPlucker.ogg \
+    $(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BentleyDubs.ogg \
+    $(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Big_Easy.ogg \
+    $(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BirdLoop.ogg \
+    $(LOCAL_PATH)/newwavelabs/Bollywood.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Bollywood.ogg \
+    $(LOCAL_PATH)/newwavelabs/BussaMove.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BussaMove.ogg \
+    $(LOCAL_PATH)/ringtones/CANISMAJOR.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CANISMAJOR.ogg \
+    $(LOCAL_PATH)/ringtones/CASSIOPEIA.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CASSIOPEIA.ogg \
+    $(LOCAL_PATH)/newwavelabs/Cairo.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cairo.ogg \
+    $(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Calypso_Steel.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CanisMajor.ogg \
+    $(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CaribbeanIce.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Carina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Carina.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Centaurus.ogg \
+    $(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Champagne_Edition.ogg \
+    $(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Club_Cubano.ogg \
+    $(LOCAL_PATH)/newwavelabs/CrayonRock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CrayonRock.ogg \
+    $(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CrazyDream.ogg \
+    $(LOCAL_PATH)/newwavelabs/CurveBall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CurveBall.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cygnus.ogg \
+    $(LOCAL_PATH)/newwavelabs/DancinFool.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DancinFool.ogg \
+    $(LOCAL_PATH)/newwavelabs/Ding.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ding.ogg \
+    $(LOCAL_PATH)/newwavelabs/DonMessWivIt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DonMessWivIt.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Draco.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Draco.ogg \
+    $(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DreamTheme.ogg \
+    $(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Eastern_Sky.ogg \
+    $(LOCAL_PATH)/newwavelabs/Enter_the_Nexus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Enter_the_Nexus.ogg \
+    $(LOCAL_PATH)/ringtones/Eridani.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Eridani.ogg \
+    $(LOCAL_PATH)/newwavelabs/EtherShake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/EtherShake.ogg \
+    $(LOCAL_PATH)/ringtones/FreeFlight.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/FreeFlight.ogg \
+    $(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/FriendlyGhost.ogg \
+    $(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Funk_Yall.ogg \
+    $(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/GameOverGuitar.ogg \
+    $(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Gimme_Mo_Town.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Girtab.ogg \
+    $(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Glacial_Groove.ogg \
+    $(LOCAL_PATH)/newwavelabs/Growl.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Growl.ogg \
+    $(LOCAL_PATH)/newwavelabs/HalfwayHome.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/HalfwayHome.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Hydra.ogg \
+    $(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/InsertCoin.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Kuma.ogg \
+    $(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoopyLounge.ogg \
+    $(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoveFlute.ogg \
+    $(LOCAL_PATH)/ringtones/Lyra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Lyra.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Machina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Machina.ogg \
+    $(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MidEvilJaunt.ogg \
+    $(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MildlyAlarming.ogg \
+    $(LOCAL_PATH)/newwavelabs/Nairobi.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Nairobi.ogg \
+    $(LOCAL_PATH)/newwavelabs/Nassau.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Nassau.ogg \
+    $(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/NewPlayer.ogg \
+    $(LOCAL_PATH)/newwavelabs/No_Limits.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/No_Limits.ogg \
+    $(LOCAL_PATH)/newwavelabs/Noises1.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises1.ogg \
+    $(LOCAL_PATH)/newwavelabs/Noises2.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises2.ogg \
+    $(LOCAL_PATH)/newwavelabs/Noises3.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises3.ogg \
+    $(LOCAL_PATH)/newwavelabs/OrganDub.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/OrganDub.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Orion.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Orion.ogg \
+    $(LOCAL_PATH)/ringtones/PERSEUS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/PERSEUS.ogg \
+    $(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Paradise_Island.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pegasus.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Perseus.ogg \
+    $(LOCAL_PATH)/newwavelabs/Playa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Playa.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pyxis.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Rasalas.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rasalas.ogg \
+    $(LOCAL_PATH)/newwavelabs/Revelation.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Revelation.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rigel.ogg \
+    $(LOCAL_PATH)/Ring_Classic_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Classic_02.ogg \
+    $(LOCAL_PATH)/Ring_Digital_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Digital_02.ogg \
+    $(LOCAL_PATH)/Ring_Synth_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_02.ogg \
+    $(LOCAL_PATH)/Ring_Synth_04.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_04.ogg \
+    $(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Road_Trip.ogg \
+    $(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/RomancingTheTone.ogg \
+    $(LOCAL_PATH)/newwavelabs/Safari.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Safari.ogg \
+    $(LOCAL_PATH)/newwavelabs/Savannah.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Savannah.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Scarabaeus.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
+    $(LOCAL_PATH)/newwavelabs/Seville.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Seville.ogg \
+    $(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Shes_All_That.ogg \
+    $(LOCAL_PATH)/newwavelabs/SilkyWay.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SilkyWay.ogg \
+    $(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SitarVsSitar.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Solarium.ogg \
+    $(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SpringyJalopy.ogg \
+    $(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Steppin_Out.ogg \
+    $(LOCAL_PATH)/newwavelabs/Terminated.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Terminated.ogg \
+    $(LOCAL_PATH)/ringtones/Testudo.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Testudo.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Themos.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Themos.ogg \
+    $(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Third_Eye.ogg \
+    $(LOCAL_PATH)/newwavelabs/Thunderfoot.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Thunderfoot.ogg \
+    $(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/TwirlAway.ogg \
+    $(LOCAL_PATH)/ringtones/URSAMINOR.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/URSAMINOR.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/UrsaMinor.ogg \
+    $(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/VeryAlarmed.ogg \
+    $(LOCAL_PATH)/ringtones/Vespa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Vespa.ogg \
+    $(LOCAL_PATH)/newwavelabs/World.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/World.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Zeta.ogg \
+    $(LOCAL_PATH)/ringtones/hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/hydra.ogg \
+    $(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+    $(LOCAL_PATH)/effects/ogg/Effect_Tick_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressDelete_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressReturn_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressStandard_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressInvalid_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+    $(LOCAL_PATH)/effects/ogg/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+    $(LOCAL_PATH)/effects/ogg/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+    $(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+    $(LOCAL_PATH)/effects/ogg/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+    $(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+    $(LOCAL_PATH)/effects/ogg/VideoRecord_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+    $(LOCAL_PATH)/effects/ogg/VideoStop_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+    $(LOCAL_PATH)/effects/ogg/WirelessChargingStarted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/WirelessChargingStarted.ogg \
+    $(LOCAL_PATH)/effects/ogg/camera_click_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+    $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+    $(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/ChargingStarted.ogg \
+    $(LOCAL_PATH)/effects/ogg/InCallNotification.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/InCallNotification.ogg \
+    $(LOCAL_PATH)/effects/ogg/NFCFailure.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/NFCFailure.ogg \
+    $(LOCAL_PATH)/effects/ogg/NFCInitiated.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/NFCInitiated.ogg \
+    $(LOCAL_PATH)/effects/ogg/NFCSuccess.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/NFCSuccess.ogg \
+    $(LOCAL_PATH)/effects/ogg/NFCTransferComplete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/NFCTransferComplete.ogg \
+    $(LOCAL_PATH)/effects/ogg/NFCTransferInitiated.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/NFCTransferInitiated.ogg \
diff --git a/data/sounds/AudioPackage10.mk b/data/sounds/AudioPackage10.mk
index 72aa7fe..699dbd6 100644
--- a/data/sounds/AudioPackage10.mk
+++ b/data/sounds/AudioPackage10.mk
@@ -8,63 +8,63 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-        $(LOCAL_PATH)/alarms/ogg/Argon.ogg:system/media/audio/alarms/Argon.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Carbon.ogg:system/media/audio/alarms/Carbon.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Helium.ogg:system/media/audio/alarms/Helium.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Krypton.ogg:system/media/audio/alarms/Krypton.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Neon.ogg:system/media/audio/alarms/Neon.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:system/media/audio/alarms/Oxygen.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Osmium.ogg:system/media/audio/alarms/Osmium.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:system/media/audio/alarms/Platinum.ogg \
-	$(LOCAL_PATH)/effects/ogg/Effect_Tick_48k.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressStandard_48k.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_48k.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressDelete_48k.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_48k.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressReturn_48k.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoRecord_48k.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoStop_48k.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/camera_click_48k.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/LowBattery_48k.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Lock_48k.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Unlock_48k.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:system/media/audio/ui/ChargingStarted.ogg \
-	$(LOCAL_PATH)/effects/ogg/InCallNotification.ogg:system/media/audio/ui/InCallNotification.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/WirelessChargingStarted_48k.ogg:system/media/audio/ui/WirelessChargingStarted.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Alya.ogg:system/media/audio/notifications/Alya.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Syrma.ogg:system/media/audio/notifications/Syrma.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Talitha.ogg:system/media/audio/notifications/Talitha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:system/media/audio/notifications/Tejat.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Atria.ogg:system/media/audio/ringtones/Atria.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:system/media/audio/ringtones/Kuma.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Rasalas.ogg:system/media/audio/ringtones/Rasalas.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:system/media/audio/ringtones/Zeta.ogg
+        $(LOCAL_PATH)/alarms/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Argon.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Carbon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Carbon.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Helium.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Krypton.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Krypton.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Neon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neon.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Oxygen.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Osmium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Osmium.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Platinum.ogg \
+	$(LOCAL_PATH)/effects/ogg/Effect_Tick_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressStandard_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressDelete_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressReturn_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoRecord_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoStop_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/camera_click_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/LowBattery_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Lock_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Unlock_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/ChargingStarted.ogg \
+	$(LOCAL_PATH)/effects/ogg/InCallNotification.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/InCallNotification.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/WirelessChargingStarted_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/WirelessChargingStarted.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Alya.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Alya.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Arcturus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Hojus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Mira.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Shaula.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Spica.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Syrma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Syrma.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Talitha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Talitha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tejat.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Vega.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Andromeda.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Aquila.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Atria.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Atria.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ArgoNavis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Centaurus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Girtab.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Hydra.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Kuma.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Machina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Orion.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pegasus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pyxis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Rasalas.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rasalas.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Scarabaeus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Solarium.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Themos.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Zeta.ogg
diff --git a/data/sounds/AudioPackage11.mk b/data/sounds/AudioPackage11.mk
index 665ce52..99dfd0a 100644
--- a/data/sounds/AudioPackage11.mk
+++ b/data/sounds/AudioPackage11.mk
@@ -8,63 +8,63 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/alarms/ogg/Argon.ogg:system/media/audio/alarms/Argon.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Carbon.ogg:system/media/audio/alarms/Carbon.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Helium.ogg:system/media/audio/alarms/Helium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Krypton.ogg:system/media/audio/alarms/Krypton.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Neon.ogg:system/media/audio/alarms/Neon.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:system/media/audio/alarms/Oxygen.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Osmium.ogg:system/media/audio/alarms/Osmium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Platinum.ogg:system/media/audio/alarms/Platinum.ogg \
-	$(LOCAL_PATH)/effects/ogg/Effect_Tick_48k.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressStandard_48k.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_48k.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressDelete_48k.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_48k.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressReturn_48k.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoRecord_48k.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoStop_48k.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/camera_click_48k.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/LowBattery_48k.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Lock_48k.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Unlock_48k.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:system/media/audio/ui/ChargingStarted.ogg \.
-	$(LOCAL_PATH)/effects/ogg/InCallNotification.ogg:system/media/audio/ui/InCallNotification.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/WirelessChargingStarted_48k.ogg:system/media/audio/ui/WirelessChargingStarted.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Alya.ogg:system/media/audio/notifications/Alya.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Syrma.ogg:system/media/audio/notifications/Syrma.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Talitha.ogg:system/media/audio/notifications/Talitha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Tejat_proc48.ogg:system/media/audio/notifications/Tejat.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Atria.ogg:system/media/audio/ringtones/Atria.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:system/media/audio/ringtones/Kuma.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Rasalas.ogg:system/media/audio/ringtones/Rasalas.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:system/media/audio/ringtones/Zeta.ogg
+	$(LOCAL_PATH)/alarms/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Argon.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Carbon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Carbon.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Helium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Krypton.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Krypton.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Neon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neon.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Oxygen.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Osmium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Osmium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Platinum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Platinum.ogg \
+	$(LOCAL_PATH)/effects/ogg/Effect_Tick_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressStandard_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressDelete_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressReturn_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoRecord_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoStop_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/camera_click_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/LowBattery_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Lock_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Unlock_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/ChargingStarted.ogg \.
+	$(LOCAL_PATH)/effects/ogg/InCallNotification.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/InCallNotification.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/WirelessChargingStarted_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/WirelessChargingStarted.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Alya.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Alya.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Arcturus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Hojus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Mira.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Shaula.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Spica.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Syrma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Syrma.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Talitha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Talitha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Tejat_proc48.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tejat.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Vega.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Andromeda.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Aquila.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Atria.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Atria.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ArgoNavis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Centaurus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Girtab.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Hydra.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Kuma.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Machina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Orion.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pegasus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pyxis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Rasalas.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rasalas.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Scarabaeus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Solarium.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Themos.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Zeta.ogg
diff --git a/data/sounds/AudioPackage12.mk b/data/sounds/AudioPackage12.mk
index 44a8f9e..6159a89 100644
--- a/data/sounds/AudioPackage12.mk
+++ b/data/sounds/AudioPackage12.mk
@@ -16,15 +16,15 @@
 MATERIAL_EFFECT_FILES := camera_click VideoRecord LowBattery WirelessChargingStarted VideoStop
 
 PRODUCT_COPY_FILES += $(foreach fn,$(ALARM_FILES),\
-	$(LOCAL_PATH)/alarms/ogg/$(fn).ogg:system/media/audio/alarms/$(fn).ogg)
+	$(LOCAL_PATH)/alarms/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(NOTIFICATION_FILES),\
-	$(LOCAL_PATH)/notifications/ogg/$(fn).ogg:system/media/audio/notifications/$(fn).ogg)
+	$(LOCAL_PATH)/notifications/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(RINGTONE_FILES),\
-	$(LOCAL_PATH)/ringtones/ogg/$(fn).ogg:system/media/audio/ringtones/$(fn).ogg)
+	$(LOCAL_PATH)/ringtones/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/ogg/$(fn).ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
 PRODUCT_COPY_FILES += $(foreach fn,$(MATERIAL_EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/material/ogg/$(fn).ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
diff --git a/data/sounds/AudioPackage12_48.mk b/data/sounds/AudioPackage12_48.mk
index 09fab04..2899cd1 100644
--- a/data/sounds/AudioPackage12_48.mk
+++ b/data/sounds/AudioPackage12_48.mk
@@ -17,21 +17,21 @@
 
 # Alarms not yet available in 48 kHz
 PRODUCT_COPY_FILES += $(foreach fn,$(ALARM_FILES),\
-	$(LOCAL_PATH)/alarms/ogg/$(fn).ogg:system/media/audio/alarms/$(fn).ogg)
+	$(LOCAL_PATH)/alarms/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(NOTIFICATION_FILES),\
-	$(LOCAL_PATH)/notifications/ogg/$(fn)_48k.ogg:system/media/audio/notifications/$(fn).ogg)
+	$(LOCAL_PATH)/notifications/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(RINGTONE_FILES),\
-	$(LOCAL_PATH)/ringtones/ogg/$(fn)_48k.ogg:system/media/audio/ringtones/$(fn).ogg)
+	$(LOCAL_PATH)/ringtones/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/ogg/$(fn)_48k.ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
 PRODUCT_COPY_FILES += $(foreach fn,$(MATERIAL_EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/material/ogg/$(fn)_48k.ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/material/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
 
 # no gold-plated version yet
 PRODUCT_COPY_FILES += \
-    $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-    $(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-    $(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg
+    $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+    $(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+    $(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg
diff --git a/data/sounds/AudioPackage13.mk b/data/sounds/AudioPackage13.mk
index de4ee04..9423c0b 100644
--- a/data/sounds/AudioPackage13.mk
+++ b/data/sounds/AudioPackage13.mk
@@ -17,15 +17,15 @@
 MATERIAL_EFFECT_FILES := camera_click VideoRecord WirelessChargingStarted LowBattery VideoStop
 
 PRODUCT_COPY_FILES += $(foreach fn,$(ALARM_FILES),\
-	$(LOCAL_PATH)/alarms/material/ogg/$(fn).ogg:system/media/audio/alarms/$(fn).ogg)
+	$(LOCAL_PATH)/alarms/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(NOTIFICATION_FILES),\
-	$(LOCAL_PATH)/notifications/material/ogg/$(fn).ogg:system/media/audio/notifications/$(fn).ogg)
+	$(LOCAL_PATH)/notifications/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(RINGTONE_FILES),\
-	$(LOCAL_PATH)/ringtones/material/ogg/$(fn).ogg:system/media/audio/ringtones/$(fn).ogg)
+	$(LOCAL_PATH)/ringtones/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/ogg/$(fn).ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
 PRODUCT_COPY_FILES += $(foreach fn,$(MATERIAL_EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/material/ogg/$(fn).ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
diff --git a/data/sounds/AudioPackage13_48.mk b/data/sounds/AudioPackage13_48.mk
index 889d581..806c4e2 100644
--- a/data/sounds/AudioPackage13_48.mk
+++ b/data/sounds/AudioPackage13_48.mk
@@ -17,21 +17,21 @@
 MATERIAL_EFFECT_FILES := camera_click VideoRecord WirelessChargingStarted LowBattery VideoStop
 
 PRODUCT_COPY_FILES += $(foreach fn,$(ALARM_FILES),\
-	$(LOCAL_PATH)/alarms/material/ogg/$(fn)_48k.ogg:system/media/audio/alarms/$(fn).ogg)
+	$(LOCAL_PATH)/alarms/material/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(NOTIFICATION_FILES),\
-	$(LOCAL_PATH)/notifications/material/ogg/$(fn)_48k.ogg:system/media/audio/notifications/$(fn).ogg)
+	$(LOCAL_PATH)/notifications/material/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(RINGTONE_FILES),\
-	$(LOCAL_PATH)/ringtones/material/ogg/$(fn)_48k.ogg:system/media/audio/ringtones/$(fn).ogg)
+	$(LOCAL_PATH)/ringtones/material/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/ogg/$(fn)_48k.ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
 PRODUCT_COPY_FILES += $(foreach fn,$(MATERIAL_EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/material/ogg/$(fn)_48k.ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/material/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
 
 # no gold-plated version yet
 PRODUCT_COPY_FILES += \
-    $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-    $(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-    $(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg
+    $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+    $(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+    $(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg
diff --git a/data/sounds/AudioPackage14.mk b/data/sounds/AudioPackage14.mk
index c903a2b..3d161aa 100644
--- a/data/sounds/AudioPackage14.mk
+++ b/data/sounds/AudioPackage14.mk
@@ -18,15 +18,15 @@
 MATERIAL_EFFECT_FILES := camera_click VideoRecord WirelessChargingStarted LowBattery VideoStop
 
 PRODUCT_COPY_FILES += $(foreach fn,$(ALARM_FILES),\
-	$(LOCAL_PATH)/alarms/material/ogg/$(fn).ogg:system/media/audio/alarms/$(fn).ogg)
+	$(LOCAL_PATH)/alarms/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(NOTIFICATION_FILES),\
-	$(LOCAL_PATH)/notifications/material/ogg/$(fn).ogg:system/media/audio/notifications/$(fn).ogg)
+	$(LOCAL_PATH)/notifications/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(RINGTONE_FILES),\
-	$(LOCAL_PATH)/ringtones/material/ogg/$(fn).ogg:system/media/audio/ringtones/$(fn).ogg)
+	$(LOCAL_PATH)/ringtones/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/ogg/$(fn).ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
 PRODUCT_COPY_FILES += $(foreach fn,$(MATERIAL_EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/material/ogg/$(fn).ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
diff --git a/data/sounds/AudioPackage2.mk b/data/sounds/AudioPackage2.mk
index 40319c4..bc4e8fb 100644
--- a/data/sounds/AudioPackage2.mk
+++ b/data/sounds/AudioPackage2.mk
@@ -10,98 +10,98 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \
-	$(LOCAL_PATH)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \
-	$(LOCAL_PATH)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \
-	$(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
-	$(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
-	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \
-	$(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \
-	$(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
-	$(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
-	$(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
-	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \
-	$(LOCAL_PATH)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \
-	$(LOCAL_PATH)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \
-	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \
-	$(LOCAL_PATH)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/moonbeam.ogg:system/media/audio/notifications/moonbeam.ogg \
-	$(LOCAL_PATH)/notifications/pixiedust.ogg:system/media/audio/notifications/pixiedust.ogg \
-	$(LOCAL_PATH)/notifications/pizzicato.ogg:system/media/audio/notifications/pizzicato.ogg \
-	$(LOCAL_PATH)/notifications/tweeters.ogg:system/media/audio/notifications/tweeters.ogg \
-	$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \
-	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg
+	$(LOCAL_PATH)/F1_MissedCall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_MissedCall.ogg \
+	$(LOCAL_PATH)/F1_New_MMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_MMS.ogg \
+	$(LOCAL_PATH)/F1_New_SMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_SMS.ogg \
+	$(LOCAL_PATH)/Alarm_Buzzer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Buzzer.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_01.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_01.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_02.ogg \
+	$(LOCAL_PATH)/Alarm_Classic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Classic.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_03.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_03.ogg \
+	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Rooster_02.ogg \
+	$(LOCAL_PATH)/Ring_Classic_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Classic_02.ogg \
+	$(LOCAL_PATH)/Ring_Digital_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Digital_02.ogg \
+	$(LOCAL_PATH)/Ring_Synth_04.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_04.ogg \
+	$(LOCAL_PATH)/Ring_Synth_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_02.ogg \
+	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Beat_Box_Android.ogg \
+	$(LOCAL_PATH)/notifications/Heaven.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Heaven.ogg \
+	$(LOCAL_PATH)/notifications/TaDa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/TaDa.ogg \
+	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tinkerbell.ogg \
+	$(LOCAL_PATH)/effects/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/moonbeam.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/moonbeam.ogg \
+	$(LOCAL_PATH)/notifications/pixiedust.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pixiedust.ogg \
+	$(LOCAL_PATH)/notifications/pizzicato.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pizzicato.ogg \
+	$(LOCAL_PATH)/notifications/tweeters.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/tweeters.ogg \
+	$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BeatPlucker.ogg \
+	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CaffeineSnake.ogg
 
 ifneq ($(MINIMAL_NEWWAVELABS),true)
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \
-	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \
-	$(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:system/media/audio/ringtones/CaribbeanIce.ogg \
-	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \
-	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \
-	$(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:system/media/audio/ringtones/FriendlyGhost.ogg \
-	$(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:system/media/audio/ringtones/GameOverGuitar.ogg \
-	$(LOCAL_PATH)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \
-	$(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:system/media/audio/ringtones/InsertCoin.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \
-	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \
-	$(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:system/media/audio/ringtones/NewPlayer.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises1.ogg:system/media/audio/ringtones/Noises1.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises2.ogg:system/media/audio/ringtones/Noises2.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises3.ogg:system/media/audio/ringtones/Noises3.ogg \
-	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \
-	$(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:system/media/audio/ringtones/RomancingTheTone.ogg \
-	$(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:system/media/audio/ringtones/SitarVsSitar.ogg \
-	$(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:system/media/audio/ringtones/SpringyJalopy.ogg \
-	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \
-	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \
-	$(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:system/media/audio/ringtones/VeryAlarmed.ogg \
-	$(LOCAL_PATH)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg \
-	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \
-	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \
-	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \
-	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \
-	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \
-	$(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:system/media/audio/ringtones/CrazyDream.ogg \
-	$(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:system/media/audio/ringtones/DreamTheme.ogg \
-	$(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:system/media/audio/ringtones/Big_Easy.ogg \
-	$(LOCAL_PATH)/newwavelabs/Bollywood.ogg:system/media/audio/ringtones/Bollywood.ogg \
-	$(LOCAL_PATH)/newwavelabs/Cairo.ogg:system/media/audio/ringtones/Cairo.ogg \
-	$(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:system/media/audio/ringtones/Calypso_Steel.ogg \
-	$(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:system/media/audio/ringtones/Champagne_Edition.ogg \
-	$(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:system/media/audio/ringtones/Club_Cubano.ogg \
-	$(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:system/media/audio/ringtones/Eastern_Sky.ogg \
-	$(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:system/media/audio/ringtones/Funk_Yall.ogg \
-	$(LOCAL_PATH)/newwavelabs/Savannah.ogg:system/media/audio/ringtones/Savannah.ogg \
-	$(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:system/media/audio/ringtones/Gimme_Mo_Town.ogg \
-	$(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:system/media/audio/ringtones/Glacial_Groove.ogg \
-	$(LOCAL_PATH)/newwavelabs/Seville.ogg:system/media/audio/ringtones/Seville.ogg \
-	$(LOCAL_PATH)/newwavelabs/No_Limits.ogg:system/media/audio/ringtones/No_Limits.ogg \
-	$(LOCAL_PATH)/newwavelabs/Revelation.ogg:system/media/audio/ringtones/Revelation.ogg \
-	$(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:system/media/audio/ringtones/Paradise_Island.ogg \
-	$(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:system/media/audio/ringtones/Road_Trip.ogg \
-	$(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:system/media/audio/ringtones/Shes_All_That.ogg \
-	$(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:system/media/audio/ringtones/Steppin_Out.ogg \
-	$(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:system/media/audio/ringtones/Third_Eye.ogg \
-	$(LOCAL_PATH)/newwavelabs/Thunderfoot.ogg:system/media/audio/ringtones/Thunderfoot.ogg
+	$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BentleyDubs.ogg \
+	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BirdLoop.ogg \
+	$(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CaribbeanIce.ogg \
+	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CurveBall.ogg \
+	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/EtherShake.ogg \
+	$(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/FriendlyGhost.ogg \
+	$(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/GameOverGuitar.ogg \
+	$(LOCAL_PATH)/newwavelabs/Growl.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Growl.ogg \
+	$(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/InsertCoin.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoopyLounge.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoveFlute.ogg \
+	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MidEvilJaunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MildlyAlarming.ogg \
+	$(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/NewPlayer.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises1.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises1.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises2.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises2.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises3.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises3.ogg \
+	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/OrganDub.ogg \
+	$(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/RomancingTheTone.ogg \
+	$(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SitarVsSitar.ogg \
+	$(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SpringyJalopy.ogg \
+	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Terminated.ogg \
+	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/TwirlAway.ogg \
+	$(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/VeryAlarmed.ogg \
+	$(LOCAL_PATH)/newwavelabs/World.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/World.ogg \
+	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DearDeer.ogg \
+	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DontPanic.ogg \
+	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Highwire.ogg \
+	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/KzurbSonar.ogg \
+	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/OnTheHunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/Voila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Voila.ogg \
+	$(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CrazyDream.ogg \
+	$(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DreamTheme.ogg \
+	$(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Big_Easy.ogg \
+	$(LOCAL_PATH)/newwavelabs/Bollywood.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Bollywood.ogg \
+	$(LOCAL_PATH)/newwavelabs/Cairo.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cairo.ogg \
+	$(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Calypso_Steel.ogg \
+	$(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Champagne_Edition.ogg \
+	$(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Club_Cubano.ogg \
+	$(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Eastern_Sky.ogg \
+	$(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Funk_Yall.ogg \
+	$(LOCAL_PATH)/newwavelabs/Savannah.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Savannah.ogg \
+	$(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Gimme_Mo_Town.ogg \
+	$(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Glacial_Groove.ogg \
+	$(LOCAL_PATH)/newwavelabs/Seville.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Seville.ogg \
+	$(LOCAL_PATH)/newwavelabs/No_Limits.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/No_Limits.ogg \
+	$(LOCAL_PATH)/newwavelabs/Revelation.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Revelation.ogg \
+	$(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Paradise_Island.ogg \
+	$(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Road_Trip.ogg \
+	$(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Shes_All_That.ogg \
+	$(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Steppin_Out.ogg \
+	$(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Third_Eye.ogg \
+	$(LOCAL_PATH)/newwavelabs/Thunderfoot.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Thunderfoot.ogg
 endif
diff --git a/data/sounds/AudioPackage3.mk b/data/sounds/AudioPackage3.mk
index a05de72..a98fb74 100644
--- a/data/sounds/AudioPackage3.mk
+++ b/data/sounds/AudioPackage3.mk
@@ -10,94 +10,94 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \
-	$(LOCAL_PATH)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \
-	$(LOCAL_PATH)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \
-	$(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
-	$(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
-	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \
-	$(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \
-	$(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
-	$(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
-	$(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
-	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \
-	$(LOCAL_PATH)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \
-	$(LOCAL_PATH)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \
-	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \
-	$(LOCAL_PATH)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/moonbeam.ogg:system/media/audio/notifications/moonbeam.ogg \
-	$(LOCAL_PATH)/notifications/pixiedust.ogg:system/media/audio/notifications/pixiedust.ogg \
-	$(LOCAL_PATH)/notifications/pizzicato.ogg:system/media/audio/notifications/pizzicato.ogg \
-	$(LOCAL_PATH)/notifications/tweeters.ogg:system/media/audio/notifications/tweeters.ogg \
-	$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \
-	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg
+	$(LOCAL_PATH)/F1_MissedCall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_MissedCall.ogg \
+	$(LOCAL_PATH)/F1_New_MMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_MMS.ogg \
+	$(LOCAL_PATH)/F1_New_SMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_SMS.ogg \
+	$(LOCAL_PATH)/Alarm_Buzzer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Buzzer.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_01.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_01.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_02.ogg \
+	$(LOCAL_PATH)/Alarm_Classic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Classic.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_03.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_03.ogg \
+	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Rooster_02.ogg \
+	$(LOCAL_PATH)/Ring_Classic_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Classic_02.ogg \
+	$(LOCAL_PATH)/Ring_Digital_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Digital_02.ogg \
+	$(LOCAL_PATH)/Ring_Synth_04.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_04.ogg \
+	$(LOCAL_PATH)/Ring_Synth_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_02.ogg \
+	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Beat_Box_Android.ogg \
+	$(LOCAL_PATH)/notifications/Heaven.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Heaven.ogg \
+	$(LOCAL_PATH)/notifications/TaDa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/TaDa.ogg \
+	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tinkerbell.ogg \
+	$(LOCAL_PATH)/effects/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/moonbeam.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/moonbeam.ogg \
+	$(LOCAL_PATH)/notifications/pixiedust.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pixiedust.ogg \
+	$(LOCAL_PATH)/notifications/pizzicato.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pizzicato.ogg \
+	$(LOCAL_PATH)/notifications/tweeters.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/tweeters.ogg \
+	$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BeatPlucker.ogg \
+	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CaffeineSnake.ogg
 
 ifneq ($(MINIMAL_NEWWAVELABS),true)
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \
-	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \
-	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \
-	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \
-	$(LOCAL_PATH)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \
-	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \
-	$(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:system/media/audio/ringtones/NewPlayer.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises1.ogg:system/media/audio/ringtones/Noises1.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises2.ogg:system/media/audio/ringtones/Noises2.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises3.ogg:system/media/audio/ringtones/Noises3.ogg \
-	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \
-	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \
-	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \
-	$(LOCAL_PATH)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg \
-	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \
-	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \
-	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \
-	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \
-	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \
-	$(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:system/media/audio/ringtones/Big_Easy.ogg \
-	$(LOCAL_PATH)/newwavelabs/Bollywood.ogg:system/media/audio/ringtones/Bollywood.ogg \
-	$(LOCAL_PATH)/newwavelabs/Cairo.ogg:system/media/audio/ringtones/Cairo.ogg \
-	$(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:system/media/audio/ringtones/Calypso_Steel.ogg \
-	$(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:system/media/audio/ringtones/Champagne_Edition.ogg \
-	$(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:system/media/audio/ringtones/Club_Cubano.ogg \
-	$(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:system/media/audio/ringtones/Eastern_Sky.ogg \
-	$(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:system/media/audio/ringtones/Funk_Yall.ogg \
-	$(LOCAL_PATH)/newwavelabs/Savannah.ogg:system/media/audio/ringtones/Savannah.ogg \
-	$(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:system/media/audio/ringtones/Gimme_Mo_Town.ogg \
-	$(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:system/media/audio/ringtones/Glacial_Groove.ogg \
-	$(LOCAL_PATH)/newwavelabs/Seville.ogg:system/media/audio/ringtones/Seville.ogg \
-	$(LOCAL_PATH)/newwavelabs/No_Limits.ogg:system/media/audio/ringtones/No_Limits.ogg \
-	$(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:system/media/audio/ringtones/Paradise_Island.ogg \
-	$(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:system/media/audio/ringtones/Road_Trip.ogg \
-	$(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:system/media/audio/ringtones/Shes_All_That.ogg \
-	$(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:system/media/audio/ringtones/Steppin_Out.ogg \
-	$(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:system/media/audio/ringtones/Third_Eye.ogg \
-	$(LOCAL_PATH)/newwavelabs/Thunderfoot.ogg:system/media/audio/ringtones/Thunderfoot.ogg \
-	$(LOCAL_PATH)/newwavelabs/HalfwayHome.ogg:system/media/audio/ringtones/HalfwayHome.ogg \
-	$(LOCAL_PATH)/newwavelabs/CrayonRock.ogg:system/media/audio/ringtones/CrayonRock.ogg \
-	$(LOCAL_PATH)/newwavelabs/DancinFool.ogg:system/media/audio/ringtones/DancinFool.ogg \
-	$(LOCAL_PATH)/newwavelabs/BussaMove.ogg:system/media/audio/ringtones/BussaMove.ogg \
-	$(LOCAL_PATH)/newwavelabs/DonMessWivIt.ogg:system/media/audio/ringtones/DonMessWivIt.ogg \
-	$(LOCAL_PATH)/newwavelabs/SilkyWay.ogg:system/media/audio/ringtones/SilkyWay.ogg \
-	$(LOCAL_PATH)/newwavelabs/Playa.ogg:system/media/audio/ringtones/Playa.ogg
+	$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BentleyDubs.ogg \
+	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BirdLoop.ogg \
+	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CurveBall.ogg \
+	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/EtherShake.ogg \
+	$(LOCAL_PATH)/newwavelabs/Growl.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Growl.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoopyLounge.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoveFlute.ogg \
+	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MidEvilJaunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MildlyAlarming.ogg \
+	$(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/NewPlayer.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises1.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises1.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises2.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises2.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises3.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises3.ogg \
+	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/OrganDub.ogg \
+	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Terminated.ogg \
+	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/TwirlAway.ogg \
+	$(LOCAL_PATH)/newwavelabs/World.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/World.ogg \
+	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DearDeer.ogg \
+	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DontPanic.ogg \
+	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Highwire.ogg \
+	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/KzurbSonar.ogg \
+	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/OnTheHunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/Voila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Voila.ogg \
+	$(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Big_Easy.ogg \
+	$(LOCAL_PATH)/newwavelabs/Bollywood.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Bollywood.ogg \
+	$(LOCAL_PATH)/newwavelabs/Cairo.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cairo.ogg \
+	$(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Calypso_Steel.ogg \
+	$(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Champagne_Edition.ogg \
+	$(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Club_Cubano.ogg \
+	$(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Eastern_Sky.ogg \
+	$(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Funk_Yall.ogg \
+	$(LOCAL_PATH)/newwavelabs/Savannah.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Savannah.ogg \
+	$(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Gimme_Mo_Town.ogg \
+	$(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Glacial_Groove.ogg \
+	$(LOCAL_PATH)/newwavelabs/Seville.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Seville.ogg \
+	$(LOCAL_PATH)/newwavelabs/No_Limits.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/No_Limits.ogg \
+	$(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Paradise_Island.ogg \
+	$(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Road_Trip.ogg \
+	$(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Shes_All_That.ogg \
+	$(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Steppin_Out.ogg \
+	$(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Third_Eye.ogg \
+	$(LOCAL_PATH)/newwavelabs/Thunderfoot.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Thunderfoot.ogg \
+	$(LOCAL_PATH)/newwavelabs/HalfwayHome.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/HalfwayHome.ogg \
+	$(LOCAL_PATH)/newwavelabs/CrayonRock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CrayonRock.ogg \
+	$(LOCAL_PATH)/newwavelabs/DancinFool.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DancinFool.ogg \
+	$(LOCAL_PATH)/newwavelabs/BussaMove.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BussaMove.ogg \
+	$(LOCAL_PATH)/newwavelabs/DonMessWivIt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DonMessWivIt.ogg \
+	$(LOCAL_PATH)/newwavelabs/SilkyWay.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SilkyWay.ogg \
+	$(LOCAL_PATH)/newwavelabs/Playa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Playa.ogg
 endif
diff --git a/data/sounds/AudioPackage4.mk b/data/sounds/AudioPackage4.mk
index d376a2d..54c3c02 100644
--- a/data/sounds/AudioPackage4.mk
+++ b/data/sounds/AudioPackage4.mk
@@ -10,98 +10,98 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \
-	$(LOCAL_PATH)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \
-	$(LOCAL_PATH)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \
-	$(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
-	$(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
-	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \
-	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \
-	$(LOCAL_PATH)/notifications/Cricket.ogg:system/media/audio/notifications/Cricket.ogg \
-	$(LOCAL_PATH)/notifications/Doink.ogg:system/media/audio/notifications/Doink.ogg \
-	$(LOCAL_PATH)/notifications/Drip.ogg:system/media/audio/notifications/Drip.ogg \
-	$(LOCAL_PATH)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \
-	$(LOCAL_PATH)/notifications/SpaceSeed.ogg:system/media/audio/notifications/SpaceSeed.ogg \
-	$(LOCAL_PATH)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \
-	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \
-	$(LOCAL_PATH)/notifications/moonbeam.ogg:system/media/audio/notifications/moonbeam.ogg \
-	$(LOCAL_PATH)/notifications/pixiedust.ogg:system/media/audio/notifications/pixiedust.ogg \
-	$(LOCAL_PATH)/notifications/pizzicato.ogg:system/media/audio/notifications/pizzicato.ogg \
-	$(LOCAL_PATH)/notifications/Plastic_Pipe.ogg:system/media/audio/notifications/Plastic_Pipe.ogg \
-	$(LOCAL_PATH)/notifications/tweeters.ogg:system/media/audio/notifications/tweeters.ogg \
-	$(LOCAL_PATH)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \
-	$(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
-	$(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
-	$(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
-	$(LOCAL_PATH)/ringtones/FreeFlight.ogg:system/media/audio/ringtones/FreeFlight.ogg \
-	$(LOCAL_PATH)/newwavelabs/Backroad.ogg:system/media/audio/ringtones/Backroad.ogg \
-	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg
+	$(LOCAL_PATH)/F1_MissedCall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_MissedCall.ogg \
+	$(LOCAL_PATH)/F1_New_MMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_MMS.ogg \
+	$(LOCAL_PATH)/F1_New_SMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_SMS.ogg \
+	$(LOCAL_PATH)/Alarm_Buzzer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Buzzer.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_01.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_01.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_02.ogg \
+	$(LOCAL_PATH)/Alarm_Classic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Classic.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_03.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_03.ogg \
+	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Rooster_02.ogg \
+	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Beat_Box_Android.ogg \
+	$(LOCAL_PATH)/notifications/Cricket.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Cricket.ogg \
+	$(LOCAL_PATH)/notifications/Doink.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Doink.ogg \
+	$(LOCAL_PATH)/notifications/Drip.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Drip.ogg \
+	$(LOCAL_PATH)/notifications/Heaven.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Heaven.ogg \
+	$(LOCAL_PATH)/notifications/SpaceSeed.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/SpaceSeed.ogg \
+	$(LOCAL_PATH)/notifications/TaDa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/TaDa.ogg \
+	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tinkerbell.ogg \
+	$(LOCAL_PATH)/notifications/moonbeam.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/moonbeam.ogg \
+	$(LOCAL_PATH)/notifications/pixiedust.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pixiedust.ogg \
+	$(LOCAL_PATH)/notifications/pizzicato.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pizzicato.ogg \
+	$(LOCAL_PATH)/notifications/Plastic_Pipe.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Plastic_Pipe.ogg \
+	$(LOCAL_PATH)/notifications/tweeters.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/tweeters.ogg \
+	$(LOCAL_PATH)/effects/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/Ring_Classic_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Classic_02.ogg \
+	$(LOCAL_PATH)/Ring_Digital_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Digital_02.ogg \
+	$(LOCAL_PATH)/Ring_Synth_04.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_04.ogg \
+	$(LOCAL_PATH)/Ring_Synth_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_02.ogg \
+	$(LOCAL_PATH)/ringtones/FreeFlight.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/FreeFlight.ogg \
+	$(LOCAL_PATH)/newwavelabs/Backroad.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Backroad.ogg \
+	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CaffeineSnake.ogg
 
 ifneq ($(MINIMAL_NEWWAVELABS),true)
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \
-	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \
-	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \
-	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \
-	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \
-	$(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:system/media/audio/ringtones/Big_Easy.ogg \
-	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \
-	$(LOCAL_PATH)/newwavelabs/Bollywood.ogg:system/media/audio/ringtones/Bollywood.ogg \
-	$(LOCAL_PATH)/newwavelabs/BussaMove.ogg:system/media/audio/ringtones/BussaMove.ogg \
-	$(LOCAL_PATH)/newwavelabs/Cairo.ogg:system/media/audio/ringtones/Cairo.ogg \
-	$(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:system/media/audio/ringtones/Calypso_Steel.ogg \
-	$(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:system/media/audio/ringtones/Champagne_Edition.ogg \
-	$(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:system/media/audio/ringtones/Club_Cubano.ogg \
-	$(LOCAL_PATH)/newwavelabs/CrayonRock.ogg:system/media/audio/ringtones/CrayonRock.ogg \
-	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \
-	$(LOCAL_PATH)/newwavelabs/DancinFool.ogg:system/media/audio/ringtones/DancinFool.ogg \
-	$(LOCAL_PATH)/newwavelabs/Ding.ogg:system/media/audio/ringtones/Ding.ogg \
-	$(LOCAL_PATH)/newwavelabs/DonMessWivIt.ogg:system/media/audio/ringtones/DonMessWivIt.ogg \
-	$(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:system/media/audio/ringtones/Eastern_Sky.ogg \
-	$(LOCAL_PATH)/newwavelabs/Enter_the_Nexus.ogg:system/media/audio/ringtones/Enter_the_Nexus.ogg \
-	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \
-	$(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:system/media/audio/ringtones/Funk_Yall.ogg \
-	$(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:system/media/audio/ringtones/Gimme_Mo_Town.ogg \
-	$(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:system/media/audio/ringtones/Glacial_Groove.ogg \
-	$(LOCAL_PATH)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \
-	$(LOCAL_PATH)/newwavelabs/HalfwayHome.ogg:system/media/audio/ringtones/HalfwayHome.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \
-	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \
-	$(LOCAL_PATH)/newwavelabs/Nairobi.ogg:system/media/audio/ringtones/Nairobi.ogg \
-	$(LOCAL_PATH)/newwavelabs/Nassau.ogg:system/media/audio/ringtones/Nassau.ogg \
-	$(LOCAL_PATH)/newwavelabs/No_Limits.ogg:system/media/audio/ringtones/No_Limits.ogg \
-	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \
-	$(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:system/media/audio/ringtones/Paradise_Island.ogg \
-	$(LOCAL_PATH)/newwavelabs/Playa.ogg:system/media/audio/ringtones/Playa.ogg \
-	$(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:system/media/audio/ringtones/Road_Trip.ogg \
-	$(LOCAL_PATH)/newwavelabs/Safari.ogg:system/media/audio/ringtones/Safari.ogg \
-	$(LOCAL_PATH)/newwavelabs/Seville.ogg:system/media/audio/ringtones/Seville.ogg \
-	$(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:system/media/audio/ringtones/Shes_All_That.ogg \
-	$(LOCAL_PATH)/newwavelabs/SilkyWay.ogg:system/media/audio/ringtones/SilkyWay.ogg \
-	$(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:system/media/audio/ringtones/Steppin_Out.ogg \
-	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \
-	$(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:system/media/audio/ringtones/Third_Eye.ogg \
-	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \
-	$(LOCAL_PATH)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg
+	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DearDeer.ogg \
+	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DontPanic.ogg \
+	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Highwire.ogg \
+	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/KzurbSonar.ogg \
+	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/OnTheHunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/Voila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Voila.ogg \
+	$(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Big_Easy.ogg \
+	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BirdLoop.ogg \
+	$(LOCAL_PATH)/newwavelabs/Bollywood.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Bollywood.ogg \
+	$(LOCAL_PATH)/newwavelabs/BussaMove.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BussaMove.ogg \
+	$(LOCAL_PATH)/newwavelabs/Cairo.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cairo.ogg \
+	$(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Calypso_Steel.ogg \
+	$(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Champagne_Edition.ogg \
+	$(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Club_Cubano.ogg \
+	$(LOCAL_PATH)/newwavelabs/CrayonRock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CrayonRock.ogg \
+	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CurveBall.ogg \
+	$(LOCAL_PATH)/newwavelabs/DancinFool.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DancinFool.ogg \
+	$(LOCAL_PATH)/newwavelabs/Ding.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ding.ogg \
+	$(LOCAL_PATH)/newwavelabs/DonMessWivIt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DonMessWivIt.ogg \
+	$(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Eastern_Sky.ogg \
+	$(LOCAL_PATH)/newwavelabs/Enter_the_Nexus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Enter_the_Nexus.ogg \
+	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/EtherShake.ogg \
+	$(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Funk_Yall.ogg \
+	$(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Gimme_Mo_Town.ogg \
+	$(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Glacial_Groove.ogg \
+	$(LOCAL_PATH)/newwavelabs/Growl.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Growl.ogg \
+	$(LOCAL_PATH)/newwavelabs/HalfwayHome.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/HalfwayHome.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoopyLounge.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoveFlute.ogg \
+	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MidEvilJaunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MildlyAlarming.ogg \
+	$(LOCAL_PATH)/newwavelabs/Nairobi.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Nairobi.ogg \
+	$(LOCAL_PATH)/newwavelabs/Nassau.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Nassau.ogg \
+	$(LOCAL_PATH)/newwavelabs/No_Limits.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/No_Limits.ogg \
+	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/OrganDub.ogg \
+	$(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Paradise_Island.ogg \
+	$(LOCAL_PATH)/newwavelabs/Playa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Playa.ogg \
+	$(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Road_Trip.ogg \
+	$(LOCAL_PATH)/newwavelabs/Safari.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Safari.ogg \
+	$(LOCAL_PATH)/newwavelabs/Seville.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Seville.ogg \
+	$(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Shes_All_That.ogg \
+	$(LOCAL_PATH)/newwavelabs/SilkyWay.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SilkyWay.ogg \
+	$(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Steppin_Out.ogg \
+	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Terminated.ogg \
+	$(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Third_Eye.ogg \
+	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/TwirlAway.ogg \
+	$(LOCAL_PATH)/newwavelabs/World.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/World.ogg
 endif
diff --git a/data/sounds/AudioPackage5.mk b/data/sounds/AudioPackage5.mk
index 72384c8..8a03a2e1 100644
--- a/data/sounds/AudioPackage5.mk
+++ b/data/sounds/AudioPackage5.mk
@@ -8,69 +8,69 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
-	$(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
-	$(LOCAL_PATH)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-	$(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/Aldebaran.ogg:system/media/audio/notifications/Aldebaran.ogg \
-	$(LOCAL_PATH)/notifications/Altair.ogg:system/media/audio/notifications/Altair.ogg \
-	$(LOCAL_PATH)/notifications/Antares.ogg:system/media/audio/notifications/Antares.ogg \
-	$(LOCAL_PATH)/notifications/arcturus.ogg:system/media/audio/notifications/arcturus.ogg \
-	$(LOCAL_PATH)/notifications/Betelgeuse.ogg:system/media/audio/notifications/Betelgeuse.ogg \
-	$(LOCAL_PATH)/notifications/Canopus.ogg:system/media/audio/notifications/Canopus.ogg \
-	$(LOCAL_PATH)/notifications/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-	$(LOCAL_PATH)/notifications/Castor.ogg:system/media/audio/notifications/Castor.ogg \
-	$(LOCAL_PATH)/notifications/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-	$(LOCAL_PATH)/notifications/Deneb.ogg:system/media/audio/notifications/Deneb.ogg \
-	$(LOCAL_PATH)/notifications/Electra.ogg:system/media/audio/notifications/Electra.ogg \
-	$(LOCAL_PATH)/notifications/Fomalhaut.ogg:system/media/audio/notifications/Fomalhaut.ogg \
-	$(LOCAL_PATH)/notifications/Merope.ogg:system/media/audio/notifications/Merope.ogg \
-	$(LOCAL_PATH)/notifications/Polaris.ogg:system/media/audio/notifications/Polaris.ogg \
-	$(LOCAL_PATH)/notifications/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-	$(LOCAL_PATH)/notifications/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-	$(LOCAL_PATH)/notifications/regulus.ogg:system/media/audio/notifications/regulus.ogg \
-	$(LOCAL_PATH)/notifications/sirius.ogg:system/media/audio/notifications/sirius.ogg \
-	$(LOCAL_PATH)/notifications/Sirrah.ogg:system/media/audio/notifications/Sirrah.ogg \
-	$(LOCAL_PATH)/notifications/vega.ogg:system/media/audio/notifications/vega.ogg \
-	$(LOCAL_PATH)/ringtones/ANDROMEDA.ogg:system/media/audio/ringtones/ANDROMEDA.ogg \
-	$(LOCAL_PATH)/ringtones/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-	$(LOCAL_PATH)/ringtones/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-	$(LOCAL_PATH)/ringtones/BOOTES.ogg:system/media/audio/ringtones/BOOTES.ogg \
-	$(LOCAL_PATH)/ringtones/CANISMAJOR.ogg:system/media/audio/ringtones/CANISMAJOR.ogg \
-	$(LOCAL_PATH)/ringtones/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
-	$(LOCAL_PATH)/ringtones/CASSIOPEIA.ogg:system/media/audio/ringtones/CASSIOPEIA.ogg \
-	$(LOCAL_PATH)/ringtones/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-	$(LOCAL_PATH)/ringtones/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
-	$(LOCAL_PATH)/ringtones/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
-	$(LOCAL_PATH)/ringtones/Eridani.ogg:system/media/audio/ringtones/Eridani.ogg \
-	$(LOCAL_PATH)/ringtones/hydra.ogg:system/media/audio/ringtones/hydra.ogg \
-	$(LOCAL_PATH)/ringtones/Lyra.ogg:system/media/audio/ringtones/Lyra.ogg \
-	$(LOCAL_PATH)/ringtones/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-	$(LOCAL_PATH)/ringtones/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-	$(LOCAL_PATH)/ringtones/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-	$(LOCAL_PATH)/ringtones/PERSEUS.ogg:system/media/audio/ringtones/PERSEUS.ogg \
-	$(LOCAL_PATH)/ringtones/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-	$(LOCAL_PATH)/ringtones/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \
-	$(LOCAL_PATH)/ringtones/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
-	$(LOCAL_PATH)/ringtones/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-	$(LOCAL_PATH)/ringtones/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-	$(LOCAL_PATH)/ringtones/Testudo.ogg:system/media/audio/ringtones/Testudo.ogg \
-	$(LOCAL_PATH)/ringtones/URSAMINOR.ogg:system/media/audio/ringtones/URSAMINOR.ogg \
-	$(LOCAL_PATH)/ringtones/Vespa.ogg:system/media/audio/ringtones/Vespa.ogg
+	$(LOCAL_PATH)/Alarm_Buzzer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Buzzer.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_01.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_01.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_02.ogg \
+	$(LOCAL_PATH)/Alarm_Classic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Classic.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_03.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_03.ogg \
+	$(LOCAL_PATH)/effects/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+	$(LOCAL_PATH)/effects/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/Aldebaran.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Aldebaran.ogg \
+	$(LOCAL_PATH)/notifications/Altair.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Altair.ogg \
+	$(LOCAL_PATH)/notifications/Antares.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Antares.ogg \
+	$(LOCAL_PATH)/notifications/arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/arcturus.ogg \
+	$(LOCAL_PATH)/notifications/Betelgeuse.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Betelgeuse.ogg \
+	$(LOCAL_PATH)/notifications/Canopus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Canopus.ogg \
+	$(LOCAL_PATH)/notifications/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+	$(LOCAL_PATH)/notifications/Castor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Castor.ogg \
+	$(LOCAL_PATH)/notifications/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+	$(LOCAL_PATH)/notifications/Deneb.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Deneb.ogg \
+	$(LOCAL_PATH)/notifications/Electra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Electra.ogg \
+	$(LOCAL_PATH)/notifications/Fomalhaut.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Fomalhaut.ogg \
+	$(LOCAL_PATH)/notifications/Merope.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Merope.ogg \
+	$(LOCAL_PATH)/notifications/Polaris.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Polaris.ogg \
+	$(LOCAL_PATH)/notifications/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+	$(LOCAL_PATH)/notifications/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+	$(LOCAL_PATH)/notifications/regulus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/regulus.ogg \
+	$(LOCAL_PATH)/notifications/sirius.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/sirius.ogg \
+	$(LOCAL_PATH)/notifications/Sirrah.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Sirrah.ogg \
+	$(LOCAL_PATH)/notifications/vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/vega.ogg \
+	$(LOCAL_PATH)/ringtones/ANDROMEDA.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ANDROMEDA.ogg \
+	$(LOCAL_PATH)/ringtones/Aquila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Aquila.ogg \
+	$(LOCAL_PATH)/ringtones/ArgoNavis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ArgoNavis.ogg \
+	$(LOCAL_PATH)/ringtones/BOOTES.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BOOTES.ogg \
+	$(LOCAL_PATH)/ringtones/CANISMAJOR.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CANISMAJOR.ogg \
+	$(LOCAL_PATH)/ringtones/Carina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Carina.ogg \
+	$(LOCAL_PATH)/ringtones/CASSIOPEIA.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CASSIOPEIA.ogg \
+	$(LOCAL_PATH)/ringtones/Centaurus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Centaurus.ogg \
+	$(LOCAL_PATH)/ringtones/Cygnus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cygnus.ogg \
+	$(LOCAL_PATH)/ringtones/Draco.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Draco.ogg \
+	$(LOCAL_PATH)/ringtones/Eridani.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Eridani.ogg \
+	$(LOCAL_PATH)/ringtones/hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/hydra.ogg \
+	$(LOCAL_PATH)/ringtones/Lyra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Lyra.ogg \
+	$(LOCAL_PATH)/ringtones/Machina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Machina.ogg \
+	$(LOCAL_PATH)/ringtones/Orion.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Orion.ogg \
+	$(LOCAL_PATH)/ringtones/Pegasus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pegasus.ogg \
+	$(LOCAL_PATH)/ringtones/PERSEUS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/PERSEUS.ogg \
+	$(LOCAL_PATH)/ringtones/Pyxis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pyxis.ogg \
+	$(LOCAL_PATH)/ringtones/Rigel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rigel.ogg \
+	$(LOCAL_PATH)/ringtones/Scarabaeus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Scarabaeus.ogg \
+	$(LOCAL_PATH)/ringtones/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/ringtones/Solarium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Solarium.ogg \
+	$(LOCAL_PATH)/ringtones/Testudo.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Testudo.ogg \
+	$(LOCAL_PATH)/ringtones/URSAMINOR.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/URSAMINOR.ogg \
+	$(LOCAL_PATH)/ringtones/Vespa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Vespa.ogg
diff --git a/data/sounds/AudioPackage6.mk b/data/sounds/AudioPackage6.mk
index 5413704..a778261 100644
--- a/data/sounds/AudioPackage6.mk
+++ b/data/sounds/AudioPackage6.mk
@@ -8,41 +8,41 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/alarms/ogg/Barium.ogg:system/media/audio/alarms/Barium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Cesium.ogg:system/media/audio/alarms/Cesium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:system/media/audio/alarms/Plutonium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Scandium.ogg:system/media/audio/alarms/Scandium.ogg \
-	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Antimony.ogg:system/media/audio/notifications/Antimony.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Argon.ogg:system/media/audio/notifications/Argon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Beryllium.ogg:system/media/audio/notifications/Beryllium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Cobalt.ogg:system/media/audio/notifications/Cobalt.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Fluorine.ogg:system/media/audio/notifications/Fluorine.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Gallium.ogg:system/media/audio/notifications/Gallium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Helium.ogg:system/media/audio/notifications/Helium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Iridium.ogg:system/media/audio/notifications/Iridium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Krypton.ogg:system/media/audio/notifications/Krypton.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Palladium.ogg:system/media/audio/notifications/Palladium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Radon.ogg:system/media/audio/notifications/Radon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Rubidium.ogg:system/media/audio/notifications/Rubidium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Selenium.ogg:system/media/audio/notifications/Selenium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Strontium.ogg:system/media/audio/notifications/Strontium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Thallium.ogg:system/media/audio/notifications/Thallium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Xenon.ogg:system/media/audio/notifications/Xenon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Zirconium.ogg:system/media/audio/notifications/Zirconium.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Barium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Barium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Cesium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Cesium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Plutonium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Scandium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Scandium.ogg \
+	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Antimony.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Antimony.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Argon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Beryllium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Beryllium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Cobalt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Cobalt.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Fluorine.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Fluorine.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Gallium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Gallium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Helium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Iridium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Iridium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Krypton.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Krypton.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Palladium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Palladium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Radon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Radon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Rubidium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Rubidium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Selenium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Selenium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Strontium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Strontium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Thallium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Thallium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Xenon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Xenon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Zirconium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Zirconium.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
diff --git a/data/sounds/AudioPackage7.mk b/data/sounds/AudioPackage7.mk
index e4763be..27e349d2 100644
--- a/data/sounds/AudioPackage7.mk
+++ b/data/sounds/AudioPackage7.mk
@@ -8,64 +8,64 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/alarms/ogg/Cesium.ogg:system/media/audio/alarms/Cesium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Fermium.ogg:system/media/audio/alarms/Fermium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Hassium.ogg:system/media/audio/alarms/Hassium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:system/media/audio/alarms/Neptunium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:system/media/audio/alarms/Nobelium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:system/media/audio/alarms/Plutonium.ogg \
-	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressStandard_120.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressDelete_120.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_120.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressReturn_120.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:system/media/audio/notifications/Bellatrix.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Lalande.ogg:system/media/audio/notifications/Lalande.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Polaris.ogg:system/media/audio/notifications/Polaris.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Proxima.ogg:system/media/audio/notifications/Proxima.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:system/media/audio/notifications/Tejat.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:system/media/audio/notifications/Upsilon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:system/media/audio/ringtones/CanisMajor.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:system/media/audio/ringtones/Perseus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:system/media/audio/ringtones/UrsaMinor.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:system/media/audio/ringtones/Zeta.ogg
+	$(LOCAL_PATH)/alarms/ogg/Cesium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Cesium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Fermium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Fermium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Hassium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Hassium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neptunium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Nobelium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Plutonium.ogg \
+	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressStandard_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressDelete_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressReturn_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Arcturus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Bellatrix.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Hojus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Lalande.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Lalande.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Mira.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Polaris.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Polaris.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Proxima.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Proxima.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Shaula.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Spica.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tejat.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Upsilon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Vega.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Andromeda.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Aquila.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ArgoNavis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CanisMajor.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Carina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Carina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Centaurus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cygnus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Draco.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Draco.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Girtab.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Hydra.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Machina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Orion.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pegasus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Perseus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pyxis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rigel.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Scarabaeus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Solarium.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Themos.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/UrsaMinor.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Zeta.ogg
diff --git a/data/sounds/AudioPackage7alt.mk b/data/sounds/AudioPackage7alt.mk
index 30e6173..a0f4d89 100644
--- a/data/sounds/AudioPackage7alt.mk
+++ b/data/sounds/AudioPackage7alt.mk
@@ -8,63 +8,63 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/alarms/ogg-jp/Argon.ogg:system/media/audio/alarms/Argon.ogg \
-	$(LOCAL_PATH)/alarms/ogg-jp/Carbon.ogg:system/media/audio/alarms/Carbon.ogg \
-	$(LOCAL_PATH)/alarms/ogg-jp/Helium.ogg:system/media/audio/alarms/Helium.ogg \
-	$(LOCAL_PATH)/alarms/ogg-jp/Krypton.ogg:system/media/audio/alarms/Krypton.ogg \
-	$(LOCAL_PATH)/alarms/ogg-jp/Neon.ogg:system/media/audio/alarms/Neon.ogg \
-	$(LOCAL_PATH)/alarms/ogg-jp/Oxygen.ogg:system/media/audio/alarms/Oxygen.ogg \
-	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressStandard_120.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressDelete_120.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_120.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressReturn_120.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/ogg/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:system/media/audio/notifications/Bellatrix.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Lalande.ogg:system/media/audio/notifications/Lalande.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Polaris.ogg:system/media/audio/notifications/Polaris.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Proxima.ogg:system/media/audio/notifications/Proxima.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:system/media/audio/notifications/Tejat.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:system/media/audio/notifications/Upsilon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:system/media/audio/ringtones/CanisMajor.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:system/media/audio/ringtones/Perseus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:system/media/audio/ringtones/UrsaMinor.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:system/media/audio/ringtones/Zeta.ogg
+	$(LOCAL_PATH)/alarms/ogg-jp/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Argon.ogg \
+	$(LOCAL_PATH)/alarms/ogg-jp/Carbon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Carbon.ogg \
+	$(LOCAL_PATH)/alarms/ogg-jp/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Helium.ogg \
+	$(LOCAL_PATH)/alarms/ogg-jp/Krypton.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Krypton.ogg \
+	$(LOCAL_PATH)/alarms/ogg-jp/Neon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neon.ogg \
+	$(LOCAL_PATH)/alarms/ogg-jp/Oxygen.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Oxygen.ogg \
+	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressStandard_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressDelete_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressReturn_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/ogg/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/ogg/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Arcturus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Bellatrix.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Hojus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Lalande.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Lalande.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Mira.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Polaris.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Polaris.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Proxima.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Proxima.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Shaula.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Spica.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tejat.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Upsilon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Vega.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Andromeda.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Aquila.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ArgoNavis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CanisMajor.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Carina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Carina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Centaurus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cygnus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Draco.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Draco.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Girtab.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Hydra.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Machina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Orion.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pegasus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Perseus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pyxis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rigel.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Scarabaeus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Solarium.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Themos.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/UrsaMinor.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Zeta.ogg
diff --git a/data/sounds/AudioPackage8.mk b/data/sounds/AudioPackage8.mk
index b38e62d..032b4d2 100644
--- a/data/sounds/AudioPackage8.mk
+++ b/data/sounds/AudioPackage8.mk
@@ -8,66 +8,66 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/alarms/ogg/Cesium.ogg:system/media/audio/alarms/Cesium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Fermium.ogg:system/media/audio/alarms/Fermium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Hassium.ogg:system/media/audio/alarms/Hassium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:system/media/audio/alarms/Neptunium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:system/media/audio/alarms/Nobelium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Osmium.ogg:system/media/audio/alarms/Osmium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:system/media/audio/alarms/Plutonium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Promethium.ogg:system/media/audio/alarms/Promethium.ogg \
-	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:system/media/audio/notifications/Bellatrix.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Lalande.ogg:system/media/audio/notifications/Lalande.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Polaris.ogg:system/media/audio/notifications/Polaris.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Proxima.ogg:system/media/audio/notifications/Proxima.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:system/media/audio/notifications/Tejat.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:system/media/audio/notifications/Upsilon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:system/media/audio/ringtones/CanisMajor.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:system/media/audio/ringtones/Perseus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:system/media/audio/ringtones/UrsaMinor.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:system/media/audio/ringtones/Zeta.ogg
+	$(LOCAL_PATH)/alarms/ogg/Cesium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Cesium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Fermium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Fermium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Hassium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Hassium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neptunium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Nobelium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Osmium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Osmium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Plutonium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Promethium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Promethium.ogg \
+	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Arcturus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Bellatrix.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Hojus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Lalande.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Lalande.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Mira.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Polaris.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Polaris.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Proxima.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Proxima.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Shaula.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Spica.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tejat.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Upsilon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Vega.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Andromeda.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Aquila.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ArgoNavis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CanisMajor.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Carina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Carina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Centaurus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cygnus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Draco.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Draco.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Girtab.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Hydra.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Machina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Orion.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pegasus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Perseus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pyxis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rigel.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Scarabaeus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Solarium.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Themos.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/UrsaMinor.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Zeta.ogg
diff --git a/data/sounds/AudioPackage9.mk b/data/sounds/AudioPackage9.mk
index dbe1350..53cc8c0 100644
--- a/data/sounds/AudioPackage9.mk
+++ b/data/sounds/AudioPackage9.mk
@@ -8,43 +8,43 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-        $(LOCAL_PATH)/alarms/ogg/Argon.ogg:system/media/audio/alarms/Argon.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Carbon.ogg:system/media/audio/alarms/Carbon.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Helium.ogg:system/media/audio/alarms/Helium.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Krypton.ogg:system/media/audio/alarms/Krypton.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Neon.ogg:system/media/audio/alarms/Neon.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:system/media/audio/alarms/Oxygen.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Osmium.ogg:system/media/audio/alarms/Osmium.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:system/media/audio/alarms/Platinum.ogg \
-	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Alya.ogg:system/media/audio/notifications/Alya.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Syrma.ogg:system/media/audio/notifications/Syrma.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Talitha.ogg:system/media/audio/notifications/Talitha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:system/media/audio/notifications/Tejat.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg
+        $(LOCAL_PATH)/alarms/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Argon.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Carbon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Carbon.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Helium.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Krypton.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Krypton.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Neon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neon.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Oxygen.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Osmium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Osmium.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Platinum.ogg \
+	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Alya.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Alya.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Arcturus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Hojus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Mira.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Shaula.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Spica.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Syrma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Syrma.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Talitha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Talitha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tejat.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Vega.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Girtab.ogg
diff --git a/data/sounds/AudioPackageGo.mk b/data/sounds/AudioPackageGo.mk
index 0296219..e3b27f2 100644
--- a/data/sounds/AudioPackageGo.mk
+++ b/data/sounds/AudioPackageGo.mk
@@ -20,30 +20,30 @@
 # Ring_Synth_04 : Flutey Phone
 # Alarm_Beep_03 : Beep Beep Beep
 PRODUCT_COPY_FILES += \
-    $(LOCAL_PATH)/notifications/ogg/Alya.ogg:system/media/audio/notifications/Alya.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Argon.ogg:system/media/audio/notifications/Argon.ogg \
-    $(LOCAL_PATH)/notifications/Canopus.ogg:system/media/audio/notifications/Canopus.ogg \
-    $(LOCAL_PATH)/notifications/Deneb.ogg:system/media/audio/notifications/Deneb.ogg \
-    $(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Iridium.ogg:system/media/audio/notifications/Iridium.ogg \
-    $(LOCAL_PATH)/notifications/pixiedust.ogg:system/media/audio/notifications/pixiedust.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Talitha.ogg:system/media/audio/notifications/Talitha.ogg \
-    $(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \
-    $(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
-    $(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
-    $(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:system/media/audio/ringtones/Kuma.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
-    $(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Argon.ogg:system/media/audio/alarms/Argon.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:system/media/audio/alarms/Platinum.ogg \
-    $(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Helium.ogg:system/media/audio/alarms/Helium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:system/media/audio/alarms/Oxygen.ogg \
-    $(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Alya.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Alya.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Argon.ogg \
+    $(LOCAL_PATH)/notifications/Canopus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Canopus.ogg \
+    $(LOCAL_PATH)/notifications/Deneb.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Deneb.ogg \
+    $(LOCAL_PATH)/newwavelabs/Highwire.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Highwire.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Iridium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Iridium.ogg \
+    $(LOCAL_PATH)/notifications/pixiedust.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pixiedust.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Talitha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Talitha.ogg \
+    $(LOCAL_PATH)/Ring_Classic_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Classic_02.ogg \
+    $(LOCAL_PATH)/Ring_Synth_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_02.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cygnus.ogg \
+    $(LOCAL_PATH)/Ring_Digital_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Digital_02.ogg \
+    $(LOCAL_PATH)/Ring_Synth_04.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_04.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Kuma.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Themos.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Themos.ogg \
+    $(LOCAL_PATH)/Alarm_Classic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Classic.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Argon.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Platinum.ogg \
+    $(LOCAL_PATH)/Alarm_Beep_03.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_03.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Helium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Oxygen.ogg \
+    $(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
diff --git a/data/sounds/AudioTv.mk b/data/sounds/AudioTv.mk
index 91265af..d0006b7 100644
--- a/data/sounds/AudioTv.mk
+++ b/data/sounds/AudioTv.mk
@@ -15,8 +15,8 @@
 LOCAL_PATH := frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-    $(LOCAL_PATH)/effects/ogg/KeypressDelete_120_48k.ogg:system/media/audio/ui/KeypressDelete.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressInvalid_120_48k.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressReturn_120_48k.ogg:system/media/audio/ui/KeypressReturn.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120_48k.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressStandard_120_48k.ogg:system/media/audio/ui/KeypressStandard.ogg
+    $(LOCAL_PATH)/effects/ogg/KeypressDelete_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressInvalid_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressReturn_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressStandard_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg
diff --git a/data/sounds/OriginalAudio.mk b/data/sounds/OriginalAudio.mk
index f683752..4d74d12 100644
--- a/data/sounds/OriginalAudio.mk
+++ b/data/sounds/OriginalAudio.mk
@@ -9,67 +9,67 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \
-	$(LOCAL_PATH)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \
-	$(LOCAL_PATH)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \
-	$(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
-	$(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
-	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \
-	$(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \
-	$(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
-	$(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
-	$(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
-	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \
-	$(LOCAL_PATH)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \
-	$(LOCAL_PATH)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \
-	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \
-	$(LOCAL_PATH)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \
-	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg
+	$(LOCAL_PATH)/F1_MissedCall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_MissedCall.ogg \
+	$(LOCAL_PATH)/F1_New_MMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_MMS.ogg \
+	$(LOCAL_PATH)/F1_New_SMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_SMS.ogg \
+	$(LOCAL_PATH)/Alarm_Buzzer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Buzzer.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_01.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_01.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_02.ogg \
+	$(LOCAL_PATH)/Alarm_Classic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Classic.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_03.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_03.ogg \
+	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Rooster_02.ogg \
+	$(LOCAL_PATH)/Ring_Classic_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Classic_02.ogg \
+	$(LOCAL_PATH)/Ring_Digital_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Digital_02.ogg \
+	$(LOCAL_PATH)/Ring_Synth_04.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_04.ogg \
+	$(LOCAL_PATH)/Ring_Synth_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_02.ogg \
+	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Beat_Box_Android.ogg \
+	$(LOCAL_PATH)/notifications/Heaven.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Heaven.ogg \
+	$(LOCAL_PATH)/notifications/TaDa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/TaDa.ogg \
+	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tinkerbell.ogg \
+	$(LOCAL_PATH)/effects/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BeatPlucker.ogg \
+	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CaffeineSnake.ogg
 
 ifneq ($(MINIMAL_NEWWAVELABS),true)
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \
-	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \
-	$(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:system/media/audio/ringtones/CaribbeanIce.ogg \
-	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \
-	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \
-	$(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:system/media/audio/ringtones/FriendlyGhost.ogg \
-	$(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:system/media/audio/ringtones/GameOverGuitar.ogg \
-	$(LOCAL_PATH)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \
-	$(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:system/media/audio/ringtones/InsertCoin.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \
-	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \
-	$(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:system/media/audio/ringtones/NewPlayer.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises1.ogg:system/media/audio/ringtones/Noises1.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises2.ogg:system/media/audio/ringtones/Noises2.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises3.ogg:system/media/audio/ringtones/Noises3.ogg \
-	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \
-	$(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:system/media/audio/ringtones/RomancingTheTone.ogg \
-	$(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:system/media/audio/ringtones/SitarVsSitar.ogg \
-	$(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:system/media/audio/ringtones/SpringyJalopy.ogg \
-	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \
-	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \
-	$(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:system/media/audio/ringtones/VeryAlarmed.ogg \
-	$(LOCAL_PATH)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg \
-	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \
-	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \
-	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \
-	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \
-	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \
-	$(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:system/media/audio/ringtones/CrazyDream.ogg \
-	$(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:system/media/audio/ringtones/DreamTheme.ogg
+	$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BentleyDubs.ogg \
+	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BirdLoop.ogg \
+	$(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CaribbeanIce.ogg \
+	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CurveBall.ogg \
+	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/EtherShake.ogg \
+	$(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/FriendlyGhost.ogg \
+	$(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/GameOverGuitar.ogg \
+	$(LOCAL_PATH)/newwavelabs/Growl.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Growl.ogg \
+	$(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/InsertCoin.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoopyLounge.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoveFlute.ogg \
+	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MidEvilJaunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MildlyAlarming.ogg \
+	$(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/NewPlayer.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises1.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises1.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises2.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises2.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises3.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises3.ogg \
+	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/OrganDub.ogg \
+	$(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/RomancingTheTone.ogg \
+	$(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SitarVsSitar.ogg \
+	$(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SpringyJalopy.ogg \
+	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Terminated.ogg \
+	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/TwirlAway.ogg \
+	$(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/VeryAlarmed.ogg \
+	$(LOCAL_PATH)/newwavelabs/World.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/World.ogg \
+	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DearDeer.ogg \
+	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DontPanic.ogg \
+	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Highwire.ogg \
+	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/KzurbSonar.ogg \
+	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/OnTheHunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/Voila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Voila.ogg \
+	$(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CrazyDream.ogg \
+	$(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DreamTheme.ogg
 endif
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 6798ab2..63a806e 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -556,7 +556,7 @@
      * @hide
      */
     public int saveUnclippedLayer(int left, int top, int right, int bottom) {
-        return nSaveLayer(mNativeCanvasWrapper, left, top, right, bottom, 0, 0);
+        return nSaveUnclippedLayer(mNativeCanvasWrapper, left, top, right, bottom);
     }
 
     /**
@@ -1395,6 +1395,8 @@
     private static native int nSaveLayerAlpha(long nativeCanvas, float l, float t, float r, float b,
             int alpha, int layerFlags);
     @CriticalNative
+    private static native int nSaveUnclippedLayer(long nativeCanvas, int l, int t, int r, int b);
+    @CriticalNative
     private static native boolean nRestore(long canvasHandle);
     @CriticalNative
     private static native void nRestoreToCount(long canvasHandle, int saveCount);
diff --git a/graphics/java/android/graphics/Insets.java b/graphics/java/android/graphics/Insets.java
index d9da27c..8258b57 100644
--- a/graphics/java/android/graphics/Insets.java
+++ b/graphics/java/android/graphics/Insets.java
@@ -93,6 +93,41 @@
     }
 
     /**
+     * Subtract two Insets.
+     *
+     * @param a The minuend.
+     * @param b The subtrahend.
+     * @return a - b, i. e. all insets on every side are subtracted from each other.
+     */
+    public static @NonNull Insets subtract(@NonNull Insets a, @NonNull Insets b) {
+        return Insets.of(a.left - b.left, a.top - b.top, a.right - b.right, a.bottom - b.bottom);
+    }
+
+    /**
+     * Retrieves the maximum of two Insets.
+     *
+     * @param a The first Insets.
+     * @param b The second Insets.
+     * @return max(a, b), i. e. the larger of every inset on every side is taken for the result.
+     */
+    public static @NonNull Insets max(@NonNull Insets a, @NonNull Insets b) {
+        return Insets.of(Math.max(a.left, b.left), Math.max(a.top, b.top),
+                Math.max(a.right, b.right), Math.max(a.bottom, b.bottom));
+    }
+
+    /**
+     * Retrieves the minimum of two Insets.
+     *
+     * @param a The first Insets.
+     * @param b The second Insets.
+     * @return min(a, b), i. e. the smaller of every inset on every side is taken for the result.
+     */
+    public static @NonNull Insets min(@NonNull Insets a, @NonNull Insets b) {
+        return Insets.of(Math.min(a.left, b.left), Math.min(a.top, b.top),
+                Math.min(a.right, b.right), Math.min(a.bottom, b.bottom));
+    }
+
+    /**
      * Two Insets instances are equal iff they belong to the same class and their fields are
      * pairwise equal.
      *
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 6821282..3342fd2 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -25,6 +25,7 @@
 import android.annotation.Size;
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.fonts.FontVariationAxis;
+import android.os.Build;
 import android.os.LocaleList;
 import android.text.GraphicsOperations;
 import android.text.SpannableString;
@@ -69,7 +70,7 @@
     private MaskFilter  mMaskFilter;
     private PathEffect  mPathEffect;
     private Shader      mShader;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private Typeface    mTypeface;
     private Xfermode    mXfermode;
 
diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java
index 67ad404..515532f 100644
--- a/graphics/java/android/graphics/RecordingCanvas.java
+++ b/graphics/java/android/graphics/RecordingCanvas.java
@@ -189,6 +189,14 @@
         nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunctor, releasedCallback);
     }
 
+    /**
+     * Calls the provided functor that was created via WebViewFunctor_create()
+     * @hide
+     */
+    public void drawWebViewFunctor(int functor) {
+        nDrawWebViewFunctor(mNativeCanvasWrapper, functor);
+    }
+
     ///////////////////////////////////////////////////////////////////////////
     // Display list
     ///////////////////////////////////////////////////////////////////////////
@@ -303,4 +311,6 @@
     @CriticalNative
     private static native void nDrawRoundRect(long renderer, long propLeft, long propTop,
             long propRight, long propBottom, long propRx, long propRy, long propPaint);
+    @CriticalNative
+    private static native void nDrawWebViewFunctor(long canvas, int functor);
 }
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index d6f08b9..3b1d44b 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -446,7 +446,21 @@
     }
 
     /**
-     * Sets the clip bounds of the RenderNode.
+     * Gets whether or not a compositing layer is forced to be used. The default & recommended
+     * is false, as it is typically faster to avoid using compositing layers.
+     * See {@link #setUseCompositingLayer(boolean, Paint)}.
+     *
+     * @return true if a compositing layer is forced, false otherwise
+     */
+    public boolean getUseCompositingLayer() {
+        return nGetLayerType(mNativeRenderNode) != 0;
+    }
+
+    /**
+     * Sets the clip bounds of the RenderNode. If null, the clip bounds is removed from the
+     * RenderNode. If non-null, the RenderNode will be clipped to this rect. If
+     * {@link #setClipToBounds(boolean)} is true, then the RenderNode will be clipped to the
+     * intersection of this rectangle and the bounds of the render node.
      *
      * @param rect the bounds to clip to. If null, the clip bounds are reset
      * @return True if the clip bounds changed, false otherwise
@@ -460,16 +474,30 @@
     }
 
     /**
-     * Set whether the Render node should clip itself to its bounds. This property is controlled by
-     * the view's parent.
+     * Set whether the Render node should clip itself to its bounds. This defaults to true,
+     * and is useful to the renderer in enable quick-rejection of chunks of the tree as well as
+     * better partial invalidation support. Clipping can be further restricted or controlled
+     * through the combination of this property as well as {@link #setClipBounds(Rect)}, which
+     * allows for a different clipping rectangle to be used in addition to or instead of the
+     * {@link #setLeftTopRightBottom(int, int, int, int)} or the RenderNode.
      *
-     * @param clipToBounds true if the display list should clip to its bounds
+     * @param clipToBounds true if the display list should clip to its bounds, false otherwise.
      */
     public boolean setClipToBounds(boolean clipToBounds) {
         return nSetClipToBounds(mNativeRenderNode, clipToBounds);
     }
 
     /**
+     * Returns whether or not the RenderNode is clipping to its bounds. See
+     * {@link #setClipToBounds(boolean)} and {@link #setLeftTopRightBottom(int, int, int, int)}
+     *
+     * @return true if the render node clips to its bounds, false otherwise.
+     */
+    public boolean getClipToBounds() {
+        return nGetClipToBounds(mNativeRenderNode);
+    }
+
+    /**
      * Sets whether the RenderNode should be drawn immediately after the
      * closest ancestor RenderNode containing a projection receiver.
      *
@@ -1339,12 +1367,18 @@
     private static native boolean nSetLayerType(long renderNode, int layerType);
 
     @CriticalNative
+    private static native int nGetLayerType(long renderNode);
+
+    @CriticalNative
     private static native boolean nSetLayerPaint(long renderNode, long paint);
 
     @CriticalNative
     private static native boolean nSetClipToBounds(long renderNode, boolean clipToBounds);
 
     @CriticalNative
+    private static native boolean nGetClipToBounds(long renderNode);
+
+    @CriticalNative
     private static native boolean nSetClipBounds(long renderNode, int left, int top,
             int right, int bottom);
 
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 10f6fae..77e77c4 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -29,6 +29,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff.Mode;
 import android.graphics.Rect;
+import android.os.Build;
 import android.os.SystemClock;
 import android.util.DisplayMetrics;
 import android.util.LayoutDirection;
@@ -736,7 +737,7 @@
         /**
          * @hide
          */
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         protected DrawableContainerState(DrawableContainerState orig, DrawableContainer owner,
                 Resources res) {
             mOwner = owner;
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 6e6ed30..213ed7d 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -26,6 +26,7 @@
 import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.Binder;
+import android.os.Build;
 import android.os.IBinder;
 import android.os.Process;
 import android.os.RemoteException;
@@ -55,6 +56,7 @@
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.security.InvalidKeyException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
@@ -315,13 +317,14 @@
     }
 
     /**
-     * List uids of all keys that are auth bound to the current user. 
+     * List uids of all keys that are auth bound to the current user.
      * Only system is allowed to call this method.
      */
     @UnsupportedAppUsage
     public int[] listUidsOfAuthBoundKeys() {
-        final int MAX_RESULT_SIZE = 100;
-        int[] uidsOut = new int[MAX_RESULT_SIZE];
+        // uids are returned as a list of strings because list of integers
+        // as an output parameter is not supported by aidl-cpp.
+        List<String> uidsOut = new ArrayList<>();
         try {
             int rc = mBinder.listUidsOfAuthBoundKeys(uidsOut);
             if (rc != NO_ERROR) {
@@ -335,15 +338,15 @@
             Log.w(TAG, "KeyStore exception", e);
             return null;
         }
-        // Remove any 0 entries
-        return Arrays.stream(uidsOut).filter(x -> x > 0).toArray();
+        // Turn list of strings into an array of uid integers.
+        return uidsOut.stream().mapToInt(Integer::parseInt).toArray();
    }
 
     public String[] list(String prefix) {
         return list(prefix, UID_SELF);
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public boolean reset() {
         try {
             return mBinder.reset() == NO_ERROR;
@@ -411,7 +414,7 @@
         }
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public boolean isEmpty() {
         return isEmpty(UserHandle.myUserId());
     }
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index 953cef7d..aa29174 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -210,6 +210,10 @@
                         throw new InvalidAlgorithmParameterException(
                             "HMAC key size must be at least 64 bits.");
                     }
+                    if (mKeySizeBits > 512 && spec.isStrongBoxBacked()) {
+                        throw new InvalidAlgorithmParameterException(
+                            "StrongBox HMAC key size must be smaller than 512 bits.");
+                    }
 
                     // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm
                     // implies SHA-256 digest). Because keymaster HMAC key is authorized only for
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index 5fc742a..d44c894 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -303,7 +303,7 @@
             if (mKeySizeBits == -1) {
                 mKeySizeBits = getDefaultKeySize(keymasterAlgorithm);
             }
-            checkValidKeySize(keymasterAlgorithm, mKeySizeBits);
+            checkValidKeySize(keymasterAlgorithm, mKeySizeBits, mSpec.isStrongBoxBacked());
 
             if (spec.getKeystoreAlias() == null) {
                 throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
@@ -724,10 +724,18 @@
         }
     }
 
-    private static void checkValidKeySize(int keymasterAlgorithm, int keySize)
+    private static void checkValidKeySize(
+            int keymasterAlgorithm,
+            int keySize,
+            boolean isStrongBoxBacked)
             throws InvalidAlgorithmParameterException {
         switch (keymasterAlgorithm) {
             case KeymasterDefs.KM_ALGORITHM_EC:
+                if (isStrongBoxBacked && keySize != 256) {
+                    throw new InvalidAlgorithmParameterException(
+                            "Unsupported StrongBox EC key size: "
+                            + keySize + " bits. Supported: 256");
+                }
                 if (!SUPPORTED_EC_NIST_CURVE_SIZES.contains(keySize)) {
                     throw new InvalidAlgorithmParameterException("Unsupported EC key size: "
                             + keySize + " bits. Supported: " + SUPPORTED_EC_NIST_CURVE_SIZES);
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 98af3eb..eeaefc5 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -167,6 +167,7 @@
         },
     },
     data: ["tests/data/**/*.apk"],
+    test_suites: ["device-tests"],
 }
 
 cc_benchmark {
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index c20c720..5a26780 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -623,7 +623,7 @@
               }
 
               // Add the pairing of overlayable properties to resource ids to the package
-              OverlayableInfo overlayable_info;
+              OverlayableInfo overlayable_info{};
               overlayable_info.policy_flags = policy_header->policy_flags;
               loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids));
               break;
diff --git a/libs/androidfw/TEST_MAPPING b/libs/androidfw/TEST_MAPPING
new file mode 100644
index 0000000..a58b47f
--- /dev/null
+++ b/libs/androidfw/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+  "presubmit": [
+    {
+      "name": "libandroidfw_tests",
+      "host": true
+    }
+  ]
+}
\ No newline at end of file
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 441356b..22d587a 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -365,6 +365,6 @@
 // structs with size fields (like Res_value, ResTable_entry) should be
 // backwards and forwards compatible (aka checking the size field against
 // sizeof(Res_value) might not be backwards compatible.
-TEST(LoadedArscTest, LoadingShouldBeForwardsAndBackwardsCompatible) { ASSERT_TRUE(false); }
+// TEST(LoadedArscTest, LoadingShouldBeForwardsAndBackwardsCompatible) { ASSERT_TRUE(false); }
 
 }  // namespace android
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 0503f36..96798f9 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -9,6 +9,8 @@
         "hwui_lto",
     ],
 
+    cpp_std: "experimental",
+
     cflags: [
         "-DEGL_EGLEXT_PROTOTYPES",
         "-DGL_GLEXT_PROTOTYPES",
@@ -59,6 +61,7 @@
         "libstatslog",
         "libutils",
         "libEGL",
+        "libGLESv1_CM",
         "libGLESv2",
         "libGLESv3",
         "libvulkan",
@@ -208,6 +211,7 @@
         "FrameInfoVisualizer.cpp",
         "GpuMemoryTracker.cpp",
         "HardwareBitmapUploader.cpp",
+        "HWUIProperties.sysprop",
         "Interpolator.cpp",
         "JankTracker.cpp",
         "Layer.cpp",
@@ -225,6 +229,7 @@
         "RenderProperties.cpp",
         "SkiaCanvas.cpp",
         "TreeInfo.cpp",
+        "WebViewFunctorManager.cpp",
         "VectorDrawable.cpp",
         "protos/graphicsstats.proto",
     ],
@@ -329,6 +334,7 @@
         "tests/unit/TypefaceTests.cpp",
         "tests/unit/VectorDrawableTests.cpp",
         "tests/unit/VectorDrawableAtlasTests.cpp",
+        "tests/unit/WebViewFunctorManagerTests.cpp",
     ],
 }
 
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index 0b9d82b..ed167e5 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -20,6 +20,7 @@
 
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
+#include <ui/GraphicTypes.h>
 
 #include <mutex>
 #include <thread>
@@ -61,6 +62,50 @@
     return displayInfo;
 }
 
+static void queryWideColorGamutPreference(SkColorSpace::Gamut* colorGamut,
+                                          sk_sp<SkColorSpace>* colorSpace, SkColorType* colorType) {
+    if (Properties::isolatedProcess) {
+        *colorGamut = SkColorSpace::Gamut::kSRGB_Gamut;
+        *colorSpace = SkColorSpace::MakeSRGB();
+        *colorType = SkColorType::kN32_SkColorType;
+        return;
+    }
+    ui::Dataspace defaultDataspace, wcgDataspace;
+    ui::PixelFormat defaultPixelFormat, wcgPixelFormat;
+    status_t status =
+        SurfaceComposerClient::getCompositionPreference(&defaultDataspace, &defaultPixelFormat,
+                                                        &wcgDataspace, &wcgPixelFormat);
+    LOG_ALWAYS_FATAL_IF(status, "Failed to get composition preference, error %d", status);
+    switch (wcgDataspace) {
+        case ui::Dataspace::DISPLAY_P3:
+            *colorGamut = SkColorSpace::Gamut::kDCIP3_D65_Gamut;
+            *colorSpace = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+                                                SkColorSpace::Gamut::kDCIP3_D65_Gamut);
+            break;
+        case ui::Dataspace::V0_SCRGB:
+            *colorGamut = SkColorSpace::Gamut::kSRGB_Gamut;
+            *colorSpace = SkColorSpace::MakeSRGB();
+            break;
+        case ui::Dataspace::V0_SRGB:
+            // when sRGB is returned, it means wide color gamut is not supported.
+            *colorGamut = SkColorSpace::Gamut::kSRGB_Gamut;
+            *colorSpace = SkColorSpace::MakeSRGB();
+            break;
+        default:
+            LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
+    }
+    switch (wcgPixelFormat) {
+        case ui::PixelFormat::RGBA_8888:
+            *colorType = SkColorType::kN32_SkColorType;
+            break;
+        case ui::PixelFormat::RGBA_FP16:
+            *colorType = SkColorType::kRGBA_F16_SkColorType;
+            break;
+        default:
+            LOG_ALWAYS_FATAL("Unreachable: unsupported pixel format.");
+    }
+}
+
 DeviceInfo::DeviceInfo() {
 #if HWUI_NULL_GPU
         mMaxTextureSize = NULL_GPU_MAX_TEXTURE_SIZE;
@@ -68,6 +113,7 @@
         mMaxTextureSize = -1;
 #endif
     mDisplayInfo = QueryDisplayInfo();
+    queryWideColorGamutPreference(&mWideColorGamut, &mWideColorSpace, &mWideColorType);
 }
 
 int DeviceInfo::maxTextureSize() const {
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index 5956215..9bcc8e8 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -16,6 +16,7 @@
 #ifndef DEVICEINFO_H
 #define DEVICEINFO_H
 
+#include <SkImageInfo.h>
 #include <ui/DisplayInfo.h>
 
 #include "utils/Macros.h"
@@ -37,6 +38,9 @@
     // context or if you are using the HWUI_NULL_GPU
     int maxTextureSize() const;
     const DisplayInfo& displayInfo() const { return mDisplayInfo; }
+    SkColorSpace::Gamut getWideColorGamut() const { return mWideColorGamut; }
+    sk_sp<SkColorSpace> getWideColorSpace() const { return mWideColorSpace; }
+    SkColorType getWideColorType() const { return mWideColorType; }
 
 private:
     friend class renderthread::RenderThread;
@@ -46,6 +50,9 @@
 
     int mMaxTextureSize;
     DisplayInfo mDisplayInfo;
+    SkColorSpace::Gamut mWideColorGamut;
+    sk_sp<SkColorSpace> mWideColorSpace;
+    SkColorType mWideColorType;
 };
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in
index 04cf611..14e3a32 100644
--- a/libs/hwui/DisplayListOps.in
+++ b/libs/hwui/DisplayListOps.in
@@ -18,6 +18,7 @@
 X(Save) 
 X(Restore) 
 X(SaveLayer)
+X(SaveBehind)
 X(Concat) 
 X(SetMatrix) 
 X(Translate)
@@ -40,14 +41,10 @@
 X(DrawImageNine) 
 X(DrawImageRect) 
 X(DrawImageLattice)
-X(DrawText) 
-X(DrawPosText) 
-X(DrawPosTextH)
-X(DrawTextRSXform) 
 X(DrawTextBlob)
 X(DrawPatch) 
 X(DrawPoints) 
 X(DrawVertices) 
 X(DrawAtlas) 
 X(DrawShadowRec)
-X(DrawVectorDrawable)
\ No newline at end of file
+X(DrawVectorDrawable)
diff --git a/libs/hwui/HWUIProperties.sysprop b/libs/hwui/HWUIProperties.sysprop
new file mode 100644
index 0000000..42191ca
--- /dev/null
+++ b/libs/hwui/HWUIProperties.sysprop
@@ -0,0 +1,9 @@
+owner: Platform
+module: "android.uirenderer"
+prop {
+    api_name: "use_vulkan"
+    type: Boolean
+    prop_name: "ro.hwui.use_vulkan"
+    scope: Public
+    access: Readonly
+}
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index a97c12c..b9860ad 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -253,7 +253,8 @@
         eglDestroySyncKHR(display, fence);
     }
 
-    return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info(), Bitmap::computePalette(bitmap)));
+    return Bitmap::createFrom(buffer.get(), bitmap.refColorSpace(), bitmap.alphaType(),
+                              Bitmap::computePalette(bitmap));
 }
 
 }  // namespace android::uirenderer
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 8067313..046ffc4 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -18,6 +18,7 @@
 #include "Debug.h"
 #include "DeviceInfo.h"
 #include "SkTraceEventCommon.h"
+#include "HWUIProperties.sysprop.h"
 
 #include <algorithm>
 #include <cstdlib>
@@ -174,8 +175,13 @@
     if (sRenderPipelineType != RenderPipelineType::NotInitialized) {
         return sRenderPipelineType;
     }
+    bool useVulkan = use_vulkan().value_or(false);
     char prop[PROPERTY_VALUE_MAX];
-    property_get(PROPERTY_RENDERER, prop, "skiagl");
+    if (useVulkan) {
+        property_get(PROPERTY_RENDERER, prop, "skiavk");
+    } else {
+        property_get(PROPERTY_RENDERER, prop, "skiagl");
+    }
     if (!strcmp(prop, "skiavk")) {
         ALOGD("Skia Vulkan Pipeline");
         sRenderPipelineType = RenderPipelineType::SkiaVulkan;
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 0b847af..6dc9d34 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -18,6 +18,7 @@
 
 #include "VectorDrawable.h"
 
+#include "SkAndroidFrameworkUtils.h"
 #include "SkCanvas.h"
 #include "SkData.h"
 #include "SkDrawShadowInfo.h"
@@ -116,6 +117,16 @@
                       clipMatrix.isIdentity() ? nullptr : &clipMatrix, flags});
     }
 };
+struct SaveBehind final : Op {
+    static const auto kType = Type::SaveBehind;
+    SaveBehind(const SkRect* subset) {
+        if (subset) { this->subset = *subset; }
+    }
+    SkRect  subset = kUnset;
+    void draw(SkCanvas* c, const SkMatrix&) const {
+        SkAndroidFrameworkUtils::SaveBehind(c, &subset);
+    }
+};
 
 struct Concat final : Op {
     static const auto kType = Type::Concat;
@@ -362,61 +373,6 @@
     }
 };
 
-struct DrawText final : Op {
-    static const auto kType = Type::DrawText;
-    DrawText(size_t bytes, SkScalar x, SkScalar y, const SkPaint& paint)
-            : bytes(bytes), x(x), y(y), paint(paint) {}
-    size_t bytes;
-    SkScalar x, y;
-    SkPaint paint;
-    void draw(SkCanvas* c, const SkMatrix&) const {
-        c->drawText(pod<void>(this), bytes, x, y, paint);
-    }
-};
-struct DrawPosText final : Op {
-    static const auto kType = Type::DrawPosText;
-    DrawPosText(size_t bytes, const SkPaint& paint, int n) : bytes(bytes), paint(paint), n(n) {}
-    size_t bytes;
-    SkPaint paint;
-    int n;
-    void draw(SkCanvas* c, const SkMatrix&) const {
-        auto points = pod<SkPoint>(this);
-        auto text = pod<void>(this, n * sizeof(SkPoint));
-        c->drawPosText(text, bytes, points, paint);
-    }
-};
-struct DrawPosTextH final : Op {
-    static const auto kType = Type::DrawPosTextH;
-    DrawPosTextH(size_t bytes, SkScalar y, const SkPaint& paint, int n)
-            : bytes(bytes), y(y), paint(paint), n(n) {}
-    size_t bytes;
-    SkScalar y;
-    SkPaint paint;
-    int n;
-    void draw(SkCanvas* c, const SkMatrix&) const {
-        auto xs = pod<SkScalar>(this);
-        auto text = pod<void>(this, n * sizeof(SkScalar));
-        c->drawPosTextH(text, bytes, xs, y, paint);
-    }
-};
-struct DrawTextRSXform final : Op {
-    static const auto kType = Type::DrawTextRSXform;
-    DrawTextRSXform(size_t bytes, int xforms, const SkRect* cull, const SkPaint& paint)
-            : bytes(bytes), xforms(xforms), paint(paint) {
-        if (cull) {
-            this->cull = *cull;
-        }
-    }
-    size_t bytes;
-    int xforms;
-    SkRect cull = kUnset;
-    SkPaint paint;
-    void draw(SkCanvas* c, const SkMatrix&) const {
-        // For alignment, the SkRSXforms are first in the pod section, followed by the text.
-        c->drawTextRSXform(pod<void>(this, xforms * sizeof(SkRSXform)), bytes, pod<SkRSXform>(this),
-                           maybe_unset(cull), paint);
-    }
-};
 struct DrawTextBlob final : Op {
     static const auto kType = Type::DrawTextBlob;
     DrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint)
@@ -579,6 +535,10 @@
     this->push<SaveLayer>(0, bounds, paint, backdrop, clipMask, clipMatrix, flags);
 }
 
+void DisplayListData::saveBehind(const SkRect* subset) {
+    this->push<SaveBehind>(0, subset);
+}
+
 void DisplayListData::concat(const SkMatrix& matrix) {
     this->push<Concat>(0, matrix);
 }
@@ -667,33 +627,6 @@
            fs);
 }
 
-void DisplayListData::drawText(const void* text, size_t bytes, SkScalar x, SkScalar y,
-                               const SkPaint& paint) {
-    void* pod = this->push<DrawText>(bytes, bytes, x, y, paint);
-    copy_v(pod, (const char*)text, bytes);
-    mHasText = true;
-}
-void DisplayListData::drawPosText(const void* text, size_t bytes, const SkPoint pos[],
-                                  const SkPaint& paint) {
-    int n = paint.countText(text, bytes);
-    void* pod = this->push<DrawPosText>(n * sizeof(SkPoint) + bytes, bytes, paint, n);
-    copy_v(pod, pos, n, (const char*)text, bytes);
-    mHasText = true;
-}
-void DisplayListData::drawPosTextH(const void* text, size_t bytes, const SkScalar xs[], SkScalar y,
-                                   const SkPaint& paint) {
-    int n = paint.countText(text, bytes);
-    void* pod = this->push<DrawPosTextH>(n * sizeof(SkScalar) + bytes, bytes, y, paint, n);
-    copy_v(pod, xs, n, (const char*)text, bytes);
-    mHasText = true;
-}
-void DisplayListData::drawTextRSXform(const void* text, size_t bytes, const SkRSXform xforms[],
-                                      const SkRect* cull, const SkPaint& paint) {
-    int n = paint.countText(text, bytes);
-    void* pod = this->push<DrawTextRSXform>(bytes + n * sizeof(SkRSXform), bytes, n, cull, paint);
-    copy_v(pod, xforms, n, (const char*)text, bytes);
-    mHasText = true;
-}
 void DisplayListData::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
                                    const SkPaint& paint) {
     this->push<DrawTextBlob>(0, blob, x, y, paint);
@@ -848,6 +781,11 @@
     fDL->restore();
 }
 
+bool RecordingCanvas::onDoSaveBehind(const SkRect* subset) {
+    fDL->saveBehind(subset);
+    return false;
+}
+
 void RecordingCanvas::didConcat(const SkMatrix& matrix) {
     fDL->concat(matrix);
 }
@@ -912,10 +850,6 @@
     fDL->drawAnnotation(rect, key, val);
 }
 
-void RecordingCanvas::onDrawTextRSXform(const void* text, size_t bytes, const SkRSXform xform[],
-                                        const SkRect* cull, const SkPaint& paint) {
-    fDL->drawTextRSXform(text, bytes, xform, cull, paint);
-}
 void RecordingCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
                                      const SkPaint& paint) {
     fDL->drawTextBlob(blob, x, y, paint);
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 35cf707..caaef67 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -65,6 +65,7 @@
     void applyColorTransform(ColorTransform transform);
 
     bool hasText() const { return mHasText; }
+    size_t usedSize() const { return fUsed; }
 
 private:
     friend class RecordingCanvas;
@@ -74,6 +75,7 @@
     void save();
     void saveLayer(const SkRect*, const SkPaint*, const SkImageFilter*, const SkImage*,
                    const SkMatrix*, SkCanvas::SaveLayerFlags);
+    void saveBehind(const SkRect*);
     void restore();
 
     void concat(const SkMatrix&);
@@ -99,10 +101,6 @@
     void drawDrawable(SkDrawable*, const SkMatrix*);
     void drawPicture(const SkPicture*, const SkMatrix*, const SkPaint*);
 
-    void drawText(const void*, size_t, SkScalar, SkScalar, const SkPaint&);
-    void drawPosText(const void*, size_t, const SkPoint[], const SkPaint&);
-    void drawPosTextH(const void*, size_t, const SkScalar[], SkScalar, const SkPaint&);
-    void drawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*, const SkPaint&);
     void drawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&);
 
     void drawImage(sk_sp<const SkImage>, SkScalar, SkScalar, const SkPaint*, BitmapPalette palette);
@@ -145,6 +143,7 @@
     void willSave() override;
     SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override;
     void willRestore() override;
+    bool onDoSaveBehind(const SkRect*) override;
 
     void onFlush() override;
 
@@ -170,8 +169,6 @@
     void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override;
     void onDrawAnnotation(const SkRect&, const char[], SkData*) override;
 
-    void onDrawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*,
-                           const SkPaint&) override;
     void onDrawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&) override;
 
     void onDrawBitmap(const SkBitmap&, SkScalar, SkScalar, const SkPaint*) override;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 00ce28a..1ff7ffe 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -288,7 +288,10 @@
     mDisplayList = mStagingDisplayList;
     mStagingDisplayList = nullptr;
     if (mDisplayList) {
-        mDisplayList->syncContents();
+        WebViewSyncData syncData {
+            .applyForceDark = info && !info->disableForceDark
+        };
+        mDisplayList->syncContents(syncData);
         handleForceDark(info);
     }
 }
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 04379ae..ddb7e4e 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -98,15 +98,15 @@
 
     LayerProperties& operator=(const LayerProperties& other);
 
+    // Strongly recommend using effectiveLayerType instead
+    LayerType type() const { return mType; }
+
 private:
     LayerProperties();
     ~LayerProperties();
     void reset();
     bool setColorFilter(SkColorFilter* filter);
 
-    // Private since external users should go through properties().effectiveLayerType()
-    LayerType type() const { return mType; }
-
     friend class RenderProperties;
 
     LayerType mType = LayerType::None;
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 6be7ef7..ab95e69 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -24,6 +24,7 @@
 #include "hwui/PaintFilter.h"
 #include "pipeline/skia/AnimatedDrawables.h"
 
+#include <SkAndroidFrameworkUtils.h>
 #include <SkAnimatedImage.h>
 #include <SkCanvasStateUtils.h>
 #include <SkColorFilter.h>
@@ -185,6 +186,11 @@
     return this->saveLayer(left, top, right, bottom, nullptr, flags);
 }
 
+int SkiaCanvas::saveUnclippedLayer(int left, int top, int right, int bottom) {
+    SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
+    return SkAndroidFrameworkUtils::SaveBehind(mCanvas, &bounds);
+}
+
 class SkiaCanvas::Clip {
 public:
     Clip(const SkRect& rect, SkClipOp op, const SkMatrix& m)
@@ -704,6 +710,7 @@
 void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
                                   const SkPaint& paint, const SkPath& path, size_t start,
                                   size_t end) {
+    SkFont font = SkFont::LEGACY_ExtractFromPaint(paint);
     SkPaint paintCopy(paint);
     if (mPaintFilter) {
         mPaintFilter->filter(&paintCopy);
@@ -711,9 +718,10 @@
     SkASSERT(paintCopy.getTextEncoding() == kGlyphID_SkTextEncoding);
 
     const int N = end - start;
-    SkAutoSTMalloc<1024, uint8_t> storage(N * (sizeof(uint16_t) + sizeof(SkRSXform)));
-    SkRSXform* xform = (SkRSXform*)storage.get();
-    uint16_t* glyphs = (uint16_t*)(xform + N);
+    SkTextBlobBuilder builder;
+    auto rec = builder.allocRunRSXform(font, N);
+    SkRSXform* xform = (SkRSXform*)rec.pos;
+    uint16_t* glyphs = rec.glyphs;
     SkPathMeasure meas(path, false);
 
     for (size_t i = start; i < end; i++) {
@@ -734,7 +742,7 @@
         xform[i - start].fTy = pos.y() + tan.x() * y - halfWidth * tan.y();
     }
 
-    this->asSkCanvas()->drawTextRSXform(glyphs, sizeof(uint16_t) * N, xform, nullptr, paintCopy);
+    this->asSkCanvas()->drawTextBlob(builder.make(), 0, 0, paintCopy);
 }
 
 // ----------------------------------------------------------------------------
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 24d9c08..4ab0a59 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -74,6 +74,7 @@
                           SaveFlags::Flags flags) override;
     virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
                                SaveFlags::Flags flags) override;
+    virtual int saveUnclippedLayer(int left, int top, int right, int bottom) override;
 
     virtual void getMatrix(SkMatrix* outMatrix) const override;
     virtual void setMatrix(const SkMatrix& matrix) override;
diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp
new file mode 100644
index 0000000..9170d6d
--- /dev/null
+++ b/libs/hwui/WebViewFunctorManager.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "WebViewFunctorManager.h"
+
+#include <private/hwui/WebViewFunctor.h>
+#include "Properties.h"
+#include "renderthread/RenderThread.h"
+
+#include <log/log.h>
+#include <utils/Trace.h>
+#include <atomic>
+
+namespace android::uirenderer {
+
+RenderMode WebViewFunctor_queryPlatformRenderMode() {
+    auto pipelineType = Properties::getRenderPipelineType();
+    switch (pipelineType) {
+        case RenderPipelineType::SkiaGL:
+            return RenderMode::OpenGL_ES;
+        case RenderPipelineType::SkiaVulkan:
+            return RenderMode::Vulkan;
+        default:
+            LOG_ALWAYS_FATAL("Unknown render pipeline type: %d", (int)pipelineType);
+    }
+}
+
+int WebViewFunctor_create(void* data, const WebViewFunctorCallbacks& prototype,
+                          RenderMode functorMode) {
+    if (functorMode != RenderMode::OpenGL_ES && functorMode != RenderMode::Vulkan) {
+        ALOGW("Unknown rendermode %d", (int)functorMode);
+        return -1;
+    }
+    if (functorMode == RenderMode::Vulkan &&
+        WebViewFunctor_queryPlatformRenderMode() != RenderMode::Vulkan) {
+        ALOGW("Unable to map from GLES platform to a vulkan functor");
+        return -1;
+    }
+    return WebViewFunctorManager::instance().createFunctor(data, prototype, functorMode);
+}
+
+void WebViewFunctor_release(int functor) {
+    WebViewFunctorManager::instance().releaseFunctor(functor);
+}
+
+static std::atomic_int sNextId{1};
+
+WebViewFunctor::WebViewFunctor(void* data, const WebViewFunctorCallbacks& callbacks,
+                               RenderMode functorMode)
+        : mData(data) {
+    mFunctor = sNextId++;
+    mCallbacks = callbacks;
+    mMode = functorMode;
+}
+
+WebViewFunctor::~WebViewFunctor() {
+    destroyContext();
+
+    ATRACE_NAME("WebViewFunctor::onDestroy");
+    mCallbacks.onDestroyed(mFunctor, mData);
+}
+
+void WebViewFunctor::sync(const WebViewSyncData& syncData) const {
+    ATRACE_NAME("WebViewFunctor::sync");
+    mCallbacks.onSync(mFunctor, mData, syncData);
+}
+
+void WebViewFunctor::drawGl(const DrawGlInfo& drawInfo) {
+    ATRACE_NAME("WebViewFunctor::drawGl");
+    if (!mHasContext) {
+        mHasContext = true;
+    }
+    mCallbacks.gles.draw(mFunctor, mData, drawInfo);
+}
+
+void WebViewFunctor::destroyContext() {
+    if (mHasContext) {
+        mHasContext = false;
+        ATRACE_NAME("WebViewFunctor::onContextDestroyed");
+        mCallbacks.onContextDestroyed(mFunctor, mData);
+
+        // grContext may be null in unit tests.
+        auto* grContext = renderthread::RenderThread::getInstance().getGrContext();
+        if (grContext) grContext->resetContext();
+    }
+}
+
+WebViewFunctorManager& WebViewFunctorManager::instance() {
+    static WebViewFunctorManager sInstance;
+    return sInstance;
+}
+
+int WebViewFunctorManager::createFunctor(void* data, const WebViewFunctorCallbacks& callbacks,
+                                         RenderMode functorMode) {
+    auto object = std::make_unique<WebViewFunctor>(data, callbacks, functorMode);
+    int id = object->id();
+    auto handle = object->createHandle();
+    {
+        std::lock_guard _lock{mLock};
+        mActiveFunctors.push_back(std::move(handle));
+        mFunctors.push_back(std::move(object));
+    }
+    return id;
+}
+
+void WebViewFunctorManager::releaseFunctor(int functor) {
+    sp<WebViewFunctor::Handle> toRelease;
+    {
+        std::lock_guard _lock{mLock};
+        for (auto iter = mActiveFunctors.begin(); iter != mActiveFunctors.end(); iter++) {
+            if ((*iter)->id() == functor) {
+                toRelease = std::move(*iter);
+                mActiveFunctors.erase(iter);
+                break;
+            }
+        }
+    }
+}
+
+void WebViewFunctorManager::onContextDestroyed() {
+    // WARNING: SKETCHY
+    // Because we know that we always remove from mFunctors on RenderThread, the same
+    // thread that always invokes onContextDestroyed, we know that the functor pointers
+    // will remain valid without the lock held.
+    // However, we won't block new functors from being added in the meantime.
+    mLock.lock();
+    const size_t size = mFunctors.size();
+    WebViewFunctor* toDestroyContext[size];
+    for (size_t i = 0; i < size; i++) {
+        toDestroyContext[i] = mFunctors[i].get();
+    }
+    mLock.unlock();
+    for (size_t i = 0; i < size; i++) {
+        toDestroyContext[i]->destroyContext();
+    }
+}
+
+void WebViewFunctorManager::destroyFunctor(int functor) {
+    std::unique_ptr<WebViewFunctor> toRelease;
+    {
+        std::lock_guard _lock{mLock};
+        for (auto iter = mFunctors.begin(); iter != mFunctors.end(); iter++) {
+            if ((*iter)->id() == functor) {
+                toRelease = std::move(*iter);
+                mFunctors.erase(iter);
+                break;
+            }
+        }
+    }
+}
+
+sp<WebViewFunctor::Handle> WebViewFunctorManager::handleFor(int functor) {
+    std::lock_guard _lock{mLock};
+    for (auto& iter : mActiveFunctors) {
+        if (iter->id() == functor) {
+            return iter;
+        }
+    }
+    return nullptr;
+}
+
+}  // namespace android::uirenderer
diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h
new file mode 100644
index 0000000..1719ce7
--- /dev/null
+++ b/libs/hwui/WebViewFunctorManager.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <private/hwui/WebViewFunctor.h>
+#include <renderthread/RenderProxy.h>
+
+#include <utils/LightRefBase.h>
+#include <mutex>
+#include <vector>
+
+namespace android::uirenderer {
+
+class WebViewFunctorManager;
+
+class WebViewFunctor {
+public:
+    WebViewFunctor(void* data, const WebViewFunctorCallbacks& callbacks, RenderMode functorMode);
+    ~WebViewFunctor();
+
+    class Handle : public LightRefBase<Handle> {
+    public:
+        ~Handle() { renderthread::RenderProxy::destroyFunctor(id()); }
+
+        int id() const { return mReference.id(); }
+
+        void sync(const WebViewSyncData& syncData) const { mReference.sync(syncData); }
+
+        void drawGl(const DrawGlInfo& drawInfo) const { mReference.drawGl(drawInfo); }
+
+    private:
+        friend class WebViewFunctor;
+
+        Handle(WebViewFunctor& ref) : mReference(ref) {}
+
+        WebViewFunctor& mReference;
+    };
+
+    int id() const { return mFunctor; }
+    void sync(const WebViewSyncData& syncData) const;
+    void drawGl(const DrawGlInfo& drawInfo);
+    void destroyContext();
+
+    sp<Handle> createHandle() {
+        LOG_ALWAYS_FATAL_IF(mCreatedHandle);
+        mCreatedHandle = true;
+        return sp<Handle>{new Handle(*this)};
+    }
+
+private:
+    WebViewFunctorCallbacks mCallbacks;
+    void* const mData;
+    int mFunctor;
+    RenderMode mMode;
+    bool mHasContext = false;
+    bool mCreatedHandle = false;
+};
+
+class WebViewFunctorManager {
+public:
+    static WebViewFunctorManager& instance();
+
+    int createFunctor(void* data, const WebViewFunctorCallbacks& callbacks, RenderMode functorMode);
+    void releaseFunctor(int functor);
+    void onContextDestroyed();
+    void destroyFunctor(int functor);
+
+    sp<WebViewFunctor::Handle> handleFor(int functor);
+
+private:
+    WebViewFunctorManager() = default;
+    ~WebViewFunctorManager() = default;
+
+    std::mutex mLock;
+    std::vector<std::unique_ptr<WebViewFunctor>> mFunctors;
+    std::vector<sp<WebViewFunctor::Handle>> mActiveFunctors;
+};
+
+}  // namespace android::uirenderer
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 6c77f9e..6e0258c 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -75,31 +75,6 @@
     return allocateBitmap(bitmap, &Bitmap::allocateAshmemBitmap);
 }
 
-static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) {
-    void* addr = calloc(size, 1);
-    if (!addr) {
-        return nullptr;
-    }
-    return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes));
-}
-
-sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(SkBitmap& bitmap) {
-    return uirenderer::HardwareBitmapUploader::allocateHardwareBitmap(bitmap);
-}
-
-sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap) {
-    return allocateBitmap(bitmap, &android::allocateHeapBitmap);
-}
-
-sk_sp<Bitmap> Bitmap::allocateHeapBitmap(const SkImageInfo& info) {
-    size_t size;
-    if (!computeAllocationSize(info.minRowBytes(), info.height(), &size)) {
-        LOG_ALWAYS_FATAL("trying to allocate too large bitmap");
-        return nullptr;
-    }
-    return android::allocateHeapBitmap(size, info, info.minRowBytes());
-}
-
 sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) {
     // Create new ashmem region with read/write priv
     int fd = ashmem_create_region("bitmap", size);
@@ -121,6 +96,31 @@
     return sk_sp<Bitmap>(new Bitmap(addr, fd, size, info, rowBytes));
 }
 
+sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(const SkBitmap& bitmap) {
+    return uirenderer::HardwareBitmapUploader::allocateHardwareBitmap(bitmap);
+}
+
+sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap) {
+    return allocateBitmap(bitmap, &Bitmap::allocateHeapBitmap);
+}
+
+sk_sp<Bitmap> Bitmap::allocateHeapBitmap(const SkImageInfo& info) {
+    size_t size;
+    if (!computeAllocationSize(info.minRowBytes(), info.height(), &size)) {
+        LOG_ALWAYS_FATAL("trying to allocate too large bitmap");
+        return nullptr;
+    }
+    return allocateHeapBitmap(size, info, info.minRowBytes());
+}
+
+sk_sp<Bitmap> Bitmap::allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) {
+    void* addr = calloc(size, 1);
+    if (!addr) {
+        return nullptr;
+    }
+    return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes));
+}
+
 void FreePixelRef(void* addr, void* context) {
     auto pixelRef = (SkPixelRef*)context;
     pixelRef->unref();
@@ -132,17 +132,38 @@
                                     pixelRef.rowBytes()));
 }
 
-sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer) {
-    return createFrom(graphicBuffer, SkColorSpace::MakeSRGB());
+
+sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer, sk_sp<SkColorSpace> colorSpace,
+                                 SkAlphaType alphaType, BitmapPalette palette) {
+    // As we will be effectively texture-sampling the buffer (using either EGL or Vulkan), we can
+    // view the format as RGBA8888.
+    SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(),
+                                         kRGBA_8888_SkColorType, alphaType, colorSpace);
+    return sk_sp<Bitmap>(new Bitmap(graphicBuffer.get(), info, palette));
 }
 
-sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer, sk_sp<SkColorSpace> colorSpace) {
-    // As we will be effectively texture-sampling the buffer (using either EGL or Vulkan), we can
-    // view the colorspace as RGBA8888.
-    SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(),
-                                         kRGBA_8888_SkColorType, kPremul_SkAlphaType,
-                                         colorSpace);
-    return sk_sp<Bitmap>(new Bitmap(graphicBuffer.get(), info));
+sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, size_t rowBytes, int fd, void* addr,
+                                 size_t size, bool readOnly) {
+    if (info.colorType() == kUnknown_SkColorType) {
+        LOG_ALWAYS_FATAL("unknown bitmap configuration");
+        return nullptr;
+    }
+
+    if (!addr) {
+        // Map existing ashmem region if not already mapped.
+        int flags = readOnly ? (PROT_READ) : (PROT_READ | PROT_WRITE);
+        size = ashmem_get_size_region(fd);
+        addr = mmap(NULL, size, flags, MAP_SHARED, fd, 0);
+        if (addr == MAP_FAILED) {
+            return nullptr;
+        }
+    }
+
+    sk_sp<Bitmap> bitmap(new Bitmap(addr, fd, size, info, rowBytes));
+    if (readOnly) {
+        bitmap->setImmutable();
+    }
+    return bitmap;
 }
 
 void Bitmap::setColorSpace(sk_sp<SkColorSpace> colorSpace) {
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index d446377..2138040 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -54,28 +54,31 @@
 
 class ANDROID_API Bitmap : public SkPixelRef {
 public:
+    /* The allocate factories not only construct the Bitmap object but also allocate the
+     * backing store whose type is determined by the specific method that is called.
+     *
+     * The factories that accept SkBitmap* as a param will modify those params by
+     * installing the returned bitmap as their SkPixelRef.
+     *
+     * The factories that accept const SkBitmap& as a param will copy the contents of the
+     * provided bitmap into the newly allocated buffer.
+     */
+    static sk_sp<Bitmap> allocateAshmemBitmap(SkBitmap* bitmap);
+    static sk_sp<Bitmap> allocateHardwareBitmap(const SkBitmap& bitmap);
     static sk_sp<Bitmap> allocateHeapBitmap(SkBitmap* bitmap);
     static sk_sp<Bitmap> allocateHeapBitmap(const SkImageInfo& info);
 
-    static sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& bitmap);
-
-    static sk_sp<Bitmap> allocateAshmemBitmap(SkBitmap* bitmap);
-    static sk_sp<Bitmap> allocateAshmemBitmap(size_t allocSize, const SkImageInfo& info,
-                                              size_t rowBytes);
-
-    static sk_sp<Bitmap> createFrom(sp<GraphicBuffer> graphicBuffer);
+    /* The createFrom factories construct a new Bitmap object by wrapping the already allocated
+     * memory that is provided as an input param.
+     */
     static sk_sp<Bitmap> createFrom(sp<GraphicBuffer> graphicBuffer,
-                                    sk_sp<SkColorSpace> colorSpace);
-
+                                    sk_sp<SkColorSpace> colorSpace,
+                                    SkAlphaType alphaType = kPremul_SkAlphaType,
+                                    BitmapPalette palette = BitmapPalette::Unknown);
+    static sk_sp<Bitmap> createFrom(const SkImageInfo& info, size_t rowBytes, int fd, void* addr,
+                                    size_t size, bool readOnly);
     static sk_sp<Bitmap> createFrom(const SkImageInfo&, SkPixelRef&);
 
-    Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes);
-    Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info,
-           size_t rowBytes);
-    Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes);
-    Bitmap(GraphicBuffer* buffer, const SkImageInfo& info,
-           BitmapPalette palette = BitmapPalette::Unknown);
-
     int rowBytesAsPixels() const { return rowBytes() >> mInfo.shiftPerPixel(); }
 
     void reconfigure(const SkImageInfo& info, size_t rowBytes);
@@ -123,6 +126,15 @@
     }
 
 private:
+    static sk_sp<Bitmap> allocateAshmemBitmap(size_t size, const SkImageInfo& i, size_t rowBytes);
+    static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& i, size_t rowBytes);
+
+    Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes);
+    Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info,
+           size_t rowBytes);
+    Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes);
+    Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette);
+
     virtual ~Bitmap();
     void* getStorage() const;
 
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index e2ea2bc..a09da6b 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -52,7 +52,7 @@
         const SkScalar left = x;
         const SkScalar right = x + length;
         if (flags & SkPaint::kUnderlineText_ReserveFlag) {
-            Paint::FontMetrics metrics;
+            SkFontMetrics metrics;
             paint.getFontMetrics(&metrics);
             SkScalar position;
             if (!metrics.hasUnderlinePosition(&position)) {
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index a5f21d8..4c5365d 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -178,6 +178,9 @@
     virtual void drawRenderNode(uirenderer::RenderNode* renderNode) = 0;
     virtual void callDrawGLFunction(Functor* functor,
                                     uirenderer::GlFunctorLifecycleListener* listener) = 0;
+    virtual void drawWebViewFunctor(int /*functor*/) {
+        LOG_ALWAYS_FATAL("Not supported");
+    }
 
     // ----------------------------------------------------------------------------
     // Canvas state operations
@@ -193,6 +196,7 @@
                           SaveFlags::Flags flags) = 0;
     virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
                                SaveFlags::Flags flags) = 0;
+    virtual int saveUnclippedLayer(int, int, int, int) = 0;
 
     // Matrix
     virtual void getMatrix(SkMatrix* outMatrix) const = 0;
diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index 2b5d580..0eb526a 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -82,11 +82,6 @@
         mOutput << mIdent << "drawDRRect" << std::endl;
     }
 
-    void onDrawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*,
-                           const SkPaint&) override {
-        mOutput << mIdent << "drawTextRSXform" << std::endl;
-    }
-
     void onDrawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&) override {
         mOutput << mIdent << "drawTextBlob" << std::endl;
     }
diff --git a/libs/hwui/pipeline/skia/FunctorDrawable.h b/libs/hwui/pipeline/skia/FunctorDrawable.h
index af3a056..cf2f93b 100644
--- a/libs/hwui/pipeline/skia/FunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/FunctorDrawable.h
@@ -21,7 +21,9 @@
 #include <SkCanvas.h>
 #include <SkDrawable.h>
 
+#include <WebViewFunctorManager.h>
 #include <utils/Functor.h>
+#include <variant>
 
 namespace android {
 namespace uirenderer {
@@ -35,17 +37,43 @@
 class FunctorDrawable : public SkDrawable {
 public:
     FunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
-            : mFunctor(functor), mListener(listener), mBounds(canvas->getLocalClipBounds()) {}
+            : mBounds(canvas->getLocalClipBounds())
+            , mAnyFunctor(std::in_place_type<LegacyFunctor>, functor, listener) {}
+
+    FunctorDrawable(int functor, SkCanvas* canvas)
+            : mBounds(canvas->getLocalClipBounds())
+            , mAnyFunctor(std::in_place_type<NewFunctor>, functor) {}
+
     virtual ~FunctorDrawable() {}
 
-    virtual void syncFunctor() const = 0;
+    virtual void syncFunctor(const WebViewSyncData& data) const {
+        if (mAnyFunctor.index() == 0) {
+            std::get<0>(mAnyFunctor).handle->sync(data);
+        } else {
+            (*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeSync, nullptr);
+        }
+    }
 
 protected:
     virtual SkRect onGetBounds() override { return mBounds; }
 
-    Functor* mFunctor;
-    sp<GlFunctorLifecycleListener> mListener;
     const SkRect mBounds;
+
+    struct LegacyFunctor {
+        explicit LegacyFunctor(Functor* functor, GlFunctorLifecycleListener* listener)
+                : functor(functor), listener(listener) {}
+        Functor* functor;
+        sp<GlFunctorLifecycleListener> listener;
+    };
+
+    struct NewFunctor {
+        explicit NewFunctor(int functor) {
+            handle = WebViewFunctorManager::instance().handleFor(functor);
+        }
+        sp<WebViewFunctor::Handle> handle;
+    };
+
+    std::variant<NewFunctor, LegacyFunctor> mAnyFunctor;
 };
 
 }  // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index 4a87e75..240efb4 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -17,29 +17,28 @@
 #include "GLFunctorDrawable.h"
 #include <GrContext.h>
 #include <private/hwui/DrawGlInfo.h>
+#include "FunctorDrawable.h"
 #include "GlFunctorLifecycleListener.h"
+#include "GrBackendSurface.h"
+#include "GrRenderTarget.h"
+#include "GrRenderTargetContext.h"
 #include "RenderNode.h"
 #include "SkAndroidFrameworkUtils.h"
 #include "SkClipStack.h"
 #include "SkRect.h"
-#include "GrBackendSurface.h"
-#include "GrRenderTarget.h"
-#include "GrRenderTargetContext.h"
 
 namespace android {
 namespace uirenderer {
 namespace skiapipeline {
 
 GLFunctorDrawable::~GLFunctorDrawable() {
-    if (mListener.get() != nullptr) {
-        mListener->onGlFunctorReleased(mFunctor);
+    if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
+        if (lp->listener) {
+            lp->listener->onGlFunctorReleased(lp->functor);
+        }
     }
 }
 
-void GLFunctorDrawable::syncFunctor() const {
-    (*mFunctor)(DrawGlInfo::kModeSync, nullptr);
-}
-
 static void setScissor(int viewportHeight, const SkIRect& clip) {
     SkASSERT(!clip.isEmpty());
     // transform to Y-flipped GL space, and prevent negatives
@@ -49,14 +48,14 @@
 }
 
 static bool GetFboDetails(SkCanvas* canvas, GLuint* outFboID, SkISize* outFboSize) {
-    GrRenderTargetContext *renderTargetContext =
+    GrRenderTargetContext* renderTargetContext =
             canvas->internal_private_accessTopLayerRenderTargetContext();
     if (!renderTargetContext) {
         ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw");
         return false;
     }
 
-    GrRenderTarget *renderTarget = renderTargetContext->accessRenderTarget();
+    GrRenderTarget* renderTarget = renderTargetContext->accessRenderTarget();
     if (!renderTarget) {
         ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw");
         return false;
@@ -94,16 +93,16 @@
     sk_sp<SkSurface> tmpSurface;
     // we are in a state where there is an unclipped saveLayer
     if (fboID != 0 && !surfaceBounds.contains(clipBounds)) {
-
         // create an offscreen layer and clear it
-        SkImageInfo surfaceInfo = canvas->imageInfo().makeWH(clipBounds.width(), clipBounds.height());
-        tmpSurface = SkSurface::MakeRenderTarget(canvas->getGrContext(), SkBudgeted::kYes,
-                                                 surfaceInfo);
+        SkImageInfo surfaceInfo =
+                canvas->imageInfo().makeWH(clipBounds.width(), clipBounds.height());
+        tmpSurface =
+                SkSurface::MakeRenderTarget(canvas->getGrContext(), SkBudgeted::kYes, surfaceInfo);
         tmpSurface->getCanvas()->clear(SK_ColorTRANSPARENT);
 
         GrGLFramebufferInfo fboInfo;
         if (!tmpSurface->getBackendRenderTarget(SkSurface::kFlushWrite_BackendHandleAccess)
-                .getGLFramebufferInfo(&fboInfo)) {
+                     .getGLFramebufferInfo(&fboInfo)) {
             ALOGW("Unable to extract renderTarget info from offscreen canvas; aborting GLFunctor");
             return;
         }
@@ -144,7 +143,7 @@
     bool clearStencilAfterFunctor = false;
     if (CC_UNLIKELY(clipRegion.isComplex())) {
         // clear the stencil
-        //TODO: move stencil clear and canvas flush to SkAndroidFrameworkUtils::clipWithStencil
+        // TODO: move stencil clear and canvas flush to SkAndroidFrameworkUtils::clipWithStencil
         glDisable(GL_SCISSOR_TEST);
         glStencilMask(0x1);
         glClearStencil(0);
@@ -163,7 +162,7 @@
 
         // GL ops get inserted here if previous flush is missing, which could dirty the stencil
         bool stencilWritten = SkAndroidFrameworkUtils::clipWithStencil(tmpCanvas);
-        tmpCanvas->flush(); //need this flush for the single op that draws into the stencil
+        tmpCanvas->flush();  // need this flush for the single op that draws into the stencil
 
         // ensure that the framebuffer that the webview will render into is bound before after we
         // draw into the stencil
@@ -188,7 +187,11 @@
         setScissor(info.height, clipRegion.getBounds());
     }
 
-    (*mFunctor)(DrawGlInfo::kModeDraw, &info);
+    if (mAnyFunctor.index() == 0) {
+        std::get<0>(mAnyFunctor).handle->drawGl(info);
+    } else {
+        (*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeDraw, &info);
+    }
 
     if (clearStencilAfterFunctor) {
         // clear stencil buffer as it may be used by Skia
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
index 215979c..2ea4f67 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
@@ -31,11 +31,9 @@
  */
 class GLFunctorDrawable : public FunctorDrawable {
 public:
-    GLFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
-            : FunctorDrawable(functor, listener, canvas) {}
-    virtual ~GLFunctorDrawable();
+    using FunctorDrawable::FunctorDrawable;
 
-    void syncFunctor() const override;
+    virtual ~GLFunctorDrawable();
 
 protected:
     void onDraw(SkCanvas* canvas) override;
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index f08ac17..eed1942 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#include <utils/MathUtils.h>
 #include "LayerDrawable.h"
+#include <utils/MathUtils.h>
 
 #include "GrBackendSurface.h"
 #include "SkColorFilter.h"
@@ -44,10 +44,9 @@
     if (!matrix.isScaleTranslate()) return true;
 
     // We only care about meaningful scale here
-    bool noScale = MathUtils::isOne(matrix.getScaleX())
-            && MathUtils::isOne(matrix.getScaleY());
-    bool pixelAligned = SkScalarIsInt(matrix.getTranslateX())
-            && SkScalarIsInt(matrix.getTranslateY());
+    bool noScale = MathUtils::isOne(matrix.getScaleX()) && MathUtils::isOne(matrix.getScaleY());
+    bool pixelAligned =
+            SkScalarIsInt(matrix.getTranslateX()) && SkScalarIsInt(matrix.getTranslateY());
     return !(noScale && pixelAligned);
 }
 
@@ -120,11 +119,12 @@
             // Integer translation is defined as when src rect and dst rect align fractionally.
             // Skia TextureOp has the above logic build-in, but not NonAAFillRectOp. TextureOp works
             // only for SrcOver blending and without color filter (readback uses Src blending).
-            bool isIntegerTranslate = isBasicallyTranslate(totalMatrix)
-                    && SkScalarFraction(skiaDestRect.fLeft + totalMatrix[SkMatrix::kMTransX])
-                    == SkScalarFraction(skiaSrcRect.fLeft)
-                    && SkScalarFraction(skiaDestRect.fTop + totalMatrix[SkMatrix::kMTransY])
-                    == SkScalarFraction(skiaSrcRect.fTop);
+            bool isIntegerTranslate =
+                    isBasicallyTranslate(totalMatrix) &&
+                    SkScalarFraction(skiaDestRect.fLeft + totalMatrix[SkMatrix::kMTransX]) ==
+                            SkScalarFraction(skiaSrcRect.fLeft) &&
+                    SkScalarFraction(skiaDestRect.fTop + totalMatrix[SkMatrix::kMTransY]) ==
+                            SkScalarFraction(skiaSrcRect.fTop);
             if (layer->getForceFilter() || !isIntegerTranslate) {
                 paint.setFilterQuality(kLow_SkFilterQuality);
             }
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h
index 95dc6d0..7cd515a 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.h
+++ b/libs/hwui/pipeline/skia/LayerDrawable.h
@@ -32,8 +32,8 @@
 public:
     explicit LayerDrawable(DeferredLayerUpdater* layerUpdater) : mLayerUpdater(layerUpdater) {}
 
-    static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
-                          const SkRect* srcRect, const SkRect* dstRect, bool useLayerTransform);
+    static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer, const SkRect* srcRect,
+                          const SkRect* dstRect, bool useLayerTransform);
 
 protected:
     virtual SkRect onGetBounds() override {
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 4494cb0..df1537e 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -127,6 +127,7 @@
             mNode.markDrawEnd(mCanvas);
         }
     }
+
 private:
     SkCanvas& mCanvas;
     RenderNode& mNode;
@@ -140,7 +141,7 @@
     // ensures that we paint the layer even if it is not currently visible in the
     // event that the properties change and it becomes visible.
     if ((mProjectedDisplayList == nullptr && !renderNode->isRenderable()) ||
-            (renderNode->nothingToDraw() && mComposeLayer)) {
+        (renderNode->nothingToDraw() && mComposeLayer)) {
         return;
     }
 
@@ -234,8 +235,8 @@
             // we need to restrict the portion of the surface drawn to the size of the renderNode.
             SkASSERT(renderNode->getLayerSurface()->width() >= bounds.width());
             SkASSERT(renderNode->getLayerSurface()->height() >= bounds.height());
-            canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(),
-                    bounds, bounds, &paint);
+            canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(), bounds,
+                                  bounds, &paint);
 
             if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
                 renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true;
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index 073b481..562a3b2 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -15,11 +15,11 @@
  */
 
 #include "ShaderCache.h"
-#include <algorithm>
 #include <log/log.h>
-#include <thread>
-#include <array>
 #include <openssl/sha.h>
+#include <algorithm>
+#include <array>
+#include <thread>
 #include "FileBlobCache.h"
 #include "Properties.h"
 #include "utils/TraceUtils.h"
@@ -44,8 +44,7 @@
 }
 
 bool ShaderCache::validateCache(const void* identity, ssize_t size) {
-    if (nullptr == identity && size == 0)
-        return true;
+    if (nullptr == identity && size == 0) return true;
 
     if (nullptr == identity || size < 0) {
         if (CC_UNLIKELY(Properties::debugLevel & kDebugCaches)) {
@@ -66,8 +65,7 @@
     auto key = sIDKey;
     auto loaded = mBlobCache->get(&key, sizeof(key), hash.data(), hash.size());
 
-    if (loaded && std::equal(hash.begin(), hash.end(), mIDHash.begin()))
-        return true;
+    if (loaded && std::equal(hash.begin(), hash.end(), mIDHash.begin())) return true;
 
     if (CC_UNLIKELY(Properties::debugLevel & kDebugCaches)) {
         ALOGW("ShaderCache::validateCache cache validation fails");
@@ -119,7 +117,7 @@
     int maxTries = 3;
     while (valueSize > mObservedBlobValueSize && maxTries > 0) {
         mObservedBlobValueSize = std::min(valueSize, maxValueSize);
-        void *newValueBuffer = realloc(valueBuffer, mObservedBlobValueSize);
+        void* newValueBuffer = realloc(valueBuffer, mObservedBlobValueSize);
         if (!newValueBuffer) {
             free(valueBuffer);
             return nullptr;
@@ -133,7 +131,7 @@
         return nullptr;
     }
     if (valueSize > mObservedBlobValueSize) {
-        ALOGE("ShaderCache::load value size is too big %d", (int) valueSize);
+        ALOGE("ShaderCache::load value size is too big %d", (int)valueSize);
         free(valueBuffer);
         return nullptr;
     }
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
index 82804cf..d41aadb 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.h
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -16,12 +16,12 @@
 
 #pragma once
 
+#include <GrContextOptions.h>
 #include <cutils/compiler.h>
 #include <memory>
 #include <mutex>
 #include <string>
 #include <vector>
-#include <GrContextOptions.h>
 
 namespace android {
 
@@ -52,7 +52,7 @@
      * the initialized state the load and store methods will return without
      * performing any cache operations.
      */
-    virtual void initShaderDiskCache(const void *identity, ssize_t size);
+    virtual void initShaderDiskCache(const void* identity, ssize_t size);
 
     virtual void initShaderDiskCache() { initShaderDiskCache(nullptr, 0); }
 
@@ -153,7 +153,7 @@
     /**
      *  "mObservedBlobValueSize" is the maximum value size observed by the cache reading function.
      */
-    size_t mObservedBlobValueSize = 20*1024;
+    size_t mObservedBlobValueSize = 20 * 1024;
 
     /**
      *  The time in seconds to wait before saving newly inserted cache entries.
@@ -176,7 +176,7 @@
      */
     static constexpr uint8_t sIDKey = 0;
 
-    friend class ShaderCacheTestUtils; //used for unit testing
+    friend class ShaderCacheTestUtils;  // used for unit testing
 };
 
 } /* namespace skiapipeline */
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index ac6f6a3..230065c 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -27,9 +27,9 @@
 namespace uirenderer {
 namespace skiapipeline {
 
-void SkiaDisplayList::syncContents() {
+void SkiaDisplayList::syncContents(const WebViewSyncData& data) {
     for (auto& functor : mChildFunctors) {
-        functor->syncFunctor();
+        functor->syncFunctor(data);
     }
     for (auto& animatedImage : mAnimatedImages) {
         animatedImage->syncProperties();
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index d7879e7..3219ad1 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -16,11 +16,11 @@
 
 #pragma once
 
-#include "hwui/AnimatedImageDrawable.h"
 #include "FunctorDrawable.h"
 #include "RecordingCanvas.h"
 #include "RenderNodeDrawable.h"
 #include "TreeInfo.h"
+#include "hwui/AnimatedImageDrawable.h"
 #include "utils/LinearAllocator.h"
 
 #include <deque>
@@ -49,7 +49,7 @@
  */
 class SkiaDisplayList {
 public:
-    size_t getUsedSize() { return allocator.usedSize(); }
+    size_t getUsedSize() { return allocator.usedSize() + mDisplayList.usedSize(); }
 
     ~SkiaDisplayList() {
         /* Given that we are using a LinearStdAllocator to store some of the
@@ -109,7 +109,7 @@
      * NOTE: This function can be folded into RenderNode when we no longer need
      *       to subclass from DisplayList
      */
-    void syncContents();
+    void syncContents(const WebViewSyncData& data);
 
     /**
      * ONLY to be called by RenderNode::prepareTree in order to prepare this
diff --git a/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp b/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp
index ea578cb..e48ecf4 100644
--- a/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp
+++ b/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp
@@ -21,16 +21,16 @@
 namespace skiapipeline {
 
 SkiaMemoryTracer::SkiaMemoryTracer(std::vector<ResourcePair> resourceMap, bool itemizeType)
-            : mResourceMap(resourceMap)
-            , mItemizeType(itemizeType)
-            , mTotalSize("bytes", 0)
-            , mPurgeableSize("bytes", 0) {}
+        : mResourceMap(resourceMap)
+        , mItemizeType(itemizeType)
+        , mTotalSize("bytes", 0)
+        , mPurgeableSize("bytes", 0) {}
 
 SkiaMemoryTracer::SkiaMemoryTracer(const char* categoryKey, bool itemizeType)
-            : mCategoryKey(categoryKey)
-            , mItemizeType(itemizeType)
-            , mTotalSize("bytes", 0)
-            , mPurgeableSize("bytes", 0) {}
+        : mCategoryKey(categoryKey)
+        , mItemizeType(itemizeType)
+        , mTotalSize("bytes", 0)
+        , mPurgeableSize("bytes", 0) {}
 
 const char* SkiaMemoryTracer::mapName(const char* resourceName) {
     for (auto& resource : mResourceMap) {
@@ -42,7 +42,7 @@
 }
 
 void SkiaMemoryTracer::processElement() {
-    if(!mCurrentElement.empty()) {
+    if (!mCurrentElement.empty()) {
         // Only count elements that contain "size", other values just provide metadata.
         auto sizeResult = mCurrentValues.find("size");
         if (sizeResult != mCurrentValues.end()) {
@@ -136,8 +136,8 @@
             for (const auto& typedValue : namedItem.second) {
                 TraceValue traceValue = convertUnits(typedValue.second);
                 const char* entry = (traceValue.count > 1) ? "entries" : "entry";
-                log.appendFormat("    %s: %.2f %s (%d %s)\n", typedValue.first,
-                                 traceValue.value, traceValue.units, traceValue.count, entry);
+                log.appendFormat("    %s: %.2f %s (%d %s)\n", typedValue.first, traceValue.value,
+                                 traceValue.units, traceValue.count, entry);
             }
         } else {
             auto result = namedItem.second.find("size");
diff --git a/libs/hwui/pipeline/skia/SkiaMemoryTracer.h b/libs/hwui/pipeline/skia/SkiaMemoryTracer.h
index abf1f4b..e9a7981 100644
--- a/libs/hwui/pipeline/skia/SkiaMemoryTracer.h
+++ b/libs/hwui/pipeline/skia/SkiaMemoryTracer.h
@@ -50,8 +50,8 @@
     }
 
     bool shouldDumpWrappedObjects() const override { return true; }
-    void setMemoryBacking(const char*, const char*, const char*) override { }
-    void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override { }
+    void setMemoryBacking(const char*, const char*, const char*) override {}
+    void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override {}
 
 private:
     struct TraceValue {
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 07979a2..4338b1c 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -162,22 +162,17 @@
         mEglSurface = EGL_NO_SURFACE;
     }
 
+    setSurfaceColorProperties(colorMode);
+
     if (surface) {
         mRenderThread.requireGlContext();
-        auto newSurface = mEglManager.createSurface(surface, colorMode);
+        auto newSurface = mEglManager.createSurface(surface, colorMode, mSurfaceColorGamut);
         if (!newSurface) {
             return false;
         }
         mEglSurface = newSurface.unwrap();
     }
 
-    if (colorMode == ColorMode::SRGB) {
-        mSurfaceColorType = SkColorType::kN32_SkColorType;
-    } else if (colorMode == ColorMode::WideColorGamut) {
-        mSurfaceColorType = SkColorType::kRGBA_F16_SkColorType;
-    }
-    mSurfaceColorSpace = SkColorSpace::MakeSRGB();
-
     if (mEglSurface != EGL_NO_SURFACE) {
         const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer);
         mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 7a255c1..2e7850d 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -17,6 +17,7 @@
 #include "SkiaPipeline.h"
 
 #include <SkImageEncoder.h>
+#include <SkImageInfo.h>
 #include <SkImagePriv.h>
 #include <SkOverdrawCanvas.h>
 #include <SkOverdrawColorFilter.h>
@@ -183,15 +184,15 @@
         } else {
             String8 cachesOutput;
             mRenderThread.cacheManager().dumpMemoryUsage(cachesOutput,
-                    &mRenderThread.renderState());
+                                                         &mRenderThread.renderState());
             ALOGE("%s", cachesOutput.string());
             if (errorHandler) {
                 std::ostringstream err;
                 err << "Unable to create layer for " << node->getName();
                 const int maxTextureSize = DeviceInfo::get()->maxTextureSize();
                 err << ", size " << info.width() << "x" << info.height() << " max size "
-                    << maxTextureSize << " color type " << (int)info.colorType()
-                    << " has context " << (int)(mRenderThread.getGrContext() != nullptr);
+                    << maxTextureSize << " color type " << (int)info.colorType() << " has context "
+                    << (int)(mRenderThread.getGrContext() != nullptr);
                 errorHandler->onError(err.str());
             }
         }
@@ -300,8 +301,7 @@
                 mSavePictureProcessor->savePicture(data, mCapturedFile);
             } else {
                 mSavePictureProcessor->savePicture(
-                        data,
-                        mCapturedFile + "_" + std::to_string(mCaptureSequence));
+                        data, mCapturedFile + "_" + std::to_string(mCaptureSequence));
             }
             mCaptureSequence--;
         }
@@ -453,6 +453,20 @@
     ALOGD("%s", log.c_str());
 }
 
+void SkiaPipeline::setSurfaceColorProperties(ColorMode colorMode) {
+    if (colorMode == ColorMode::SRGB) {
+        mSurfaceColorType = SkColorType::kN32_SkColorType;
+        mSurfaceColorGamut = SkColorSpace::Gamut::kSRGB_Gamut;
+        mSurfaceColorSpace = SkColorSpace::MakeSRGB();
+    } else if (colorMode == ColorMode::WideColorGamut) {
+        mSurfaceColorType = DeviceInfo::get()->getWideColorType();
+        mSurfaceColorGamut = DeviceInfo::get()->getWideColorGamut();
+        mSurfaceColorSpace = DeviceInfo::get()->getWideColorSpace();
+    } else {
+        LOG_ALWAYS_FATAL("Unreachable: unsupported color mode.");
+    }
+}
+
 // Overdraw debugging
 
 // These colors should be kept in sync with Caches::getOverdrawColor() with a few differences.
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index 42a411a..ff87313 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -30,7 +30,7 @@
 
 class SkiaPipeline : public renderthread::IRenderPipeline {
 public:
-    SkiaPipeline(renderthread::RenderThread& thread);
+    explicit SkiaPipeline(renderthread::RenderThread& thread);
     virtual ~SkiaPipeline();
 
     TaskManager* getTaskManager() override;
@@ -97,8 +97,7 @@
         return mLightCenter;
     }
 
-    static void updateLighting(const LightGeometry& lightGeometry,
-                               const LightInfo& lightInfo) {
+    static void updateLighting(const LightGeometry& lightGeometry, const LightInfo& lightInfo) {
         mLightRadius = lightGeometry.radius;
         mAmbientShadowAlpha = lightInfo.ambientShadowAlpha;
         mSpotShadowAlpha = lightInfo.spotShadowAlpha;
@@ -107,9 +106,11 @@
 
 protected:
     void dumpResourceCacheUsage() const;
+    void setSurfaceColorProperties(renderthread::ColorMode colorMode);
 
     renderthread::RenderThread& mRenderThread;
     SkColorType mSurfaceColorType;
+    SkColorSpace::Gamut mSurfaceColorGamut;
     sk_sp<SkColorSpace> mSurfaceColorSpace;
 
 private:
diff --git a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
index daa4c18..dc8420f 100644
--- a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
+++ b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
@@ -23,7 +23,7 @@
 
 class SkiaProfileRenderer : public IProfileRenderer {
 public:
-    SkiaProfileRenderer(SkCanvas* canvas) : mCanvas(canvas) {}
+    explicit SkiaProfileRenderer(SkCanvas* canvas) : mCanvas(canvas) {}
 
     void drawRect(float left, float top, float right, float bottom, const SkPaint& paint) override;
     void drawRects(const float* rects, int count, const SkPaint& paint) override;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index b56c3ef..6eefed9 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -24,8 +24,8 @@
 #include "RenderNode.h"
 #include "pipeline/skia/AnimatedDrawables.h"
 #include "pipeline/skia/GLFunctorDrawable.h"
-#include "pipeline/skia/VkInteropFunctorDrawable.h"
 #include "pipeline/skia/VkFunctorDrawable.h"
+#include "pipeline/skia/VkInteropFunctorDrawable.h"
 
 namespace android {
 namespace uirenderer {
@@ -95,8 +95,8 @@
         drawDrawable(drawable);
     }
     if (enableReorder) {
-        mCurrentBarrier = mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(
-                mDisplayList.get());
+        mCurrentBarrier =
+                mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(mDisplayList.get());
         drawDrawable(mCurrentBarrier);
     }
 }
@@ -127,11 +127,25 @@
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
         // TODO(cblume) use VkFunctorDrawable instead of VkInteropFunctorDrawable here when the
         // interop is disabled/moved.
-        functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(functor,
-                listener, asSkCanvas());
+        functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(
+                functor, listener, asSkCanvas());
     } else {
-        functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, listener,
-                asSkCanvas());
+        functorDrawable =
+                mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, listener, asSkCanvas());
+    }
+    mDisplayList->mChildFunctors.push_back(functorDrawable);
+    drawDrawable(functorDrawable);
+}
+
+void SkiaRecordingCanvas::drawWebViewFunctor(int functor) {
+    FunctorDrawable* functorDrawable;
+    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+        // TODO(cblume) use VkFunctorDrawable instead of VkInteropFunctorDrawable here when the
+        // interop is disabled.
+        functorDrawable =
+                mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(functor, asSkCanvas());
+    } else {
+        functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, asSkCanvas());
     }
     mDisplayList->mChildFunctors.push_back(functorDrawable);
     drawDrawable(functorDrawable);
@@ -167,7 +181,7 @@
         if (colorSpaceFilter) {
             if (tmpPaint.getColorFilter()) {
                 tmpPaint.setColorFilter(SkColorFilter::MakeComposeFilter(
-                       tmpPaint.refColorFilter(), std::move(colorSpaceFilter)));
+                        tmpPaint.refColorFilter(), std::move(colorSpaceFilter)));
             } else {
                 tmpPaint.setColorFilter(std::move(colorSpaceFilter));
             }
@@ -248,8 +262,7 @@
         filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality);
     }
     sk_sp<SkImage> image = bitmap.makeImage();
-    mRecorder.drawImageLattice(image, lattice, dst,
-                               filterPaint(std::move(filteredPaint)),
+    mRecorder.drawImageLattice(image, lattice, dst, filterPaint(std::move(filteredPaint)),
                                bitmap.palette());
     if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
         mDisplayList->mMutableImages.push_back(image.get());
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index d6107a9..afeccea 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -74,6 +74,7 @@
     virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override;
     virtual void callDrawGLFunction(Functor* functor,
                                     uirenderer::GlFunctorLifecycleListener* listener) override;
+    void drawWebViewFunctor(int functor) override;
 
 private:
     RecordingCanvas mRecorder;
diff --git a/libs/hwui/pipeline/skia/SkiaUtils.h b/libs/hwui/pipeline/skia/SkiaUtils.h
index 8344469..fa7f1fe 100644
--- a/libs/hwui/pipeline/skia/SkiaUtils.h
+++ b/libs/hwui/pipeline/skia/SkiaUtils.h
@@ -22,7 +22,7 @@
 
 static inline SkRect SkRectMakeLargest() {
     const SkScalar v = SK_ScalarMax;
-    return { -v, -v, v, v };
+    return {-v, -v, v, v};
 };
 
 } /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 437b5dc..1d3a244 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -20,9 +20,9 @@
 #include "Readback.h"
 #include "SkiaPipeline.h"
 #include "SkiaProfileRenderer.h"
+#include "VkInteropFunctorDrawable.h"
 #include "renderstate/RenderState.h"
 #include "renderthread/Frame.h"
-#include "VkInteropFunctorDrawable.h"
 
 #include <SkSurface.h>
 #include <SkTypes.h>
@@ -122,15 +122,10 @@
         mVkSurface = nullptr;
     }
 
-    mSurfaceColorSpace = SkColorSpace::MakeSRGB();
+    setSurfaceColorProperties(colorMode);
     if (surface) {
-        mVkSurface = mVkManager.createSurface(surface, colorMode, mSurfaceColorSpace);
-    }
-
-    if (colorMode == ColorMode::SRGB) {
-        mSurfaceColorType = SkColorType::kN32_SkColorType;
-    } else if (colorMode == ColorMode::WideColorGamut) {
-        mSurfaceColorType = SkColorType::kRGBA_F16_SkColorType;
+        mVkSurface = mVkManager.createSurface(surface, colorMode, mSurfaceColorSpace,
+                                              mSurfaceColorGamut, mSurfaceColorType);
     }
 
     return mVkSurface != nullptr;
@@ -163,7 +158,7 @@
         ALOGW("SkiaVulkanPipeline::allocateHardwareBitmap() failed in GraphicBuffer.create()");
         return nullptr;
     }
-    return sk_sp<Bitmap>(new Bitmap(buffer.get(), skBitmap.info()));
+    return Bitmap::createFrom(buffer, skBitmap.refColorSpace());
 }
 
 } /* namespace skiapipeline */
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index 02874c7..53ffc44 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -25,7 +25,7 @@
 
 class SkiaVulkanPipeline : public SkiaPipeline {
 public:
-    SkiaVulkanPipeline(renderthread::RenderThread& thread);
+    explicit SkiaVulkanPipeline(renderthread::RenderThread& thread);
     virtual ~SkiaVulkanPipeline() {}
 
     renderthread::MakeCurrentResult makeCurrent() override;
diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h
index 74e48ce..5e892aa 100644
--- a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h
+++ b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h
@@ -62,8 +62,8 @@
 public:
     enum class StorageMode { allowSharedSurface, disallowSharedSurface };
 
-    VectorDrawableAtlas(size_t surfaceArea,
-                        StorageMode storageMode = StorageMode::allowSharedSurface);
+    explicit VectorDrawableAtlas(size_t surfaceArea,
+                                 StorageMode storageMode = StorageMode::allowSharedSurface);
 
     /**
      * "prepareForDraw" may allocate a new surface if needed. It may schedule to repack the
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
index 71ad5e1..156f74a 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -17,23 +17,21 @@
 #include "VkFunctorDrawable.h"
 #include <private/hwui/DrawVkInfo.h>
 
-#include "thread/ThreadBase.h"
-#include "utils/TimeUtils.h"
 #include <GrBackendDrawableInfo.h>
-#include <thread>
+#include <SkImage.h>
 #include <utils/Color.h>
 #include <utils/Trace.h>
 #include <utils/TraceUtils.h>
-#include <SkImage.h>
 #include <vk/GrVkTypes.h>
+#include <thread>
+#include "thread/ThreadBase.h"
+#include "utils/TimeUtils.h"
 
 namespace android {
 namespace uirenderer {
 namespace skiapipeline {
 
-VkFunctorDrawHandler::VkFunctorDrawHandler(Functor *functor)
-    : INHERITED()
-    , mFunctor(functor) {}
+VkFunctorDrawHandler::VkFunctorDrawHandler(Functor* functor) : INHERITED(), mFunctor(functor) {}
 
 VkFunctorDrawHandler::~VkFunctorDrawHandler() {
     // TODO(cblume) Fill in the DrawVkInfo parameters.
@@ -55,14 +53,12 @@
     (*mFunctor)(DrawVkInfo::kModeComposite, &draw_vk_info);
 }
 
-VkFunctorDrawable::VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener,
-                                     SkCanvas* canvas)
-    : FunctorDrawable(functor, listener, canvas) {}
-
-VkFunctorDrawable::~VkFunctorDrawable() = default;
-
-void VkFunctorDrawable::syncFunctor() const {
-    (*mFunctor)(DrawVkInfo::kModeSync, nullptr);
+VkFunctorDrawable::~VkFunctorDrawable() {
+    if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
+        if (lp->listener) {
+            lp->listener->onGlFunctorReleased(lp->functor);
+        }
+    }
 }
 
 void VkFunctorDrawable::onDraw(SkCanvas* /*canvas*/) {
@@ -71,12 +67,17 @@
 }
 
 std::unique_ptr<FunctorDrawable::GpuDrawHandler> VkFunctorDrawable::onSnapGpuDrawHandler(
-    GrBackendApi backendApi, const SkMatrix& matrix) {
+        GrBackendApi backendApi, const SkMatrix& matrix) {
     if (backendApi != GrBackendApi::kVulkan) {
         return nullptr;
     }
-    std::unique_ptr<VkFunctorDrawHandler> draw(new VkFunctorDrawHandler(mFunctor));
-    return std::move(draw);
+    std::unique_ptr<VkFunctorDrawHandler> draw;
+    if (mAnyFunctor.index() == 0) {
+        LOG_ALWAYS_FATAL("Not implemented");
+        return nullptr;
+    } else {
+        return std::make_unique<VkFunctorDrawHandler>(std::get<1>(mAnyFunctor).functor);
+    }
 }
 
 }  // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.h b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
index 5cd1314..d6fefc1 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
@@ -18,9 +18,9 @@
 
 #include "FunctorDrawable.h"
 
-#include <utils/RefBase.h>
-#include <ui/GraphicBuffer.h>
 #include <SkImageInfo.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/RefBase.h>
 
 namespace android {
 namespace uirenderer {
@@ -36,6 +36,7 @@
     ~VkFunctorDrawHandler() override;
 
     void draw(const GrBackendDrawableInfo& info) override;
+
 private:
     typedef GpuDrawHandler INHERITED;
 
@@ -48,17 +49,15 @@
  */
 class VkFunctorDrawable : public FunctorDrawable {
 public:
-    VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener,
-            SkCanvas* canvas);
-    ~VkFunctorDrawable() override;
+    using FunctorDrawable::FunctorDrawable;
 
-    void syncFunctor() const override;
+    ~VkFunctorDrawable() override;
 
 protected:
     // SkDrawable functions:
     void onDraw(SkCanvas* canvas) override;
-    std::unique_ptr<FunctorDrawable::GpuDrawHandler> onSnapGpuDrawHandler(GrBackendApi backendApi,
-            const SkMatrix& matrix) override;
+    std::unique_ptr<FunctorDrawable::GpuDrawHandler> onSnapGpuDrawHandler(
+            GrBackendApi backendApi, const SkMatrix& matrix) override;
 };
 
 }  // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index 8228550..a5faae7 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -17,13 +17,13 @@
 #include "VkInteropFunctorDrawable.h"
 #include <private/hwui/DrawGlInfo.h>
 
-#include "renderthread/EglManager.h"
-#include "thread/ThreadBase.h"
-#include "utils/TimeUtils.h"
-#include <thread>
 #include <utils/Color.h>
 #include <utils/Trace.h>
 #include <utils/TraceUtils.h>
+#include <thread>
+#include "renderthread/EglManager.h"
+#include "thread/ThreadBase.h"
+#include "utils/TimeUtils.h"
 
 #include <EGL/eglext.h>
 #include <GLES2/gl2.h>
@@ -44,6 +44,7 @@
 class ScopedDrawRequest {
 public:
     ScopedDrawRequest() { beginDraw(); }
+
 private:
     void beginDraw() {
         std::lock_guard _lock{sLock};
@@ -57,9 +58,7 @@
         }
 
         if (!sEglManager.hasEglContext()) {
-            sGLDrawThread->queue().runSync([]() {
-                sEglManager.initialize();
-            });
+            sGLDrawThread->queue().runSync([]() { sEglManager.initialize(); });
         }
     }
 };
@@ -93,14 +92,14 @@
     if (!mFrameBuffer.get() || mFBInfo != surfaceInfo) {
         // Buffer will be used as an OpenGL ES render target.
         mFrameBuffer = new GraphicBuffer(
-                 //TODO: try to reduce the size of the buffer: possibly by using clip bounds.
-                 static_cast<uint32_t>(surfaceInfo.width()),
-                 static_cast<uint32_t>(surfaceInfo.height()),
-                 ColorTypeToPixelFormat(surfaceInfo.colorType()),
-                 GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
-                         GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_HW_RENDER,
-                 std::string("VkInteropFunctorDrawable::onDraw pid [") + std::to_string(getpid()) +
-                         "]");
+                // TODO: try to reduce the size of the buffer: possibly by using clip bounds.
+                static_cast<uint32_t>(surfaceInfo.width()),
+                static_cast<uint32_t>(surfaceInfo.height()),
+                ColorTypeToPixelFormat(surfaceInfo.colorType()),
+                GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
+                        GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_HW_RENDER,
+                std::string("VkInteropFunctorDrawable::onDraw pid [") + std::to_string(getpid()) +
+                        "]");
         status_t error = mFrameBuffer->initCheck();
         if (error < 0) {
             ALOGW("VkInteropFunctorDrawable::onDraw() failed in GraphicBuffer.create()");
@@ -110,16 +109,15 @@
         mFBInfo = surfaceInfo;
     }
 
-    //TODO: Synchronization is needed on mFrameBuffer to guarantee that the previous Vulkan
-    //TODO: draw command has completed.
-    //TODO: A simple but inefficient way is to flush and issue a QueueWaitIdle call. See
-    //TODO: GrVkGpu::destroyResources() for example.
+    // TODO: Synchronization is needed on mFrameBuffer to guarantee that the previous Vulkan
+    // TODO: draw command has completed.
+    // TODO: A simple but inefficient way is to flush and issue a QueueWaitIdle call. See
+    // TODO: GrVkGpu::destroyResources() for example.
     bool success = sGLDrawThread->queue().runSync([&]() -> bool {
         ATRACE_FORMAT("WebViewDraw_%dx%d", mFBInfo.width(), mFBInfo.height());
         EGLDisplay display = sEglManager.eglDisplay();
-        LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY,
-                "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
-                uirenderer::renderthread::EglManager::eglErrorString());
+        LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
+                            uirenderer::renderthread::EglManager::eglErrorString());
         // We use an EGLImage to access the content of the GraphicBuffer
         // The EGL image is later bound to a 2D texture
         EGLClientBuffer clientBuffer = (EGLClientBuffer)mFrameBuffer->getNativeBuffer();
@@ -154,10 +152,10 @@
         AutoGLFramebuffer glFb;
         // Bind texture to the frame buffer.
         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
-                             glTexture.mTexture, 0);
+                               glTexture.mTexture, 0);
         if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
             ALOGE("Failed framebuffer check for created target buffer: %s",
-                    GLUtils::getGLFramebufferError());
+                  GLUtils::getGLFramebufferError());
             return false;
         }
 
@@ -166,19 +164,22 @@
         glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
         glClear(GL_COLOR_BUFFER_BIT);
 
-        (*mFunctor)(DrawGlInfo::kModeDraw, &info);
+        if (mAnyFunctor.index() == 0) {
+            std::get<0>(mAnyFunctor).handle->drawGl(info);
+        } else {
+            (*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeDraw, &info);
+        }
 
         EGLSyncKHR glDrawFinishedFence =
                 eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
         LOG_ALWAYS_FATAL_IF(glDrawFinishedFence == EGL_NO_SYNC_KHR,
-                "Could not create sync fence %#x", eglGetError());
+                            "Could not create sync fence %#x", eglGetError());
         glFlush();
         // TODO: export EGLSyncKHR in file descr
         // TODO: import file desc in Vulkan Semaphore
         // TODO: instead block the GPU: probably by using external Vulkan semaphore.
         // Block the CPU until the glFlush finish.
-        EGLint waitStatus = eglClientWaitSyncKHR(display, glDrawFinishedFence, 0,
-                FENCE_TIMEOUT);
+        EGLint waitStatus = eglClientWaitSyncKHR(display, glDrawFinishedFence, 0, FENCE_TIMEOUT);
         LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
                             "Failed to wait for the fence %#x", eglGetError());
         eglDestroySyncKHR(display, glDrawFinishedFence);
@@ -197,26 +198,25 @@
     canvas->resetMatrix();
 
     auto functorImage = SkImage::MakeFromAHardwareBuffer(
-        reinterpret_cast<AHardwareBuffer*>(mFrameBuffer.get()), kPremul_SkAlphaType,
-        nullptr, kBottomLeft_GrSurfaceOrigin);
+            reinterpret_cast<AHardwareBuffer*>(mFrameBuffer.get()), kPremul_SkAlphaType, nullptr,
+            kBottomLeft_GrSurfaceOrigin);
     canvas->drawImage(functorImage, 0, 0, &paint);
     canvas->restore();
 }
 
 VkInteropFunctorDrawable::~VkInteropFunctorDrawable() {
-    if (mListener.get() != nullptr) {
-        ScopedDrawRequest _drawRequest{};
-        sGLDrawThread->queue().runSync([&]() {
-             mListener->onGlFunctorReleased(mFunctor);
-        });
+    if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
+        if (lp->listener) {
+            ScopedDrawRequest _drawRequest{};
+            sGLDrawThread->queue().runSync(
+                    [&]() { lp->listener->onGlFunctorReleased(lp->functor); });
+        }
     }
 }
 
-void VkInteropFunctorDrawable::syncFunctor() const {
+void VkInteropFunctorDrawable::syncFunctor(const WebViewSyncData& data) const {
     ScopedDrawRequest _drawRequest{};
-    sGLDrawThread->queue().runSync([&]() {
-         (*mFunctor)(DrawGlInfo::kModeSync, nullptr);
-    });
+    sGLDrawThread->queue().runSync([&]() { FunctorDrawable::syncFunctor(data); });
 }
 
 }  // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
index 8fe52c5..c47ee11 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
@@ -18,8 +18,8 @@
 
 #include "FunctorDrawable.h"
 
-#include <utils/RefBase.h>
 #include <ui/GraphicBuffer.h>
+#include <utils/RefBase.h>
 
 namespace android {
 namespace uirenderer {
@@ -32,20 +32,18 @@
  */
 class VkInteropFunctorDrawable : public FunctorDrawable {
 public:
-    VkInteropFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener,
-            SkCanvas* canvas)
-            : FunctorDrawable(functor, listener, canvas) {}
+    using FunctorDrawable::FunctorDrawable;
+
     virtual ~VkInteropFunctorDrawable();
 
-    void syncFunctor() const override;
-
     static void vkInvokeFunctor(Functor* functor);
 
+    void syncFunctor(const WebViewSyncData& data) const override;
+
 protected:
     virtual void onDraw(SkCanvas* canvas) override;
 
 private:
-
     // Variables below describe/store temporary offscreen buffer used for Vulkan pipeline.
     sp<GraphicBuffer> mFrameBuffer;
     SkImageInfo mFBInfo;
diff --git a/libs/hwui/private/hwui/WebViewFunctor.h b/libs/hwui/private/hwui/WebViewFunctor.h
new file mode 100644
index 0000000..da3d06a
--- /dev/null
+++ b/libs/hwui/private/hwui/WebViewFunctor.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
+#define FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
+
+#include <cutils/compiler.h>
+#include <private/hwui/DrawGlInfo.h>
+
+namespace android::uirenderer {
+
+enum class RenderMode {
+    OpenGL_ES,
+    Vulkan,
+};
+
+// Static for the lifetime of the process
+ANDROID_API RenderMode WebViewFunctor_queryPlatformRenderMode();
+
+struct WebViewSyncData {
+    bool applyForceDark;
+};
+
+struct WebViewFunctorCallbacks {
+    // kModeSync, called on RenderThread
+    void (*onSync)(int functor, void* data, const WebViewSyncData& syncData);
+
+    // Called when either the context is destroyed _or_ when the functor's last reference goes
+    // away. Will always be called with an active context and always on renderthread.
+    void (*onContextDestroyed)(int functor, void* data);
+
+    // Called when the last reference to the handle goes away and the handle is considered
+    // irrevocably destroyed. Will always be proceeded by a call to onContextDestroyed if
+    // this functor had ever been drawn.
+    void (*onDestroyed)(int functor, void* data);
+
+    union {
+        struct {
+            // Called on RenderThread. initialize is guaranteed to happen before this call
+            void (*draw)(int functor, void* data, const DrawGlInfo& params);
+        } gles;
+        // TODO: VK support. The current DrawVkInfo is monolithic and needs to be split up for
+        // what params are valid on what callbacks
+        struct {
+            // Called either the first time the functor is used or the first time it's used after
+            // a call to onContextDestroyed.
+            // void (*initialize)(int functor, const InitParams& params);
+            // void (*frameStart)(int functor, /* todo: what params are actually needed for this to
+            // be useful? Is this useful? */)
+            // void (*draw)(int functor, const CompositeParams& params /* todo: rename - composite
+            // almost always means something else, and we aren't compositing */);
+            // void (*frameEnd)(int functor, const PostCompositeParams& params /* todo: same as
+            // CompositeParams - rename */);
+        } vk;
+    };
+};
+
+// Creates a new WebViewFunctor from the given prototype. The prototype is copied after
+// this function returns. Caller retains full ownership of it.
+// Returns -1 if the creation fails (such as an unsupported functorMode + platform mode combination)
+ANDROID_API int WebViewFunctor_create(void* data, const WebViewFunctorCallbacks& prototype, RenderMode functorMode);
+
+// May be called on any thread to signal that the functor should be destroyed.
+// The functor will receive an onDestroyed when the last usage of it is released,
+// and it should be considered alive & active until that point.
+ANDROID_API void WebViewFunctor_release(int functor);
+
+}  // namespace android::uirenderer
+
+#endif  // FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index 35fc91a..66f04f1 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -59,7 +59,7 @@
 private:
     friend class RenderThread;
 
-    CacheManager(const DisplayInfo& display);
+    explicit CacheManager(const DisplayInfo& display);
 
     void reset(sk_sp<GrContext> grContext);
     void destroy();
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 4e4262c..8e57a3a 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -576,8 +576,7 @@
     ATRACE_CALL();
     if (level >= TRIM_MEMORY_COMPLETE) {
         thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
-        thread.destroyGlContext();
-        thread.vulkanManager().destroy();
+        thread.destroyRenderingContext();
     } else if (level >= TRIM_MEMORY_UI_HIDDEN) {
         thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
     }
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 8230dfd..56eedff 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -89,7 +89,8 @@
         , mEglConfigWideGamut(nullptr)
         , mEglContext(EGL_NO_CONTEXT)
         , mPBufferSurface(EGL_NO_SURFACE)
-        , mCurrentSurface(EGL_NO_SURFACE) {}
+        , mCurrentSurface(EGL_NO_SURFACE)
+        , mHasWideColorGamutSupport(false) {}
 
 EglManager::~EglManager() {
     destroy();
@@ -128,6 +129,81 @@
     createContext();
     createPBufferSurface();
     makeCurrent(mPBufferSurface, nullptr, /* force */ true);
+
+    SkColorSpace::Gamut wideColorGamut = DeviceInfo::get()->getWideColorGamut();
+    bool hasWideColorSpaceExtension = false;
+    if (wideColorGamut == SkColorSpace::Gamut::kDCIP3_D65_Gamut) {
+        hasWideColorSpaceExtension = EglExtensions.displayP3;
+    } else if (wideColorGamut == SkColorSpace::Gamut::kSRGB_Gamut) {
+        hasWideColorSpaceExtension = EglExtensions.scRGB;
+    } else {
+        LOG_ALWAYS_FATAL("Unsupported wide color space.");
+    }
+    mHasWideColorGamutSupport = EglExtensions.glColorSpace && hasWideColorSpaceExtension &&
+                                mEglConfigWideGamut != EGL_NO_CONFIG_KHR;
+}
+
+EGLConfig EglManager::load8BitsConfig(EGLDisplay display, EglManager::SwapBehavior swapBehavior) {
+    EGLint eglSwapBehavior =
+            (swapBehavior == SwapBehavior::Preserved) ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
+    EGLint attribs[] = {EGL_RENDERABLE_TYPE,
+                        EGL_OPENGL_ES2_BIT,
+                        EGL_RED_SIZE,
+                        8,
+                        EGL_GREEN_SIZE,
+                        8,
+                        EGL_BLUE_SIZE,
+                        8,
+                        EGL_ALPHA_SIZE,
+                        8,
+                        EGL_DEPTH_SIZE,
+                        0,
+                        EGL_CONFIG_CAVEAT,
+                        EGL_NONE,
+                        EGL_STENCIL_SIZE,
+                        STENCIL_BUFFER_SIZE,
+                        EGL_SURFACE_TYPE,
+                        EGL_WINDOW_BIT | eglSwapBehavior,
+                        EGL_NONE};
+    EGLConfig config = EGL_NO_CONFIG_KHR;
+    EGLint numConfigs = 1;
+    if (!eglChooseConfig(display, attribs, &config, numConfigs, &numConfigs) ||
+        numConfigs != 1) {
+        return EGL_NO_CONFIG_KHR;
+    }
+    return config;
+}
+
+EGLConfig EglManager::loadFP16Config(EGLDisplay display, SwapBehavior swapBehavior) {
+    EGLint eglSwapBehavior =
+            (swapBehavior == SwapBehavior::Preserved) ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
+    // If we reached this point, we have a valid swap behavior
+    EGLint attribs[] = {EGL_RENDERABLE_TYPE,
+                        EGL_OPENGL_ES2_BIT,
+                        EGL_COLOR_COMPONENT_TYPE_EXT,
+                        EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT,
+                        EGL_RED_SIZE,
+                        16,
+                        EGL_GREEN_SIZE,
+                        16,
+                        EGL_BLUE_SIZE,
+                        16,
+                        EGL_ALPHA_SIZE,
+                        16,
+                        EGL_DEPTH_SIZE,
+                        0,
+                        EGL_STENCIL_SIZE,
+                        STENCIL_BUFFER_SIZE,
+                        EGL_SURFACE_TYPE,
+                        EGL_WINDOW_BIT | eglSwapBehavior,
+                        EGL_NONE};
+    EGLConfig config = EGL_NO_CONFIG_KHR;
+    EGLint numConfigs = 1;
+    if (!eglChooseConfig(display, attribs, &config, numConfigs, &numConfigs) ||
+        numConfigs != 1) {
+        return EGL_NO_CONFIG_KHR;
+    }
+    return config;
 }
 
 void EglManager::initExtensions() {
@@ -146,12 +222,8 @@
     EglExtensions.glColorSpace = extensions.has("EGL_KHR_gl_colorspace");
     EglExtensions.noConfigContext = extensions.has("EGL_KHR_no_config_context");
     EglExtensions.pixelFormatFloat = extensions.has("EGL_EXT_pixel_format_float");
-#ifdef ANDROID_ENABLE_LINEAR_BLENDING
-    EglExtensions.scRGB = extensions.has("EGL_EXT_gl_colorspace_scrgb_linear");
-#else
     EglExtensions.scRGB = extensions.has("EGL_EXT_gl_colorspace_scrgb");
-#endif
-    EglExtensions.displayP3 = extensions.has("EGL_EXT_gl_colorspace_display_p3");
+    EglExtensions.displayP3 = extensions.has("EGL_EXT_gl_colorspace_display_p3_passthrough");
     EglExtensions.contextPriority = extensions.has("EGL_IMG_context_priority");
     EglExtensions.surfacelessContext = extensions.has("EGL_KHR_surfaceless_context");
 }
@@ -162,77 +234,35 @@
 
 void EglManager::loadConfigs() {
     ALOGD("Swap behavior %d", static_cast<int>(mSwapBehavior));
-    EGLint swapBehavior =
-            (mSwapBehavior == SwapBehavior::Preserved) ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
 
     // Note: The default pixel format is RGBA_8888, when other formats are
     // available, we should check the target pixel format and configure the
     // attributes list properly.
-    EGLint attribs[] = {EGL_RENDERABLE_TYPE,
-                        EGL_OPENGL_ES2_BIT,
-                        EGL_RED_SIZE,
-                        8,
-                        EGL_GREEN_SIZE,
-                        8,
-                        EGL_BLUE_SIZE,
-                        8,
-                        EGL_ALPHA_SIZE,
-                        8,
-                        EGL_DEPTH_SIZE,
-                        0,
-                        EGL_CONFIG_CAVEAT,
-                        EGL_NONE,
-                        EGL_STENCIL_SIZE,
-                        STENCIL_BUFFER_SIZE,
-                        EGL_SURFACE_TYPE,
-                        EGL_WINDOW_BIT | swapBehavior,
-                        EGL_NONE};
-
-    EGLint numConfigs = 1;
-    if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, numConfigs, &numConfigs) ||
-        numConfigs != 1) {
+    mEglConfig = load8BitsConfig(mEglDisplay, mSwapBehavior);
+    if (mEglConfig == EGL_NO_CONFIG_KHR) {
         if (mSwapBehavior == SwapBehavior::Preserved) {
             // Try again without dirty regions enabled
             ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
             mSwapBehavior = SwapBehavior::Discard;
-            loadConfigs();
-            return;  // the call to loadConfigs() we just made picks the wide gamut config
+            ALOGD("Swap behavior %d", static_cast<int>(mSwapBehavior));
+            mEglConfig = load8BitsConfig(mEglDisplay, mSwapBehavior);
         } else {
             // Failed to get a valid config
             LOG_ALWAYS_FATAL("Failed to choose config, error = %s", eglErrorString());
         }
     }
+    SkColorType wideColorType = DeviceInfo::get()->getWideColorType();
 
-    if (EglExtensions.pixelFormatFloat) {
-        // If we reached this point, we have a valid swap behavior
-        EGLint attribs16F[] = {EGL_RENDERABLE_TYPE,
-                               EGL_OPENGL_ES2_BIT,
-                               EGL_COLOR_COMPONENT_TYPE_EXT,
-                               EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT,
-                               EGL_RED_SIZE,
-                               16,
-                               EGL_GREEN_SIZE,
-                               16,
-                               EGL_BLUE_SIZE,
-                               16,
-                               EGL_ALPHA_SIZE,
-                               16,
-                               EGL_DEPTH_SIZE,
-                               0,
-                               EGL_STENCIL_SIZE,
-                               STENCIL_BUFFER_SIZE,
-                               EGL_SURFACE_TYPE,
-                               EGL_WINDOW_BIT | swapBehavior,
-                               EGL_NONE};
-
-        numConfigs = 1;
-        if (!eglChooseConfig(mEglDisplay, attribs16F, &mEglConfigWideGamut, numConfigs,
-                             &numConfigs) ||
-            numConfigs != 1) {
+    // When we reach this point, we have a valid swap behavior
+    if (wideColorType == SkColorType::kRGBA_F16_SkColorType && EglExtensions.pixelFormatFloat) {
+        mEglConfigWideGamut = loadFP16Config(mEglDisplay, mSwapBehavior);
+        if (mEglConfigWideGamut == EGL_NO_CONFIG_KHR) {
             ALOGE("Device claims wide gamut support, cannot find matching config, error = %s",
                     eglErrorString());
             EglExtensions.pixelFormatFloat = false;
         }
+    } else if (wideColorType == SkColorType::kN32_SkColorType) {
+        mEglConfigWideGamut = load8BitsConfig(mEglDisplay, mSwapBehavior);
     }
 }
 
@@ -263,11 +293,12 @@
     }
 }
 
-Result<EGLSurface, EGLint> EglManager::createSurface(EGLNativeWindowType window, ColorMode colorMode) {
+Result<EGLSurface, EGLint> EglManager::createSurface(EGLNativeWindowType window,
+                                                     ColorMode colorMode,
+                                                     SkColorSpace::Gamut colorGamut) {
     LOG_ALWAYS_FATAL_IF(!hasEglContext(), "Not initialized");
 
-    bool wideColorGamut = colorMode == ColorMode::WideColorGamut && EglExtensions.glColorSpace &&
-                          EglExtensions.scRGB && EglExtensions.pixelFormatFloat &&
+    bool wideColorGamut = colorMode == ColorMode::WideColorGamut && mHasWideColorGamutSupport &&
                           EglExtensions.noConfigContext;
 
     // The color space we want to use depends on whether linear blending is turned
@@ -285,8 +316,8 @@
     // When wide gamut rendering is on we cannot rely on the GPU performing
     // linear blending for us. We use two different color spaces to tag the
     // surface appropriately for SurfaceFlinger:
-    // - Gamma blending (default) requires the use of the scRGB-nl color space
-    // - Linear blending requires the use of the scRGB color space
+    // - Gamma blending (default) requires the use of the non-linear color space
+    // - Linear blending requires the use of the linear color space
 
     // Not all Android targets support the EGL_GL_COLORSPACE_KHR extension
     // We insert to placeholders to set EGL_GL_COLORSPACE_KHR and its value.
@@ -296,19 +327,20 @@
 
     if (EglExtensions.glColorSpace) {
         attribs[0] = EGL_GL_COLORSPACE_KHR;
-#ifdef ANDROID_ENABLE_LINEAR_BLENDING
         if (wideColorGamut) {
-            attribs[1] = EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT;
-        } else {
-            attribs[1] = EGL_GL_COLORSPACE_SRGB_KHR;
-        }
-#else
-        if (wideColorGamut) {
-            attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT;
+            switch (colorGamut) {
+                case SkColorSpace::Gamut::kDCIP3_D65_Gamut:
+                    attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
+                    break;
+                case SkColorSpace::Gamut::kSRGB_Gamut:
+                    attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT;
+                    break;
+                default:
+                    LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
+            }
         } else {
             attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR;
         }
-#endif
     }
 
     EGLSurface surface = eglCreateWindowSurface(
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index 2a44f7e..4dd9096 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -48,7 +48,8 @@
 
     bool hasEglContext();
 
-    Result<EGLSurface, EGLint> createSurface(EGLNativeWindowType window, ColorMode colorMode);
+    Result<EGLSurface, EGLint> createSurface(EGLNativeWindowType window, ColorMode colorMode,
+                                             SkColorSpace::Gamut colorGamut);
     void destroySurface(EGLSurface surface);
 
     void destroy();
@@ -80,6 +81,14 @@
     status_t createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, sp<Fence>& nativeFence);
 
 private:
+    enum class SwapBehavior {
+        Discard,
+        Preserved,
+        BufferAge,
+    };
+
+    static EGLConfig load8BitsConfig(EGLDisplay display, SwapBehavior swapBehavior);
+    static EGLConfig loadFP16Config(EGLDisplay display, SwapBehavior swapBehavior);
 
     void initExtensions();
     void createPBufferSurface();
@@ -93,12 +102,7 @@
     EGLContext mEglContext;
     EGLSurface mPBufferSurface;
     EGLSurface mCurrentSurface;
-
-    enum class SwapBehavior {
-        Discard,
-        Preserved,
-        BufferAge,
-    };
+    bool mHasWideColorGamutSupport;
     SwapBehavior mSwapBehavior = SwapBehavior::Discard;
 };
 
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 085812a..aa6af23 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -30,6 +30,7 @@
 #include "renderthread/RenderThread.h"
 #include "utils/Macros.h"
 #include "utils/TimeUtils.h"
+#include "WebViewFunctorManager.h"
 
 #include <ui/GraphicBuffer.h>
 
@@ -143,6 +144,14 @@
     }
 }
 
+void RenderProxy::destroyFunctor(int functor) {
+    ATRACE_CALL();
+    RenderThread& thread = RenderThread::getInstance();
+    thread.queue().post([=]() {
+        WebViewFunctorManager::instance().destroyFunctor(functor);
+    });
+}
+
 DeferredLayerUpdater* RenderProxy::createTextureLayer() {
     return mRenderThread.queue().runSync([this]() -> auto {
         return mContext->createTextureLayer();
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index d9b789f..9dc9181 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -82,6 +82,7 @@
     ANDROID_API void destroy();
 
     ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion);
+    static void destroyFunctor(int functor);
 
     ANDROID_API DeferredLayerUpdater* createTextureLayer();
     ANDROID_API void buildLayer(RenderNode* node);
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 207673c1..c06fadd 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -132,6 +132,7 @@
         , mFrameCallbackTaskPending(false)
         , mRenderState(nullptr)
         , mEglManager(nullptr)
+        , mFunctorManager(WebViewFunctorManager::instance())
         , mVkManager(nullptr) {
     Properties::load();
     start("RenderThread");
@@ -197,11 +198,13 @@
     setGrContext(grContext);
 }
 
-void RenderThread::destroyGlContext() {
+void RenderThread::destroyRenderingContext() {
+    mFunctorManager.onContextDestroyed();
     if (mEglManager->hasEglContext()) {
         setGrContext(nullptr);
         mEglManager->destroy();
     }
+    vulkanManager().destroy();
 }
 
 void RenderThread::dumpGraphicsMemory(int fd) {
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 2384f95..5272227 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -23,6 +23,7 @@
 #include "CacheManager.h"
 #include "TimeLord.h"
 #include "thread/ThreadBase.h"
+#include "WebViewFunctorManager.h"
 
 #include <GrContext.h>
 #include <SkBitmap.h>
@@ -104,7 +105,7 @@
     void dumpGraphicsMemory(int fd);
 
     void requireGlContext();
-    void destroyGlContext();
+    void destroyRenderingContext();
 
     /**
      * isCurrent provides a way to query, if the caller is running on
@@ -122,6 +123,7 @@
     friend class RenderProxy;
     friend class DummyVsyncSource;
     friend class android::uirenderer::TestUtils;
+    friend class android::uirenderer::WebViewFunctor;
 
     RenderThread();
     virtual ~RenderThread();
@@ -151,6 +153,7 @@
     TimeLord mTimeLord;
     RenderState* mRenderState;
     EglManager* mEglManager;
+    WebViewFunctorManager& mFunctorManager;
 
     ProfileDataContainer mGlobalProfileData;
     Readback* mReadback = nullptr;
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 4be8bd9..aa7a141 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -473,8 +473,10 @@
     if (windowWidth != surface->mWindowWidth || windowHeight != surface->mWindowHeight) {
         ColorMode colorMode = surface->mColorMode;
         sk_sp<SkColorSpace> colorSpace = surface->mColorSpace;
+        SkColorSpace::Gamut colorGamut = surface->mColorGamut;
+        SkColorType colorType = surface->mColorType;
         destroySurface(surface);
-        *surfaceOut = createSurface(window, colorMode, colorSpace);
+        *surfaceOut = createSurface(window, colorMode, colorSpace, colorGamut, colorType);
         surface = *surfaceOut;
     }
 
@@ -647,8 +649,7 @@
         VulkanSurface::ImageInfo& imageInfo = surface->mImageInfos[i];
         imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget(
                 mRenderThread.getGrContext(), backendRT, kTopLeft_GrSurfaceOrigin,
-                surface->mColorMode == ColorMode::WideColorGamut ? kRGBA_F16_SkColorType
-                : kRGBA_8888_SkColorType, surface->mColorSpace, &props);
+                surface->mColorType, surface->mColorSpace, &props);
     }
 
     SkASSERT(mCommandPool != VK_NULL_HANDLE);
@@ -767,10 +768,20 @@
 
     VkFormat surfaceFormat = VK_FORMAT_R8G8B8A8_UNORM;
     VkColorSpaceKHR colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
-    if (surface->mColorMode == ColorMode::WideColorGamut) {
+    if (surface->mColorType == SkColorType::kRGBA_F16_SkColorType) {
         surfaceFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
-        colorSpace = VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT;
     }
+
+    if (surface->mColorMode == ColorMode::WideColorGamut) {
+        if (surface->mColorGamut == SkColorSpace::Gamut::kSRGB_Gamut) {
+            colorSpace = VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT;
+        } else if (surface->mColorGamut == SkColorSpace::Gamut::kDCIP3_D65_Gamut) {
+            colorSpace = VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT;
+        } else {
+            LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
+        }
+    }
+
     bool foundSurfaceFormat = false;
     for (uint32_t i = 0; i < surfaceFormatCount; ++i) {
         if (surfaceFormat == surfaceFormats[i].format
@@ -840,14 +851,17 @@
 }
 
 VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode,
-        sk_sp<SkColorSpace> surfaceColorSpace) {
+                                            sk_sp<SkColorSpace> surfaceColorSpace,
+                                            SkColorSpace::Gamut surfaceColorGamut,
+                                            SkColorType surfaceColorType) {
     initialize();
 
     if (!window) {
         return nullptr;
     }
 
-    VulkanSurface* surface = new VulkanSurface(colorMode, window, surfaceColorSpace);
+    VulkanSurface* surface = new VulkanSurface(colorMode, window, surfaceColorSpace,
+                                               surfaceColorGamut, surfaceColorType);
 
     VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
     memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR));
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index d67d2c8..9eb942c 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -38,8 +38,10 @@
 
 class VulkanSurface {
 public:
-    VulkanSurface(ColorMode colorMode, ANativeWindow* window, sk_sp<SkColorSpace> colorSpace)
-            : mColorMode(colorMode), mNativeWindow(window), mColorSpace(colorSpace) {}
+    VulkanSurface(ColorMode colorMode, ANativeWindow* window, sk_sp<SkColorSpace> colorSpace,
+                  SkColorSpace::Gamut colorGamut, SkColorType colorType)
+            : mColorMode(colorMode), mNativeWindow(window), mColorSpace(colorSpace),
+              mColorGamut(colorGamut), mColorType(colorType) {}
 
     sk_sp<SkSurface> getBackBufferSurface() { return mBackbuffer; }
 
@@ -80,6 +82,8 @@
     int mWindowWidth = 0;
     int mWindowHeight = 0;
     sk_sp<SkColorSpace> mColorSpace;
+    SkColorSpace::Gamut mColorGamut;
+    SkColorType mColorType;
 };
 
 // This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue,
@@ -98,7 +102,9 @@
     // Given a window this creates a new VkSurfaceKHR and VkSwapchain and stores them inside a new
     // VulkanSurface object which is returned.
     VulkanSurface* createSurface(ANativeWindow* window, ColorMode colorMode,
-            sk_sp<SkColorSpace> surfaceColorSpace);
+                                 sk_sp<SkColorSpace> surfaceColorSpace,
+                                 SkColorSpace::Gamut surfaceColorGamut,
+                                 SkColorType surfaceColorType);
 
     // Destroy the VulkanSurface and all associated vulkan objects.
     void destroySurface(VulkanSurface* surface);
@@ -154,6 +160,7 @@
             fPtr = ptr;
             return *this;
         }
+        // NOLINTNEXTLINE(google-explicit-constructor)
         operator FNPTR_TYPE() const { return fPtr; }
 
     private:
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index 3d50d2d..29e4256 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -139,6 +139,7 @@
     uint32_t file_version = *reinterpret_cast<uint32_t*>(addr);
     if (file_version != sCurrentFileVersion) {
         ALOGW("file_version mismatch! expected %d got %d", sCurrentFileVersion, file_version);
+        munmap(addr, sb.st_size);
         return false;
     }
 
@@ -150,6 +151,7 @@
         ALOGW("Parse failed on '%s' error='%s'", path.c_str(),
               output->InitializationErrorString().c_str());
     }
+    munmap(addr, sb.st_size);
     return success;
 }
 
diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp
index 15aec9f..4a2f57e 100644
--- a/libs/hwui/surfacetexture/ImageConsumer.cpp
+++ b/libs/hwui/surfacetexture/ImageConsumer.cpp
@@ -70,7 +70,8 @@
             int slot = st.mCurrentTexture;
             if (slot != BufferItem::INVALID_BUFFER_SLOT) {
                 *queueEmpty = true;
-                mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace);
+                mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer,
+                        st.mCurrentDataSpace);
                 return mImageSlots[slot].mImage;
             }
         }
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index f812022..7aa9b82 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -32,6 +32,8 @@
 namespace android {
 namespace uirenderer {
 
+std::unordered_map<int, TestUtils::CallCounts> TestUtils::sMockFunctorCounts{};
+
 SkColor TestUtils::interpolateColor(float fraction, SkColor start, SkColor end) {
     int startA = (start >> 24) & 0xff;
     int startR = (start >> 16) & 0xff;
@@ -82,12 +84,10 @@
     uint32_t length = strlen(text);
     SkPaint glyphPaint(paint);
     glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding);
-    canvas->drawText(
-            utf16.get(), length,  // text buffer
-            0, length,  // draw range
-            0, length,  // context range
-            x, y, minikin::Bidi::LTR,
-            glyphPaint, nullptr, nullptr /* measured text */);
+    canvas->drawText(utf16.get(), length,  // text buffer
+                     0, length,            // draw range
+                     0, length,            // context range
+                     x, y, minikin::Bidi::LTR, glyphPaint, nullptr, nullptr /* measured text */);
 }
 
 void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint& paint,
@@ -96,7 +96,7 @@
     SkPaint glyphPaint(paint);
     glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding);
     canvas->drawTextOnPath(utf16.get(), strlen(text), minikin::Bidi::LTR, path, 0, 0, glyphPaint,
-            nullptr);
+                           nullptr);
 }
 
 void TestUtils::TestTask::run() {
@@ -110,11 +110,7 @@
 
     rtCallback(renderThread);
 
-    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
-        renderThread.vulkanManager().destroy();
-    } else {
-        renderThread.destroyGlContext();
-    }
+    renderThread.destroyRenderingContext();
 }
 
 std::unique_ptr<uint16_t[]> TestUtils::asciiToUtf16(const char* str) {
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index c5db861d..6a1ca5a 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -27,6 +27,7 @@
 #include <renderstate/RenderState.h>
 #include <renderthread/RenderThread.h>
 
+#include <gtest/gtest.h>
 #include <memory>
 
 namespace android {
@@ -201,8 +202,7 @@
 
     static void recordNode(RenderNode& node, std::function<void(Canvas&)> contentCallback) {
         std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(
-                node.stagingProperties().getWidth(), node.stagingProperties().getHeight(),
-                &node));
+                node.stagingProperties().getWidth(), node.stagingProperties().getHeight(), &node));
         contentCallback(*canvas.get());
         node.setStagingDisplayList(canvas->finishRecording());
     }
@@ -267,7 +267,14 @@
         renderthread::RenderThread::getInstance().queue().runSync([&]() { task.run(); });
     }
 
+    static void runOnRenderThreadUnmanaged(RtCallback rtCallback) {
+        auto& rt = renderthread::RenderThread::getInstance();
+        rt.queue().runSync([&]() { rtCallback(rt); });
+    }
+
+
     static bool isRenderThreadRunning() { return renderthread::RenderThread::hasInstance(); }
+    static pid_t getRenderThreadTid() { return renderthread::RenderThread::getInstance().getTid(); }
 
     static SkColor interpolateColor(float fraction, SkColor start, SkColor end);
 
@@ -296,7 +303,52 @@
     static SkRect getClipBounds(const SkCanvas* canvas);
     static SkRect getLocalClipBounds(const SkCanvas* canvas);
 
+    struct CallCounts {
+        int sync = 0;
+        int contextDestroyed = 0;
+        int destroyed = 0;
+        int glesDraw = 0;
+    };
+
+    static void expectOnRenderThread() { EXPECT_EQ(gettid(), TestUtils::getRenderThreadTid()); }
+
+    static WebViewFunctorCallbacks createMockFunctor(RenderMode mode) {
+        auto callbacks = WebViewFunctorCallbacks{
+                .onSync =
+                        [](int functor, void* client_data, const WebViewSyncData& data) {
+                            expectOnRenderThread();
+                            sMockFunctorCounts[functor].sync++;
+                        },
+                .onContextDestroyed =
+                        [](int functor, void* client_data) {
+                            expectOnRenderThread();
+                            sMockFunctorCounts[functor].contextDestroyed++;
+                        },
+                .onDestroyed =
+                        [](int functor, void* client_data) {
+                            expectOnRenderThread();
+                            sMockFunctorCounts[functor].destroyed++;
+                        },
+        };
+        switch (mode) {
+            case RenderMode::OpenGL_ES:
+                callbacks.gles.draw = [](int functor, void* client_data, const DrawGlInfo& params) {
+                    expectOnRenderThread();
+                    sMockFunctorCounts[functor].glesDraw++;
+                };
+                break;
+            default:
+                ADD_FAILURE();
+                return WebViewFunctorCallbacks{};
+        }
+        return callbacks;
+    }
+
+    static CallCounts& countsForFunctor(int functor) { return sMockFunctorCounts[functor]; }
+
 private:
+    static std::unordered_map<int, CallCounts> sMockFunctorCounts;
+
     static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
         MarkAndSweepRemoved observer(nullptr);
         node->syncProperties();
@@ -306,9 +358,9 @@
         }
         auto displayList = node->getDisplayList();
         if (displayList) {
-            for (auto&& childDr : static_cast<skiapipeline::SkiaDisplayList*>(
-                                          const_cast<DisplayList*>(displayList))
-                                          ->mChildNodes) {
+            for (auto&& childDr :
+                 static_cast<skiapipeline::SkiaDisplayList*>(const_cast<DisplayList*>(displayList))
+                         ->mChildNodes) {
                 syncHierarchyPropertiesAndDisplayListImpl(childDr.getRenderNode());
             }
         }
diff --git a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
index 1d3d607..5af7d43 100644
--- a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
@@ -31,7 +31,7 @@
 
 class BitmapFillrate : public TestScene {
 public:
-    BitmapFillrate(BitmapAllocationTestUtils::BitmapAllocator allocator)
+    explicit BitmapFillrate(BitmapAllocationTestUtils::BitmapAllocator allocator)
             : TestScene(), mAllocator(allocator) {}
 
     void createContent(int width, int height, Canvas& canvas) override {
@@ -70,4 +70,4 @@
 
     BitmapAllocationTestUtils::BitmapAllocator mAllocator;
     std::vector<sp<RenderNode> > mNodes;
-};
\ No newline at end of file
+};
diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
index ad11a1d..5107660 100644
--- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
@@ -26,7 +26,7 @@
 
 class BitmapShaders : public TestScene {
 public:
-    BitmapShaders(BitmapAllocationTestUtils::BitmapAllocator allocator)
+    explicit BitmapShaders(BitmapAllocationTestUtils::BitmapAllocator allocator)
             : TestScene(), mAllocator(allocator) {}
 
     sp<RenderNode> card;
diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
index 448408d..ec81f62 100644
--- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
+++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
@@ -50,7 +50,7 @@
             pixels[4000 + 4 * i + 3] = 255;
         }
         buffer->unlock();
-        sk_sp<Bitmap> hardwareBitmap(Bitmap::createFrom(buffer));
+        sk_sp<Bitmap> hardwareBitmap(Bitmap::createFrom(buffer, SkColorSpace::MakeSRGB()));
         sk_sp<SkShader> hardwareShader(createBitmapShader(*hardwareBitmap));
 
         SkPoint center;
diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
index 9a1ee54..4111bd2 100644
--- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
@@ -16,7 +16,7 @@
 
 #include "TestSceneBase.h"
 #include "tests/common/TestListViewSceneBase.h"
-
+#include <SkFont.h>
 #include <cstdio>
 
 class ListViewAnimation;
@@ -46,11 +46,13 @@
                 SkColorGetR(randomColor) + SkColorGetG(randomColor) + SkColorGetB(randomColor) <
                 128 * 3;
         paint.setColor(bgDark ? Color::White : Color::Grey_700);
-        paint.setTextSize(size / 2);
+
+	SkFont font;
+        font.setSize(size / 2);
         char charToShow = 'A' + (rand() % 26);
-        const SkPoint pos[] = {{SkIntToScalar(size / 2),
-                                /*approximate centering*/ SkFloatToScalar(size * 0.7f)}};
-        canvas.drawPosText(&charToShow, 1, pos, paint);
+        const SkPoint pos = {SkIntToScalar(size / 2),
+                                /*approximate centering*/ SkFloatToScalar(size * 0.7f)};
+        canvas.drawSimpleText(&charToShow, 1, kUTF8_SkTextEncoding, pos.fX, pos.fY, font, paint);
         return bitmap;
     }
 
diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
index 0d87776..d189a93 100644
--- a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
@@ -76,7 +76,7 @@
                             paint.setStrokeWidth(strokeWidth);
                             // fill column with each op
                             int middleCount = canvas.save(SaveFlags::MatrixClip);
-                            for (auto op : ops) {
+                            for (const auto& op : ops) {
                                 int innerCount = canvas.save(SaveFlags::MatrixClip);
                                 canvas.clipRect(0, 0, cellSize, cellSize, SkClipOp::kIntersect);
                                 canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
diff --git a/libs/hwui/tests/common/scenes/TvApp.cpp b/libs/hwui/tests/common/scenes/TvApp.cpp
index a64e844..286f5f1 100644
--- a/libs/hwui/tests/common/scenes/TvApp.cpp
+++ b/libs/hwui/tests/common/scenes/TvApp.cpp
@@ -48,7 +48,7 @@
 
 class TvApp : public TestScene {
 public:
-    TvApp(BitmapAllocationTestUtils::BitmapAllocator allocator)
+    explicit TvApp(BitmapAllocationTestUtils::BitmapAllocator allocator)
             : TestScene(), mAllocator(allocator) {}
 
     sp<RenderNode> mBg;
@@ -232,7 +232,7 @@
 
 class TvAppNoRoundedCorner : public TvApp {
 public:
-    TvAppNoRoundedCorner(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {}
+    explicit TvAppNoRoundedCorner(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {}
 
 private:
     virtual float roundedCornerRadius() override { return dp(0); }
@@ -240,7 +240,7 @@
 
 class TvAppColorFilter : public TvApp {
 public:
-    TvAppColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {}
+    explicit TvAppColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {}
 
 private:
     virtual bool useOverlay() override { return false; }
@@ -248,7 +248,7 @@
 
 class TvAppNoRoundedCornerColorFilter : public TvApp {
 public:
-    TvAppNoRoundedCornerColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator)
+    explicit TvAppNoRoundedCornerColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator)
             : TvApp(allocator) {}
 
 private:
diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
index a686979..f4c3e13 100644
--- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
+++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
@@ -19,9 +19,9 @@
 
 #include "tests/common/TestUtils.h"
 
-#include <gtest/gtest.h>
 #include <SkBitmap.h>
 #include <SkImage.h>
+#include <gtest/gtest.h>
 
 using namespace android;
 using namespace android::uirenderer;
diff --git a/libs/hwui/tests/unit/FatalTestCanvas.h b/libs/hwui/tests/unit/FatalTestCanvas.h
index 146662b..1723c2e 100644
--- a/libs/hwui/tests/unit/FatalTestCanvas.h
+++ b/libs/hwui/tests/unit/FatalTestCanvas.h
@@ -30,10 +30,6 @@
     void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) {
         ADD_FAILURE() << "onDrawDRRect not expected in this test";
     }
-    void onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform[],
-                           const SkRect* cullRect, const SkPaint& paint) {
-        ADD_FAILURE() << "onDrawTextRSXform not expected in this test";
-    }
     void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) {
         ADD_FAILURE() << "onDrawTextBlob not expected in this test";
     }
@@ -116,4 +112,4 @@
 
     int mDrawCounter = 0;  // counts how may draw calls of any kind were made to this canvas
 };
-}
\ No newline at end of file
+}
diff --git a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
index 08b9679..dac888c 100644
--- a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
+++ b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
@@ -39,7 +39,7 @@
 // current thread can spoof being a GPU thread
 static void destroyEglContext() {
     if (TestUtils::isRenderThreadRunning()) {
-        TestUtils::runOnRenderThread([](RenderThread& thread) { thread.destroyGlContext(); });
+        TestUtils::runOnRenderThread([](RenderThread& thread) { thread.destroyRenderingContext(); });
     }
 }
 
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 0331581..c813cd9 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -355,9 +355,7 @@
     class ProjectionTestCanvas : public SkCanvas {
     public:
         ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
-        void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
-            mDrawCounter++;
-        }
+        void onDrawRect(const SkRect& rect, const SkPaint& paint) override { mDrawCounter++; }
 
         int getDrawCounter() { return mDrawCounter; }
 
@@ -370,7 +368,7 @@
             [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
                 properties.setProjectionReceiver(true);
             },
-            "B"); // a receiver with an empty display list
+            "B");  // a receiver with an empty display list
 
     auto projectingRipple = TestUtils::createSkiaNode(
             0, 0, 100, 100,
@@ -389,14 +387,14 @@
                 canvas.drawRenderNode(projectingRipple.get());
             },
             "C");
-    auto parent = TestUtils::createSkiaNode(
-            0, 0, 100, 100,
-            [&receiverBackground, &child](RenderProperties& properties,
-                                          SkiaRecordingCanvas& canvas) {
-                canvas.drawRenderNode(receiverBackground.get());
-                canvas.drawRenderNode(child.get());
-            },
-            "A");
+    auto parent =
+            TestUtils::createSkiaNode(0, 0, 100, 100,
+                                      [&receiverBackground, &child](RenderProperties& properties,
+                                                                    SkiaRecordingCanvas& canvas) {
+                                          canvas.drawRenderNode(receiverBackground.get());
+                                          canvas.drawRenderNode(child.get());
+                                      },
+                                      "A");
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
             CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
@@ -1058,7 +1056,7 @@
     public:
         FrameTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
         void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
-                const SkPaint* paint, SrcRectConstraint constraint) override {
+                             const SkPaint* paint, SrcRectConstraint constraint) override {
             mDrawCounter++;
             EXPECT_EQ(kLow_SkFilterQuality, paint->getFilterQuality());
         }
@@ -1076,7 +1074,7 @@
     FrameTestCanvas canvas;
     RenderNodeDrawable drawable(layerNode.get(), &canvas, true);
     canvas.drawDrawable(&drawable);
-    EXPECT_EQ(1, canvas.mDrawCounter);  //make sure the layer was composed
+    EXPECT_EQ(1, canvas.mDrawCounter);  // make sure the layer was composed
 
     // clean up layer pointer, so we can safely destruct RenderNode
     layerNode->setLayerSurface(nullptr);
@@ -1129,15 +1127,14 @@
                           getTotalMatrix());
             } else {
                 // Second invocation is preparing the matrix for an elevated RenderNodeDrawable.
-                EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y),
-                          matrix);
-                EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y),
-                          getTotalMatrix());
+                EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y), matrix);
+                EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y), getTotalMatrix());
             }
         }
 
     protected:
         int mDrawCounter = 0;
+
     private:
         bool mFirstDidConcat = true;
     };
@@ -1174,14 +1171,14 @@
     public:
         VectorDrawableTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
         void onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
-                const SkPaint* paint, SrcRectConstraint constraint) override {
+                              const SkPaint* paint, SrcRectConstraint constraint) override {
             const int index = mDrawCounter++;
             switch (index) {
                 case 0:
                     EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT));
                     break;
                 case 1:
-                    EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH/2, CANVAS_HEIGHT));
+                    EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH / 2, CANVAS_HEIGHT));
                     break;
                 default:
                     ADD_FAILURE();
@@ -1191,17 +1188,18 @@
 
     VectorDrawable::Group* group = new VectorDrawable::Group();
     sp<VectorDrawableRoot> vectorDrawable(new VectorDrawableRoot(group));
-    vectorDrawable->mutateStagingProperties()->setScaledSize(CANVAS_WIDTH/10, CANVAS_HEIGHT/10);
+    vectorDrawable->mutateStagingProperties()->setScaledSize(CANVAS_WIDTH / 10, CANVAS_HEIGHT / 10);
 
-    auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
-            [&](RenderProperties& props, SkiaRecordingCanvas& canvas) {
-                vectorDrawable->mutateStagingProperties()->setBounds(SkRect::MakeWH(CANVAS_WIDTH,
-                        CANVAS_HEIGHT));
-                canvas.drawVectorDrawable(vectorDrawable.get());
-                vectorDrawable->mutateStagingProperties()->setBounds(SkRect::MakeWH(CANVAS_WIDTH/2,
-                        CANVAS_HEIGHT));
-                canvas.drawVectorDrawable(vectorDrawable.get());
-            });
+    auto node =
+            TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+                                      [&](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+                                          vectorDrawable->mutateStagingProperties()->setBounds(
+                                                  SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT));
+                                          canvas.drawVectorDrawable(vectorDrawable.get());
+                                          vectorDrawable->mutateStagingProperties()->setBounds(
+                                                  SkRect::MakeWH(CANVAS_WIDTH / 2, CANVAS_HEIGHT));
+                                          canvas.drawVectorDrawable(vectorDrawable.get());
+                                      });
 
     VectorDrawableTestCanvas canvas;
     RenderNodeDrawable drawable(node.get(), &canvas, true);
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index a6073eb..1cd9bd8 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -295,7 +295,8 @@
     canvasContext->destroy();
 }
 
-RENDERTHREAD_TEST(RenderNode, prepareTree_HwLayer_AVD_enqueueDamage) {
+// TODO: Is this supposed to work in SkiaGL/SkiaVK?
+RENDERTHREAD_TEST(DISABLED_RenderNode, prepareTree_HwLayer_AVD_enqueueDamage) {
     VectorDrawable::Group* group = new VectorDrawable::Group();
     sp<VectorDrawableRoot> vectorDrawable(new VectorDrawableRoot(group));
 
@@ -306,6 +307,7 @@
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
             CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+    canvasContext->setSurface(nullptr);
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
     DamageAccumulator damageAccumulator;
     LayerUpdateQueue layerUpdateQueue;
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
index 1433aa0..87981f1 100644
--- a/libs/hwui/tests/unit/ShaderCacheTests.cpp
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-#include <gtest/gtest.h>
-#include <dirent.h>
 #include <cutils/properties.h>
-#include <cstdint>
+#include <dirent.h>
 #include <errno.h>
+#include <gtest/gtest.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <utils/Log.h>
-#include "pipeline/skia/ShaderCache.h"
+#include <cstdint>
 #include "FileBlobCache.h"
+#include "pipeline/skia/ShaderCache.h"
 
 using namespace android::uirenderer::skiapipeline;
 
@@ -66,7 +66,6 @@
 } /* namespace uirenderer */
 } /* namespace android */
 
-
 namespace {
 
 std::string getExternalStorageFolder() {
@@ -82,14 +81,12 @@
     return false;
 }
 
-inline bool
-checkShader(const sk_sp<SkData>& shader1, const sk_sp<SkData>& shader2) {
-    return nullptr != shader1 && nullptr != shader2 && shader1->size() == shader2->size()
-            && 0 == memcmp(shader1->data(), shader2->data(), shader1->size());
+inline bool checkShader(const sk_sp<SkData>& shader1, const sk_sp<SkData>& shader2) {
+    return nullptr != shader1 && nullptr != shader2 && shader1->size() == shader2->size() &&
+           0 == memcmp(shader1->data(), shader2->data(), shader1->size());
 }
 
-inline bool
-checkShader(const sk_sp<SkData>& shader, const char* program) {
+inline bool checkShader(const sk_sp<SkData>& shader, const char* program) {
     sk_sp<SkData> shader2 = SkData::MakeWithCString(program);
     return checkShader(shader, shader2);
 }
@@ -116,32 +113,31 @@
     }
 }
 
-
 #define GrProgramDescTest(a) (*SkData::MakeWithCString(#a).get())
 
 TEST(ShaderCacheTest, testWriteAndRead) {
     if (!folderExist(getExternalStorageFolder())) {
-        //don't run the test if external storage folder is not available
+        // don't run the test if external storage folder is not available
         return;
     }
-    std::string cacheFile1 =  getExternalStorageFolder() + "/shaderCacheTest1";
-    std::string cacheFile2 =  getExternalStorageFolder() + "/shaderCacheTest2";
+    std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
+    std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
 
-    //remove any test files from previous test run
+    // remove any test files from previous test run
     int deleteFile = remove(cacheFile1.c_str());
     ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
     std::srand(0);
 
-    //read the cache from a file that does not exist
+    // read the cache from a file that does not exist
     ShaderCache::get().setFilename(cacheFile1.c_str());
-    ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); //disable deferred save
+    ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0);  // disable deferred save
     ShaderCache::get().initShaderDiskCache();
 
-    //read a key - should not be found since the cache is empty
+    // read a key - should not be found since the cache is empty
     sk_sp<SkData> outVS;
     ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
 
-    //write to the in-memory cache without storing on disk and verify we read the same values
+    // write to the in-memory cache without storing on disk and verify we read the same values
     sk_sp<SkData> inVS;
     setShader(inVS, "sassas");
     ShaderCache::get().store(GrProgramDescTest(100), *inVS.get());
@@ -152,23 +148,23 @@
     ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
     ASSERT_TRUE(checkShader(outVS, "someVS"));
 
-    //store content to disk and release in-memory cache
+    // store content to disk and release in-memory cache
     ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
 
-    //change to a file that does not exist and verify load fails
+    // change to a file that does not exist and verify load fails
     ShaderCache::get().setFilename(cacheFile2.c_str());
     ShaderCache::get().initShaderDiskCache();
     ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
     ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
 
-    //load again content from disk from an existing file and check the data is read correctly
+    // load again content from disk from an existing file and check the data is read correctly
     ShaderCache::get().setFilename(cacheFile1.c_str());
     ShaderCache::get().initShaderDiskCache();
     sk_sp<SkData> outVS2;
     ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
     ASSERT_TRUE(checkShader(outVS2, "someVS"));
 
-    //change data, store to disk, read back again and verify data has been changed
+    // change data, store to disk, read back again and verify data has been changed
     setShader(inVS, "ewData1");
     ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
     ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
@@ -176,9 +172,8 @@
     ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
     ASSERT_TRUE(checkShader(outVS2, "ewData1"));
 
-
-    //write and read big data chunk (50K)
-    size_t dataSize = 50*1024;
+    // write and read big data chunk (50K)
+    size_t dataSize = 50 * 1024;
     std::vector<uint8_t> dataBuffer(dataSize);
     genRandomData(dataBuffer);
     setShader(inVS, dataBuffer);
@@ -194,31 +189,31 @@
 
 TEST(ShaderCacheTest, testCacheValidation) {
     if (!folderExist(getExternalStorageFolder())) {
-        //don't run the test if external storage folder is not available
+        // don't run the test if external storage folder is not available
         return;
     }
-    std::string cacheFile1 =  getExternalStorageFolder() + "/shaderCacheTest1";
-    std::string cacheFile2 =  getExternalStorageFolder() + "/shaderCacheTest2";
+    std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
+    std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
 
-    //remove any test files from previous test run
+    // remove any test files from previous test run
     int deleteFile = remove(cacheFile1.c_str());
     ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
     std::srand(0);
 
-    //generate identity and read the cache from a file that does not exist
+    // generate identity and read the cache from a file that does not exist
     ShaderCache::get().setFilename(cacheFile1.c_str());
-    ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); //disable deferred save
+    ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0);  // disable deferred save
     std::vector<uint8_t> identity(1024);
     genRandomData(identity);
-    ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
-                                           sizeof(decltype(identity)::value_type));
+    ShaderCache::get().initShaderDiskCache(
+            identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
 
     // generate random content in cache and store to disk
     constexpr size_t numBlob(10);
     constexpr size_t keySize(1024);
     constexpr size_t dataSize(50 * 1024);
 
-    std::vector< std::pair<sk_sp<SkData>, sk_sp<SkData>> > blobVec(numBlob);
+    std::vector<std::pair<sk_sp<SkData>, sk_sp<SkData>>> blobVec(numBlob);
     for (auto& blob : blobVec) {
         std::vector<uint8_t> keyBuffer(keySize);
         std::vector<uint8_t> dataBuffer(dataSize);
@@ -237,47 +232,47 @@
     // change to a file that does not exist and verify validation fails
     ShaderCache::get().setFilename(cacheFile2.c_str());
     ShaderCache::get().initShaderDiskCache();
-    ASSERT_FALSE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
+    ASSERT_FALSE(ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity));
     ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
 
     // restore the original file and verify validation succeeds
     ShaderCache::get().setFilename(cacheFile1.c_str());
-    ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
-                                           sizeof(decltype(identity)::value_type));
-    ASSERT_TRUE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
+    ShaderCache::get().initShaderDiskCache(
+            identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
+    ASSERT_TRUE(ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity));
     for (const auto& blob : blobVec) {
         auto outVS = ShaderCache::get().load(*blob.first.get());
-        ASSERT_TRUE( checkShader(outVS, blob.second) );
+        ASSERT_TRUE(checkShader(outVS, blob.second));
     }
 
     // generate error identity and verify load fails
     ShaderCache::get().initShaderDiskCache(identity.data(), -1);
     for (const auto& blob : blobVec) {
-        ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
+        ASSERT_EQ(ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>());
     }
-    ShaderCache::get().initShaderDiskCache(nullptr, identity.size() *
-                                           sizeof(decltype(identity)::value_type));
+    ShaderCache::get().initShaderDiskCache(
+            nullptr, identity.size() * sizeof(decltype(identity)::value_type));
     for (const auto& blob : blobVec) {
-        ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
+        ASSERT_EQ(ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>());
     }
 
     // verify the cache validation again after load fails
-    ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
-                                           sizeof(decltype(identity)::value_type));
-    ASSERT_TRUE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
+    ShaderCache::get().initShaderDiskCache(
+            identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
+    ASSERT_TRUE(ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity));
     for (const auto& blob : blobVec) {
         auto outVS = ShaderCache::get().load(*blob.first.get());
-        ASSERT_TRUE( checkShader(outVS, blob.second) );
+        ASSERT_TRUE(checkShader(outVS, blob.second));
     }
 
     // generate another identity and verify load fails
     for (auto& data : identity) {
         data += std::rand();
     }
-    ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
-                                           sizeof(decltype(identity)::value_type));
+    ShaderCache::get().initShaderDiskCache(
+            identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
     for (const auto& blob : blobVec) {
-        ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
+        ASSERT_EQ(ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>());
     }
 
     ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 415f9e8..1b4cf7e 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -100,16 +100,35 @@
     GLFunctorDrawable functorDrawable(&functor, nullptr, &dummyCanvas);
     skiaDL.mChildFunctors.push_back(&functorDrawable);
 
+    int functor2 = WebViewFunctor_create(
+            nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES);
+    auto& counts = TestUtils::countsForFunctor(functor2);
+    skiaDL.mChildFunctors.push_back(
+            skiaDL.allocateDrawable<GLFunctorDrawable>(functor2, &dummyCanvas));
+    WebViewFunctor_release(functor2);
+
     SkRect bounds = SkRect::MakeWH(200, 200);
     VectorDrawableRoot vectorDrawable(new VectorDrawable::Group());
     vectorDrawable.mutateStagingProperties()->setBounds(bounds);
     skiaDL.mVectorDrawables.push_back(&vectorDrawable);
 
     // ensure that the functor and vectorDrawable are properly synced
-    skiaDL.syncContents();
+    TestUtils::runOnRenderThread([&](auto&) {
+        skiaDL.syncContents(WebViewSyncData{
+                .applyForceDark = false,
+        });
+    });
 
-    ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeSync);
-    ASSERT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds);
+    EXPECT_EQ(functor.getLastMode(), DrawGlInfo::kModeSync);
+    EXPECT_EQ(counts.sync, 1);
+    EXPECT_EQ(counts.destroyed, 0);
+    EXPECT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds);
+
+    skiaDL.reset();
+    TestUtils::runOnRenderThread([](auto&) {
+        // Fence
+    });
+    EXPECT_EQ(counts.destroyed, 1);
 }
 
 class ContextFactory : public IContextFactory {
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index d16b8be..3c06dab 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -24,10 +24,10 @@
 #include "DamageAccumulator.h"
 #include "IContextFactory.h"
 #include "SkiaCanvas.h"
-#include "pipeline/skia/SkiaUtils.h"
 #include "pipeline/skia/SkiaDisplayList.h"
 #include "pipeline/skia/SkiaOpenGLPipeline.h"
 #include "pipeline/skia/SkiaRecordingCanvas.h"
+#include "pipeline/skia/SkiaUtils.h"
 #include "renderthread/CanvasContext.h"
 #include "tests/common/TestUtils.h"
 
@@ -51,8 +51,7 @@
     auto surface = SkSurface::MakeRasterN32Premul(1, 1);
     surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
-                          surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
 }
 
@@ -84,8 +83,7 @@
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
 
     // drawFrame will crash if "SkiaPipeline::onPrepareTree" did not clean invalid VD pointer
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
-                          surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
 }
 
@@ -106,12 +104,10 @@
     auto surface = SkSurface::MakeRasterN32Premul(2, 2);
     surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds,
-                          surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds,
-                          surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned int)SK_ColorTRANSPARENT);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
 }
@@ -130,8 +126,7 @@
     auto surface = SkSurface::MakeRasterN32Premul(2, 2);
     surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds,
-                          surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
     ASSERT_EQ(TestUtils::getColor(surface, 1, 0), SK_ColorBLUE);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorRED);
@@ -203,38 +198,32 @@
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
 
     // Single draw, should be white.
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
-                          surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
 
     // 1 Overdraw, should be blue blended onto white.
     renderNodes.push_back(whiteNode);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
-                          surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0d0ff);
 
     // 2 Overdraw, should be green blended onto white
     renderNodes.push_back(whiteNode);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
-                          surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0ffd0);
 
     // 3 Overdraw, should be pink blended onto white.
     renderNodes.push_back(whiteNode);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
-                          surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffffc0c0);
 
     // 4 Overdraw, should be red blended onto white.
     renderNodes.push_back(whiteNode);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
-                          surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080);
 
     // 5 Overdraw, should be red blended onto white.
     renderNodes.push_back(whiteNode);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
-                          surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080);
 }
 
@@ -389,7 +378,6 @@
     EXPECT_FALSE(pipeline->isSurfaceReady());
     EXPECT_TRUE(pipeline->setSurface((Surface*)0x01, SwapBehavior::kSwap_default, ColorMode::SRGB));
     EXPECT_TRUE(pipeline->isSurfaceReady());
-    renderThread.destroyGlContext();
+    renderThread.destroyRenderingContext();
     EXPECT_FALSE(pipeline->isSurfaceReady());
 }
-
diff --git a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
index 479c462..635429d 100644
--- a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
+++ b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
@@ -44,7 +44,7 @@
     static const int CANVAS_HEIGHT = 100;
     class PropertyTestCanvas : public TestCanvasBase {
     public:
-        PropertyTestCanvas(std::function<void(const SkCanvas&)> callback)
+        explicit PropertyTestCanvas(std::function<void(const SkCanvas&)> callback)
                 : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT), mCallback(callback) {}
         void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
             EXPECT_EQ(mDrawCounter++, 0);
diff --git a/libs/hwui/tests/unit/ThreadBaseTests.cpp b/libs/hwui/tests/unit/ThreadBaseTests.cpp
index 1168ff2..817c1f3 100644
--- a/libs/hwui/tests/unit/ThreadBaseTests.cpp
+++ b/libs/hwui/tests/unit/ThreadBaseTests.cpp
@@ -95,7 +95,7 @@
     };
 
     struct Counter {
-        Counter(EventCount* count) : mCount(count) { mCount->construct++; }
+        explicit Counter(EventCount* count) : mCount(count) { mCount->construct++; }
 
         Counter(const Counter& other) : mCount(other.mCount) {
             if (mCount) mCount->copy++;
@@ -148,4 +148,4 @@
     ASSERT_EQ(1, dummyObject->getStrongCount());
     ASSERT_EQ(2, lifecycleTestHelper(dummyObject));
     ASSERT_EQ(1, dummyObject->getStrongCount());
-}
\ No newline at end of file
+}
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index b645aeb..1a09b1c 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -54,9 +54,9 @@
     sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
     sk_sp<SkTypeface> typeface(fm->makeFromStream(std::move(fontData)));
     LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName);
-    std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
-            std::move(typeface), data, st.st_size, fileName, 0,
-            std::vector<minikin::FontVariation>());
+    std::shared_ptr<minikin::MinikinFont> font =
+            std::make_shared<MinikinFontSkia>(std::move(typeface), data, st.st_size, fileName, 0,
+                                              std::vector<minikin::FontVariation>());
     std::vector<minikin::Font> fonts;
     fonts.push_back(minikin::Font::Builder(font).build());
     return std::make_shared<minikin::FontFamily>(std::move(fonts));
diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp
index ee6beba..5db0028 100644
--- a/libs/hwui/tests/unit/VectorDrawableTests.cpp
+++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp
@@ -85,8 +85,10 @@
              outPath->rCubicTo(8.0, 8.0, 8.0, 8.0, 8.0, 8.0);
              outPath->cubicTo(16.0, 16.0, 9.0, 9.0, 9.0, 9.0);
              outPath->rCubicTo(0.0, 0.0, 9.0, 9.0, 9.0, 9.0);
-             outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 10.0, 10.0);
-             outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 20.0, 20.0);
+             outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 10.0,
+                            10.0);
+             outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 20.0,
+                            20.0);
          }},
 
         // Check box VectorDrawable path data
@@ -157,7 +159,8 @@
          },
          [](SkPath* outPath) {
              outPath->moveTo(300.0, 70.0);
-             outPath->arcTo(230.0, 230.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCCW_Direction, 301.0, 70.0);
+             outPath->arcTo(230.0, 230.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCCW_Direction,
+                            301.0, 70.0);
              outPath->close();
              outPath->moveTo(300.0, 70.0);
          }},
@@ -236,14 +239,14 @@
 };
 
 const StringPath sStringPaths[] = {
-        {"3e...3", false},     // Not starting with a verb and ill-formatted float
-        {"L.M.F.A.O", false},  // No floats following verbs
-        {"m 1 1", true},       // Valid path data
-        {"\n \t   z", true},   // Valid path data with leading spaces
-        {"1-2e34567", false},  // Not starting with a verb and ill-formatted float
-        {"f 4 5", false},      // Invalid verb
-        {"\r      ", false},   // Empty string
-        {"L1,0 L1,1 L0,1 z M1000", false}    // Not enough floats following verb M.
+        {"3e...3", false},                 // Not starting with a verb and ill-formatted float
+        {"L.M.F.A.O", false},              // No floats following verbs
+        {"m 1 1", true},                   // Valid path data
+        {"\n \t   z", true},               // Valid path data with leading spaces
+        {"1-2e34567", false},              // Not starting with a verb and ill-formatted float
+        {"f 4 5", false},                  // Invalid verb
+        {"\r      ", false},               // Empty string
+        {"L1,0 L1,1 L0,1 z M1000", false}  // Not enough floats following verb M.
 };
 
 static bool hasSameVerbs(const PathData& from, const PathData& to) {
@@ -251,7 +254,7 @@
 }
 
 TEST(PathParser, parseStringForData) {
-    for (TestData testData : sTestDataSet) {
+    for (const TestData& testData : sTestDataSet) {
         PathParser::ParseResult result;
         // Test generated path data against the given data.
         PathData pathData;
@@ -271,7 +274,7 @@
 }
 
 TEST(VectorDrawableUtils, createSkPathFromPathData) {
-    for (TestData testData : sTestDataSet) {
+    for (const TestData& testData : sTestDataSet) {
         SkPath expectedPath;
         testData.skPathLamda(&expectedPath);
         SkPath actualPath;
@@ -281,7 +284,7 @@
 }
 
 TEST(PathParser, parseAsciiStringForSkPath) {
-    for (TestData testData : sTestDataSet) {
+    for (const TestData& testData : sTestDataSet) {
         PathParser::ParseResult result;
         size_t length = strlen(testData.pathString);
         // Check the return value as well as the SkPath generated.
@@ -304,8 +307,8 @@
 }
 
 TEST(VectorDrawableUtils, morphPathData) {
-    for (TestData fromData : sTestDataSet) {
-        for (TestData toData : sTestDataSet) {
+    for (const TestData& fromData : sTestDataSet) {
+        for (const TestData& toData : sTestDataSet) {
             bool canMorph = VectorDrawableUtils::canMorph(fromData.pathData, toData.pathData);
             if (fromData.pathData == toData.pathData) {
                 EXPECT_TRUE(canMorph);
@@ -319,8 +322,8 @@
 
 TEST(VectorDrawableUtils, interpolatePathData) {
     // Interpolate path data with itself and every other path data
-    for (TestData fromData : sTestDataSet) {
-        for (TestData toData : sTestDataSet) {
+    for (const TestData& fromData : sTestDataSet) {
+        for (const TestData& toData : sTestDataSet) {
             PathData outData;
             bool success = VectorDrawableUtils::interpolatePathData(&outData, fromData.pathData,
                                                                     toData.pathData, 0.5);
@@ -331,7 +334,7 @@
 
     float fractions[] = {0, 0.00001, 0.28, 0.5, 0.7777, 0.9999999, 1};
     // Now try to interpolate with a slightly modified version of self and expect success
-    for (TestData fromData : sTestDataSet) {
+    for (const TestData& fromData : sTestDataSet) {
         PathData toPathData = fromData.pathData;
         for (size_t i = 0; i < toPathData.points.size(); i++) {
             toPathData.points[i]++;
diff --git a/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
new file mode 100644
index 0000000..e1fb8b7
--- /dev/null
+++ b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "WebViewFunctorManager.h"
+#include "private/hwui/WebViewFunctor.h"
+#include "renderthread/RenderProxy.h"
+#include "tests/common/TestUtils.h"
+
+#include <unordered_map>
+
+using namespace android;
+using namespace android::uirenderer;
+
+TEST(WebViewFunctor, createDestroyGLES) {
+    int functor = WebViewFunctor_create(
+            nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES);
+    ASSERT_NE(-1, functor);
+    WebViewFunctor_release(functor);
+    TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
+        // Empty, don't care
+    });
+    auto& counts = TestUtils::countsForFunctor(functor);
+    // We never initialized, so contextDestroyed == 0
+    EXPECT_EQ(0, counts.contextDestroyed);
+    EXPECT_EQ(1, counts.destroyed);
+}
+
+TEST(WebViewFunctor, createSyncHandleGLES) {
+    int functor = WebViewFunctor_create(
+            nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES);
+    ASSERT_NE(-1, functor);
+    auto handle = WebViewFunctorManager::instance().handleFor(functor);
+    ASSERT_TRUE(handle);
+    WebViewFunctor_release(functor);
+    EXPECT_FALSE(WebViewFunctorManager::instance().handleFor(functor));
+    TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
+        // fence
+    });
+    auto& counts = TestUtils::countsForFunctor(functor);
+    EXPECT_EQ(0, counts.sync);
+    EXPECT_EQ(0, counts.contextDestroyed);
+    EXPECT_EQ(0, counts.destroyed);
+
+    TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
+        WebViewSyncData syncData;
+        handle->sync(syncData);
+    });
+
+    EXPECT_EQ(1, counts.sync);
+
+    TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
+        WebViewSyncData syncData;
+        handle->sync(syncData);
+    });
+
+    EXPECT_EQ(2, counts.sync);
+
+    handle.clear();
+
+    TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
+        // fence
+    });
+
+    EXPECT_EQ(2, counts.sync);
+    EXPECT_EQ(0, counts.contextDestroyed);
+    EXPECT_EQ(1, counts.destroyed);
+}
+
+TEST(WebViewFunctor, createSyncDrawGLES) {
+    int functor = WebViewFunctor_create(
+            nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES);
+    ASSERT_NE(-1, functor);
+    auto handle = WebViewFunctorManager::instance().handleFor(functor);
+    ASSERT_TRUE(handle);
+    WebViewFunctor_release(functor);
+    auto& counts = TestUtils::countsForFunctor(functor);
+    for (int i = 0; i < 5; i++) {
+        TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
+            WebViewSyncData syncData;
+            handle->sync(syncData);
+            DrawGlInfo drawInfo;
+            handle->drawGl(drawInfo);
+            handle->drawGl(drawInfo);
+        });
+    }
+    handle.clear();
+    TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
+        // fence
+    });
+    EXPECT_EQ(5, counts.sync);
+    EXPECT_EQ(10, counts.glesDraw);
+    EXPECT_EQ(1, counts.contextDestroyed);
+    EXPECT_EQ(1, counts.destroyed);
+}
+
+TEST(WebViewFunctor, contextDestroyed) {
+    int functor = WebViewFunctor_create(
+            nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES);
+    ASSERT_NE(-1, functor);
+    auto handle = WebViewFunctorManager::instance().handleFor(functor);
+    ASSERT_TRUE(handle);
+    WebViewFunctor_release(functor);
+    auto& counts = TestUtils::countsForFunctor(functor);
+    TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
+        WebViewSyncData syncData;
+        handle->sync(syncData);
+        DrawGlInfo drawInfo;
+        handle->drawGl(drawInfo);
+    });
+    EXPECT_EQ(1, counts.sync);
+    EXPECT_EQ(1, counts.glesDraw);
+    EXPECT_EQ(0, counts.contextDestroyed);
+    EXPECT_EQ(0, counts.destroyed);
+    TestUtils::runOnRenderThreadUnmanaged([](auto& rt) {
+        rt.destroyRenderingContext();
+    });
+    EXPECT_EQ(1, counts.sync);
+    EXPECT_EQ(1, counts.glesDraw);
+    EXPECT_EQ(1, counts.contextDestroyed);
+    EXPECT_EQ(0, counts.destroyed);
+    TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
+        WebViewSyncData syncData;
+        handle->sync(syncData);
+        DrawGlInfo drawInfo;
+        handle->drawGl(drawInfo);
+    });
+    EXPECT_EQ(2, counts.sync);
+    EXPECT_EQ(2, counts.glesDraw);
+    EXPECT_EQ(1, counts.contextDestroyed);
+    EXPECT_EQ(0, counts.destroyed);
+    handle.clear();
+    TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
+        // fence
+    });
+    EXPECT_EQ(2, counts.sync);
+    EXPECT_EQ(2, counts.glesDraw);
+    EXPECT_EQ(2, counts.contextDestroyed);
+    EXPECT_EQ(1, counts.destroyed);
+}
diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp
index aecceb3..63d1540 100644
--- a/libs/hwui/tests/unit/main.cpp
+++ b/libs/hwui/tests/unit/main.cpp
@@ -17,14 +17,14 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
+#include "Properties.h"
 #include "debug/GlesDriver.h"
 #include "debug/NullGlesDriver.h"
 #include "hwui/Typeface.h"
-#include "Properties.h"
 #include "tests/common/LeakChecker.h"
-#include "thread/TaskProcessor.h"
 #include "thread/Task.h"
 #include "thread/TaskManager.h"
+#include "thread/TaskProcessor.h"
 
 #include <signal.h>
 
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index 4daccda..4473ce6 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -17,6 +17,7 @@
 #define COLOR_H
 
 #include <math.h>
+#include <cutils/compiler.h>
 #include <system/graphics.h>
 #include <ui/PixelFormat.h>
 
@@ -117,7 +118,7 @@
 
 android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType);
 
-sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
+ANDROID_API sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
 
 struct Lab {
     float L;
diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include/android/os/IncidentReportArgs.h
index c56f689..ee1e33c 100644
--- a/libs/incident/include/android/os/IncidentReportArgs.h
+++ b/libs/incident/include/android/os/IncidentReportArgs.h
@@ -40,7 +40,7 @@
 class IncidentReportArgs : public Parcelable {
 public:
     IncidentReportArgs();
-    explicit IncidentReportArgs(const IncidentReportArgs& that);
+    IncidentReportArgs(const IncidentReportArgs& that);
     virtual ~IncidentReportArgs();
 
     virtual status_t writeToParcel(Parcel* out) const;
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 80d8e72..0a90f85 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -89,6 +89,10 @@
 
     mLocked.animationPending = false;
 
+    mLocked.displayWidth = -1;
+    mLocked.displayHeight = -1;
+    mLocked.displayOrientation = DISPLAY_ORIENTATION_0;
+
     mLocked.presentation = PRESENTATION_POINTER;
     mLocked.presentationChanged = false;
 
@@ -106,6 +110,15 @@
     mLocked.lastFrameUpdatedTime = 0;
 
     mLocked.buttonState = 0;
+
+    mPolicy->loadPointerIcon(&mLocked.pointerIcon);
+
+    loadResources();
+
+    if (mLocked.pointerIcon.isValid()) {
+        mLocked.pointerIconChanged = true;
+        updatePointerLocked();
+    }
 }
 
 PointerController::~PointerController() {
@@ -131,15 +144,23 @@
 
 bool PointerController::getBoundsLocked(float* outMinX, float* outMinY,
         float* outMaxX, float* outMaxY) const {
-
-    if (!mLocked.viewport.isValid()) {
+    if (mLocked.displayWidth <= 0 || mLocked.displayHeight <= 0) {
         return false;
     }
 
-    *outMinX = mLocked.viewport.logicalLeft;
-    *outMinY = mLocked.viewport.logicalTop;
-    *outMaxX = mLocked.viewport.logicalRight - 1;
-    *outMaxY = mLocked.viewport.logicalBottom - 1;
+    *outMinX = 0;
+    *outMinY = 0;
+    switch (mLocked.displayOrientation) {
+    case DISPLAY_ORIENTATION_90:
+    case DISPLAY_ORIENTATION_270:
+        *outMaxX = mLocked.displayHeight - 1;
+        *outMaxY = mLocked.displayWidth - 1;
+        break;
+    default:
+        *outMaxX = mLocked.displayWidth - 1;
+        *outMaxY = mLocked.displayHeight - 1;
+        break;
+    }
     return true;
 }
 
@@ -210,12 +231,6 @@
     *outY = mLocked.pointerY;
 }
 
-int32_t PointerController::getDisplayId() const {
-    AutoMutex _l(mLock);
-
-    return mLocked.viewport.displayId;
-}
-
 void PointerController::fade(Transition transition) {
     AutoMutex _l(mLock);
 
@@ -340,57 +355,48 @@
 void PointerController::reloadPointerResources() {
     AutoMutex _l(mLock);
 
-    loadResourcesLocked();
+    loadResources();
+
+    if (mLocked.presentation == PRESENTATION_POINTER) {
+        mLocked.additionalMouseResources.clear();
+        mLocked.animationResources.clear();
+        mPolicy->loadPointerIcon(&mLocked.pointerIcon);
+        mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
+                                              &mLocked.animationResources);
+    }
+
+    mLocked.presentationChanged = true;
     updatePointerLocked();
 }
 
-/**
- * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation,
- * so here we are getting the dimensions in the original, unrotated orientation (orientation 0).
- */
-static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) {
-    if (viewport.orientation == DISPLAY_ORIENTATION_90
-            || viewport.orientation == DISPLAY_ORIENTATION_270) {
-        width = viewport.deviceHeight;
-        height = viewport.deviceWidth;
-    } else {
-        width = viewport.deviceWidth;
-        height = viewport.deviceHeight;
-    }
-}
-
-void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
+void PointerController::setDisplayViewport(int32_t width, int32_t height, int32_t orientation) {
     AutoMutex _l(mLock);
-    if (viewport == mLocked.viewport) {
-        return;
+
+    // Adjust to use the display's unrotated coordinate frame.
+    if (orientation == DISPLAY_ORIENTATION_90
+            || orientation == DISPLAY_ORIENTATION_270) {
+        int32_t temp = height;
+        height = width;
+        width = temp;
     }
 
-    const DisplayViewport oldViewport = mLocked.viewport;
-    mLocked.viewport = viewport;
-
-    int32_t oldDisplayWidth, oldDisplayHeight;
-    getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight);
-    int32_t newDisplayWidth, newDisplayHeight;
-    getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight);
-
-    // Reset cursor position to center if size or display changed.
-    if (oldViewport.displayId != viewport.displayId
-            || oldDisplayWidth != newDisplayWidth
-            || oldDisplayHeight != newDisplayHeight) {
+    if (mLocked.displayWidth != width || mLocked.displayHeight != height) {
+        mLocked.displayWidth = width;
+        mLocked.displayHeight = height;
 
         float minX, minY, maxX, maxY;
         if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
             mLocked.pointerX = (minX + maxX) * 0.5f;
             mLocked.pointerY = (minY + maxY) * 0.5f;
-            // Reload icon resources for density may be changed.
-            loadResourcesLocked();
         } else {
             mLocked.pointerX = 0;
             mLocked.pointerY = 0;
         }
 
         fadeOutAndReleaseAllSpotsLocked();
-    } else if (oldViewport.orientation != viewport.orientation) {
+    }
+
+    if (mLocked.displayOrientation != orientation) {
         // Apply offsets to convert from the pixel top-left corner position to the pixel center.
         // This creates an invariant frame of reference that we can easily rotate when
         // taking into account that the pointer may be located at fractional pixel offsets.
@@ -399,37 +405,37 @@
         float temp;
 
         // Undo the previous rotation.
-        switch (oldViewport.orientation) {
+        switch (mLocked.displayOrientation) {
         case DISPLAY_ORIENTATION_90:
             temp = x;
-            x =  oldViewport.deviceHeight - y;
+            x = mLocked.displayWidth - y;
             y = temp;
             break;
         case DISPLAY_ORIENTATION_180:
-            x = oldViewport.deviceWidth - x;
-            y = oldViewport.deviceHeight - y;
+            x = mLocked.displayWidth - x;
+            y = mLocked.displayHeight - y;
             break;
         case DISPLAY_ORIENTATION_270:
             temp = x;
             x = y;
-            y = oldViewport.deviceWidth - temp;
+            y = mLocked.displayHeight - temp;
             break;
         }
 
         // Perform the new rotation.
-        switch (viewport.orientation) {
+        switch (orientation) {
         case DISPLAY_ORIENTATION_90:
             temp = x;
             x = y;
-            y = viewport.deviceHeight - temp;
+            y = mLocked.displayWidth - temp;
             break;
         case DISPLAY_ORIENTATION_180:
-            x = viewport.deviceWidth - x;
-            y = viewport.deviceHeight - y;
+            x = mLocked.displayWidth - x;
+            y = mLocked.displayHeight - y;
             break;
         case DISPLAY_ORIENTATION_270:
             temp = x;
-            x = viewport.deviceWidth - y;
+            x = mLocked.displayHeight - y;
             y = temp;
             break;
         }
@@ -438,6 +444,7 @@
         // and save the results.
         mLocked.pointerX = x - 0.5f;
         mLocked.pointerY = y - 0.5f;
+        mLocked.displayOrientation = orientation;
     }
 
     updatePointerLocked();
@@ -607,16 +614,11 @@
     mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
 }
 
-void PointerController::updatePointerLocked() REQUIRES(mLock) {
-    if (!mLocked.viewport.isValid()) {
-        return;
-    }
-
+void PointerController::updatePointerLocked() {
     mSpriteController->openTransaction();
 
     mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
     mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
-    mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
 
     if (mLocked.pointerAlpha > 0) {
         mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
@@ -727,18 +729,8 @@
     }
 }
 
-void PointerController::loadResourcesLocked() REQUIRES(mLock) {
+void PointerController::loadResources() {
     mPolicy->loadPointerResources(&mResources);
-
-    if (mLocked.presentation == PRESENTATION_POINTER) {
-        mLocked.additionalMouseResources.clear();
-        mLocked.animationResources.clear();
-        mPolicy->loadPointerIcon(&mLocked.pointerIcon);
-        mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
-                                              &mLocked.animationResources);
-    }
-
-    mLocked.pointerIconChanged = true;
 }
 
 
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index a32cc42..7f4e5a5 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -23,7 +23,6 @@
 #include <vector>
 
 #include <ui/DisplayInfo.h>
-#include <input/DisplayViewport.h>
 #include <input/Input.h>
 #include <PointerControllerInterface.h>
 #include <utils/BitSet.h>
@@ -97,7 +96,6 @@
     virtual int32_t getButtonState() const;
     virtual void setPosition(float x, float y);
     virtual void getPosition(float* outX, float* outY) const;
-    virtual int32_t getDisplayId() const;
     virtual void fade(Transition transition);
     virtual void unfade(Transition transition);
 
@@ -108,7 +106,7 @@
 
     void updatePointerIcon(int32_t iconId);
     void setCustomPointerIcon(const SpriteIcon& icon);
-    void setDisplayViewport(const DisplayViewport& viewport);
+    void setDisplayViewport(int32_t width, int32_t height, int32_t orientation);
     void setInactivityTimeout(InactivityTimeout inactivityTimeout);
     void reloadPointerResources();
 
@@ -158,7 +156,9 @@
         size_t animationFrameIndex;
         nsecs_t lastFrameUpdatedTime;
 
-        DisplayViewport viewport;
+        int32_t displayWidth;
+        int32_t displayHeight;
+        int32_t displayOrientation;
 
         InactivityTimeout inactivityTimeout;
 
@@ -182,7 +182,7 @@
 
         Vector<Spot*> spots;
         Vector<sp<Sprite> > recycledSprites;
-    } mLocked GUARDED_BY(mLock);
+    } mLocked;
 
     bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
     void setPositionLocked(float x, float y);
@@ -207,7 +207,7 @@
     void fadeOutAndReleaseSpotLocked(Spot* spot);
     void fadeOutAndReleaseAllSpotsLocked();
 
-    void loadResourcesLocked();
+    void loadResources();
 };
 
 } // namespace android
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index c1868d3..eb2bc98 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -144,16 +144,13 @@
         }
     }
 
-    // Resize and/or reparent sprites if needed.
+    // Resize sprites if needed.
     SurfaceComposerClient::Transaction t;
     bool needApplyTransaction = false;
     for (size_t i = 0; i < numSprites; i++) {
         SpriteUpdate& update = updates.editItemAt(i);
-        if (update.state.surfaceControl == nullptr) {
-            continue;
-        }
 
-        if (update.state.wantSurfaceVisible()) {
+        if (update.state.surfaceControl != NULL && update.state.wantSurfaceVisible()) {
             int32_t desiredWidth = update.state.icon.bitmap.width();
             int32_t desiredHeight = update.state.icon.bitmap.height();
             if (update.state.surfaceWidth < desiredWidth
@@ -173,12 +170,6 @@
                 }
             }
         }
-
-        // If surface is a new one, we have to set right layer stack.
-        if (update.surfaceChanged || update.state.dirty & DIRTY_DISPLAY_ID) {
-            t.setLayerStack(update.state.surfaceControl, update.state.displayId);
-            needApplyTransaction = true;
-        }
     }
     if (needApplyTransaction) {
         t.apply();
@@ -245,7 +236,7 @@
         if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
                 || (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
                         | DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
-                        | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID))))) {
+                        | DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) {
             needApplyTransaction = true;
 
             if (wantSurfaceVisibleAndDrawn
@@ -454,15 +445,6 @@
     }
 }
 
-void SpriteController::SpriteImpl::setDisplayId(int32_t displayId) {
-    AutoMutex _l(mController->mLock);
-
-    if (mLocked.state.displayId != displayId) {
-        mLocked.state.displayId = displayId;
-        invalidateLocked(DIRTY_DISPLAY_ID);
-    }
-}
-
 void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) {
     bool wasDirty = mLocked.state.dirty;
     mLocked.state.dirty |= dirty;
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 5b216f5..31e43e9 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -125,9 +125,6 @@
 
     /* Sets the sprite transformation matrix. */
     virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix) = 0;
-
-    /* Sets the id of the display where the sprite should be shown. */
-    virtual void setDisplayId(int32_t displayId) = 0;
 };
 
 /*
@@ -173,7 +170,6 @@
         DIRTY_LAYER = 1 << 4,
         DIRTY_VISIBILITY = 1 << 5,
         DIRTY_HOTSPOT = 1 << 6,
-        DIRTY_DISPLAY_ID = 1 << 7,
     };
 
     /* Describes the state of a sprite.
@@ -184,7 +180,7 @@
     struct SpriteState {
         inline SpriteState() :
                 dirty(0), visible(false),
-                positionX(0), positionY(0), layer(0), alpha(1.0f), displayId(ADISPLAY_ID_DEFAULT),
+                positionX(0), positionY(0), layer(0), alpha(1.0f),
                 surfaceWidth(0), surfaceHeight(0), surfaceDrawn(false), surfaceVisible(false) {
         }
 
@@ -197,7 +193,6 @@
         int32_t layer;
         float alpha;
         SpriteTransformationMatrix transformationMatrix;
-        int32_t displayId;
 
         sp<SurfaceControl> surfaceControl;
         int32_t surfaceWidth;
@@ -230,7 +225,6 @@
         virtual void setLayer(int32_t layer);
         virtual void setAlpha(float alpha);
         virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix);
-        virtual void setDisplayId(int32_t displayId);
 
         inline const SpriteState& getStateLocked() const {
             return mLocked.state;
diff --git a/location/java/android/location/GnssMeasurementCallbackTransport.java b/location/java/android/location/GnssMeasurementCallbackTransport.java
index 21f6306..1188b13b 100644
--- a/location/java/android/location/GnssMeasurementCallbackTransport.java
+++ b/location/java/android/location/GnssMeasurementCallbackTransport.java
@@ -19,6 +19,8 @@
 import android.content.Context;
 import android.os.RemoteException;
 
+import com.android.internal.util.Preconditions;
+
 /**
  * A handler class to manage transport callbacks for {@link GnssMeasurementsEvent.Callback}.
  *
@@ -26,12 +28,13 @@
  */
 class GnssMeasurementCallbackTransport
         extends LocalListenerHelper<GnssMeasurementsEvent.Callback> {
+    private static final String TAG = "GnssMeasCbTransport";
     private final ILocationManager mLocationManager;
 
     private final IGnssMeasurementsListener mListenerTransport = new ListenerTransport();
 
     public GnssMeasurementCallbackTransport(Context context, ILocationManager locationManager) {
-        super(context, "GnssMeasurementListenerTransport");
+        super(context, TAG);
         mLocationManager = locationManager;
     }
 
@@ -47,17 +50,34 @@
         mLocationManager.removeGnssMeasurementsListener(mListenerTransport);
     }
 
+    /**
+     * Injects GNSS measurement corrections into the GNSS chipset.
+     *
+     * @param measurementCorrections a {@link GnssMeasurementCorrections} object with the GNSS
+     *     measurement corrections to be injected into the GNSS chipset.
+     */
+    protected void injectGnssMeasurementCorrections(
+            GnssMeasurementCorrections measurementCorrections) throws RemoteException {
+        Preconditions.checkNotNull(measurementCorrections);
+        mLocationManager.injectGnssMeasurementCorrections(
+                measurementCorrections, getContext().getPackageName());
+    }
+
+    protected int getGnssCapabilities() throws RemoteException {
+        return mLocationManager.getGnssCapabilities(getContext().getPackageName());
+    }
+
     private class ListenerTransport extends IGnssMeasurementsListener.Stub {
         @Override
         public void onGnssMeasurementsReceived(final GnssMeasurementsEvent event) {
             ListenerOperation<GnssMeasurementsEvent.Callback> operation =
                     new ListenerOperation<GnssMeasurementsEvent.Callback>() {
-                @Override
-                public void execute(GnssMeasurementsEvent.Callback callback)
-                        throws RemoteException {
-                    callback.onGnssMeasurementsReceived(event);
-                }
-            };
+                        @Override
+                        public void execute(GnssMeasurementsEvent.Callback callback)
+                                throws RemoteException {
+                            callback.onGnssMeasurementsReceived(event);
+                        }
+                    };
             foreach(operation);
         }
 
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/location/java/android/location/GnssMeasurementCorrections.aidl
similarity index 74%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to location/java/android/location/GnssMeasurementCorrections.aidl
index b7a7920..b05eb54 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/location/java/android/location/GnssMeasurementCorrections.aidl
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ * Copyright (C) 208 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
+ *      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,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.location;
 
-parcelable UsbPort;
+parcelable GnssMeasurementCorrections;
diff --git a/location/java/android/location/GnssMeasurementCorrections.java b/location/java/android/location/GnssMeasurementCorrections.java
new file mode 100644
index 0000000..b81bf90
--- /dev/null
+++ b/location/java/android/location/GnssMeasurementCorrections.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class representing a GNSS measurement corrections for all used GNSS satellites at the location
+ * and time specified
+ *
+ * @hide
+ */
+@SystemApi
+public final class GnssMeasurementCorrections implements Parcelable {
+
+    /** Represents latitude in degrees at which the corrections are computed. */
+    private double mLatitudeDegrees;
+    /** Represents longitude in degrees at which the corrections are computed. */
+    private double mLongitudeDegrees;
+    /**
+     * Represents altitude in meters above the WGS 84 reference ellipsoid at which the corrections
+     * are computed.
+     */
+    private double mAltitudeMeters;
+
+    /** Time Of Applicability, GPS time of week */
+    private long mToaGpsNanosecondsOfWeek;
+
+    /**
+     * A set of {@link GnssSingleSatCorrection} each containing measurement corrections for a
+     * satellite in view
+     */
+    private @Nullable List<GnssSingleSatCorrection> mSingleSatCorrectionList;
+
+    private GnssMeasurementCorrections(Builder builder) {
+        mLatitudeDegrees = builder.mLatitudeDegrees;
+        mLongitudeDegrees = builder.mLongitudeDegrees;
+        mAltitudeMeters = builder.mAltitudeMeters;
+        mToaGpsNanosecondsOfWeek = builder.mToaGpsNanosecondsOfWeek;
+        mSingleSatCorrectionList =
+                builder.mSingleSatCorrectionList == null
+                        ? null
+                        : Collections.unmodifiableList(
+                                new ArrayList<>(builder.mSingleSatCorrectionList));
+    }
+
+    /** Gets the latitude in degrees at which the corrections are computed. */
+    public double getLatitudeDegrees() {
+        return mLatitudeDegrees;
+    }
+
+    /** Gets the longitude in degrees at which the corrections are computed. */
+    public double getLongitudeDegrees() {
+        return mLongitudeDegrees;
+    }
+
+    /**
+     * Gets the altitude in meters above the WGS 84 reference ellipsoid at which the corrections are
+     * computed.
+     */
+    public double getAltitudeMeters() {
+        return mAltitudeMeters;
+    }
+
+    /** Gets the time of applicability, GPS time of week in nanoseconds. */
+    public long getToaGpsNanosecondsOfWeek() {
+        return mToaGpsNanosecondsOfWeek;
+    }
+
+    /**
+     * Gets a set of {@link GnssSingleSatCorrection} each containing measurement corrections for a
+     * satellite in view
+     */
+    public @Nullable List<GnssSingleSatCorrection> getSingleSatCorrectionList() {
+        return mSingleSatCorrectionList;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<GnssMeasurementCorrections> CREATOR =
+            new Creator<GnssMeasurementCorrections>() {
+                @Override
+                public GnssMeasurementCorrections createFromParcel(Parcel parcel) {
+                    GnssMeasurementCorrections.Builder gnssMeasurementCorrectons =
+                            new Builder()
+                                    .setLatitudeDegrees(parcel.readDouble())
+                                    .setLongitudeDegrees(parcel.readDouble())
+                                    .setAltitudeMeters(parcel.readDouble())
+                                    .setToaGpsNanosecondsOfWeek(parcel.readLong());
+                    List<GnssSingleSatCorrection> singleSatCorrectionList = new ArrayList<>();
+                    parcel.readTypedList(singleSatCorrectionList, GnssSingleSatCorrection.CREATOR);
+                    gnssMeasurementCorrectons.setSingleSatCorrectionList(
+                            singleSatCorrectionList.isEmpty() ? null : singleSatCorrectionList);
+                    return gnssMeasurementCorrectons.build();
+                }
+
+                @Override
+                public GnssMeasurementCorrections[] newArray(int i) {
+                    return new GnssMeasurementCorrections[i];
+                }
+            };
+
+    @Override
+    public String toString() {
+        final String format = "   %-29s = %s\n";
+        StringBuilder builder = new StringBuilder("GnssMeasurementCorrections:\n");
+        builder.append(String.format(format, "LatitudeDegrees = ", mLatitudeDegrees));
+        builder.append(String.format(format, "LongitudeDegrees = ", mLongitudeDegrees));
+        builder.append(String.format(format, "AltitudeMeters = ", mAltitudeMeters));
+        builder.append(
+                String.format(format, "ToaGpsNanosecondsOfWeek = ", mToaGpsNanosecondsOfWeek));
+        builder.append(
+                String.format(format, "mSingleSatCorrectionList = ", mSingleSatCorrectionList));
+        return builder.toString();
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeDouble(mLatitudeDegrees);
+        parcel.writeDouble(mLongitudeDegrees);
+        parcel.writeDouble(mAltitudeMeters);
+        parcel.writeLong(mToaGpsNanosecondsOfWeek);
+        parcel.writeTypedList(mSingleSatCorrectionList);
+    }
+
+    /** Builder for {@link GnssMeasurementCorrections} */
+    public static class Builder {
+        /**
+         * For documentation of below fields, see corresponding fields in {@link
+         * GnssMeasurementCorrections}.
+         */
+        private double mLatitudeDegrees;
+
+        private double mLongitudeDegrees;
+        private double mAltitudeMeters;
+        private long mToaGpsNanosecondsOfWeek;
+        private List<GnssSingleSatCorrection> mSingleSatCorrectionList;
+
+        /** Sets the latitude in degrees at which the corrections are computed. */
+        public Builder setLatitudeDegrees(double latitudeDegrees) {
+            mLatitudeDegrees = latitudeDegrees;
+            return this;
+        }
+
+        /** Sets the longitude in degrees at which the corrections are computed. */
+        public Builder setLongitudeDegrees(double longitudeDegrees) {
+            mLongitudeDegrees = longitudeDegrees;
+            return this;
+        }
+
+        /**
+         * Sets the altitude in meters above the WGS 84 reference ellipsoid at which the corrections
+         * are computed.
+         */
+        public Builder setAltitudeMeters(double altitudeMeters) {
+            mAltitudeMeters = altitudeMeters;
+            return this;
+        }
+
+        /** Sets the time of applicability, GPS time of week in nanoseconds. */
+        public Builder setToaGpsNanosecondsOfWeek(long toaGpsNanosecondsOfWeek) {
+            mToaGpsNanosecondsOfWeek = toaGpsNanosecondsOfWeek;
+            return this;
+        }
+
+        /**
+         * Sets a the list of {@link GnssSingleSatCorrection} containing measurement corrections for
+         * a satellite in view
+         */
+        public Builder setSingleSatCorrectionList(
+                @Nullable List<GnssSingleSatCorrection> singleSatCorrectionList) {
+            if (singleSatCorrectionList == null) {
+                mSingleSatCorrectionList = null;
+            } else {
+                mSingleSatCorrectionList =
+                        Collections.unmodifiableList(new ArrayList<>(singleSatCorrectionList));
+            }
+            return this;
+        }
+
+        /** Builds a {@link GnssMeasurementCorrections} instance as specified by this builder. */
+        public GnssMeasurementCorrections build() {
+            return new GnssMeasurementCorrections(this);
+        }
+    }
+}
diff --git a/location/java/android/location/GnssReflectingPlane.java b/location/java/android/location/GnssReflectingPlane.java
new file mode 100644
index 0000000..64b3752
--- /dev/null
+++ b/location/java/android/location/GnssReflectingPlane.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Holds the characteristics of the reflecting plane that a satellite signal has bounced from.
+ *
+ * @hide
+ */
+@SystemApi
+public final class GnssReflectingPlane implements Parcelable {
+
+    /** Represents latitude in degrees of the reflecting plane */
+    private double mLatitudeDegrees;
+    /** Represents longitude in degrees of the reflecting plane. */
+    private double mLongitudeDegrees;
+    /**
+     * Represents altitude in meters above the WGS 84 reference ellipsoid of the reflection point in
+     * the plane
+     */
+    private double mAltitudeMeters;
+
+    /** Represents azimuth clockwise from north of the reflecting plane in degrees. */
+    private double mAzimuthDegrees;
+
+    private GnssReflectingPlane(Builder builder) {
+        mLatitudeDegrees = builder.mLatitudeDegrees;
+        mLongitudeDegrees = builder.mLongitudeDegrees;
+        mAltitudeMeters = builder.mAltitudeMeters;
+        mAzimuthDegrees = builder.mAzimuthDegrees;
+    }
+
+    /** Gets the latitude in degrees of the reflecting plane. */
+    public double getLatitudeDegrees() {
+        return mLatitudeDegrees;
+    }
+
+    /** Gets the longitude in degrees of the reflecting plane. */
+    public double getLongitudeDegrees() {
+        return mLongitudeDegrees;
+    }
+
+    /**
+     * Gets the altitude in meters above the WGS 84 reference ellipsoid of the reflecting point
+     * within the plane
+     */
+    public double getAltitudeMeters() {
+        return mAltitudeMeters;
+    }
+
+    /** Gets the azimuth clockwise from north of the reflecting plane in degrees. */
+    public double getAzimuthDegrees() {
+        return mAzimuthDegrees;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<GnssReflectingPlane> CREATOR =
+            new Creator<GnssReflectingPlane>() {
+                @Override
+                public GnssReflectingPlane createFromParcel(Parcel parcel) {
+                    GnssReflectingPlane reflectingPlane =
+                            new Builder()
+                                    .setLatitudeDegrees(parcel.readDouble())
+                                    .setLongitudeDegrees(parcel.readDouble())
+                                    .setAltitudeMeters(parcel.readDouble())
+                                    .setAzimuthDegrees(parcel.readDouble())
+                                    .build();
+                    return reflectingPlane;
+                }
+
+                @Override
+                public GnssReflectingPlane[] newArray(int i) {
+                    return new GnssReflectingPlane[i];
+                }
+            };
+
+    @Override
+    public String toString() {
+        final String format = "   %-29s = %s\n";
+        StringBuilder builder = new StringBuilder("ReflectingPlane:\n");
+        builder.append(String.format(format, "LatitudeDegrees = ", mLatitudeDegrees));
+        builder.append(String.format(format, "LongitudeDegrees = ", mLongitudeDegrees));
+        builder.append(String.format(format, "AltitudeMeters = ", mAltitudeMeters));
+        builder.append(String.format(format, "AzimuthDegrees = ", mAzimuthDegrees));
+        return builder.toString();
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeDouble(mLatitudeDegrees);
+        parcel.writeDouble(mLongitudeDegrees);
+        parcel.writeDouble(mAltitudeMeters);
+        parcel.writeDouble(mAzimuthDegrees);
+    }
+
+    /** Builder for {@link GnssReflectingPlane} */
+    public static class Builder {
+        /** For documentation, see corresponding fields in {@link GnssReflectingPlane}. */
+        private double mLatitudeDegrees;
+
+        private double mLongitudeDegrees;
+        private double mAltitudeMeters;
+        private double mAzimuthDegrees;
+
+        /** Sets the latitude in degrees of the reflecting plane. */
+        public Builder setLatitudeDegrees(double latitudeDegrees) {
+            mLatitudeDegrees = latitudeDegrees;
+            return this;
+        }
+
+        /** Sets the longitude in degrees of the reflecting plane. */
+        public Builder setLongitudeDegrees(double longitudeDegrees) {
+            mLongitudeDegrees = longitudeDegrees;
+            return this;
+        }
+
+        /**
+         * Sets the altitude in meters above the WGS 84 reference ellipsoid of the reflecting point
+         * within the plane
+         */
+        public Builder setAltitudeMeters(double altitudeMeters) {
+            mAltitudeMeters = altitudeMeters;
+            return this;
+        }
+
+        /** Sets the azimuth clockwise from north of the reflecting plane in degrees. */
+        public Builder setAzimuthDegrees(double azimuthDegrees) {
+            mAzimuthDegrees = azimuthDegrees;
+            return this;
+        }
+
+        /** Builds a {@link GnssReflectingPlane} object as specified by this builder. */
+        public GnssReflectingPlane build() {
+            return new GnssReflectingPlane(this);
+        }
+    }
+}
diff --git a/location/java/android/location/GnssSingleSatCorrection.java b/location/java/android/location/GnssSingleSatCorrection.java
new file mode 100644
index 0000000..6c757f9
--- /dev/null
+++ b/location/java/android/location/GnssSingleSatCorrection.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A container with measurement corrections for a single visible satellite
+ *
+ * @hide
+ */
+@SystemApi
+public final class GnssSingleSatCorrection implements Parcelable {
+
+    /**
+     * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link
+     * #mSatIsLos}.
+     */
+    public static final int HAS_SAT_IS_LOS_MASK = 1 << 0;
+
+    /**
+     * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link
+     * #mExcessPathLengthMeters}.
+     */
+    public static final int HAS_EXCESS_PATH_LENGTH_MASK = 1 << 1;
+
+    /**
+     * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link
+     * #mExcessPathLengthUncertaintyMeters}.
+     */
+    public static final int HAS_EXCESS_PATH_LENGTH_UNC_MASK = 1 << 2;
+
+    /**
+     * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link
+     * #mReflectingPlane}.
+     */
+    public static final int HAS_REFLECTING_PLANE_MASK = 1 << 3;
+
+    /** A bitmask of fields present in this object (see HAS_* constants defined above) */
+    private int mSingleSatCorrectionFlags;
+
+    /** Defines the constellation of the given satellite as defined in {@link GnssStatus}. */
+    private int mConstellationType;
+
+    /**
+     * Satellite vehicle ID number
+     *
+     * <p>Interpretation depends on {@link GnssStatus#getSvid(int)}.
+     */
+    private int mSatId;
+
+    /**
+     * Carrier frequency of the signal to be corrected, for example it can be the GPS center
+     * frequency for L1 = 1,575,420,000 Hz, varying GLO channels, etc.
+     *
+     * <p>For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two correction
+     * objects will be reported for this same satellite, in one of the correction objects, all the
+     * values related to L1 will be filled, and in the other all of the values related to L5 will be
+     * filled.
+     */
+    private float mCarrierFrequencyHz;
+
+    /**
+     * True if the satellite is estimated to be in Line-of-Sight condition at the given location.
+     */
+    private boolean mSatIsLos;
+
+    /**
+     * Excess path length to be subtracted from pseudorange before using it in calculating location.
+     */
+    private float mExcessPathLengthMeters;
+
+    /** Error estimate (1-sigma) for the Excess path length estimate */
+    private float mExcessPathLengthUncertaintyMeters;
+
+    /**
+     * Defines the reflecting plane location and azimuth information
+     *
+     * <p>The flag HAS_REFLECTING_PLANE will be used to set this value to invalid if the satellite
+     * signal goes through multiple reflections or if reflection plane serving is not supported.
+     */
+    private @Nullable GnssReflectingPlane mReflectingPlane;
+
+    private GnssSingleSatCorrection(Builder builder) {
+        mSingleSatCorrectionFlags = builder.mSingleSatCorrectionFlags;
+        mSatId = builder.mSatId;
+        mConstellationType = builder.mConstellationType;
+        mCarrierFrequencyHz = builder.mCarrierFrequencyHz;
+        mSatIsLos = builder.mSatIsLos;
+        mExcessPathLengthMeters = builder.mExcessPathLengthMeters;
+        mExcessPathLengthUncertaintyMeters = builder.mExcessPathLengthUncertaintyMeters;
+        mReflectingPlane = builder.mReflectingPlane;
+    }
+
+    /** Gets a bitmask of fields present in this object */
+    public int getSingleSatCorrectionFlags() {
+        return mSingleSatCorrectionFlags;
+    }
+
+    /**
+     * Gets the constellation type.
+     *
+     * <p>The return value is one of those constants with {@code CONSTELLATION_} prefix in {@link
+     * GnssStatus}.
+     */
+    @GnssStatus.ConstellationType
+    public int getConstellationType() {
+        return mConstellationType;
+    }
+
+    /**
+     * Gets the satellite ID.
+     *
+     * <p>Interpretation depends on {@link #getConstellationType()}. See {@link
+     * GnssStatus#getSvid(int)}.
+     */
+    public int getSatId() {
+        return mSatId;
+    }
+
+    /**
+     * Gets the carrier frequency of the tracked signal.
+     *
+     * <p>For example it can be the GPS central frequency for L1 = 1575.45 MHz, or L2 = 1227.60 MHz,
+     * L5 = 1176.45 MHz, varying GLO channels, etc.
+     *
+     * <p>For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two correction
+     * objects will be reported for this same satellite, in one of the correction objects, all the
+     * values related to L1 will be filled, and in the other all of the values related to L5 will be
+     * filled.
+     *
+     * @return the carrier frequency of the signal tracked in Hz.
+     */
+    public float getCarrierFrequencyHz() {
+        return mCarrierFrequencyHz;
+    }
+
+    /** True if the satellite is line-of-sight */
+    public boolean isSatelliteLineOfSight() {
+        return mSatIsLos;
+    }
+
+    /**
+     * Returns the Excess path length to be subtracted from pseudorange before using it in
+     * calculating location.
+     */
+    public float getExcessPathLengthMeters() {
+        return mExcessPathLengthMeters;
+    }
+
+    /** Returns the error estimate (1-sigma) for the Excess path length estimate */
+    public float getExcessPathLengthUncertaintyMeters() {
+        return mExcessPathLengthUncertaintyMeters;
+    }
+
+    /**
+     * Returns the reflecting plane characteristics at which the signal has bounced
+     *
+     * <p>The flag HAS_REFLECTING_PLANE will be used to set this value to invalid if the satellite
+     * signal goes through multiple reflections or if reflection plane serving is not supported
+     */
+    public @Nullable GnssReflectingPlane getReflectingPlane() {
+        return mReflectingPlane;
+    }
+
+    /** Returns {@code true} if {@link #isSatelliteLineOfSight()} is valid. */
+    public boolean hasSatelliteLineOfSight() {
+        return (mSingleSatCorrectionFlags & HAS_SAT_IS_LOS_MASK) != 0;
+    }
+
+    /** Returns {@code true} if {@link #getExcessPathLengthMeters()} is valid. */
+    public boolean hasExcessPathLength() {
+        return (mSingleSatCorrectionFlags & HAS_EXCESS_PATH_LENGTH_MASK) != 0;
+    }
+
+    /** Returns {@code true} if {@link #getExcessPathLengthUncertaintyMeters()} is valid. */
+    public boolean hasExcessPathLengthUncertainty() {
+        return (mSingleSatCorrectionFlags & HAS_EXCESS_PATH_LENGTH_UNC_MASK) != 0;
+    }
+
+    /** Returns {@code true} if {@link #getReflectingPlane()} is valid. */
+    public boolean hasReflectingPlane() {
+        return (mSingleSatCorrectionFlags & HAS_REFLECTING_PLANE_MASK) != 0;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<GnssSingleSatCorrection> CREATOR =
+            new Creator<GnssSingleSatCorrection>() {
+                @Override
+                public GnssSingleSatCorrection createFromParcel(Parcel parcel) {
+                    GnssSingleSatCorrection singleSatCorrection =
+                            new Builder()
+                                    .setSingleSatCorrectionFlags(parcel.readInt())
+                                    .setConstellationType(parcel.readInt())
+                                    .setSatId(parcel.readInt())
+                                    .setCarrierFrequencyHz(parcel.readFloat())
+                                    .setSatIsLos(parcel.readBoolean())
+                                    .setExcessPathLengthMeters(parcel.readFloat())
+                                    .setExcessPathLengthUncertaintyMeters(parcel.readFloat())
+                                    .setReflectingPlane(
+                                            GnssReflectingPlane.CREATOR.createFromParcel(parcel))
+                                    .build();
+                    return singleSatCorrection;
+                }
+
+                @Override
+                public GnssSingleSatCorrection[] newArray(int i) {
+                    return new GnssSingleSatCorrection[i];
+                }
+            };
+
+    @Override
+    public String toString() {
+        final String format = "   %-29s = %s\n";
+        StringBuilder builder = new StringBuilder("GnssSingleSatCorrection:\n");
+        builder.append(
+                String.format(format, "SingleSatCorrectionFlags = ", mSingleSatCorrectionFlags));
+        builder.append(String.format(format, "ConstellationType = ", mConstellationType));
+        builder.append(String.format(format, "SatId = ", mSatId));
+        builder.append(String.format(format, "CarrierFrequencyHz = ", mCarrierFrequencyHz));
+        builder.append(String.format(format, "SatIsLos = ", mSatIsLos));
+        builder.append(String.format(format, "ExcessPathLengthMeters = ", mExcessPathLengthMeters));
+        builder.append(
+                String.format(
+                        format,
+                        "ExcessPathLengthUncertaintyMeters = ",
+                        mExcessPathLengthUncertaintyMeters));
+        builder.append(String.format(format, "ReflectingPlane = ", mReflectingPlane));
+        return builder.toString();
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeInt(mSingleSatCorrectionFlags);
+        parcel.writeInt(mConstellationType);
+        parcel.writeInt(mSatId);
+        parcel.writeFloat(mCarrierFrequencyHz);
+        parcel.writeBoolean(mSatIsLos);
+        parcel.writeFloat(mExcessPathLengthMeters);
+        parcel.writeFloat(mExcessPathLengthUncertaintyMeters);
+        mReflectingPlane.writeToParcel(parcel, flags);
+    }
+
+    /** Builder for {@link GnssSingleSatCorrection} */
+    public static class Builder {
+
+        /**
+         * For documentation of below fields, see corresponding fields in {@link
+         * GnssSingleSatCorrection}.
+         */
+        private int mSingleSatCorrectionFlags;
+
+        private int mConstellationType;
+        private int mSatId;
+        private float mCarrierFrequencyHz;
+        private boolean mSatIsLos;
+        private float mExcessPathLengthMeters;
+        private float mExcessPathLengthUncertaintyMeters;
+        private GnssReflectingPlane mReflectingPlane;
+
+        /** Sets a bitmask of fields present in this object */
+        public Builder setSingleSatCorrectionFlags(int singleSatCorrectionFlags) {
+            mSingleSatCorrectionFlags = singleSatCorrectionFlags;
+            return this;
+        }
+
+        /** Sets the constellation type. */
+        public Builder setConstellationType(int constellationType) {
+            mConstellationType = constellationType;
+            return this;
+        }
+
+        /** Sets the Satellite ID. */
+        public Builder setSatId(int satId) {
+            mSatId = satId;
+            return this;
+        }
+
+        /** Sets the Carrier frequency in Hz. */
+        public Builder setCarrierFrequencyHz(float carrierFrequencyHz) {
+            mCarrierFrequencyHz = carrierFrequencyHz;
+            return this;
+        }
+
+        /** Sets the line=of-sight state of the satellite */
+        public Builder setSatIsLos(boolean satIsLos) {
+            mSatIsLos = satIsLos;
+            mSingleSatCorrectionFlags = (byte) (mSingleSatCorrectionFlags | HAS_SAT_IS_LOS_MASK);
+            return this;
+        }
+
+        /**
+         * Sets the Excess path length to be subtracted from pseudorange before using it in
+         * calculating location.
+         */
+        public Builder setExcessPathLengthMeters(float excessPathLengthMeters) {
+            mExcessPathLengthMeters = excessPathLengthMeters;
+            mSingleSatCorrectionFlags =
+                    (byte) (mSingleSatCorrectionFlags | HAS_EXCESS_PATH_LENGTH_MASK);
+            return this;
+        }
+
+        /** Sets the error estimate (1-sigma) for the Excess path length estimate */
+        public Builder setExcessPathLengthUncertaintyMeters(
+                float excessPathLengthUncertaintyMeters) {
+            mExcessPathLengthUncertaintyMeters = excessPathLengthUncertaintyMeters;
+            mSingleSatCorrectionFlags =
+                    (byte) (mSingleSatCorrectionFlags | HAS_EXCESS_PATH_LENGTH_UNC_MASK);
+            return this;
+        }
+
+        /** Sets the reflecting plane information */
+        public Builder setReflectingPlane(GnssReflectingPlane reflectingPlane) {
+            mReflectingPlane = reflectingPlane;
+            mSingleSatCorrectionFlags =
+                    (byte) (mSingleSatCorrectionFlags | HAS_REFLECTING_PLANE_MASK);
+            return this;
+        }
+
+        /** Builds a {@link GnssSingleSatCorrection} instance as specified by this builder. */
+        public GnssSingleSatCorrection build() {
+            return new GnssSingleSatCorrection(this);
+        }
+    }
+}
diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java
index d90a597..609a15e 100644
--- a/location/java/android/location/GpsStatus.java
+++ b/location/java/android/location/GpsStatus.java
@@ -17,6 +17,7 @@
 package android.location;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.util.SparseArray;
 
 import java.util.Iterator;
@@ -207,7 +208,7 @@
                 status.mAzimuths);
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     void setTimeToFirstFix(int ttff) {
         mTimeToFirstFix = ttff;
     }
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 32c7520..bdc84da 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -21,6 +21,7 @@
 import android.location.Criteria;
 import android.location.GeocoderParams;
 import android.location.Geofence;
+import android.location.GnssMeasurementCorrections;
 import android.location.IBatchedLocationCallback;
 import android.location.IGnssMeasurementsListener;
 import android.location.IGnssStatusListener;
@@ -63,6 +64,9 @@
     boolean sendNiResponse(int notifId, int userResponse);
 
     boolean addGnssMeasurementsListener(in IGnssMeasurementsListener listener, in String packageName);
+    void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections,
+            in String packageName);
+    int getGnssCapabilities(in String packageName);
     void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener);
 
     boolean addGnssNavigationMessageListener(
@@ -88,6 +92,10 @@
     boolean providerMeetsCriteria(String provider, in Criteria criteria);
     ProviderProperties getProviderProperties(String provider);
     String getNetworkProviderPackage();
+    void setLocationControllerExtraPackage(String packageName);
+    String getLocationControllerExtraPackage();
+    void setLocationControllerExtraPackageEnabled(boolean enabled);
+    boolean isLocationControllerExtraPackageEnabled();
 
     boolean isProviderEnabledForUser(String provider, int userId);
     boolean setProviderEnabledForUser(String provider, boolean enabled, int userId);
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index 70a97e1..fbebbb1 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -19,6 +19,7 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -124,7 +125,7 @@
         }
     };
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private String mProvider;
     private long mTime = 0;
     @UnsupportedAppUsage
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 334170e..d96597b 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -22,6 +22,7 @@
 import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
@@ -2079,17 +2080,54 @@
     }
 
     /**
-     * No-op method to keep backward-compatibility.
-     * Don't use it. Use {@link #unregisterGnssMeasurementsCallback} instead.
+     * Injects GNSS measurement corrections into the GNSS chipset.
+     *
+     * @param measurementCorrections a {@link GnssMeasurementCorrections} object with the GNSS
+     *     measurement corrections to be injected into the GNSS chipset.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(ACCESS_FINE_LOCATION)
+    public void injectGnssMeasurementCorrections(
+            @NonNull GnssMeasurementCorrections measurementCorrections) {
+        try {
+            mGnssMeasurementCallbackTransport.injectGnssMeasurementCorrections(
+                    measurementCorrections);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns an integer with flags representing the capabilities of the GNSS chipset.
+     *
+     * @hide
+     */
+    @SystemApi
+    /**
+     * Returns the integer capability flags of the GNSS chipset as defined in {@code
+     * IGnssCallback.hal}
+     */
+    public int getGnssCapabilities() {
+        try {
+            return mGnssMeasurementCallbackTransport.getGnssCapabilities();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * No-op method to keep backward-compatibility. Don't use it. Use {@link
+     * #unregisterGnssMeasurementsCallback} instead.
+     *
      * @hide
      * @deprecated use {@link #unregisterGnssMeasurementsCallback(GnssMeasurementsEvent.Callback)}
-     * instead.
+     *     instead.
      */
     @Deprecated
     @SystemApi
     @SuppressLint("Doclava125")
-    public void removeGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) {
-    }
+    public void removeGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) {}
 
     /**
      * Unregisters a GPS Measurement callback.
@@ -2334,7 +2372,7 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public boolean sendNiResponse(int notifId, int userResponse) {
         try {
             return mService.sendNiResponse(notifId, userResponse);
@@ -2396,4 +2434,65 @@
             return null;
         }
     }
+
+    /**
+     * Set the extra location controller package for location services on the device.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+    public void setLocationControllerExtraPackage(String packageName) {
+        try {
+            mService.setLocationControllerExtraPackage(packageName);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the extra location controller package on the device.
+     *
+     * @hide
+     */
+    @SystemApi
+    public @Nullable String getLocationControllerExtraPackage() {
+        try {
+            return mService.getLocationControllerExtraPackage();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return null;
+        }
+    }
+
+    /**
+     * Set whether the extra location controller package is currently enabled on the device.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+    public void setLocationControllerExtraPackageEnabled(boolean enabled) {
+        try {
+            mService.setLocationControllerExtraPackageEnabled(enabled);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether extra location controller package is currently enabled on the device.
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean isLocationControllerExtraPackageEnabled() {
+        try {
+            return mService.isLocationControllerExtraPackageEnabled();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return false;
+        }
+    }
+
 }
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 2d7f7e3..154bd56 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -18,6 +18,7 @@
 
 import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
@@ -142,19 +143,19 @@
      */
     private static final double FASTEST_INTERVAL_FACTOR = 6.0;  // 6x
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mQuality = POWER_LOW;
     @UnsupportedAppUsage
     private long mInterval = 60 * 60 * 1000;   // 60 minutes
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private long mFastestInterval = (long) (mInterval / FASTEST_INTERVAL_FACTOR);  // 10 minutes
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private boolean mExplicitFastestInterval = false;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private long mExpireAt = Long.MAX_VALUE;  // no expiry
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mNumUpdates = Integer.MAX_VALUE;  // no expiry
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private float mSmallestDisplacement = 0.0f;    // meters
     @UnsupportedAppUsage
     private WorkSource mWorkSource = null;
@@ -603,14 +604,14 @@
         return mHideFromAppOps;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static void checkInterval(long millis) {
         if (millis < 0) {
             throw new IllegalArgumentException("invalid interval: " + millis);
         }
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static void checkQuality(int quality) {
         switch (quality) {
             case ACCURACY_FINE:
@@ -625,14 +626,14 @@
         }
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static void checkDisplacement(float meters) {
         if (meters < 0.0f) {
             throw new IllegalArgumentException("invalid displacement: " + meters);
         }
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static void checkProvider(String name) {
         if (name == null) {
             throw new IllegalArgumentException("invalid provider: " + name);
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index 9bd5994..b531325 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -17,6 +17,7 @@
 package com.android.internal.location;
 
 import java.io.UnsupportedEncodingException;
+import java.util.concurrent.TimeUnit;
 
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -27,19 +28,17 @@
 import android.content.IntentFilter;
 import android.location.LocationManager;
 import android.location.INetInitiatedListener;
+import android.os.SystemClock;
 import android.telephony.TelephonyManager;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.PhoneStateListener;
-import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.os.SystemProperties;
 import android.util.Log;
 
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.R;
 import com.android.internal.telephony.GsmAlphabet;
-import com.android.internal.telephony.TelephonyProperties;
 
 /**
  * A GPS Network-initiated Handler class used by LocationManager.
@@ -50,8 +49,7 @@
 
     private static final String TAG = "GpsNetInitiatedHandler";
 
-    private static final boolean DEBUG = true;
-    private static final boolean VERBOSE = false;
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     // NI verify activity for bringing up UI (not used yet)
     public static final String ACTION_NI_VERIFY = "android.intent.action.NETWORK_INITIATED_VERIFY";
@@ -94,6 +92,9 @@
     public static final int GPS_ENC_SUPL_UCS2 = 3;
     public static final int GPS_ENC_UNKNOWN = -1;
 
+    // Limit on SUPL NI emergency mode time extension after emergency sessions ends
+    private static final int MAX_EMERGENCY_MODE_EXTENSION_SECONDS = 300;  // 5 minute maximum
+
     private final Context mContext;
     private final TelephonyManager mTelephonyManager;
     private final PhoneStateListener mPhoneStateListener;
@@ -109,7 +110,7 @@
     private volatile boolean mIsSuplEsEnabled;
 
     // Set to true if the phone is having emergency call.
-    private volatile boolean mIsInEmergency;
+    private volatile boolean mIsInEmergencyCall;
 
     // If Location function is enabled.
     private volatile boolean mIsLocationEnabled = false;
@@ -119,6 +120,10 @@
     // Set to true if string from HAL is encoded as Hex, e.g., "3F0039"
     static private boolean mIsHexInput = true;
 
+    // End time of emergency call, and extension, if set
+    private long mCallEndElapsedRealtimeMillis = 0;
+    private long mEmergencyExtensionMillis = 0;
+
     public static class GpsNiNotification
     {
         public int notificationId;
@@ -146,16 +151,12 @@
             if (action.equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
                 String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
                 /*
-                   Emergency Mode is when during emergency call or in emergency call back mode.
-                   For checking if it is during emergency call:
-                       mIsInEmergency records if the phone is in emergency call or not. It will
+                   Tracks the emergency call:
+                       mIsInEmergencyCall records if the phone is in emergency call or not. It will
                        be set to true when the phone is having emergency call, and then will
                        be set to false by mPhoneStateListener when the emergency call ends.
-                   For checking if it is in emergency call back mode:
-                       Emergency call back mode will be checked by reading system properties
-                       when necessary: SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)
                 */
-                setInEmergency(PhoneNumberUtils.isEmergencyNumber(phoneNumber));
+                mIsInEmergencyCall = PhoneNumberUtils.isEmergencyNumber(phoneNumber);
                 if (DEBUG) Log.v(TAG, "ACTION_NEW_OUTGOING_CALL - " + getInEmergency());
             } else if (action.equals(LocationManager.MODE_CHANGED_ACTION)) {
                 updateLocationMode();
@@ -195,7 +196,10 @@
                 if (DEBUG) Log.d(TAG, "onCallStateChanged(): state is "+ state);
                 // listening for emergency call ends
                 if (state == TelephonyManager.CALL_STATE_IDLE) {
-                    setInEmergency(false);
+                    if (mIsInEmergencyCall) {
+                        mCallEndElapsedRealtimeMillis = SystemClock.elapsedRealtime();
+                        mIsInEmergencyCall = false;
+                    }
                 }
             }
         };
@@ -229,22 +233,35 @@
         return mIsLocationEnabled;
     }
 
-    // Note: Currently, there are two mechanisms involved to determine if a
-    // phone is in emergency mode:
-    // 1. If the user is making an emergency call, this is provided by activly
-    //    monitoring the outgoing phone number;
-    // 2. If the device is in a emergency callback state, this is provided by
-    //    system properties.
-    // If either one of above exists, the phone is considered in an emergency
-    // mode. Because of this complexity, we need to be careful about how to set
-    // and clear the emergency state.
-    public void setInEmergency(boolean isInEmergency) {
-        mIsInEmergency = isInEmergency;
+    /**
+     * Determines whether device is in user-initiated emergency session based on the following
+     * 1. If the user is making an emergency call, this is provided by actively
+     *    monitoring the outgoing phone number;
+     * 2. If the user has recently ended an emergency call, and the device is in a configured time
+     *    window after the end of that call.
+     * 3. If the device is in a emergency callback state, this is provided by querying
+     *    TelephonyManager.
+     * @return true if is considered in user initiated emergency mode for NI purposes
+     */
+    public boolean getInEmergency() {
+        boolean isInEmergencyExtension =
+                (SystemClock.elapsedRealtime() - mCallEndElapsedRealtimeMillis) <
+                        mEmergencyExtensionMillis;
+        boolean isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode();
+        return mIsInEmergencyCall || isInEmergencyCallback || isInEmergencyExtension;
     }
 
-    public boolean getInEmergency() {
-        boolean isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode();
-        return mIsInEmergency || isInEmergencyCallback;
+    public void setEmergencyExtensionSeconds(int emergencyExtensionSeconds) {
+        if (emergencyExtensionSeconds > MAX_EMERGENCY_MODE_EXTENSION_SECONDS) {
+            Log.w(TAG, "emergencyExtensionSeconds " + emergencyExtensionSeconds
+                    + " too high, reset to " + MAX_EMERGENCY_MODE_EXTENSION_SECONDS);
+            emergencyExtensionSeconds = MAX_EMERGENCY_MODE_EXTENSION_SECONDS;
+        } else if (emergencyExtensionSeconds < 0) {
+            Log.w(TAG, "emergencyExtensionSeconds " + emergencyExtensionSeconds
+                    + " is negative, reset to zero.");
+            emergencyExtensionSeconds = 0;
+        }
+        mEmergencyExtensionMillis = TimeUnit.SECONDS.toMillis(emergencyExtensionSeconds);
     }
 
 
diff --git a/location/tests/locationtests/Android.mk b/location/tests/locationtests/Android.mk
index b2fd8ec..3dcf694 100644
--- a/location/tests/locationtests/Android.mk
+++ b/location/tests/locationtests/Android.mk
@@ -12,7 +12,7 @@
 LOCAL_PRIVATE_PLATFORM_APIS := true
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
+    androidx.test.rules \
     core-test-rules \
     guava \
     mockito-target-minus-junit4 \
diff --git a/location/tests/locationtests/AndroidManifest.xml b/location/tests/locationtests/AndroidManifest.xml
index ddb8ea6..5010d3d 100644
--- a/location/tests/locationtests/AndroidManifest.xml
+++ b/location/tests/locationtests/AndroidManifest.xml
@@ -29,7 +29,7 @@
     </application>
 
     <instrumentation
-        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.frameworks.locationtests"
         android:label="Frameworks Location Tests" />
 </manifest>
diff --git a/location/tests/locationtests/AndroidTest.xml b/location/tests/locationtests/AndroidTest.xml
index bb6547b..7bddb58 100644
--- a/location/tests/locationtests/AndroidTest.xml
+++ b/location/tests/locationtests/AndroidTest.xml
@@ -22,7 +22,7 @@
     <option name="test-tag" value="FrameworksLocationTests" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.frameworks.locationtests" />
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
         <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java b/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java
new file mode 100644
index 0000000..c18d58f
--- /dev/null
+++ b/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.os.Parcel;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Unit tests for {@link GnssMeasurementCorrections}. */
+public class GnssMeasurementCorrectionsTest extends TestCase {
+    public void testDescribeContents() {
+        GnssMeasurementCorrections measurementCorrections =
+                new GnssMeasurementCorrections.Builder().build();
+        measurementCorrections.describeContents();
+    }
+
+    public void testWriteToParcel() {
+        GnssMeasurementCorrections.Builder measurementCorrections =
+                new GnssMeasurementCorrections.Builder();
+        setTestValues(measurementCorrections);
+        Parcel parcel = Parcel.obtain();
+        measurementCorrections.build().writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        GnssMeasurementCorrections newMeasurementCorrection =
+                GnssMeasurementCorrections.CREATOR.createFromParcel(parcel);
+        verifyTestValues(newMeasurementCorrection);
+        parcel.recycle();
+    }
+
+    private static void verifyTestValues(GnssMeasurementCorrections measurementCorrections) {
+        assertEquals(37.386051, measurementCorrections.getLatitudeDegrees());
+        assertEquals(-122.083855, measurementCorrections.getLongitudeDegrees());
+        assertEquals(32.0, measurementCorrections.getAltitudeMeters());
+        assertEquals(604000000000000L, measurementCorrections.getToaGpsNanosecondsOfWeek());
+
+        GnssSingleSatCorrection singleSatCorrection =
+                measurementCorrections.getSingleSatCorrectionList().get(0);
+        GnssSingleSatCorrectionsTest.verifyTestValues(singleSatCorrection);
+
+        singleSatCorrection = measurementCorrections.getSingleSatCorrectionList().get(1);
+        assertEquals(15, singleSatCorrection.getSingleSatCorrectionFlags());
+        assertEquals(GnssStatus.CONSTELLATION_GPS, singleSatCorrection.getConstellationType());
+        assertEquals(11, singleSatCorrection.getSatId());
+        assertEquals(1575430000f, singleSatCorrection.getCarrierFrequencyHz());
+        assertEquals(false, singleSatCorrection.isSatelliteLineOfSight());
+        assertEquals(50.0f, singleSatCorrection.getExcessPathLengthMeters());
+        assertEquals(55.0f, singleSatCorrection.getExcessPathLengthUncertaintyMeters());
+        GnssReflectingPlane reflectingPlane = singleSatCorrection.getReflectingPlane();
+        assertEquals(37.386054, reflectingPlane.getLatitudeDegrees());
+        assertEquals(-122.083855, reflectingPlane.getLongitudeDegrees());
+        assertEquals(120.0, reflectingPlane.getAltitudeMeters());
+        assertEquals(153.0, reflectingPlane.getAzimuthDegrees());
+    }
+
+    private static void setTestValues(GnssMeasurementCorrections.Builder measurementCorrections) {
+        measurementCorrections
+                .setLatitudeDegrees(37.386051)
+                .setLongitudeDegrees(-122.083855)
+                .setAltitudeMeters(32)
+                .setToaGpsNanosecondsOfWeek(604000000000000L);
+        List<GnssSingleSatCorrection> singleSatCorrectionList = new ArrayList<>();
+        singleSatCorrectionList.add(GnssSingleSatCorrectionsTest.generateTestSingleSatCorrection());
+        singleSatCorrectionList.add(generateTestSingleSatCorrection());
+        measurementCorrections.setSingleSatCorrectionList(singleSatCorrectionList);
+    }
+
+    private static GnssSingleSatCorrection generateTestSingleSatCorrection() {
+        GnssSingleSatCorrection.Builder singleSatCorrection = new GnssSingleSatCorrection.Builder();
+        singleSatCorrection
+                .setSingleSatCorrectionFlags(8)
+                .setConstellationType(GnssStatus.CONSTELLATION_GPS)
+                .setSatId(11)
+                .setCarrierFrequencyHz(1575430000f)
+                .setSatIsLos(false)
+                .setExcessPathLengthMeters(50.0f)
+                .setExcessPathLengthUncertaintyMeters(55.0f)
+                .setReflectingPlane(generateTestReflectingPlane());
+        return singleSatCorrection.build();
+    }
+
+    private static GnssReflectingPlane generateTestReflectingPlane() {
+        GnssReflectingPlane.Builder reflectingPlane =
+                new GnssReflectingPlane.Builder()
+                        .setLatitudeDegrees(37.386054)
+                        .setLongitudeDegrees(-122.083855)
+                        .setAltitudeMeters(120.0)
+                        .setAzimuthDegrees(153);
+        return reflectingPlane.build();
+    }
+}
diff --git a/location/tests/locationtests/src/android/location/GnssReflectingPlaneTest.java b/location/tests/locationtests/src/android/location/GnssReflectingPlaneTest.java
new file mode 100644
index 0000000..d7a3378
--- /dev/null
+++ b/location/tests/locationtests/src/android/location/GnssReflectingPlaneTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.os.Parcel;
+
+import junit.framework.TestCase;
+
+/** Unit tests for {@link GnssReflectingPlane}. */
+public class GnssReflectingPlaneTest extends TestCase {
+    public void testDescribeContents() {
+        GnssReflectingPlane reflectingPlane = new GnssReflectingPlane.Builder().build();
+        reflectingPlane.describeContents();
+    }
+
+    public void testWriteToParcel() {
+        GnssReflectingPlane.Builder reflectingPlane = new GnssReflectingPlane.Builder();
+        setTestValues(reflectingPlane);
+        Parcel parcel = Parcel.obtain();
+        reflectingPlane.build().writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        GnssReflectingPlane newReflectingPlane =
+                GnssReflectingPlane.CREATOR.createFromParcel(parcel);
+        verifyTestValues(newReflectingPlane);
+        parcel.recycle();
+    }
+
+    public static void verifyTestValues(GnssReflectingPlane reflectingPlane) {
+        assertEquals(37.386052, reflectingPlane.getLatitudeDegrees());
+        assertEquals(-122.083853, reflectingPlane.getLongitudeDegrees());
+        assertEquals(100.0, reflectingPlane.getAltitudeMeters());
+        assertEquals(123.0, reflectingPlane.getAzimuthDegrees());
+    }
+
+    private static void setTestValues(GnssReflectingPlane.Builder reflectingPlane) {
+        GnssReflectingPlane refPlane = generateTestReflectingPlane();
+        reflectingPlane
+                .setLatitudeDegrees(refPlane.getLatitudeDegrees())
+                .setLongitudeDegrees(refPlane.getLongitudeDegrees())
+                .setAltitudeMeters(refPlane.getAltitudeMeters())
+                .setAzimuthDegrees(refPlane.getAzimuthDegrees());
+    }
+
+    public static GnssReflectingPlane generateTestReflectingPlane() {
+        GnssReflectingPlane.Builder reflectingPlane =
+                new GnssReflectingPlane.Builder()
+                        .setLatitudeDegrees(37.386052)
+                        .setLongitudeDegrees(-122.083853)
+                        .setAltitudeMeters(100.0)
+                        .setAzimuthDegrees(123.0);
+        return reflectingPlane.build();
+    }
+}
diff --git a/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java b/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java
new file mode 100644
index 0000000..2e54ae4
--- /dev/null
+++ b/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.os.Parcel;
+
+import junit.framework.TestCase;
+
+/** Unit tests for {@link GnssSingleSatCorrection}. */
+public class GnssSingleSatCorrectionsTest extends TestCase {
+    public void testDescribeContents() {
+        GnssSingleSatCorrection singleSatCorrection = new GnssSingleSatCorrection.Builder().build();
+        singleSatCorrection.describeContents();
+    }
+
+    public void testWriteToParcel() {
+        GnssSingleSatCorrection.Builder singleSatCorrection = new GnssSingleSatCorrection.Builder();
+        setTestValues(singleSatCorrection);
+        Parcel parcel = Parcel.obtain();
+        singleSatCorrection.build().writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        GnssSingleSatCorrection newSingleSatCorrection =
+                GnssSingleSatCorrection.CREATOR.createFromParcel(parcel);
+        verifyTestValues(newSingleSatCorrection);
+        parcel.recycle();
+    }
+
+    public static void verifyTestValues(GnssSingleSatCorrection singleSatCorrection) {
+        assertEquals(15, singleSatCorrection.getSingleSatCorrectionFlags());
+        assertEquals(GnssStatus.CONSTELLATION_GALILEO, singleSatCorrection.getConstellationType());
+        assertEquals(12, singleSatCorrection.getSatId());
+        assertEquals(1575420000f, singleSatCorrection.getCarrierFrequencyHz());
+        assertEquals(true, singleSatCorrection.isSatelliteLineOfSight());
+        assertEquals(10.0f, singleSatCorrection.getExcessPathLengthMeters());
+        assertEquals(5.0f, singleSatCorrection.getExcessPathLengthUncertaintyMeters());
+        GnssReflectingPlane reflectingPlane = singleSatCorrection.getReflectingPlane();
+        GnssReflectingPlaneTest.verifyTestValues(reflectingPlane);
+    }
+
+    private static void setTestValues(GnssSingleSatCorrection.Builder singleSatCorrection) {
+        GnssSingleSatCorrection singleSatCorr = generateTestSingleSatCorrection();
+        singleSatCorrection
+                .setSingleSatCorrectionFlags(singleSatCorr.getSingleSatCorrectionFlags())
+                .setConstellationType(singleSatCorr.getConstellationType())
+                .setSatId(singleSatCorr.getSatId())
+                .setCarrierFrequencyHz(singleSatCorr.getCarrierFrequencyHz())
+                .setSatIsLos(singleSatCorr.isSatelliteLineOfSight())
+                .setExcessPathLengthMeters(singleSatCorr.getExcessPathLengthMeters())
+                .setExcessPathLengthUncertaintyMeters(
+                        singleSatCorr.getExcessPathLengthUncertaintyMeters())
+                .setReflectingPlane(singleSatCorr.getReflectingPlane());
+    }
+
+    public static GnssSingleSatCorrection generateTestSingleSatCorrection() {
+        GnssSingleSatCorrection.Builder singleSatCorrection =
+                new GnssSingleSatCorrection.Builder()
+                        .setSingleSatCorrectionFlags(15)
+                        .setConstellationType(GnssStatus.CONSTELLATION_GALILEO)
+                        .setSatId(12)
+                        .setCarrierFrequencyHz(1575420000f)
+                        .setSatIsLos(true)
+                        .setExcessPathLengthMeters(10.0f)
+                        .setExcessPathLengthUncertaintyMeters(5.0f)
+                        .setReflectingPlane(GnssReflectingPlaneTest.generateTestReflectingPlane());
+        return singleSatCorrection.build();
+    }
+}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index da52a26..30b5480 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -67,6 +67,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.TreeMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
 
@@ -539,37 +540,42 @@
 
     /**
      * Adjusting the volume due to a hardware key press.
+     * This flag can be used in the places in order to denote (or check) that a volume adjustment
+     * request is from a hardware key press. (e.g. {@link MediaController}).
      * @hide
      */
     @SystemApi
-    public static final int FLAG_FROM_KEY = 1 << 12;
+    public static final int FLAG_FROM_KEY = 1 << 16;
 
-    private static final String[] FLAG_NAMES = {
-        "FLAG_SHOW_UI",
-        "FLAG_ALLOW_RINGER_MODES",
-        "FLAG_PLAY_SOUND",
-        "FLAG_REMOVE_SOUND_AND_VIBRATE",
-        "FLAG_VIBRATE",
-        "FLAG_FIXED_VOLUME",
-        "FLAG_BLUETOOTH_ABS_VOLUME",
-        "FLAG_SHOW_SILENT_HINT",
-        "FLAG_HDMI_SYSTEM_AUDIO_VOLUME",
-        "FLAG_ACTIVE_MEDIA_ONLY",
-        "FLAG_SHOW_UI_WARNINGS",
-        "FLAG_SHOW_VIBRATE_HINT",
-        "FLAG_FROM_KEY",
-    };
+    // The iterator of TreeMap#entrySet() returns the entries in ascending key order.
+    private static final TreeMap<Integer, String> FLAG_NAMES = new TreeMap<>();
+
+    static {
+        FLAG_NAMES.put(FLAG_SHOW_UI, "FLAG_SHOW_UI");
+        FLAG_NAMES.put(FLAG_ALLOW_RINGER_MODES, "FLAG_ALLOW_RINGER_MODES");
+        FLAG_NAMES.put(FLAG_PLAY_SOUND, "FLAG_PLAY_SOUND");
+        FLAG_NAMES.put(FLAG_REMOVE_SOUND_AND_VIBRATE, "FLAG_REMOVE_SOUND_AND_VIBRATE");
+        FLAG_NAMES.put(FLAG_VIBRATE, "FLAG_VIBRATE");
+        FLAG_NAMES.put(FLAG_FIXED_VOLUME, "FLAG_FIXED_VOLUME");
+        FLAG_NAMES.put(FLAG_BLUETOOTH_ABS_VOLUME, "FLAG_BLUETOOTH_ABS_VOLUME");
+        FLAG_NAMES.put(FLAG_SHOW_SILENT_HINT, "FLAG_SHOW_SILENT_HINT");
+        FLAG_NAMES.put(FLAG_HDMI_SYSTEM_AUDIO_VOLUME, "FLAG_HDMI_SYSTEM_AUDIO_VOLUME");
+        FLAG_NAMES.put(FLAG_ACTIVE_MEDIA_ONLY, "FLAG_ACTIVE_MEDIA_ONLY");
+        FLAG_NAMES.put(FLAG_SHOW_UI_WARNINGS, "FLAG_SHOW_UI_WARNINGS");
+        FLAG_NAMES.put(FLAG_SHOW_VIBRATE_HINT, "FLAG_SHOW_VIBRATE_HINT");
+        FLAG_NAMES.put(FLAG_FROM_KEY, "FLAG_FROM_KEY");
+    }
 
     /** @hide */
     public static String flagsToString(int flags) {
         final StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < FLAG_NAMES.length; i++) {
-            final int flag = 1 << i;
+        for (Map.Entry<Integer, String> entry : FLAG_NAMES.entrySet()) {
+            final int flag = entry.getKey();
             if ((flags & flag) != 0) {
                 if (sb.length() > 0) {
                     sb.append(',');
                 }
-                sb.append(FLAG_NAMES[i]);
+                sb.append(entry.getValue());
                 flags &= ~flag;
             }
         }
@@ -4274,9 +4280,8 @@
      * Return codes for listAudioPorts(), createAudioPatch() ...
      */
 
-    /** @hide
-     * CANDIDATE FOR PUBLIC API
-     */
+    /** @hide */
+    @SystemApi
     public static final int SUCCESS = AudioSystem.SUCCESS;
     /**
      * A default error code.
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 4b2353c..33f81f1 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -16,8 +16,10 @@
 
 package android.media;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityThread;
@@ -43,6 +45,7 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * The AudioRecord class manages the audio resources for Java applications
@@ -58,7 +61,7 @@
  * been read yet. Data should be read from the audio hardware in chunks of sizes inferior to
  * the total recording buffer size.
  */
-public class AudioRecord implements AudioRouting
+public class AudioRecord implements AudioRouting, AudioRecordingMonitor, AudioRecordingMonitorClient
 {
     //---------------------------------------------------------
     // Constants
@@ -1654,6 +1657,56 @@
         return activeMicrophones;
     }
 
+
+    //--------------------------------------------------------------------------
+    // Implementation of AudioRecordingMonitor interface
+    //--------------------
+
+    AudioRecordingMonitorImpl mRecordingInfoImpl =
+            new AudioRecordingMonitorImpl((AudioRecordingMonitorClient) this);
+
+    /**
+     * Register a callback to be notified of audio capture changes via a
+     * {@link AudioManager.AudioRecordingCallback}. A callback is received when the capture path
+     * configuration changes (pre-processing, format, sampling rate...) or capture is
+     * silenced/unsilenced by the system.
+     * @param executor {@link Executor} to handle the callbacks.
+     * @param cb non-null callback to register
+     */
+    public void registerAudioRecordingCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull AudioManager.AudioRecordingCallback cb) {
+        mRecordingInfoImpl.registerAudioRecordingCallback(executor, cb);
+    }
+
+    /**
+     * Unregister an audio recording callback previously registered with
+     * {@link #registerAudioRecordingCallback(Executor, AudioManager.AudioRecordingCallback)}.
+     * @param cb non-null callback to unregister
+     */
+    public void unregisterAudioRecordingCallback(@NonNull AudioManager.AudioRecordingCallback cb) {
+        mRecordingInfoImpl.unregisterAudioRecordingCallback(cb);
+    }
+
+    /**
+     * Returns the current active audio recording for this audio recorder.
+     * @return a valid {@link AudioRecordingConfiguration} if this recorder is active
+     * or null otherwise.
+     * @see AudioRecordingConfiguration
+     */
+    public @Nullable AudioRecordingConfiguration getActiveRecordingConfiguration() {
+        return mRecordingInfoImpl.getActiveRecordingConfiguration();
+    }
+
+    //---------------------------------------------------------
+    // Implementation of AudioRecordingMonitorClient interface
+    //--------------------
+    /**
+     * @hide
+     */
+    public int getPortId() {
+        return native_getPortId();
+    }
+
     //---------------------------------------------------------
     // Interface definitions
     //--------------------
diff --git a/media/java/android/media/AudioRecordingConfiguration.java b/media/java/android/media/AudioRecordingConfiguration.java
index 9ada216..de76aef 100644
--- a/media/java/android/media/AudioRecordingConfiguration.java
+++ b/media/java/android/media/AudioRecordingConfiguration.java
@@ -18,7 +18,9 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
+import android.media.audiofx.AudioEffect;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -27,6 +29,8 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -48,7 +52,7 @@
 public final class AudioRecordingConfiguration implements Parcelable {
     private final static String TAG = new String("AudioRecordingConfiguration");
 
-    private final int mSessionId;
+    private final int mClientSessionId;
 
     private final int mClientSource;
 
@@ -60,18 +64,50 @@
 
     private final int mPatchHandle;
 
+    private final int mClientPortId;
+
+    private boolean mClientSilenced;
+
+    private final int mDeviceSource;
+
+    private final AudioEffect.Descriptor[] mClientEffects;
+
+    private final AudioEffect.Descriptor[] mDeviceEffects;
+
     /**
      * @hide
      */
+    @TestApi
     public AudioRecordingConfiguration(int uid, int session, int source, AudioFormat clientFormat,
-            AudioFormat devFormat, int patchHandle, String packageName) {
+            AudioFormat devFormat, int patchHandle, String packageName, int clientPortId,
+            boolean clientSilenced, int deviceSource,
+            AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] deviceEffects) {
         mClientUid = uid;
-        mSessionId = session;
+        mClientSessionId = session;
         mClientSource = source;
         mClientFormat = clientFormat;
         mDeviceFormat = devFormat;
         mPatchHandle = patchHandle;
         mClientPackageName = packageName;
+        mClientPortId = clientPortId;
+        mClientSilenced = clientSilenced;
+        mDeviceSource = deviceSource;
+        mClientEffects = clientEffects;
+        mDeviceEffects = deviceEffects;
+    }
+
+    /**
+     * @hide
+     */
+    @TestApi
+    public AudioRecordingConfiguration(int uid, int session, int source,
+                                       AudioFormat clientFormat, AudioFormat devFormat,
+                                       int patchHandle, String packageName) {
+        this(uid, session, source, clientFormat,
+                   devFormat, patchHandle, packageName, 0 /*clientPortId*/,
+                   false /*clientSilenced*/, MediaRecorder.AudioSource.DEFAULT /*deviceSource*/,
+                   new AudioEffect.Descriptor[0] /*clientEffects*/,
+                   new AudioEffect.Descriptor[0] /*deviceEffects*/);
     }
 
     /**
@@ -87,13 +123,26 @@
      * @hide
      */
     public static String toLogFriendlyString(AudioRecordingConfiguration arc) {
-        return new String("session:" + arc.mSessionId
-                + " -- source:" + MediaRecorder.toLogFriendlyAudioSource(arc.mClientSource)
+        String clientEffects = new String();
+        for (AudioEffect.Descriptor desc : arc.mClientEffects) {
+            clientEffects += "'" + desc.name + "' ";
+        }
+        String deviceEffects = new String();
+        for (AudioEffect.Descriptor desc : arc.mDeviceEffects) {
+            deviceEffects += "'" + desc.name + "' ";
+        }
+
+        return new String("session:" + arc.mClientSessionId
+                + " -- source client=" + MediaRecorder.toLogFriendlyAudioSource(arc.mClientSource)
+                + ", dev=" + arc.mDeviceFormat.toLogFriendlyString()
                 + " -- uid:" + arc.mClientUid
                 + " -- patch:" + arc.mPatchHandle
                 + " -- pack:" + arc.mClientPackageName
                 + " -- format client=" + arc.mClientFormat.toLogFriendlyString()
-                    + ", dev=" + arc.mDeviceFormat.toLogFriendlyString());
+                + ", dev=" + arc.mDeviceFormat.toLogFriendlyString()
+                + " -- silenced:" + arc.mClientSilenced
+                + " -- effects client=" + clientEffects
+                + ", dev=" + deviceEffects);
     }
 
     // Note that this method is called server side, so no "privileged" information is ever sent
@@ -106,8 +155,10 @@
      */
     public static AudioRecordingConfiguration anonymizedCopy(AudioRecordingConfiguration in) {
         return new AudioRecordingConfiguration( /*anonymized uid*/ -1,
-                in.mSessionId, in.mClientSource, in.mClientFormat,
-                in.mDeviceFormat, in.mPatchHandle, "" /*empty package name*/);
+                in.mClientSessionId, in.mClientSource, in.mClientFormat,
+                in.mDeviceFormat, in.mPatchHandle, "" /*empty package name*/,
+                in.mClientPortId, in.mClientSilenced, in.mDeviceSource, in.mClientEffects,
+                in.mDeviceEffects);
     }
 
     // matches the sources that return false in MediaRecorder.isSystemOnlyAudioSource(source)
@@ -129,16 +180,8 @@
     // documented return values match the sources that return false
     //   in MediaRecorder.isSystemOnlyAudioSource(source)
     /**
-     * Returns the audio source being used for the recording.
-     * @return one of {@link MediaRecorder.AudioSource#DEFAULT},
-     *       {@link MediaRecorder.AudioSource#MIC},
-     *       {@link MediaRecorder.AudioSource#VOICE_UPLINK},
-     *       {@link MediaRecorder.AudioSource#VOICE_DOWNLINK},
-     *       {@link MediaRecorder.AudioSource#VOICE_CALL},
-     *       {@link MediaRecorder.AudioSource#CAMCORDER},
-     *       {@link MediaRecorder.AudioSource#VOICE_RECOGNITION},
-     *       {@link MediaRecorder.AudioSource#VOICE_COMMUNICATION},
-     *       {@link MediaRecorder.AudioSource#UNPROCESSED}.
+     * Returns the audio source selected by the client.
+     * @return the audio source selected by the client.
      */
     public @AudioSource int getClientAudioSource() { return mClientSource; }
 
@@ -146,7 +189,9 @@
      * Returns the session number of the recording, see {@link AudioRecord#getAudioSessionId()}.
      * @return the session number.
      */
-    public int getClientAudioSessionId() { return mSessionId; }
+    public int getClientAudioSessionId() {
+        return mClientSessionId;
+    }
 
     /**
      * Returns the audio format at which audio is recorded on this Android device.
@@ -223,6 +268,54 @@
         return null;
     }
 
+    /**
+     * Returns the system unique ID assigned for the AudioRecord object corresponding to this
+     * AudioRecordingConfiguration client.
+     * @return the port ID.
+     */
+    int getClientPortId() {
+        return mClientPortId;
+    }
+
+    /**
+     * Returns true if the audio returned to the client is currently being silenced by the
+     * audio framework due to concurrent capture policy (e.g the capturing application does not have
+     * an active foreground process or service anymore).
+     * @return true if captured audio is silenced, false otherwise .
+     */
+    public boolean isClientSilenced() {
+        return mClientSilenced;
+    }
+
+    /**
+     * Returns the audio source currently used to configure the capture path. It can be different
+     * from the source returned by {@link #getClientAudioSource()} if another capture is active.
+     * @return the audio source active on the capture path.
+     */
+    public @AudioSource int getAudioSource() {
+        return mDeviceSource;
+    }
+
+    /**
+     * Returns the list of {@link AudioEffect.Descriptor} for all effects currently enabled on
+     * the audio capture client (e.g. {@link AudioRecord} or {@link MediaRecorder}).
+     * @return List of {@link AudioEffect.Descriptor} containing all effects enabled for the client.
+     */
+    public @NonNull List<AudioEffect.Descriptor> getClientEffects() {
+        return new ArrayList<AudioEffect.Descriptor>(Arrays.asList(mClientEffects));
+    }
+
+    /**
+     * Returns the list of {@link AudioEffect.Descriptor} for all effects currently enabled on
+     * the capture stream.
+     * @return List of {@link AudioEffect.Descriptor} containing all effects enabled on the
+     * capture stream. This can be different from the list returned by {@link #getClientEffects()}
+     * if another capture is active.
+     */
+    public @NonNull List<AudioEffect.Descriptor> getEffects() {
+        return new ArrayList<AudioEffect.Descriptor>(Arrays.asList(mDeviceEffects));
+    }
+
     public static final Parcelable.Creator<AudioRecordingConfiguration> CREATOR
             = new Parcelable.Creator<AudioRecordingConfiguration>() {
         /**
@@ -240,7 +333,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mSessionId, mClientSource);
+        return Objects.hash(mClientSessionId, mClientSource);
     }
 
     @Override
@@ -250,23 +343,45 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mSessionId);
+        dest.writeInt(mClientSessionId);
         dest.writeInt(mClientSource);
         mClientFormat.writeToParcel(dest, 0);
         mDeviceFormat.writeToParcel(dest, 0);
         dest.writeInt(mPatchHandle);
         dest.writeString(mClientPackageName);
         dest.writeInt(mClientUid);
+        dest.writeInt(mClientPortId);
+        dest.writeBoolean(mClientSilenced);
+        dest.writeInt(mDeviceSource);
+        dest.writeInt(mClientEffects.length);
+        for (int i = 0; i < mClientEffects.length; i++) {
+            mClientEffects[i].writeToParcel(dest, 0);
+        }
+        dest.writeInt(mDeviceEffects.length);
+        for (int i = 0; i < mDeviceEffects.length; i++) {
+            mDeviceEffects[i].writeToParcel(dest, 0);
+        }
     }
 
     private AudioRecordingConfiguration(Parcel in) {
-        mSessionId = in.readInt();
+        mClientSessionId = in.readInt();
         mClientSource = in.readInt();
         mClientFormat = AudioFormat.CREATOR.createFromParcel(in);
         mDeviceFormat = AudioFormat.CREATOR.createFromParcel(in);
         mPatchHandle = in.readInt();
         mClientPackageName = in.readString();
         mClientUid = in.readInt();
+        mClientPortId = in.readInt();
+        mClientSilenced = in.readBoolean();
+        mDeviceSource = in.readInt();
+        mClientEffects = AudioEffect.Descriptor.CREATOR.newArray(in.readInt());
+        for (int i = 0; i < mClientEffects.length; i++) {
+            mClientEffects[i] = AudioEffect.Descriptor.CREATOR.createFromParcel(in);
+        }
+        mDeviceEffects = AudioEffect.Descriptor.CREATOR.newArray(in.readInt());
+        for (int i = 0; i < mClientEffects.length; i++) {
+            mDeviceEffects[i] = AudioEffect.Descriptor.CREATOR.createFromParcel(in);
+        }
     }
 
     @Override
@@ -277,11 +392,16 @@
         AudioRecordingConfiguration that = (AudioRecordingConfiguration) o;
 
         return ((mClientUid == that.mClientUid)
-                && (mSessionId == that.mSessionId)
+                && (mClientSessionId == that.mClientSessionId)
                 && (mClientSource == that.mClientSource)
                 && (mPatchHandle == that.mPatchHandle)
                 && (mClientFormat.equals(that.mClientFormat))
                 && (mDeviceFormat.equals(that.mDeviceFormat))
-                && (mClientPackageName.equals(that.mClientPackageName)));
+                && (mClientPackageName.equals(that.mClientPackageName))
+                && (mClientPortId == that.mClientPortId)
+                && (mClientSilenced == that.mClientSilenced)
+                && (mDeviceSource == that.mDeviceSource)
+                && (mClientEffects.equals(that.mClientEffects))
+                && (mDeviceEffects.equals(that.mDeviceEffects)));
     }
 }
diff --git a/media/java/android/media/AudioRecordingMonitor.java b/media/java/android/media/AudioRecordingMonitor.java
new file mode 100644
index 0000000..e2605d0
--- /dev/null
+++ b/media/java/android/media/AudioRecordingMonitor.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.concurrent.Executor;
+
+/**
+ * AudioRecordingMonitor defines an interface implemented by {@link AudioRecord} and
+ * {@link MediaRecorder} allowing applications to install a callback and be notified of changes
+ * in the capture path while recoding is active.
+ */
+public interface AudioRecordingMonitor {
+    /**
+     * Register a callback to be notified of audio capture changes via a
+     * {@link AudioManager.AudioRecordingCallback}. A callback is received when the capture path
+     * configuration changes (pre-processing, format, sampling rate...) or capture is
+     * silenced/unsilenced by the system.
+     * @param executor {@link Executor} to handle the callbacks.
+     * @param cb non-null callback to register
+     */
+    void registerAudioRecordingCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull AudioManager.AudioRecordingCallback cb);
+
+    /**
+     * Unregister an audio recording callback previously registered with
+     * {@link #registerAudioRecordingCallback(Executor, AudioManager.AudioRecordingCallback)}.
+     * @param cb non-null callback to unregister
+     */
+    void unregisterAudioRecordingCallback(@NonNull AudioManager.AudioRecordingCallback cb);
+
+    /**
+     * Returns the current active audio recording for this audio recorder.
+     * @return a valid {@link AudioRecordingConfiguration} if this recorder is active
+     * or null otherwise.
+     * @see AudioRecordingConfiguration
+     */
+    @Nullable AudioRecordingConfiguration getActiveRecordingConfiguration();
+}
diff --git a/media/java/android/media/AudioRecordingMonitorClient.java b/media/java/android/media/AudioRecordingMonitorClient.java
new file mode 100644
index 0000000..7578d9b
--- /dev/null
+++ b/media/java/android/media/AudioRecordingMonitorClient.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * Interface implemented by classes using { @link AudioRecordingMonitor} interface.
+ * @hide
+ */
+public interface AudioRecordingMonitorClient {
+    /**
+     * @return the unique port ID allocated by audio framework to this recorder
+     */
+    int getPortId();
+}
diff --git a/media/java/android/media/AudioRecordingMonitorImpl.java b/media/java/android/media/AudioRecordingMonitorImpl.java
new file mode 100644
index 0000000..c2cd4bc
--- /dev/null
+++ b/media/java/android/media/AudioRecordingMonitorImpl.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Implementation of AudioRecordingMonitor interface.
+ * @hide
+ */
+public class AudioRecordingMonitorImpl implements AudioRecordingMonitor {
+
+    private static final String TAG = "android.media.AudioRecordingMonitor";
+
+    private static IAudioService sService; //lazy initialization, use getService()
+
+    private final AudioRecordingMonitorClient mClient;
+
+    AudioRecordingMonitorImpl(@NonNull AudioRecordingMonitorClient client) {
+        mClient = client;
+    }
+
+    /**
+     * Register a callback to be notified of audio capture changes via a
+     * {@link AudioManager.AudioRecordingCallback}. A callback is received when the capture path
+     * configuration changes (pre-processing, format, sampling rate...) or capture is
+     * silenced/unsilenced by the system.
+     * @param executor {@link Executor} to handle the callbacks.
+     * @param cb non-null callback to register
+     */
+    public void registerAudioRecordingCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull AudioManager.AudioRecordingCallback cb) {
+        if (cb == null) {
+            throw new IllegalArgumentException("Illegal null AudioRecordingCallback");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Illegal null Executor");
+        }
+        synchronized (mRecordCallbackLock) {
+            // check if eventCallback already in list
+            for (AudioRecordingCallbackInfo arci : mRecordCallbackList) {
+                if (arci.mCb == cb) {
+                    throw new IllegalArgumentException(
+                            "AudioRecordingCallback already registered");
+                }
+            }
+            beginRecordingCallbackHandling();
+            mRecordCallbackList.add(new AudioRecordingCallbackInfo(executor, cb));
+        }
+    }
+
+    /**
+     * Unregister an audio recording callback previously registered with
+     * {@link #registerAudioRecordingCallback(Executor, AudioManager.AudioRecordingCallback)}.
+     * @param cb non-null callback to unregister
+     */
+    public void unregisterAudioRecordingCallback(@NonNull AudioManager.AudioRecordingCallback cb) {
+        if (cb == null) {
+            throw new IllegalArgumentException("Illegal null AudioRecordingCallback argument");
+        }
+
+        synchronized (mRecordCallbackLock) {
+            for (AudioRecordingCallbackInfo arci : mRecordCallbackList) {
+                if (arci.mCb == cb) {
+                    // ok to remove while iterating over list as we exit iteration
+                    mRecordCallbackList.remove(arci);
+                    if (mRecordCallbackList.size() == 0) {
+                        endRecordingCallbackHandling();
+                    }
+                    return;
+                }
+            }
+            throw new IllegalArgumentException("AudioRecordingCallback was not registered");
+        }
+    }
+
+    /**
+     * Returns the current active audio recording for this audio recorder.
+     * @return a valid {@link AudioRecordingConfiguration} if this recorder is active
+     * or null otherwise.
+     * @see AudioRecordingConfiguration
+     */
+    public @Nullable AudioRecordingConfiguration getActiveRecordingConfiguration() {
+        final IAudioService service = getService();
+        try {
+            List<AudioRecordingConfiguration> configs = service.getActiveRecordingConfigurations();
+            return getMyConfig(configs);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private static class AudioRecordingCallbackInfo {
+        final AudioManager.AudioRecordingCallback mCb;
+        final Executor mExecutor;
+        AudioRecordingCallbackInfo(Executor e, AudioManager.AudioRecordingCallback cb) {
+            mExecutor = e;
+            mCb = cb;
+        }
+    }
+
+    private static final int MSG_RECORDING_CONFIG_CHANGE = 1;
+
+    private final Object mRecordCallbackLock = new Object();
+    @GuardedBy("mRecordCallbackLock")
+    @NonNull private LinkedList<AudioRecordingCallbackInfo> mRecordCallbackList =
+            new LinkedList<AudioRecordingCallbackInfo>();
+    @GuardedBy("mRecordCallbackLock")
+    private @Nullable HandlerThread mRecordingCallbackHandlerThread;
+    @GuardedBy("mRecordCallbackLock")
+    private @Nullable volatile Handler mRecordingCallbackHandler;
+
+    @GuardedBy("mRecordCallbackLock")
+    private final IRecordingConfigDispatcher mRecordingCallback =
+            new IRecordingConfigDispatcher.Stub() {
+        @Override
+        public void dispatchRecordingConfigChange(List<AudioRecordingConfiguration> configs) {
+            AudioRecordingConfiguration config = getMyConfig(configs);
+            if (config != null) {
+                synchronized (mRecordCallbackLock) {
+                    if (mRecordingCallbackHandler != null) {
+                        final Message m = mRecordingCallbackHandler.obtainMessage(
+                                              MSG_RECORDING_CONFIG_CHANGE/*what*/, config /*obj*/);
+                        mRecordingCallbackHandler.sendMessage(m);
+                    }
+                }
+            }
+        }
+    };
+
+    @GuardedBy("mRecordCallbackLock")
+    private void beginRecordingCallbackHandling() {
+        if (mRecordingCallbackHandlerThread == null) {
+            mRecordingCallbackHandlerThread = new HandlerThread(TAG + ".RecordingCallback");
+            mRecordingCallbackHandlerThread.start();
+            final Looper looper = mRecordingCallbackHandlerThread.getLooper();
+            if (looper != null) {
+                mRecordingCallbackHandler = new Handler(looper) {
+                    @Override
+                    public void handleMessage(Message msg) {
+                        switch (msg.what) {
+                            case MSG_RECORDING_CONFIG_CHANGE: {
+                                if (msg.obj == null) {
+                                    return;
+                                }
+                                ArrayList<AudioRecordingConfiguration> configs =
+                                        new ArrayList<AudioRecordingConfiguration>();
+                                configs.add((AudioRecordingConfiguration) msg.obj);
+
+                                final LinkedList<AudioRecordingCallbackInfo> cbInfoList;
+                                synchronized (mRecordCallbackLock) {
+                                    if (mRecordCallbackList.size() == 0) {
+                                        return;
+                                    }
+                                    cbInfoList = new LinkedList<AudioRecordingCallbackInfo>(
+                                        mRecordCallbackList);
+                                }
+
+                                final long identity = Binder.clearCallingIdentity();
+                                try {
+                                    for (AudioRecordingCallbackInfo cbi : cbInfoList) {
+                                        cbi.mExecutor.execute(() ->
+                                                cbi.mCb.onRecordingConfigChanged(configs));
+                                    }
+                                } finally {
+                                    Binder.restoreCallingIdentity(identity);
+                                }
+                            } break;
+                            default:
+                                Log.e(TAG, "Unknown event " + msg.what);
+                                break;
+                        }
+                    }
+                };
+                final IAudioService service = getService();
+                try {
+                    service.registerRecordingCallback(mRecordingCallback);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mRecordCallbackLock")
+    private void endRecordingCallbackHandling() {
+        if (mRecordingCallbackHandlerThread != null) {
+            final IAudioService service = getService();
+            try {
+                service.unregisterRecordingCallback(mRecordingCallback);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+            mRecordingCallbackHandlerThread.quit();
+            mRecordingCallbackHandlerThread = null;
+        }
+    }
+
+    AudioRecordingConfiguration getMyConfig(List<AudioRecordingConfiguration> configs) {
+        int portId = mClient.getPortId();
+        for (AudioRecordingConfiguration config : configs) {
+            if (config.getClientPortId() == portId) {
+                return config;
+            }
+        }
+        return null;
+    }
+
+    private static IAudioService getService() {
+        if (sService != null) {
+            return sService;
+        }
+        IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+        sService = IAudioService.Stub.asInterface(b);
+        return sService;
+    }
+}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 36f635a..45cde0f 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -20,6 +20,7 @@
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.media.audiofx.AudioEffect;
 import android.media.audiopolicy.AudioMix;
 import android.os.Build;
 import android.util.Log;
@@ -334,7 +335,9 @@
          * @param packName package name of the client app performing the recording. NOT SUPPORTED
          */
         void onRecordingConfigurationChanged(int event, int uid, int session, int source,
-                int[] recordingFormat, String packName);
+                        int portId, boolean silenced, int[] recordingFormat,
+                        AudioEffect.Descriptor[] clienteffects, AudioEffect.Descriptor[] effects,
+                        int activeSource, String packName);
     }
 
     private static AudioRecordingCallback sRecordingCallback;
@@ -352,19 +355,27 @@
      * @param session
      * @param source
      * @param recordingFormat see
-     *     {@link AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int, int[])}
+     *     {@link AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int, int,\
+     boolean, int[], AudioEffect.Descriptor[], AudioEffect.Descriptor[], int, String)}
      *     for the description of the record format.
      */
     @UnsupportedAppUsage
     private static void recordingCallbackFromNative(int event, int uid, int session, int source,
-            int[] recordingFormat) {
+                          int portId, boolean silenced, int[] recordingFormat,
+                          AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] effects,
+                          int activeSource) {
         AudioRecordingCallback cb = null;
         synchronized (AudioSystem.class) {
             cb = sRecordingCallback;
         }
+
+        String clientEffectName =  clientEffects.length == 0 ? "None" : clientEffects[0].name;
+        String effectName =  effects.length == 0 ? "None" : effects[0].name;
+
         if (cb != null) {
             // TODO receive package name from native
-            cb.onRecordingConfigurationChanged(event, uid, session, source, recordingFormat, "");
+            cb.onRecordingConfigurationChanged(event, uid, session, source, portId, silenced,
+                                        recordingFormat, clientEffects, effects, activeSource, "");
         }
     }
 
@@ -905,6 +916,13 @@
 
     public static native int registerPolicyMixes(ArrayList<AudioMix> mixes, boolean register);
 
+    /** see AudioPolicy.setUidDeviceAffinities() */
+    public static native int setUidDeviceAffinities(int uid, @NonNull int[] types,
+            @NonNull String[] addresses);
+
+    /** see AudioPolicy.removeUidDeviceAffinities() */
+    public static native int removeUidDeviceAffinities(int uid);
+
     public static native int systemReady();
 
     public static native float getStreamVolumeDB(int stream, int index, int device);
diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
index aa123bd..4f74ec9 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -19,6 +19,7 @@
 import android.annotation.UnsupportedAppUsage;
 import android.hardware.Camera;
 import android.hardware.Camera.CameraInfo;
+import android.os.Build;
 
 /**
  * Retrieves the
@@ -496,7 +497,7 @@
     }
 
     // Methods implemented by JNI
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static native final void native_init();
     @UnsupportedAppUsage
     private static native final CamcorderProfile native_get_camcorder_profile(
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/media/java/android/media/Controller2Link.aidl
similarity index 76%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to media/java/android/media/Controller2Link.aidl
index b7a7920..64edafc 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/media/java/android/media/Controller2Link.aidl
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.media;
 
-parcelable UsbPort;
+parcelable Controller2Link;
diff --git a/media/java/android/media/Controller2Link.java b/media/java/android/media/Controller2Link.java
new file mode 100644
index 0000000..a62db5f
--- /dev/null
+++ b/media/java/android/media/Controller2Link.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+
+import java.util.Objects;
+
+/**
+ * Handles incoming commands from {@link MediaSession2} to both {@link MediaController2}.
+ * @hide
+ */
+// @SystemApi
+public final class Controller2Link implements Parcelable {
+    private static final String TAG = "Controller2Link";
+    private static final boolean DEBUG = MediaController2.DEBUG;
+
+    public static final Parcelable.Creator<Controller2Link> CREATOR =
+            new Parcelable.Creator<Controller2Link>() {
+                @Override
+                public Controller2Link createFromParcel(Parcel in) {
+                    return new Controller2Link(in);
+                }
+
+                @Override
+                public Controller2Link[] newArray(int size) {
+                    return new Controller2Link[size];
+                }
+            };
+
+
+    private final MediaController2 mController;
+    private final IMediaController2 mIController;
+
+    public Controller2Link(MediaController2 controller) {
+        mController = controller;
+        mIController = new Controller2Stub();
+    }
+
+    Controller2Link(Parcel in) {
+        mController = null;
+        mIController = IMediaController2.Stub.asInterface(in.readStrongBinder());
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeStrongBinder(mIController.asBinder());
+    }
+
+    @Override
+    public int hashCode() {
+        return mIController.asBinder().hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof Controller2Link)) {
+            return false;
+        }
+        Controller2Link other = (Controller2Link) obj;
+        return Objects.equals(mIController.asBinder(), other.mIController.asBinder());
+    }
+
+    /** Interface method for IMediaController2.notifyConnected */
+    public void notifyConnected(int seq, Bundle connectionResult) {
+        try {
+            mIController.notifyConnected(seq, connectionResult);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** Interface method for IMediaController2.notifyDisonnected */
+    public void notifyDisconnected(int seq) {
+        try {
+            mIController.notifyDisconnected(seq);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** Interface method for IMediaController2.sendSessionCommand */
+    public void sendSessionCommand(int seq, Session2Command command, Bundle args,
+            ResultReceiver resultReceiver) {
+        try {
+            mIController.sendSessionCommand(seq, command, args, resultReceiver);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** Interface method for IMediaController2.cancelSessionCommand */
+    public void cancelSessionCommand(int seq) {
+        try {
+            mIController.cancelSessionCommand(seq);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** Stub implementation for IMediaController2.notifyConnected */
+    public void onConnected(int seq, Bundle connectionResult) {
+        if (connectionResult == null) {
+            onDisconnected(seq);
+            return;
+        }
+        mController.onConnected(seq, connectionResult);
+    }
+
+    /** Stub implementation for IMediaController2.notifyDisonnected */
+    public void onDisconnected(int seq) {
+        mController.onDisconnected(seq);
+    }
+
+    /** Stub implementation for IMediaController2.sendSessionCommand */
+    public void onSessionCommand(int seq, Session2Command command, Bundle args,
+            ResultReceiver resultReceiver) {
+        mController.onSessionCommand(seq, command, args, resultReceiver);
+    }
+
+    /** Stub implementation for IMediaController2.cancelSessionCommand */
+    public void onCancelCommand(int seq) {
+        mController.onCancelCommand(seq);
+    }
+
+    private class Controller2Stub extends IMediaController2.Stub {
+        @Override
+        public void notifyConnected(int seq, Bundle connectionResult) {
+            Controller2Link.this.onConnected(seq, connectionResult);
+        }
+
+        @Override
+        public void notifyDisconnected(int seq) {
+            Controller2Link.this.onDisconnected(seq);
+        }
+
+        @Override
+        public void sendSessionCommand(int seq, Session2Command command, Bundle args,
+                ResultReceiver resultReceiver) {
+            Controller2Link.this.onSessionCommand(seq, command, args, resultReceiver);
+        }
+
+        @Override
+        public void cancelSessionCommand(int seq) {
+            Controller2Link.this.onCancelCommand(seq);
+        }
+    }
+}
diff --git a/media/java/android/media/DataSourceCallback.java b/media/java/android/media/DataSourceCallback.java
index 0d4d531..1afcd20 100644
--- a/media/java/android/media/DataSourceCallback.java
+++ b/media/java/android/media/DataSourceCallback.java
@@ -47,7 +47,7 @@
      * @param offset the offset within buffer to read the data into.
      * @param size the number of bytes to read.
      * @throws IOException on fatal errors.
-     * @return the number of bytes read, or -1 if there was an error.
+     * @return the number of bytes read, or -1 if end of stream is reached.
      */
     public abstract int readAt(long position, byte[] buffer, int offset, int size)
             throws IOException;
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 32c4643..b9088d4 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -24,6 +24,7 @@
 import android.content.res.AssetManager;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.os.Build;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
@@ -469,7 +470,7 @@
     // See http://www.exiv2.org/makernote.html#R11
     private static final int PEF_MAKER_NOTE_SKIP_SIZE = 6;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static SimpleDateFormat sFormatter;
 
     // See Exchangeable image file format for digital still cameras: Exif version 2.2.
@@ -1303,7 +1304,7 @@
         sExifPointerTagMap.put(EXIF_POINTER_TAGS[5].number, IFD_TYPE_ORF_IMAGE_PROCESSING); // 8256
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final String mFilename;
     private final FileDescriptor mSeekableFileDescriptor;
     private final AssetManager.AssetInputStream mAssetInputStream;
@@ -1313,7 +1314,7 @@
     private final HashMap[] mAttributes = new HashMap[EXIF_TAGS.length];
     private Set<Integer> mAttributesOffsets = new HashSet<>(EXIF_TAGS.length);
     private ByteOrder mExifByteOrder = ByteOrder.BIG_ENDIAN;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private boolean mHasThumbnail;
     // The following values used for indicating a thumbnail position.
     private int mThumbnailOffset;
@@ -2177,7 +2178,7 @@
     }
 
     /** {@hide} */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static float convertRationalLatLonToFloat(String rationalString, String ref) {
         try {
             String [] parts = rationalString.split(",");
diff --git a/media/java/android/media/FileDataSourceDesc.java b/media/java/android/media/FileDataSourceDesc.java
index aca8dbe..e29bd00 100644
--- a/media/java/android/media/FileDataSourceDesc.java
+++ b/media/java/android/media/FileDataSourceDesc.java
@@ -44,6 +44,8 @@
     private ParcelFileDescriptor mPFD;
     private long mOffset = 0;
     private long mLength = FD_LENGTH_UNKNOWN;
+    private int mCount = 0;
+    private boolean mClosed = false;
 
     private FileDataSourceDesc() {
         super();
@@ -55,23 +57,48 @@
     @Override
     void close() {
         super.close();
-        closeFD();
+        decCount();
     }
 
     /**
-     * Releases the file descriptor held by this {@code FileDataSourceDesc} object.
+     * Decrements usage count by {@link MediaPlayer2}.
+     * If this is the last usage, also releases the file descriptor held by this
+     * {@code FileDataSourceDesc} object.
      */
-    void closeFD() {
+    void decCount() {
         synchronized (this) {
-            if (mPFD != null) {
-                try {
-                    mPFD.close();
-                } catch (IOException e) {
-                    Log.e(TAG, "failed to close pfd: " + e);
-                }
-
-                mPFD = null;
+            --mCount;
+            if (mCount > 0) {
+                return;
             }
+
+            try {
+                mPFD.close();
+                mClosed = true;
+            } catch (IOException e) {
+                Log.e(TAG, "failed to close pfd: " + e);
+            }
+        }
+    }
+
+    /**
+     * Increments usage count by {@link MediaPlayer2} if PFD has not been closed.
+     */
+    void incCount() {
+        synchronized (this) {
+            if (!mClosed) {
+                ++mCount;
+            }
+        }
+    }
+
+    /**
+     * Return the status of underline ParcelFileDescriptor
+     * @return true if underline ParcelFileDescriptor is closed, false otherwise.
+     */
+    boolean isPFDClosed() {
+        synchronized (this) {
+            return mClosed;
         }
     }
 
@@ -150,6 +177,16 @@
          * @return a new {@link FileDataSourceDesc} object
          */
         public @NonNull FileDataSourceDesc build() {
+            if (mPFD == null) {
+                throw new IllegalStateException(
+                        "underline ParcelFileDescriptor should not be null");
+            }
+            try {
+                mPFD.getFd();
+            } catch (IllegalStateException e) {
+                throw new IllegalStateException("ParcelFileDescriptor has been closed");
+            }
+
             FileDataSourceDesc dsd = new FileDataSourceDesc();
             super.build(dsd);
             dsd.mPFD = mPFD;
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index abd6411..9fbd7ea 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -223,6 +223,11 @@
 
     boolean isAudioServerRunning();
 
+    int setUidDeviceAffinity(in IAudioPolicyCallback pcb, in int uid, in int[] deviceTypes,
+             in String[] deviceAddresses);
+
+    int removeUidDeviceAffinity(in IAudioPolicyCallback pcb, in int uid);
+
     // WARNING: read warning at top of file, new methods that need to be used by native
     // code via IAudioManager.h need to be added to the top section.
 }
diff --git a/media/java/android/media/IMediaController2.aidl b/media/java/android/media/IMediaController2.aidl
new file mode 100644
index 0000000..ca5394f
--- /dev/null
+++ b/media/java/android/media/IMediaController2.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.media.Session2Command;
+
+/**
+ * Interface from MediaSession2 to MediaController2.
+ * <p>
+ * Keep this interface oneway. Otherwise a malicious app may implement fake version of this,
+ * and holds calls from session to make session owner(s) frozen.
+ * @hide
+ */
+ // Code for AML only
+oneway interface IMediaController2 {
+    void notifyConnected(int seq, in Bundle connectionResult) = 0;
+    void notifyDisconnected(int seq) = 1;
+    void sendSessionCommand(int seq, in Session2Command command, in Bundle args,
+            in ResultReceiver resultReceiver) = 2;
+    void cancelSessionCommand(int seq) = 3;
+    // Next Id : 4
+}
diff --git a/media/java/android/media/IMediaSession2.aidl b/media/java/android/media/IMediaSession2.aidl
new file mode 100644
index 0000000..26e717b
--- /dev/null
+++ b/media/java/android/media/IMediaSession2.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.media.Controller2Link;
+import android.media.Session2Command;
+
+/**
+ * Interface from MediaController2 to MediaSession2.
+ * <p>
+ * Keep this interface oneway. Otherwise a malicious app may implement fake version of this,
+ * and holds calls from session to make session owner(s) frozen.
+ * @hide
+ */
+ // Code for AML only
+oneway interface IMediaSession2 {
+    void connect(in Controller2Link caller, int seq, in Bundle connectionRequest) = 0;
+    void disconnect(in Controller2Link caller, int seq) = 1;
+    void sendSessionCommand(in Controller2Link caller, int seq, in Session2Command sessionCommand,
+            in Bundle args, in ResultReceiver resultReceiver) = 2;
+    void cancelSessionCommand(in Controller2Link caller, int seq) = 3;
+    // Next Id : 4
+}
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 9025829..10a1e3a 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -972,7 +972,7 @@
          * {@code null}.  The array is sorted in ascending order.
          */
         public int[] getSupportedSampleRates() {
-            return Arrays.copyOf(mSampleRates, mSampleRates.length);
+            return mSampleRates != null ? Arrays.copyOf(mSampleRates, mSampleRates.length) : null;
         }
 
         /**
diff --git a/media/java/android/media/MediaConstants.java b/media/java/android/media/MediaConstants.java
new file mode 100644
index 0000000..275b0ac
--- /dev/null
+++ b/media/java/android/media/MediaConstants.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+class MediaConstants {
+    // Bundle key for int
+    static final String KEY_PID = "android.media.key.PID";
+
+    // Bundle key for String
+    static final String KEY_PACKAGE_NAME = "android.media.key.PACKAGE_NAME";
+
+    // Bundle key for Parcelable
+    static final String KEY_SESSION2_STUB = "android.media.key.SESSION2_STUB";
+    static final String KEY_ALLOWED_COMMANDS = "android.media.key.ALLOWED_COMMANDS";
+
+    private MediaConstants() {
+    }
+}
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
new file mode 100644
index 0000000..b8381a7
--- /dev/null
+++ b/media/java/android/media/MediaController2.java
@@ -0,0 +1,369 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS;
+import static android.media.MediaConstants.KEY_PACKAGE_NAME;
+import static android.media.MediaConstants.KEY_PID;
+import static android.media.MediaConstants.KEY_SESSION2_STUB;
+import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR;
+import static android.media.Session2Command.RESULT_INFO_SKIPPED;
+import static android.media.Session2Token.TYPE_SESSION;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.ResultReceiver;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Allows an app to interact with an active {@link MediaSession2} or a
+ * {@link MediaSession2Service} which would provide {@link MediaSession2}. Media buttons and other
+ * commands can be sent to the session.
+ * <p>
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
+ * for consistent behavior across all devices.
+ * @hide
+ */
+public class MediaController2 implements AutoCloseable {
+    static final String TAG = "MediaController2";
+    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    final ControllerCallback mCallback;
+
+    private final IBinder.DeathRecipient mDeathRecipient = () -> close();
+    private final Context mContext;
+    private final Session2Token mSessionToken;
+    private final Executor mCallbackExecutor;
+    private final Controller2Link mControllerStub;
+    private final Handler mResultHandler;
+
+    private final Object mLock = new Object();
+    //@GuardedBy("mLock")
+    private int mNextSeqNumber;
+    //@GuardedBy("mLock")
+    private Session2Link mSessionBinder;
+    //@GuardedBy("mLock")
+    private Session2CommandGroup mAllowedCommands;
+    //@GuardedBy("mLock")
+    private Session2Token mConnectedToken;
+    //@GuardedBy("mLock")
+    private ArrayMap<ResultReceiver, Integer> mPendingCommands;
+    //@GuardedBy("mLock")
+    private ArraySet<Integer> mRequestedCommandSeqNumbers;
+
+    /**
+     * Create a {@link MediaController2} from the {@link Session2Token}.
+     * This connects to the session and may wake up the service if it's not available.
+     *
+     * @param context Context
+     * @param token token to connect to
+     */
+    public MediaController2(@NonNull Context context, @NonNull Session2Token token) {
+        this(context, token, context.getMainExecutor(), new ControllerCallback() {});
+    }
+
+    /**
+     * Create a {@link MediaController2} from the {@link Session2Token}.
+     * This connects to the session and may wake up the service if it's not available.
+     *
+     * @param context Context
+     * @param token token to connect to
+     * @param executor executor to run callbacks on.
+     * @param callback controller callback to receive changes in.
+     */
+    public MediaController2(@NonNull Context context, @NonNull Session2Token token,
+            @NonNull Executor executor, @NonNull ControllerCallback callback) {
+        if (context == null) {
+            throw new IllegalArgumentException("context shouldn't be null");
+        }
+        if (token == null) {
+            throw new IllegalArgumentException("token shouldn't be null");
+        }
+        mContext = context;
+        mSessionToken = token;
+        mCallbackExecutor = (executor == null) ? context.getMainExecutor() : executor;
+        mCallback = (callback == null) ? new ControllerCallback() {} : callback;
+        mControllerStub = new Controller2Link(this);
+        // NOTE: mResultHandler uses main looper, so this MUST NOT be blocked.
+        mResultHandler = new Handler(context.getMainLooper());
+
+        mNextSeqNumber = 0;
+        mPendingCommands = new ArrayMap<>();
+        mRequestedCommandSeqNumbers = new ArraySet<>();
+
+        if (token.getType() == TYPE_SESSION) {
+            connectToSession();
+        } else {
+            // TODO: Handle connect to session service.
+        }
+    }
+
+    @Override
+    public void close() {
+        synchronized (mLock) {
+            if (mSessionBinder != null) {
+                try {
+                    mSessionBinder.unlinkToDeath(mDeathRecipient, 0);
+                    mSessionBinder.disconnect(mControllerStub, getNextSeqNumber());
+                } catch (RuntimeException e) {
+                    // No-op
+                }
+            }
+            mPendingCommands.clear();
+            mRequestedCommandSeqNumbers.clear();
+            mCallbackExecutor.execute(() -> {
+                mCallback.onDisconnected(MediaController2.this);
+            });
+            mSessionBinder = null;
+        }
+    }
+
+    /**
+     * Sends a session command to the session
+     * <p>
+     * @param command the session command
+     * @param args optional arguments
+     * @return a token which will be sent together in {@link ControllerCallback#onCommandResult}
+     *        when its result is received.
+     */
+    public Object sendSessionCommand(@NonNull Session2Command command, @Nullable Bundle args) {
+        if (command == null) {
+            throw new IllegalArgumentException("command shouldn't be null");
+        }
+
+        ResultReceiver resultReceiver = new ResultReceiver(mResultHandler) {
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                synchronized (mLock) {
+                    mPendingCommands.remove(this);
+                }
+                mCallbackExecutor.execute(() -> {
+                    mCallback.onCommandResult(MediaController2.this, this,
+                            command, new Session2Command.Result(resultCode, resultData));
+                });
+            }
+        };
+
+        synchronized (mLock) {
+            if (mSessionBinder != null) {
+                int seq = getNextSeqNumber();
+                mPendingCommands.put(resultReceiver, seq);
+                try {
+                    mSessionBinder.sendSessionCommand(mControllerStub, seq, command, args,
+                            resultReceiver);
+                } catch (RuntimeException e)  {
+                    mPendingCommands.remove(resultReceiver);
+                    resultReceiver.send(RESULT_ERROR_UNKNOWN_ERROR, null);
+                }
+            }
+        }
+        return resultReceiver;
+    }
+
+    /**
+     * Cancels the session command previously sent.
+     *
+     * @param token the token which is returned from {@link #sendSessionCommand}.
+     */
+    public void cancelSessionCommand(@NonNull Object token) {
+        if (token == null) {
+            throw new IllegalArgumentException("token shouldn't be null");
+        }
+        synchronized (mLock) {
+            if (mSessionBinder == null) return;
+            Integer seq = mPendingCommands.remove(token);
+            if (seq != null) {
+                mSessionBinder.cancelSessionCommand(mControllerStub, seq);
+            }
+        }
+    }
+
+    // Called by Controller2Link.onConnected
+    void onConnected(int seq, Bundle connectionResult) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            Session2Link sessionBinder = connectionResult.getParcelable(KEY_SESSION2_STUB);
+            Session2CommandGroup allowedCommands =
+                    connectionResult.getParcelable(KEY_ALLOWED_COMMANDS);
+            if (DEBUG) {
+                Log.d(TAG, "notifyConnected sessionBinder=" + sessionBinder
+                        + ", allowedCommands=" + allowedCommands);
+            }
+            if (sessionBinder == null || allowedCommands == null) {
+                // Connection rejected.
+                close();
+                return;
+            }
+            synchronized (mLock) {
+                mSessionBinder = sessionBinder;
+                mAllowedCommands = allowedCommands;
+                // Implementation for the local binder is no-op,
+                // so can be used without worrying about deadlock.
+                sessionBinder.linkToDeath(mDeathRecipient, 0);
+                mConnectedToken = new Session2Token(mSessionToken.getUid(), TYPE_SESSION,
+                        mSessionToken.getPackageName(), sessionBinder);
+            }
+            mCallbackExecutor.execute(() -> {
+                mCallback.onConnected(MediaController2.this, allowedCommands);
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    // Called by Controller2Link.onDisconnected
+    void onDisconnected(int seq) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            // close() will call mCallback.onDisconnected
+            close();
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    // Called by Controller2Link.onSessionCommand
+    void onSessionCommand(int seq, Session2Command command, Bundle args,
+            @Nullable ResultReceiver resultReceiver) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                mRequestedCommandSeqNumbers.add(seq);
+            }
+            mCallbackExecutor.execute(() -> {
+                boolean isCanceled;
+                synchronized (mLock) {
+                    isCanceled = !mRequestedCommandSeqNumbers.remove(seq);
+                }
+                if (isCanceled) {
+                    resultReceiver.send(RESULT_INFO_SKIPPED, null);
+                    return;
+                }
+                Session2Command.Result result = mCallback.onSessionCommand(
+                        MediaController2.this, command, args);
+                if (resultReceiver != null) {
+                    if (result == null) {
+                        throw new RuntimeException("onSessionCommand shouldn't return null");
+                    } else {
+                        resultReceiver.send(result.getResultCode(), result.getResultData());
+                    }
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    // Called by Controller2Link.onSessionCommand
+    void onCancelCommand(int seq) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                mRequestedCommandSeqNumbers.remove(seq);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    private int getNextSeqNumber() {
+        synchronized (mLock) {
+            return mNextSeqNumber++;
+        }
+    }
+
+    private void connectToSession() {
+        Session2Link sessionBinder = mSessionToken.getSessionLink();
+        Bundle connectionRequest = new Bundle();
+        connectionRequest.putString(KEY_PACKAGE_NAME, mContext.getPackageName());
+        connectionRequest.putInt(KEY_PID, Process.myPid());
+
+        try {
+            sessionBinder.connect(mControllerStub, getNextSeqNumber(), connectionRequest);
+        } catch (RuntimeException e) {
+            Log.w(TAG, "Failed to call connection request. Framework will retry"
+                    + " automatically");
+        }
+    }
+
+    /**
+     * Interface for listening to change in activeness of the {@link MediaSession2}.
+     * <p>
+     * This API is not generally intended for third party application developers.
+     */
+    public abstract static class ControllerCallback {
+        /**
+         * Called when the controller is successfully connected to the session. The controller
+         * becomes available afterwards.
+         *
+         * @param controller the controller for this event
+         * @param allowedCommands commands that's allowed by the session.
+         */
+        public void onConnected(@NonNull MediaController2 controller,
+                @NonNull Session2CommandGroup allowedCommands) {}
+
+        /**
+         * Called when the session refuses the controller or the controller is disconnected from
+         * the session. The controller becomes unavailable afterwards and the callback wouldn't
+         * be called.
+         * <p>
+         * It will be also called after the {@link #close()}, so you can put clean up code here.
+         * You don't need to call {@link #close()} after this.
+         *
+         * @param controller the controller for this event
+         */
+        public void onDisconnected(@NonNull MediaController2 controller) {}
+
+        /**
+         * Called when the connected session sent a session command.
+         *
+         * @param controller the controller for this event
+         * @param command the session command
+         * @param args optional arguments
+         * @return the result for the session command. A runtime exception will be thrown if null
+         *         is returned.
+         */
+        @NonNull
+        public Session2Command.Result onSessionCommand(@NonNull MediaController2 controller,
+                @NonNull Session2Command command, @Nullable Bundle args) {
+            return null;
+        }
+
+        /**
+         * Called when the command sent to the connected session is finished.
+         *
+         * @param controller the controller for this event
+         * @param token the token got from {@link MediaController2#sendSessionCommand}
+         * @param command the session command
+         * @param result the result of the session command
+         */
+        public void onCommandResult(@NonNull MediaController2 controller, @NonNull Object token,
+                @NonNull Session2Command command, @NonNull Session2Command.Result result) {}
+    }
+}
diff --git a/media/java/android/media/MediaDataSource.java b/media/java/android/media/MediaDataSource.java
index 4ba2120..4bdc1ad 100644
--- a/media/java/android/media/MediaDataSource.java
+++ b/media/java/android/media/MediaDataSource.java
@@ -46,7 +46,7 @@
      * @param offset the offset within buffer to read the data into.
      * @param size the number of bytes to read.
      * @throws IOException on fatal errors.
-     * @return the number of bytes read, or -1 if there was an error.
+     * @return the number of bytes read, or -1 if end of stream is reached.
      */
     public abstract int readAt(long position, byte[] buffer, int offset, int size)
             throws IOException;
diff --git a/media/java/android/media/MediaItem2.java b/media/java/android/media/MediaItem2.java
new file mode 100644
index 0000000..c496cf7
--- /dev/null
+++ b/media/java/android/media/MediaItem2.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import static android.media.MediaMetadata.METADATA_KEY_MEDIA_ID;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * A class with information on a single media item with the metadata information.
+ * <p>
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
+ * for consistent behavior across all devices.
+ * <p>
+ */
+public final class MediaItem2 implements Parcelable {
+    private static final String TAG = "MediaItem2";
+
+    // intentionally less than long.MAX_VALUE.
+    // Declare this first to avoid 'illegal forward reference'.
+    static final long LONG_MAX = 0x7ffffffffffffffL;
+
+    /**
+     * Used when a position is unknown.
+     *
+     * @see #getEndPosition()
+     */
+    public static final long POSITION_UNKNOWN = LONG_MAX;
+
+    public static final Parcelable.Creator<MediaItem2> CREATOR =
+            new Parcelable.Creator<MediaItem2>() {
+                @Override
+                public MediaItem2 createFromParcel(Parcel in) {
+                    return new MediaItem2(in);
+                }
+
+                @Override
+                public MediaItem2[] newArray(int size) {
+                    return new MediaItem2[size];
+                }
+            };
+
+    private static final long UNKNOWN_TIME = -1;
+
+    private final long mStartPositionMs;
+    private final long mEndPositionMs;
+
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private MediaMetadata mMetadata;
+    @GuardedBy("mLock")
+    private final List<Pair<OnMetadataChangedListener, Executor>> mListeners = new ArrayList<>();
+
+    /**
+     * Used by {@link MediaItem2.Builder}.
+     */
+    // Note: Needs to be protected when we want to allow 3rd party player to define customized
+    //       MediaItem2.
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    MediaItem2(Builder builder) {
+        this(builder.mMetadata, builder.mStartPositionMs, builder.mEndPositionMs);
+    }
+
+    /**
+     * Used by Parcelable.Creator.
+     */
+    // Note: Needs to be protected when we want to allow 3rd party player to define customized
+    //       MediaItem2.
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    MediaItem2(Parcel in) {
+        this(in.readParcelable(MediaItem2.class.getClassLoader()), in.readLong(), in.readLong());
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    MediaItem2(MediaItem2 item) {
+        this(item.mMetadata, item.mStartPositionMs, item.mEndPositionMs);
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    MediaItem2(@Nullable MediaMetadata metadata, long startPositionMs, long endPositionMs) {
+        if (startPositionMs > endPositionMs) {
+            throw new IllegalArgumentException("Illegal start/end position: "
+                    + startPositionMs + " : " + endPositionMs);
+        }
+        if (metadata != null && metadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) {
+            long durationMs = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
+            if (durationMs != UNKNOWN_TIME && endPositionMs != POSITION_UNKNOWN
+                    && endPositionMs > durationMs) {
+                throw new IllegalArgumentException("endPositionMs shouldn't be greater than"
+                        + " duration in the metdata, endPositionMs=" + endPositionMs
+                        + ", durationMs=" + durationMs);
+            }
+        }
+        mMetadata = metadata;
+        mStartPositionMs = startPositionMs;
+        mEndPositionMs = endPositionMs;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder(getClass().getSimpleName());
+        synchronized (mLock) {
+            sb.append("{mMetadata=").append(mMetadata);
+            sb.append(", mStartPositionMs=").append(mStartPositionMs);
+            sb.append(", mEndPositionMs=").append(mEndPositionMs);
+            sb.append('}');
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Sets metadata. If the metadata is not {@code null}, its id should be matched with this
+     * instance's media id.
+     *
+     * @param metadata metadata to update
+     * @see MediaMetadata#METADATA_KEY_MEDIA_ID
+     */
+    public void setMetadata(@Nullable MediaMetadata metadata) {
+        List<Pair<OnMetadataChangedListener, Executor>> listeners = new ArrayList<>();
+        synchronized (mLock) {
+            if (mMetadata != null && metadata != null
+                    && !TextUtils.equals(getMediaId(), metadata.getString(METADATA_KEY_MEDIA_ID))) {
+                Log.d(TAG, "MediaItem2's media ID shouldn't be changed");
+                return;
+            }
+            mMetadata = metadata;
+            listeners.addAll(mListeners);
+        }
+
+        for (Pair<OnMetadataChangedListener, Executor> pair : listeners) {
+            final OnMetadataChangedListener listener = pair.first;
+            pair.second.execute(new Runnable() {
+                @Override
+                public void run() {
+                    listener.onMetadataChanged(MediaItem2.this);
+                }
+            });
+        }
+    }
+
+    /**
+     * Gets the metadata of the media.
+     *
+     * @return metadata from the session
+     */
+    public @Nullable MediaMetadata getMetadata() {
+        synchronized (mLock) {
+            return mMetadata;
+        }
+    }
+
+    /**
+     * Return the position in milliseconds at which the playback will start.
+     * @return the position in milliseconds at which the playback will start
+     */
+    public long getStartPosition() {
+        return mStartPositionMs;
+    }
+
+    /**
+     * Return the position in milliseconds at which the playback will end.
+     * {@link #POSITION_UNKNOWN} means ending at the end of source content.
+     * @return the position in milliseconds at which the playback will end
+     */
+    public long getEndPosition() {
+        return mEndPositionMs;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mMetadata, 0);
+        dest.writeLong(mStartPositionMs);
+        dest.writeLong(mEndPositionMs);
+    }
+
+    /**
+     * Gets the media id for this item. If it's not {@code null}, it's a persistent unique key
+     * for the underlying media content.
+     *
+     * @return media Id from the session
+     */
+    @Nullable String getMediaId() {
+        synchronized (mLock) {
+            return mMetadata != null
+                    ? mMetadata.getString(METADATA_KEY_MEDIA_ID) : null;
+        }
+    }
+
+    void addOnMetadataChangedListener(Executor executor, OnMetadataChangedListener listener) {
+        synchronized (mLock) {
+            for (Pair<OnMetadataChangedListener, Executor> pair : mListeners) {
+                if (pair.first == listener) {
+                    return;
+                }
+            }
+            mListeners.add(new Pair<>(listener, executor));
+        }
+    }
+
+    void removeOnMetadataChangedListener(OnMetadataChangedListener listener) {
+        synchronized (mLock) {
+            for (int i = mListeners.size() - 1; i >= 0; i--) {
+                if (mListeners.get(i).first == listener) {
+                    mListeners.remove(i);
+                    return;
+                }
+            }
+        }
+    }
+
+    /**
+     * Builder for {@link MediaItem2}.
+     */
+    public static class Builder {
+        @SuppressWarnings("WeakerAccess") /* synthetic access */
+        MediaMetadata mMetadata;
+        @SuppressWarnings("WeakerAccess") /* synthetic access */
+        long mStartPositionMs = 0;
+        @SuppressWarnings("WeakerAccess") /* synthetic access */
+        long mEndPositionMs = POSITION_UNKNOWN;
+
+        /**
+         * Set the metadata of this instance. {@code null} for unset.
+         *
+         * @param metadata metadata
+         * @return this instance for chaining
+         */
+        public @NonNull Builder setMetadata(@Nullable MediaMetadata metadata) {
+            mMetadata = metadata;
+            return this;
+        }
+
+        /**
+         * Sets the start position in milliseconds at which the playback will start.
+         * Any negative number is treated as 0.
+         *
+         * @param position the start position in milliseconds at which the playback will start
+         * @return the same Builder instance.
+         */
+        public @NonNull Builder setStartPosition(long position) {
+            if (position < 0) {
+                position = 0;
+            }
+            mStartPositionMs = position;
+            return this;
+        }
+
+        /**
+         * Sets the end position in milliseconds at which the playback will end.
+         * Any negative number is treated as maximum length of the media item.
+         *
+         * @param position the end position in milliseconds at which the playback will end
+         * @return the same Builder instance.
+         */
+        public @NonNull Builder setEndPosition(long position) {
+            if (position < 0) {
+                position = POSITION_UNKNOWN;
+            }
+            mEndPositionMs = position;
+            return this;
+        }
+
+        /**
+         * Build {@link MediaItem2}.
+         *
+         * @return a new {@link MediaItem2}.
+         */
+        public @NonNull MediaItem2 build() {
+            return new MediaItem2(this);
+        }
+    }
+
+    interface OnMetadataChangedListener {
+        void onMetadataChanged(MediaItem2 item);
+    }
+}
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 00a393a..33b8c42 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -25,6 +25,7 @@
 import android.content.res.AssetFileDescriptor;
 import android.graphics.Bitmap;
 import android.net.Uri;
+import android.os.Build;
 import android.os.IBinder;
 
 import java.io.FileDescriptor;
@@ -40,8 +41,7 @@
  * MediaMetadataRetriever class provides a unified interface for retrieving
  * frame and meta data from an input media file.
  */
-public class MediaMetadataRetriever
-{
+public class MediaMetadataRetriever implements AutoCloseable {
     static {
         System.loadLibrary("media_jni");
         native_init();
@@ -672,17 +672,22 @@
     @UnsupportedAppUsage
     private native byte[] getEmbeddedPicture(int pictureType);
 
+    @Override
+    public void close() {
+        release();
+    }
+
     /**
      * Call it when one is done with the object. This method releases the memory
      * allocated internally.
      */
     public native void release();
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private native void native_setup();
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static native void native_init();
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private native final void native_finalize();
 
     @Override
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 18d36eb..fb18c3b 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -19,15 +19,16 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityThread;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
+import android.graphics.SurfaceTexture;
+import android.media.SubtitleController.Anchor;
+import android.media.SubtitleTrack.RenderingWidget;
 import android.net.Uri;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -36,30 +37,19 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
-import android.os.Process;
 import android.os.PowerManager;
+import android.os.Process;
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
-import android.util.ArrayMap;
 import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.widget.VideoView;
-import android.graphics.SurfaceTexture;
-import android.media.AudioManager;
-import android.media.MediaDrm;
-import android.media.MediaFormat;
-import android.media.MediaTimeProvider;
-import android.media.PlaybackParams;
-import android.media.SubtitleController;
-import android.media.SubtitleController.Anchor;
-import android.media.SubtitleData;
-import android.media.SubtitleTrack.RenderingWidget;
-import android.media.SyncParams;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
@@ -73,7 +63,6 @@
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.lang.Runnable;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
@@ -1680,37 +1669,6 @@
     public native boolean isPlaying();
 
     /**
-     * Gets the current buffering management params used by the source component.
-     * Calling it only after {@code setDataSource} has been called.
-     * Each type of data source might have different set of default params.
-     *
-     * @return the current buffering management params used by the source component.
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized, or {@code setDataSource} has not been called.
-     * @hide
-     */
-    @NonNull
-    @TestApi
-    public native BufferingParams getBufferingParams();
-
-    /**
-     * Sets buffering management params.
-     * The object sets its internal BufferingParams to the input, except that the input is
-     * invalid or not supported.
-     * Call it only after {@code setDataSource} has been called.
-     * The input is a hint to MediaPlayer.
-     *
-     * @param params the buffering management params.
-     *
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized or has been released, or {@code setDataSource} has not been called.
-     * @throws IllegalArgumentException if params is invalid or not supported.
-     * @hide
-     */
-    @TestApi
-    public native void setBufferingParams(@NonNull BufferingParams params);
-
-    /**
      * Change playback speed of audio by resampling the audio.
      * <p>
      * Specifies resampling as audio mode for variable rate playback, i.e.,
@@ -2137,9 +2095,11 @@
         mOnInfoListener = null;
         mOnVideoSizeChangedListener = null;
         mOnTimedTextListener = null;
-        if (mTimeProvider != null) {
-            mTimeProvider.close();
-            mTimeProvider = null;
+        synchronized (mTimeProviderLock) {
+            if (mTimeProvider != null) {
+                mTimeProvider.close();
+                mTimeProvider = null;
+            }
         }
         synchronized(this) {
             mSubtitleDataListenerDisabled = false;
@@ -2179,9 +2139,11 @@
         if (mSubtitleController != null) {
             mSubtitleController.reset();
         }
-        if (mTimeProvider != null) {
-            mTimeProvider.close();
-            mTimeProvider = null;
+        synchronized (mTimeProviderLock) {
+            if (mTimeProvider != null) {
+                mTimeProvider.close();
+                mTimeProvider = null;
+            }
         }
 
         stayAwake(false);
@@ -2822,12 +2784,17 @@
                 synchronized (mIndexTrackPairs) {
                     mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(null, track));
                 }
-                Handler h = mTimeProvider.mEventHandler;
-                int what = TimeProvider.NOTIFY;
-                int arg1 = TimeProvider.NOTIFY_TRACK_DATA;
-                Pair<SubtitleTrack, byte[]> trackData = Pair.create(track, contents.getBytes());
-                Message m = h.obtainMessage(what, arg1, 0, trackData);
-                h.sendMessage(m);
+                synchronized (mTimeProviderLock) {
+                    if (mTimeProvider != null) {
+                        Handler h = mTimeProvider.mEventHandler;
+                        int what = TimeProvider.NOTIFY;
+                        int arg1 = TimeProvider.NOTIFY_TRACK_DATA;
+                        Pair<SubtitleTrack, byte[]> trackData =
+                                Pair.create(track, contents.getBytes());
+                        Message m = h.obtainMessage(what, arg1, 0, trackData);
+                        h.sendMessage(m);
+                    }
+                }
                 return MEDIA_INFO_EXTERNAL_METADATA_UPDATE;
             }
 
@@ -3052,12 +3019,17 @@
                             total += bytes;
                         }
                     }
-                    Handler h = mTimeProvider.mEventHandler;
-                    int what = TimeProvider.NOTIFY;
-                    int arg1 = TimeProvider.NOTIFY_TRACK_DATA;
-                    Pair<SubtitleTrack, byte[]> trackData = Pair.create(track, bos.toByteArray());
-                    Message m = h.obtainMessage(what, arg1, 0, trackData);
-                    h.sendMessage(m);
+                    synchronized (mTimeProviderLock) {
+                        if (mTimeProvider != null) {
+                            Handler h = mTimeProvider.mEventHandler;
+                            int what = TimeProvider.NOTIFY;
+                            int arg1 = TimeProvider.NOTIFY_TRACK_DATA;
+                            Pair<SubtitleTrack, byte[]> trackData =
+                                    Pair.create(track, bos.toByteArray());
+                            Message m = h.obtainMessage(what, arg1, 0, trackData);
+                            h.sendMessage(m);
+                        }
+                    }
                     return MEDIA_INFO_EXTERNAL_METADATA_UPDATE;
                 } catch (Exception e) {
                     Log.e(TAG, e.getMessage(), e);
@@ -3340,14 +3312,17 @@
     private static final int MEDIA_AUDIO_ROUTING_CHANGED = 10000;
 
     private TimeProvider mTimeProvider;
+    private final Object mTimeProviderLock = new Object();
 
     /** @hide */
     @UnsupportedAppUsage
     public MediaTimeProvider getMediaTimeProvider() {
-        if (mTimeProvider == null) {
-            mTimeProvider = new TimeProvider(this);
+        synchronized (mTimeProviderLock) {
+            if (mTimeProvider == null) {
+                mTimeProvider = new TimeProvider(this);
+            }
+            return mTimeProvider;
         }
-        return mTimeProvider;
     }
 
     private class EventHandler extends Handler
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index d4b1c7f..cef6614 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -436,13 +436,6 @@
      */
     // This is a synchronous call.
     public void reset() {
-        synchronized (mEventCbLock) {
-            mEventCallbackRecords.clear();
-        }
-        synchronized (mDrmEventCbLock) {
-            mDrmEventCallbackRecords.clear();
-        }
-
         clearSourceInfos();
 
         stayAwake(false);
@@ -544,32 +537,55 @@
     public native long getCurrentPosition();
 
     /**
-     * Gets the duration of the file.
+     * Gets the duration of the dsd.
      *
+     * @param dsd the descriptor of data source of which you want to get duration
      * @return the duration in milliseconds, if no duration is available
      *         (for example, if streaming live content), -1 is returned.
+     * @throws NullPointerException if dsd is null
      */
-    public native long getDuration();
+    public long getDuration(@NonNull DataSourceDesc dsd) {
+        if (dsd == null) {
+            throw new NullPointerException("non-null dsd is expected");
+        }
+        SourceInfo sourceInfo = getSourceInfo(dsd);
+        if (sourceInfo == null) {
+            return -1;
+        }
+
+        return native_getDuration(sourceInfo.mId);
+    }
+
+    private native long native_getDuration(long srcId);
 
     /**
-     * Gets the current buffered media source position received through progressive downloading.
+     * Gets the buffered media source position of given dsd.
      * For example a buffering update of 8000 milliseconds when 5000 milliseconds of the content
      * has already been played indicates that the next 3000 milliseconds of the
      * content to play has been buffered.
      *
+     * @param dsd the descriptor of data source of which you want to get buffered position
      * @return the current buffered media source position in milliseconds
+     * @throws NullPointerException if dsd is null
      */
-    public long getBufferedPosition() {
-        // Use cached buffered percent for now.
-        int bufferedPercentage;
-        synchronized (mSrcLock) {
-            if (mCurrentSourceInfo == null) {
-                bufferedPercentage = 0;
-            } else {
-                bufferedPercentage = mCurrentSourceInfo.mBufferedPercentage.get();
-            }
+    public long getBufferedPosition(@NonNull DataSourceDesc dsd) {
+        if (dsd == null) {
+            throw new NullPointerException("non-null dsd is expected");
         }
-        return getDuration() * bufferedPercentage / 100;
+        SourceInfo sourceInfo = getSourceInfo(dsd);
+        if (sourceInfo == null) {
+            return 0;
+        }
+
+        // Use cached buffered percent for now.
+        int bufferedPercentage = sourceInfo.mBufferedPercentage.get();
+
+        long duration = getDuration(dsd);
+        if (duration < 0) {
+            duration = 0;
+        }
+
+        return duration * bufferedPercentage / 100;
     }
 
     /**
@@ -673,7 +689,7 @@
         return addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
             @Override
             void process() throws IOException {
-                Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+                checkDataSourceDesc(dsd);
                 int state = getState();
                 try {
                     if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) {
@@ -706,7 +722,7 @@
         return addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
             @Override
             void process() {
-                Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+                checkDataSourceDesc(dsd);
                 synchronized (mSrcLock) {
                     clearNextSourceInfos_l();
                     mNextSourceInfos.add(new SourceInfo(dsd));
@@ -732,15 +748,35 @@
                 if (dsds == null || dsds.size() == 0) {
                     throw new IllegalArgumentException("data source list cannot be null or empty.");
                 }
+                boolean hasError = false;
+                for (DataSourceDesc dsd : dsds) {
+                    if (dsd != null) {
+                        hasError = true;
+                        continue;
+                    }
+                    if (dsd instanceof FileDataSourceDesc) {
+                        FileDataSourceDesc fdsd = (FileDataSourceDesc) dsd;
+                        if (fdsd.isPFDClosed()) {
+                            hasError = true;
+                            continue;
+                        }
+
+                        fdsd.incCount();
+                    }
+                }
+                if (hasError) {
+                    for (DataSourceDesc dsd : dsds) {
+                        if (dsd != null) {
+                            dsd.close();
+                        }
+                    }
+                    throw new IllegalArgumentException("invalid data source list");
+                }
 
                 synchronized (mSrcLock) {
                     clearNextSourceInfos_l();
                     for (DataSourceDesc dsd : dsds) {
-                        if (dsd != null) {
-                            mNextSourceInfos.add(new SourceInfo(dsd));
-                        } else {
-                            Log.w(TAG, "DataSourceDesc in the source list shall not be null.");
-                        }
+                        mNextSourceInfos.add(new SourceInfo(dsd));
                     }
                 }
                 prepareNextDataSource();
@@ -748,6 +784,20 @@
         });
     }
 
+    // throws IllegalArgumentException if dsd is null or underline PFD of dsd has been closed.
+    private void checkDataSourceDesc(DataSourceDesc dsd) {
+        if (dsd == null) {
+            throw new IllegalArgumentException("dsd is expected to be non null");
+        }
+        if (dsd instanceof FileDataSourceDesc) {
+            FileDataSourceDesc fdsd = (FileDataSourceDesc) dsd;
+            if (fdsd.isPFDClosed()) {
+                throw new IllegalArgumentException("the underline FileDescriptor has been closed");
+            }
+            fdsd.incCount();
+        }
+    }
+
     /**
      * Removes all data sources pending to be played.
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
@@ -1467,7 +1517,6 @@
 
     private native PersistableBundle native_getMetrics();
 
-
     /**
      * Gets the current buffering management params used by the source component.
      * Calling it only after {@code setDataSource} has been called.
@@ -1505,7 +1554,6 @@
 
     private native void native_setBufferingParams(@NonNull BufferingParams params);
 
-
     /**
      * Sets playback rate using {@link PlaybackParams}. The object sets its internal
      * PlaybackParams to the input. This allows the object to resume at previous speed
@@ -1969,19 +2017,31 @@
     /**
      * Returns a List of track information.
      *
+     * @param dsd the descriptor of data source of which you want to get track info
      * @return List of track info. The total number of tracks is the array length.
      * Must be called again if an external timed text source has been added after
      * addTimedTextSource method is called.
      * @throws IllegalStateException if it is called in an invalid state.
+     * @throws NullPointerException if dsd is null
      */
-    public @NonNull List<TrackInfo> getTrackInfo() {
-        TrackInfo[] trackInfo = getInbandTrackInfo();
+
+    public @NonNull List<TrackInfo> getTrackInfo(@NonNull DataSourceDesc dsd) {
+        if (dsd == null) {
+            throw new NullPointerException("non-null dsd is expected");
+        }
+        SourceInfo sourceInfo = getSourceInfo(dsd);
+        if (sourceInfo == null) {
+            return new ArrayList<TrackInfo>(0);
+        }
+
+        TrackInfo[] trackInfo = getInbandTrackInfo(sourceInfo);
         return (trackInfo != null ? Arrays.asList(trackInfo) : new ArrayList<TrackInfo>(0));
     }
 
-    private TrackInfo[] getInbandTrackInfo() throws IllegalStateException {
+    private TrackInfo[] getInbandTrackInfo(SourceInfo sourceInfo) throws IllegalStateException {
         PlayerMessage request = PlayerMessage.newBuilder()
                 .addValues(Value.newBuilder().setInt32Value(INVOKE_ID_GET_TRACK_INFO))
+                .addValues(Value.newBuilder().setInt64Value(sourceInfo.mId))
                 .build();
         PlayerMessage response = invoke(request);
         if (response == null) {
@@ -2001,9 +2061,10 @@
 
     /**
      * Returns the index of the audio, video, or subtitle track currently selected for playback,
-     * The return value is an index into the array returned by {@link #getTrackInfo()}, and can
-     * be used in calls to {@link #selectTrack(int)} or {@link #deselectTrack(int)}.
+     * The return value is an index into the array returned by {@link #getTrackInfo}, and can
+     * be used in calls to {@link #selectTrack} or {@link #deselectTrack}.
      *
+     * @param dsd the descriptor of data source of which you want to get selected track
      * @param trackType should be one of {@link TrackInfo#MEDIA_TRACK_TYPE_VIDEO},
      * {@link TrackInfo#MEDIA_TRACK_TYPE_AUDIO}, or
      * {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE}
@@ -2011,14 +2072,24 @@
      * a negative integer is returned when there is no selected track for {@code trackType} or
      * when {@code trackType} is not one of audio, video, or subtitle.
      * @throws IllegalStateException if called after {@link #close()}
+     * @throws NullPointerException if dsd is null
      *
-     * @see #getTrackInfo()
-     * @see #selectTrack(int)
-     * @see #deselectTrack(int)
+     * @see #getTrackInfo
+     * @see #selectTrack
+     * @see #deselectTrack
      */
-    public int getSelectedTrack(int trackType) {
+    public int getSelectedTrack(@NonNull DataSourceDesc dsd, int trackType) {
+        if (dsd == null) {
+            throw new NullPointerException("non-null dsd is expected");
+        }
+        SourceInfo sourceInfo = getSourceInfo(dsd);
+        if (sourceInfo == null) {
+            return -1;
+        }
+
         PlayerMessage request = PlayerMessage.newBuilder()
                 .addValues(Value.newBuilder().setInt32Value(INVOKE_ID_GET_SELECTED_TRACK))
+                .addValues(Value.newBuilder().setInt64Value(sourceInfo.mId))
                 .addValues(Value.newBuilder().setInt32Value(trackType))
                 .build();
         PlayerMessage response = invoke(request);
@@ -2049,19 +2120,20 @@
      * In addition, the support for selecting an audio track at runtime is pretty limited
      * in that an audio track can only be selected in the <em>Prepared</em> state.
      * </p>
+     * @param dsd the descriptor of data source of which you want to select track
      * @param index the index of the track to be selected. The valid range of the index
      * is 0..total number of track - 1. The total number of tracks as well as the type of
-     * each individual track can be found by calling {@link #getTrackInfo()} method.
+     * each individual track can be found by calling {@link #getTrackInfo} method.
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      *
      * @see MediaPlayer2#getTrackInfo
      */
     // This is an asynchronous call.
-    public Object selectTrack(int index) {
+    public Object selectTrack(@NonNull DataSourceDesc dsd, int index) {
         return addTask(new Task(CALL_COMPLETED_SELECT_TRACK, false) {
             @Override
             void process() {
-                selectOrDeselectTrack(index, true /* select */);
+                selectOrDeselectTrack(dsd, index, true /* select */);
             }
         });
     }
@@ -2073,28 +2145,37 @@
      * deselected. If the timed text track identified by index has not been
      * selected before, it throws an exception.
      * </p>
+     * @param dsd the descriptor of data source of which you want to deselect track
      * @param index the index of the track to be deselected. The valid range of the index
      * is 0..total number of tracks - 1. The total number of tracks as well as the type of
-     * each individual track can be found by calling {@link #getTrackInfo()} method.
+     * each individual track can be found by calling {@link #getTrackInfo} method.
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      *
      * @see MediaPlayer2#getTrackInfo
      */
     // This is an asynchronous call.
-    public Object deselectTrack(int index) {
+    public Object deselectTrack(@NonNull DataSourceDesc dsd, int index) {
         return addTask(new Task(CALL_COMPLETED_DESELECT_TRACK, false) {
             @Override
             void process() {
-                selectOrDeselectTrack(index, false /* select */);
+                selectOrDeselectTrack(dsd, index, false /* select */);
             }
         });
     }
 
-    private void selectOrDeselectTrack(int index, boolean select)
-            throws IllegalStateException {
+    private void selectOrDeselectTrack(@NonNull DataSourceDesc dsd, int index, boolean select) {
+        if (dsd == null) {
+            throw new IllegalArgumentException("non-null dsd is expected");
+        }
+        SourceInfo sourceInfo = getSourceInfo(dsd);
+        if (sourceInfo == null) {
+            return;
+        }
+
         PlayerMessage request = PlayerMessage.newBuilder()
                 .addValues(Value.newBuilder().setInt32Value(
                             select ? INVOKE_ID_SELECT_TRACK : INVOKE_ID_DESELECT_TRACK))
+                .addValues(Value.newBuilder().setInt64Value(sourceInfo.mId))
                 .addValues(Value.newBuilder().setInt32Value(index))
                 .build();
         invoke(request);
@@ -2568,7 +2649,7 @@
          * Currently only HTTP live streaming data URI's embedded with timed ID3 tags generates
          * {@link TimedMetaData}.
          *
-         * @see MediaPlayer2#selectTrack(int)
+         * @see MediaPlayer2#selectTrack
          * @see MediaPlayer2.OnTimedMetaDataAvailableListener
          * @see TimedMetaData
          *
@@ -2670,6 +2751,12 @@
                     "Illegal null Executor for the EventCallback");
         }
         synchronized (mEventCbLock) {
+            for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+                if (cb.first == executor && cb.second == eventCallback) {
+                    Log.w(TAG, "The callback has been registered before.");
+                    return;
+                }
+            }
             mEventCallbackRecords.add(new Pair(executor, eventCallback));
         }
     }
diff --git a/media/java/android/media/MediaPlayerBase.java b/media/java/android/media/MediaPlayerBase.java
deleted file mode 100644
index a426552..0000000
--- a/media/java/android/media/MediaPlayerBase.java
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * @hide
- * Base class for all media players that want media session.
- */
-public abstract class MediaPlayerBase implements AutoCloseable {
-    /**
-     * @hide
-     */
-    @IntDef({
-        PLAYER_STATE_IDLE,
-        PLAYER_STATE_PAUSED,
-        PLAYER_STATE_PLAYING,
-        PLAYER_STATE_ERROR })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface PlayerState {}
-
-    /**
-     * @hide
-     */
-    @IntDef({
-        BUFFERING_STATE_UNKNOWN,
-        BUFFERING_STATE_BUFFERING_AND_PLAYABLE,
-        BUFFERING_STATE_BUFFERING_AND_STARVED,
-        BUFFERING_STATE_BUFFERING_COMPLETE })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface BuffState {}
-
-    /**
-     * State when the player is idle, and needs configuration to start playback.
-     */
-    public static final int PLAYER_STATE_IDLE = 0;
-
-    /**
-     * State when the player's playback is paused
-     */
-    public static final int PLAYER_STATE_PAUSED = 1;
-
-    /**
-     * State when the player's playback is ongoing
-     */
-    public static final int PLAYER_STATE_PLAYING = 2;
-
-    /**
-     * State when the player is in error state and cannot be recovered self.
-     */
-    public static final int PLAYER_STATE_ERROR = 3;
-
-    /**
-     * Buffering state is unknown.
-     */
-    public static final int BUFFERING_STATE_UNKNOWN = 0;
-
-    /**
-     * Buffering state indicating the player is buffering but enough has been buffered
-     * for this player to be able to play the content.
-     * See {@link #getBufferedPosition()} for how far is buffered already.
-     */
-    public static final int BUFFERING_STATE_BUFFERING_AND_PLAYABLE = 1;
-
-    /**
-     * Buffering state indicating the player is buffering, but the player is currently starved
-     * for data, and cannot play.
-     */
-    public static final int BUFFERING_STATE_BUFFERING_AND_STARVED = 2;
-
-    /**
-     * Buffering state indicating the player is done buffering, and the remainder of the content is
-     * available for playback.
-     */
-    public static final int BUFFERING_STATE_BUFFERING_COMPLETE = 3;
-
-    /**
-     * Starts or resumes playback.
-     */
-    public abstract void play();
-
-    /**
-     * Prepares the player for playback.
-     * See {@link PlayerEventCallback#onMediaPrepared(MediaPlayerBase, DataSourceDesc)} for being
-     * notified when the preparation phase completed. During this time, the player may allocate
-     * resources required to play, such as audio and video decoders.
-     */
-    public abstract void prepare();
-
-    /**
-     * Pauses playback.
-     */
-    public abstract void pause();
-
-    /**
-     * Resets the MediaPlayerBase to its uninitialized state.
-     */
-    public abstract void reset();
-
-    /**
-     *
-     */
-    public abstract void skipToNext();
-
-    /**
-     * Moves the playback head to the specified position
-     * @param pos the new playback position expressed in ms.
-     */
-    public abstract void seekTo(long pos);
-
-    public static final long UNKNOWN_TIME = -1;
-
-    /**
-     * Gets the current playback head position.
-     * @return the current playback position in ms, or {@link #UNKNOWN_TIME} if unknown.
-     */
-    public long getCurrentPosition() { return UNKNOWN_TIME; }
-
-    /**
-     * Returns the duration of the current data source, or {@link #UNKNOWN_TIME} if unknown.
-     * @return the duration in ms, or {@link #UNKNOWN_TIME}.
-     */
-    public long getDuration() { return UNKNOWN_TIME; }
-
-    /**
-     * Gets the buffered position of current playback, or {@link #UNKNOWN_TIME} if unknown.
-     * @return the buffered position in ms, or {@link #UNKNOWN_TIME}.
-     */
-    public long getBufferedPosition() { return UNKNOWN_TIME; }
-
-    /**
-     * Returns the current player state.
-     * See also {@link PlayerEventCallback#onPlayerStateChanged(MediaPlayerBase, int)} for
-     * notification of changes.
-     * @return the current player state
-     */
-    public abstract @PlayerState int getPlayerState();
-
-    /**
-     * Returns the current buffering state of the player.
-     * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
-     * buffered.
-     * @return the buffering state.
-     */
-    public abstract @BuffState int getBufferingState();
-
-    /**
-     * Sets the {@link AudioAttributes} to be used during the playback of the media.
-     *
-     * @param attributes non-null <code>AudioAttributes</code>.
-     */
-    public abstract void setAudioAttributes(@NonNull AudioAttributes attributes);
-
-    /**
-     * Returns AudioAttributes that media player has.
-     */
-    public abstract @Nullable AudioAttributes getAudioAttributes();
-
-    /**
-     * Sets the data source to be played.
-     * @param dsd
-     */
-    public abstract void setDataSource(@NonNull DataSourceDesc dsd);
-
-    /**
-     * Sets the data source that will be played immediately after the current one is done playing.
-     * @param dsd
-     */
-    public abstract void setNextDataSource(@NonNull DataSourceDesc dsd);
-
-    /**
-     * Sets the list of data sources that will be sequentially played after the current one. Each
-     * data source is played immediately after the previous one is done playing.
-     * @param dsds
-     */
-    public abstract void setNextDataSources(@NonNull List<DataSourceDesc> dsds);
-
-    /**
-     * Returns the current data source.
-     * @return the current data source, or null if none is set, or none available to play.
-     */
-    public abstract @Nullable DataSourceDesc getCurrentDataSource();
-
-    /**
-     * Configures the player to loop on the current data source.
-     * @param loop true if the current data source is meant to loop.
-     */
-    public abstract void loopCurrent(boolean loop);
-
-    /**
-     * Sets the playback speed.
-     * A value of 1.0f is the default playback value.
-     * A negative value indicates reverse playback, check {@link #isReversePlaybackSupported()}
-     * before using negative values.<br>
-     * After changing the playback speed, it is recommended to query the actual speed supported
-     * by the player, see {@link #getPlaybackSpeed()}.
-     * @param speed
-     */
-    public abstract void setPlaybackSpeed(float speed);
-
-    /**
-     * Returns the actual playback speed to be used by the player when playing.
-     * Note that it may differ from the speed set in {@link #setPlaybackSpeed(float)}.
-     * @return the actual playback speed
-     */
-    public float getPlaybackSpeed() { return 1.0f; }
-
-    /**
-     * Indicates whether reverse playback is supported.
-     * Reverse playback is indicated by negative playback speeds, see
-     * {@link #setPlaybackSpeed(float)}.
-     * @return true if reverse playback is supported.
-     */
-    public boolean isReversePlaybackSupported() { return false; }
-
-    /**
-     * Sets the volume of the audio of the media to play, expressed as a linear multiplier
-     * on the audio samples.
-     * Note that this volume is specific to the player, and is separate from stream volume
-     * used across the platform.<br>
-     * A value of 0.0f indicates muting, a value of 1.0f is the nominal unattenuated and unamplified
-     * gain. See {@link #getMaxPlayerVolume()} for the volume range supported by this player.
-     * @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}.
-     */
-    public abstract void setPlayerVolume(float volume);
-
-    /**
-     * Returns the current volume of this player to this player.
-     * Note that it does not take into account the associated stream volume.
-     * @return the player volume.
-     */
-    public abstract float getPlayerVolume();
-
-    /**
-     * @return the maximum volume that can be used in {@link #setPlayerVolume(float)}.
-     */
-    public float getMaxPlayerVolume() { return 1.0f; }
-
-    /**
-     * Adds a callback to be notified of events for this player.
-     * @param e the {@link Executor} to be used for the events.
-     * @param cb the callback to receive the events.
-     */
-    public abstract void registerPlayerEventCallback(@NonNull Executor e,
-            @NonNull PlayerEventCallback cb);
-
-    /**
-     * Removes a previously registered callback for player events
-     * @param cb the callback to remove
-     */
-    public abstract void unregisterPlayerEventCallback(@NonNull PlayerEventCallback cb);
-
-    /**
-     * A callback class to receive notifications for events on the media player.
-     * See {@link MediaPlayerBase#registerPlayerEventCallback(Executor, PlayerEventCallback)} to
-     * register this callback.
-     */
-    public static abstract class PlayerEventCallback {
-        /**
-         * Called when the player's current data source has changed.
-         *
-         * @param mpb the player whose data source changed.
-         * @param dsd the new current data source. null, if no more data sources available.
-         */
-        public void onCurrentDataSourceChanged(@NonNull MediaPlayerBase mpb,
-                @Nullable DataSourceDesc dsd) { }
-        /**
-         * Called when the player is <i>prepared</i>, i.e. it is ready to play the content
-         * referenced by the given data source.
-         * @param mpb the player that is prepared.
-         * @param dsd the data source that the player is prepared to play.
-         */
-        public void onMediaPrepared(@NonNull MediaPlayerBase mpb, @NonNull DataSourceDesc dsd) { }
-
-        /**
-         * Called to indicate that the state of the player has changed.
-         * See {@link MediaPlayerBase#getPlayerState()} for polling the player state.
-         * @param mpb the player whose state has changed.
-         * @param state the new state of the player.
-         */
-        public void onPlayerStateChanged(@NonNull MediaPlayerBase mpb, @PlayerState int state) { }
-
-        /**
-         * Called to report buffering events for a data source.
-         * @param mpb the player that is buffering
-         * @param dsd the data source for which buffering is happening.
-         * @param state the new buffering state.
-         */
-        public void onBufferingStateChanged(@NonNull MediaPlayerBase mpb,
-                @NonNull DataSourceDesc dsd, @BuffState int state) { }
-
-        /**
-         * Called to indicate that the playback speed has changed.
-         * @param mpb the player that has changed the playback speed.
-         * @param speed the new playback speed.
-         */
-        public void onPlaybackSpeedChanged(@NonNull MediaPlayerBase mpb, float speed) { }
-
-        /**
-         * Called to indicate that {@link #seekTo(long)} is completed.
-         *
-         * @param mpb the player that has completed seeking.
-         * @param position the previous seeking request.
-         * @see #seekTo(long)
-         */
-        public void onSeekCompleted(@NonNull MediaPlayerBase mpb, long position) { }
-    }
-
-}
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 8ced021..b160029 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -16,12 +16,15 @@
 
 package android.media;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityThread;
 import android.hardware.Camera;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -40,6 +43,8 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.Executor;
+
 
 /**
  * Used to record audio and video. The recording control is based on a
@@ -83,7 +88,9 @@
  * <a href="{@docRoot}guide/topics/media/audio-capture.html">Audio Capture</a> developer guide.</p>
  * </div>
  */
-public class MediaRecorder implements AudioRouting
+public class MediaRecorder implements AudioRouting,
+                                      AudioRecordingMonitor,
+                                      AudioRecordingMonitorClient
 {
     static {
         System.loadLibrary("media_jni");
@@ -99,12 +106,12 @@
     @UnsupportedAppUsage
     private Surface mSurface;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private String mPath;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private FileDescriptor mFd;
     private File mFile;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private EventHandler mEventHandler;
     @UnsupportedAppUsage
     private OnErrorListener mOnErrorListener;
@@ -304,7 +311,7 @@
 
         /**
          * Audio source for preemptible, low-priority software hotword detection
-         * It presents the same gain and pre processing tuning as {@link #VOICE_RECOGNITION}.
+         * It presents the same gain and pre-processing tuning as {@link #VOICE_RECOGNITION}.
          * <p>
          * An application should use this audio source when it wishes to do
          * always-on software hotword detection, while gracefully giving in to any other application
@@ -473,7 +480,7 @@
         public static final int HE_AAC = 4;
         /** Enhanced Low Delay AAC (AAC-ELD) audio codec */
         public static final int AAC_ELD = 5;
-        /** Ogg Vorbis audio codec */
+        /** Ogg Vorbis audio codec (Support is optional) */
         public static final int VORBIS = 6;
         /** Opus audio codec */
         public static final int OPUS = 7;
@@ -952,7 +959,7 @@
     // native implementation
     private native void _setOutputFile(FileDescriptor fd) throws IllegalStateException, IOException;
     private native void _setNextOutputFile(FileDescriptor fd) throws IllegalStateException, IOException;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private native void _prepare() throws IllegalStateException, IOException;
 
     /**
@@ -1054,7 +1061,7 @@
         mEventHandler.removeCallbacksAndMessages(null);
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private native void native_reset();
 
     /**
@@ -1471,6 +1478,57 @@
     private native final int native_getActiveMicrophones(
             ArrayList<MicrophoneInfo> activeMicrophones);
 
+    //--------------------------------------------------------------------------
+    // Implementation of AudioRecordingMonitor interface
+    //--------------------
+
+    AudioRecordingMonitorImpl mRecordingInfoImpl =
+            new AudioRecordingMonitorImpl((AudioRecordingMonitorClient) this);
+
+    /**
+     * Register a callback to be notified of audio capture changes via a
+     * {@link AudioManager.AudioRecordingCallback}. A callback is received when the capture path
+     * configuration changes (pre-processing, format, sampling rate...) or capture is
+     * silenced/unsilenced by the system.
+     * @param executor {@link Executor} to handle the callbacks.
+     * @param cb non-null callback to register
+     */
+    public void registerAudioRecordingCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull AudioManager.AudioRecordingCallback cb) {
+        mRecordingInfoImpl.registerAudioRecordingCallback(executor, cb);
+    }
+
+    /**
+     * Unregister an audio recording callback previously registered with
+     * {@link #registerAudioRecordingCallback(Executor, AudioManager.AudioRecordingCallback)}.
+     * @param cb non-null callback to unregister
+     */
+    public void unregisterAudioRecordingCallback(@NonNull AudioManager.AudioRecordingCallback cb) {
+        mRecordingInfoImpl.unregisterAudioRecordingCallback(cb);
+    }
+
+    /**
+     * Returns the current active audio recording for this audio recorder.
+     * @return a valid {@link AudioRecordingConfiguration} if this recorder is active
+     * or null otherwise.
+     * @see AudioRecordingConfiguration
+     */
+    public @Nullable AudioRecordingConfiguration getActiveRecordingConfiguration() {
+        return mRecordingInfoImpl.getActiveRecordingConfiguration();
+    }
+
+    //---------------------------------------------------------
+    // Implementation of AudioRecordingMonitorClient interface
+    //--------------------
+    /**
+     * @hide
+     */
+    public int getPortId() {
+        return native_getPortId();
+    }
+
+    private native int native_getPortId();
+
     /**
      * Called from native code when an interesting event happens.  This method
      * just uses the EventHandler system to post the event back to the main app thread.
@@ -1512,14 +1570,14 @@
      */
     public native void release();
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static native final void native_init();
 
     @UnsupportedAppUsage
     private native final void native_setup(Object mediarecorder_this,
             String clientName, String opPackageName) throws IllegalStateException;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private native final void native_finalize();
 
     @UnsupportedAppUsage
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 90cfc53..4eed12f 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -902,6 +902,7 @@
             map.put(MediaStore.MediaColumns.SIZE, mFileSize);
             map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType);
             map.put(MediaStore.MediaColumns.IS_DRM, mIsDrm);
+            map.putNull(MediaStore.MediaColumns.HASH);
 
             String resolution = null;
             if (mWidth > 0 && mHeight > 0) {
@@ -934,7 +935,7 @@
                     }
                 } else if (MediaFile.isImageMimeType(mMimeType)) {
                     // FIXME - add DESCRIPTION
-                } else if (mScanSuccess && MediaFile.isAudioMimeType(mMimeType)) {
+                } else if (MediaFile.isAudioMimeType(mMimeType)) {
                     map.put(Audio.Media.ARTIST, (mArtist != null && mArtist.length() > 0) ?
                             mArtist : MediaStore.UNKNOWN_STRING);
                     map.put(Audio.Media.ALBUM_ARTIST, (mAlbumArtist != null &&
@@ -950,10 +951,6 @@
                     map.put(Audio.Media.DURATION, mDuration);
                     map.put(Audio.Media.COMPILATION, mCompilation);
                 }
-                if (!mScanSuccess) {
-                    // force mediaprovider to not determine the media type from the mime type
-                    map.put(Files.FileColumns.MEDIA_TYPE, 0);
-                }
             }
             return map;
         }
@@ -1056,7 +1053,7 @@
             Uri tableUri = mFilesUri;
             int mediaType = FileColumns.MEDIA_TYPE_NONE;
             MediaInserter inserter = mMediaInserter;
-            if (mScanSuccess && !mNoMedia) {
+            if (!mNoMedia) {
                 if (MediaFile.isVideoMimeType(mMimeType)) {
                     tableUri = mVideoUri;
                     mediaType = FileColumns.MEDIA_TYPE_VIDEO;
@@ -1131,7 +1128,7 @@
                 // with squashed lower case paths
                 values.remove(MediaStore.MediaColumns.DATA);
 
-                if (mScanSuccess && !mNoMedia) {
+                if (!mNoMedia) {
                     // Changing media type must be done as separate update
                     if (mediaType != entry.mMediaType) {
                         final ContentValues mediaTypeValues = new ContentValues();
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
new file mode 100644
index 0000000..1ee851f
--- /dev/null
+++ b/media/java/android/media/MediaSession2.java
@@ -0,0 +1,715 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS;
+import static android.media.MediaConstants.KEY_PACKAGE_NAME;
+import static android.media.MediaConstants.KEY_PID;
+import static android.media.MediaConstants.KEY_SESSION2_STUB;
+import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR;
+import static android.media.Session2Command.RESULT_INFO_SKIPPED;
+import static android.media.Session2Token.TYPE_SESSION;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.media.session.MediaSessionManager;
+import android.media.session.MediaSessionManager.RemoteUserInfo;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Process;
+import android.os.ResultReceiver;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Allows a media app to expose its transport controls and playback information in a process to
+ * other processes including the Android framework and other apps.
+ * <p>
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
+ * for consistent behavior across all devices.
+ * @hide
+ */
+public class MediaSession2 implements AutoCloseable {
+    static final String TAG = "MediaSession";
+    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    // Note: This checks the uniqueness of a session ID only in a single process.
+    // When the framework becomes able to check the uniqueness, this logic should be removed.
+    //@GuardedBy("MediaSession.class")
+    private static final List<String> SESSION_ID_LIST = new ArrayList<>();
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    final Object mLock = new Object();
+    //@GuardedBy("mLock")
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    final Map<Controller2Link, ControllerInfo> mConnectedControllers = new HashMap<>();
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    final Context mContext;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    final Executor mCallbackExecutor;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    final SessionCallback mCallback;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    final Session2Link mSessionStub;
+
+    private final String mSessionId;
+    private final PendingIntent mSessionActivity;
+    private final Session2Token mSessionToken;
+    private final MediaSessionManager mSessionManager;
+    private final Handler mResultHandler;
+
+    //@GuardedBy("mLock")
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    private boolean mClosed;
+
+    MediaSession2(@NonNull Context context, @NonNull String id, PendingIntent sessionActivity,
+            @NonNull Executor callbackExecutor, @NonNull SessionCallback callback) {
+        synchronized (MediaSession2.class) {
+            if (SESSION_ID_LIST.contains(id)) {
+                throw new IllegalStateException("Session ID must be unique. ID=" + id);
+            }
+            SESSION_ID_LIST.add(id);
+        }
+
+        mContext = context;
+        mSessionId = id;
+        mSessionActivity = sessionActivity;
+        mCallbackExecutor = callbackExecutor;
+        mCallback = callback;
+        mSessionStub = new Session2Link(this);
+        mSessionToken = new Session2Token(Process.myUid(), TYPE_SESSION, context.getPackageName(),
+                mSessionStub);
+        mSessionManager = (MediaSessionManager) mContext.getSystemService(
+                Context.MEDIA_SESSION_SERVICE);
+        // NOTE: mResultHandler uses main looper, so this MUST NOT be blocked.
+        mResultHandler = new Handler(context.getMainLooper());
+    }
+
+    @Override
+    public void close() {
+        try {
+            synchronized (MediaSession2.class) {
+                SESSION_ID_LIST.remove(mSessionId);
+            }
+            Collection<ControllerInfo> controllerInfos;
+            synchronized (mLock) {
+                controllerInfos = mConnectedControllers.values();
+                mConnectedControllers.clear();
+                mClosed = true;
+            }
+            for (ControllerInfo info : controllerInfos) {
+                info.notifyDisconnected();
+            }
+        } catch (Exception e) {
+            // Should not be here.
+        }
+    }
+
+    /**
+     * Returns the session ID
+     */
+    @NonNull
+    public String getSessionId() {
+        return mSessionId;
+    }
+
+    /**
+     * Returns the {@link Session2Token} for creating {@link MediaController2}.
+     */
+    @NonNull
+    public Session2Token getSessionToken() {
+        return mSessionToken;
+    }
+
+    /**
+     * Broadcasts a session command to all the connected controllers
+     * <p>
+     * @param command the session command
+     * @param args optional arguments
+     */
+    public void broadcastSessionCommand(@NonNull Session2Command command, @Nullable Bundle args) {
+        if (command == null) {
+            throw new IllegalArgumentException("command shouldn't be null");
+        }
+        Collection<ControllerInfo> controllerInfos;
+        synchronized (mLock) {
+            controllerInfos = mConnectedControllers.values();
+        }
+        for (ControllerInfo controller : controllerInfos) {
+            controller.sendSessionCommand(command, args, null);
+        }
+    }
+
+    /**
+     * Sends a session command to a specific controller
+     * <p>
+     * @param controller the controller to get the session command
+     * @param command the session command
+     * @param args optional arguments
+     * @return a token which will be sent together in {@link SessionCallback#onCommandResult}
+     *     when its result is received.
+     */
+    public Object sendSessionCommand(@NonNull ControllerInfo controller,
+            @NonNull Session2Command command, @Nullable Bundle args) {
+        if (controller == null) {
+            throw new IllegalArgumentException("controller shouldn't be null");
+        }
+        if (command == null) {
+            throw new IllegalArgumentException("command shouldn't be null");
+        }
+        ResultReceiver resultReceiver = new ResultReceiver(mResultHandler) {
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                controller.receiveCommandResult(this);
+                mCallbackExecutor.execute(() -> {
+                    mCallback.onCommandResult(MediaSession2.this, controller, this,
+                            command, new Session2Command.Result(resultCode, resultData));
+                });
+            }
+        };
+        controller.sendSessionCommand(command, args, resultReceiver);
+        return resultReceiver;
+    }
+
+    /**
+     * Cancels the session command previously sent.
+     *
+     * @param controller the controller to get the session command
+     * @param token the token which is returned from {@link #sendSessionCommand}.
+     */
+    public void cancelSessionCommand(ControllerInfo controller, Object token) {
+        if (token == null) {
+            throw new IllegalArgumentException("token shouldn't be null");
+        }
+        controller.cancelSessionCommand(token);
+    }
+
+    boolean isClosed() {
+        synchronized (mLock) {
+            return mClosed;
+        }
+    }
+
+    // Called by Session2Link.onConnect
+    void onConnect(final Controller2Link controller, int seq, Bundle connectionRequest) {
+        if (controller == null || connectionRequest == null) {
+            return;
+        }
+        final int uid = Binder.getCallingUid();
+        final int callingPid = Binder.getCallingPid();
+        final long token = Binder.clearCallingIdentity();
+        // Binder.getCallingPid() can be 0 for an oneway call from the remote process.
+        // If it's the case, use PID from the ConnectionRequest.
+        final int pid = (callingPid != 0) ? callingPid : connectionRequest.getInt(KEY_PID);
+        final String pkg = connectionRequest.getString(KEY_PACKAGE_NAME);
+        try {
+            RemoteUserInfo remoteUserInfo = new RemoteUserInfo(pkg, pid, uid);
+            final ControllerInfo controllerInfo = new ControllerInfo(remoteUserInfo,
+                    mSessionManager.isTrustedForMediaControl(remoteUserInfo), controller);
+            mCallbackExecutor.execute(() -> {
+                if (isClosed()) {
+                    return;
+                }
+                controllerInfo.mAllowedCommands =
+                        mCallback.onConnect(MediaSession2.this, controllerInfo);
+                // Don't reject connection for the request from trusted app.
+                // Otherwise server will fail to retrieve session's information to dispatch
+                // media keys to.
+                boolean accept =
+                        controllerInfo.mAllowedCommands != null || controllerInfo.isTrusted();
+                if (accept) {
+                    if (controllerInfo.mAllowedCommands == null) {
+                        // For trusted apps, send non-null allowed commands to keep
+                        // connection.
+                        controllerInfo.mAllowedCommands = new Session2CommandGroup();
+                    }
+                    if (DEBUG) {
+                        Log.d(TAG, "Accepting connection: " + controllerInfo);
+                    }
+                    synchronized (mLock) {
+                        if (mConnectedControllers.containsKey(controller)) {
+                            Log.w(TAG, "Controller " + controllerInfo + " has sent connection"
+                                    + " request multiple times");
+                        }
+                        mConnectedControllers.put(controller, controllerInfo);
+                    }
+                    // If connection is accepted, notify the current state to the controller.
+                    // It's needed because we cannot call synchronous calls between
+                    // session/controller.
+                    Bundle connectionResult = new Bundle();
+                    connectionResult.putParcelable(KEY_SESSION2_STUB, mSessionStub);
+                    connectionResult.putParcelable(KEY_ALLOWED_COMMANDS,
+                            controllerInfo.mAllowedCommands);
+
+                    // Double check if session is still there, because close() can be called in
+                    // another thread.
+                    if (isClosed()) {
+                        return;
+                    }
+                    controllerInfo.notifyConnected(connectionResult);
+                } else {
+                    if (DEBUG) {
+                        Log.d(TAG, "Rejecting connection, controllerInfo=" + controllerInfo);
+                    }
+                    controllerInfo.notifyDisconnected();
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    // Called by Session2Link.onDisconnect
+    void onDisconnect(final Controller2Link controller, int seq) {
+        if (controller == null) {
+            return;
+        }
+        final ControllerInfo controllerInfo;
+        synchronized (mLock) {
+            controllerInfo = mConnectedControllers.get(controller);
+        }
+        if (controllerInfo == null) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mCallbackExecutor.execute(() -> {
+                mCallback.onDisconnected(MediaSession2.this, controllerInfo);
+            });
+            mConnectedControllers.remove(controller);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    // Called by Session2Link.onSessionCommand
+    void onSessionCommand(final Controller2Link controller, final int seq,
+            final Session2Command command, final Bundle args,
+            @Nullable ResultReceiver resultReceiver) {
+        if (controller == null) {
+            return;
+        }
+        final ControllerInfo controllerInfo;
+        synchronized (mLock) {
+            controllerInfo = mConnectedControllers.get(controller);
+        }
+        if (controllerInfo == null) {
+            return;
+        }
+
+        // TODO: check allowed commands.
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                controllerInfo.addRequestedCommandSeqNumber(seq);
+            }
+
+            mCallbackExecutor.execute(() -> {
+                if (!controllerInfo.removeRequestedCommandSeqNumber(seq)) {
+                    resultReceiver.send(RESULT_INFO_SKIPPED, null);
+                    return;
+                }
+                Session2Command.Result result = mCallback.onSessionCommand(
+                        MediaSession2.this, controllerInfo, command, args);
+                if (resultReceiver != null) {
+                    if (result == null) {
+                        throw new RuntimeException("onSessionCommand shouldn't return null");
+                    } else {
+                        resultReceiver.send(result.getResultCode(), result.getResultData());
+                    }
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    // Called by Session2Link.onCancelCommand
+    void onCancelCommand(final Controller2Link controller, final int seq) {
+        final ControllerInfo controllerInfo;
+        synchronized (mLock) {
+            controllerInfo = mConnectedControllers.get(controller);
+        }
+        if (controllerInfo == null) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            controllerInfo.removeRequestedCommandSeqNumber(seq);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    /**
+     * Builder for {@link MediaSession2}.
+     * <p>
+     * Any incoming event from the {@link MediaController2} will be handled on the callback
+     * executor. If it's not set, {@link Context#getMainExecutor()} will be used by default.
+     */
+    public static final class Builder {
+        private Context mContext;
+        private String mId;
+        private PendingIntent mSessionActivity;
+        private Executor mCallbackExecutor;
+        private SessionCallback mCallback;
+
+        /**
+         * Creates a builder for {@link MediaSession2}.
+         *
+         * @param context Context
+         * @throws IllegalArgumentException if context is {@code null}.
+         */
+        public Builder(@NonNull Context context) {
+            if (context == null) {
+                throw new IllegalArgumentException("context shouldn't be null");
+            }
+            mContext = context;
+        }
+
+        /**
+         * Set an intent for launching UI for this Session. This can be used as a
+         * quick link to an ongoing media screen. The intent should be for an
+         * activity that may be started using {@link Context#startActivity(Intent)}.
+         *
+         * @param pi The intent to launch to show UI for this session.
+         * @return The Builder to allow chaining
+         */
+        @NonNull
+        public Builder setSessionActivity(@Nullable PendingIntent pi) {
+            mSessionActivity = pi;
+            return this;
+        }
+
+        /**
+         * Set ID of the session. If it's not set, an empty string will be used to create a session.
+         * <p>
+         * Use this if and only if your app supports multiple playback at the same time and also
+         * wants to provide external apps to have finer controls of them.
+         *
+         * @param id id of the session. Must be unique per package.
+         * @throws IllegalArgumentException if id is {@code null}.
+         * @return The Builder to allow chaining
+         */
+        @NonNull
+        public Builder setId(@NonNull String id) {
+            if (id == null) {
+                throw new IllegalArgumentException("id shouldn't be null");
+            }
+            mId = id;
+            return this;
+        }
+
+        /**
+         * Set callback for the session and its executor.
+         *
+         * @param executor callback executor
+         * @param callback session callback.
+         * @return The Builder to allow chaining
+         */
+        @NonNull
+        public Builder setSessionCallback(@NonNull Executor executor,
+                @NonNull SessionCallback callback) {
+            mCallbackExecutor = executor;
+            mCallback = callback;
+            return this;
+        }
+
+        /**
+         * Build {@link MediaSession2}.
+         *
+         * @return a new session
+         * @throws IllegalStateException if the session with the same id is already exists for the
+         *      package.
+         */
+        @NonNull
+        public MediaSession2 build() {
+            if (mCallbackExecutor == null) {
+                mCallbackExecutor = mContext.getMainExecutor();
+            }
+            if (mCallback == null) {
+                mCallback = new SessionCallback() {};
+            }
+            if (mId == null) {
+                mId = "";
+            }
+            MediaSession2 session2 = new MediaSession2(mContext, mId, mSessionActivity,
+                    mCallbackExecutor, mCallback);
+
+            // Notify framework about the newly create session after the constructor is finished.
+            // Otherwise, framework may access the session before the initialization is finished.
+            try {
+                MediaSessionManager manager = (MediaSessionManager) mContext.getSystemService(
+                        Context.MEDIA_SESSION_SERVICE);
+                manager.notifySession2Created(session2.getSessionToken());
+            } catch (Exception e) {
+                session2.close();
+                throw e;
+            }
+
+            return session2;
+        }
+    }
+
+    /**
+     * Information of a controller.
+     * <p>
+     * This API is not generally intended for third party application developers.
+     */
+    public static final class ControllerInfo {
+        private final RemoteUserInfo mRemoteUserInfo;
+        private final boolean mIsTrusted;
+        private final Controller2Link mControllerBinder;
+        private final Object mLock = new Object();
+        //@GuardedBy("mLock")
+        private int mNextSeqNumber;
+        //@GuardedBy("mLock")
+        private ArrayMap<ResultReceiver, Integer> mPendingCommands;
+        //@GuardedBy("mLock")
+        private ArraySet<Integer> mRequestedCommandSeqNumbers;
+
+        @SuppressWarnings("WeakerAccess") /* synthetic access */
+        Session2CommandGroup mAllowedCommands;
+
+        /**
+         * @param remoteUserInfo remote user info
+         * @param trusted {@code true} if trusted, {@code false} otherwise
+         * @param controllerBinder Controller2Link for the connected controller.
+         */
+        ControllerInfo(@NonNull RemoteUserInfo remoteUserInfo, boolean trusted,
+                @Nullable Controller2Link controllerBinder) {
+            mRemoteUserInfo = remoteUserInfo;
+            mIsTrusted = trusted;
+            mControllerBinder = controllerBinder;
+            mPendingCommands = new ArrayMap<>();
+            mRequestedCommandSeqNumbers = new ArraySet<>();
+        }
+
+        /**
+         * @return remote user info of the controller.
+         */
+        @NonNull
+        public RemoteUserInfo getRemoteUserInfo() {
+            return mRemoteUserInfo;
+        }
+
+        /**
+         * @return package name of the controller.
+         */
+        @NonNull
+        public String getPackageName() {
+            return mRemoteUserInfo.getPackageName();
+        }
+
+        /**
+         * @return uid of the controller. Can be a negative value if the uid cannot be obtained.
+         */
+        public int getUid() {
+            return mRemoteUserInfo.getUid();
+        }
+
+        /**
+         * Return if the controller has granted {@code android.permission.MEDIA_CONTENT_CONTROL} or
+         * has a enabled notification listener so can be trusted to accept connection and incoming
+         * command request.
+         *
+         * @return {@code true} if the controller is trusted.
+         * @hide
+         */
+        public boolean isTrusted() {
+            return mIsTrusted;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mControllerBinder, mRemoteUserInfo);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof ControllerInfo)) return false;
+            if (this == obj) return true;
+
+            ControllerInfo other = (ControllerInfo) obj;
+            if (mControllerBinder != null || other.mControllerBinder != null) {
+                return Objects.equals(mControllerBinder, other.mControllerBinder);
+            }
+            return mRemoteUserInfo.equals(other.mRemoteUserInfo);
+        }
+
+        @Override
+        public String toString() {
+            return "ControllerInfo {pkg=" + mRemoteUserInfo.getPackageName() + ", uid="
+                    + mRemoteUserInfo.getUid() + ", allowedCommands=" + mAllowedCommands + "})";
+        }
+
+        void notifyConnected(Bundle connectionResult) {
+            if (mControllerBinder == null) return;
+
+            try {
+                mControllerBinder.notifyConnected(getNextSeqNumber(), connectionResult);
+            } catch (RuntimeException e) {
+                // Controller may be died prematurely.
+            }
+        }
+
+        void notifyDisconnected() {
+            if (mControllerBinder == null) return;
+
+            try {
+                mControllerBinder.notifyDisconnected(getNextSeqNumber());
+            } catch (RuntimeException e) {
+                // Controller may be died prematurely.
+            }
+        }
+
+        void sendSessionCommand(Session2Command command, Bundle args,
+                ResultReceiver resultReceiver) {
+            if (mControllerBinder == null) return;
+
+            try {
+                int seq = getNextSeqNumber();
+                synchronized (mLock) {
+                    mPendingCommands.put(resultReceiver, seq);
+                }
+                mControllerBinder.sendSessionCommand(seq, command, args, resultReceiver);
+            } catch (RuntimeException e) {
+                // Controller may be died prematurely.
+                synchronized (mLock) {
+                    mPendingCommands.remove(resultReceiver);
+                }
+                resultReceiver.send(RESULT_ERROR_UNKNOWN_ERROR, null);
+            }
+        }
+
+        void cancelSessionCommand(@NonNull Object token) {
+            if (mControllerBinder == null) return;
+            Integer seq;
+            synchronized (mLock) {
+                seq = mPendingCommands.remove(token);
+            }
+            if (seq != null) {
+                mControllerBinder.cancelSessionCommand(seq);
+            }
+        }
+
+        void receiveCommandResult(ResultReceiver resultReceiver) {
+            synchronized (mLock) {
+                mPendingCommands.remove(resultReceiver);
+            }
+        }
+
+        void addRequestedCommandSeqNumber(int seq) {
+            synchronized (mLock) {
+                mRequestedCommandSeqNumbers.add(seq);
+            }
+        }
+
+        boolean removeRequestedCommandSeqNumber(int seq) {
+            synchronized (mLock) {
+                return mRequestedCommandSeqNumbers.remove(seq);
+            }
+        }
+
+        private int getNextSeqNumber() {
+            synchronized (mLock) {
+                return mNextSeqNumber++;
+            }
+        }
+    }
+
+    /**
+     * Callback to be called for all incoming commands from {@link MediaController2}s.
+     * <p>
+     * This API is not generally intended for third party application developers.
+     */
+    public abstract static class SessionCallback {
+        /**
+         * Called when a controller is created for this session. Return allowed commands for
+         * controller. By default it returns {@code null}.
+         * <p>
+         * You can reject the connection by returning {@code null}. In that case, controller
+         * receives {@link MediaController2.ControllerCallback#onDisconnected(MediaController2)}
+         * and cannot be used.
+         *
+         * @param session the session for this event
+         * @param controller controller information.
+         * @return allowed commands. Can be {@code null} to reject connection.
+         */
+        @Nullable
+        public Session2CommandGroup onConnect(@NonNull MediaSession2 session,
+                @NonNull ControllerInfo controller) {
+            return null;
+        }
+
+        /**
+         * Called when a controller is disconnected
+         *
+         * @param session the session for this event
+         * @param controller controller information
+         */
+        public void onDisconnected(@NonNull MediaSession2 session,
+                @NonNull ControllerInfo controller) {}
+
+        /**
+         * Called when a controller sent a session command.
+         *
+         * @param session the session for this event
+         * @param controller controller information
+         * @param command the session command
+         * @param args optional arguments
+         * @return the result for the session command. A runtime exception will be thrown if null
+         *         is returned.
+         */
+        @NonNull
+        public Session2Command.Result onSessionCommand(@NonNull MediaSession2 session,
+                @NonNull ControllerInfo controller, @NonNull Session2Command command,
+                @Nullable Bundle args) {
+            return null;
+        }
+
+        /**
+         * Called when the command sent to the controller is finished.
+         *
+         * @param session the session for this event
+         * @param controller controller information
+         * @param token the token got from {@link MediaSession2#sendSessionCommand}
+         * @param command the session command
+         * @param result the result of the session command
+         */
+        public void onCommandResult(@NonNull MediaSession2 session,
+                @NonNull ControllerInfo controller, @NonNull Object token,
+                @NonNull Session2Command command, @NonNull Session2Command.Result result) {}
+    }
+}
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index 84dfcb1..42597aa 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
@@ -60,6 +61,8 @@
 
     private final Context mContext;
     private final AudioManager mAudioManager;
+    private VolumeShaper.Configuration mVolumeShaperConfig;
+    private VolumeShaper mVolumeShaper;
 
     /**
      * Flag indicating if we're allowed to fall back to remote playback using
@@ -302,6 +305,18 @@
      */
     @UnsupportedAppUsage
     public void setUri(Uri uri) {
+        setUri(uri, null);
+    }
+
+    /**
+     * Set {@link Uri} to be used for ringtone playback. Attempts to open
+     * locally, otherwise will delegate playback to remote
+     * {@link IRingtonePlayer}. Add {@link VolumeShaper} if required.
+     *
+     * @hide
+     */
+    public void setUri(Uri uri, @Nullable VolumeShaper.Configuration volumeShaperConfig) {
+        mVolumeShaperConfig = volumeShaperConfig;
         destroyLocalPlayer();
 
         mUri = uri;
@@ -319,6 +334,9 @@
             synchronized (mPlaybackSettingsLock) {
                 applyPlaybackProperties_sync();
             }
+            if (mVolumeShaperConfig != null) {
+                mVolumeShaper = mLocalPlayer.createVolumeShaper(mVolumeShaperConfig);
+            }
             mLocalPlayer.prepare();
 
         } catch (SecurityException | IOException e) {
@@ -412,6 +430,9 @@
         }
         mLocalPlayer.setOnCompletionListener(mCompletionListener);
         mLocalPlayer.start();
+        if (mVolumeShaper != null) {
+            mVolumeShaper.apply(VolumeShaper.Operation.PLAY);
+        }
     }
 
     /**
@@ -458,6 +479,9 @@
                         synchronized (mPlaybackSettingsLock) {
                             applyPlaybackProperties_sync();
                         }
+                        if (mVolumeShaperConfig != null) {
+                            mVolumeShaper = mLocalPlayer.createVolumeShaper(mVolumeShaperConfig);
+                        }
                         mLocalPlayer.prepare();
                         startLocalPlayer();
                         afd.close();
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 874f21e..0679e8e9 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -694,6 +694,27 @@
         return getRingtone(context, ringtoneUri, -1);
     }
 
+    /**
+     * Returns a {@link Ringtone} with {@link VolumeShaper} if required for a given sound URI.
+     * <p>
+     * If the given URI cannot be opened for any reason, this method will
+     * attempt to fallback on another sound. If it cannot find any, it will
+     * return null.
+     *
+     * @param context A context used to query.
+     * @param ringtoneUri The {@link Uri} of a sound or ringtone.
+     * @param volumeShaperConfig config for volume shaper of the ringtone if applied.
+     * @return A {@link Ringtone} for the given URI, or null.
+     *
+     * @hide
+     */
+    public static Ringtone getRingtone(
+            final Context context, Uri ringtoneUri,
+            @Nullable VolumeShaper.Configuration volumeShaperConfig) {
+        // Don't set the stream type
+        return getRingtone(context, ringtoneUri, -1 /* streamType */, volumeShaperConfig);
+    }
+
     //FIXME bypass the notion of stream types within the class
     /**
      * Returns a {@link Ringtone} for a given sound URI on the given stream
@@ -707,13 +728,32 @@
      */
     @UnsupportedAppUsage
     private static Ringtone getRingtone(final Context context, Uri ringtoneUri, int streamType) {
+        return getRingtone(context, ringtoneUri, streamType, null /* volumeShaperConfig */);
+    }
+
+    //FIXME bypass the notion of stream types within the class
+    /**
+     * Returns a {@link Ringtone} with {@link VolumeShaper} if required for a given sound URI on
+     * the given stream type. Normally, if you change the stream type on the returned
+     * {@link Ringtone}, it will re-create the {@link MediaPlayer}. This is just
+     * an optimized route to avoid that.
+     *
+     * @param streamType The stream type for the ringtone, or -1 if it should
+     *            not be set (and the default used instead).
+     * @param volumeShaperConfig config for volume shaper of the ringtone if applied.
+     * @see #getRingtone(Context, Uri)
+     */
+    @UnsupportedAppUsage
+    private static Ringtone getRingtone(
+            final Context context, Uri ringtoneUri, int streamType,
+            @Nullable VolumeShaper.Configuration volumeShaperConfig) {
         try {
             final Ringtone r = new Ringtone(context, true);
             if (streamType >= 0) {
                 //FIXME deprecated call
                 r.setStreamType(streamType);
             }
-            r.setUri(ringtoneUri);
+            r.setUri(ringtoneUri, volumeShaperConfig);
             return r;
         } catch (Exception ex) {
             Log.e(TAG, "Failed to open ringtone " + ringtoneUri + ": " + ex);
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/media/java/android/media/Session2Command.aidl
similarity index 76%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to media/java/android/media/Session2Command.aidl
index b7a7920..43a7b12 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/media/java/android/media/Session2Command.aidl
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.media;
 
-parcelable UsbPort;
+parcelable Session2Command;
diff --git a/media/java/android/media/Session2Command.java b/media/java/android/media/Session2Command.java
new file mode 100644
index 0000000..d2a5aae
--- /dev/null
+++ b/media/java/android/media/Session2Command.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * Define a command that a {@link MediaController2} can send to a {@link MediaSession2}.
+ * <p>
+ * If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command.
+ * If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and
+ * {@link #getCustomCommand()} shouldn't be {@code null}.
+ * <p>
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
+ * for consistent behavior across all devices.
+ * </p>
+ * @hide
+ */
+public final class Session2Command implements Parcelable {
+    /**
+     * Command code for the custom command which can be defined by string action in the
+     * {@link Session2Command}.
+     */
+    public static final int COMMAND_CODE_CUSTOM = 0;
+
+    /**
+     * Result code representing that the command is skipped or canceled. For an example, a seek
+     * command can be skipped if it is followed by another seek command.
+     */
+    public static final int RESULT_INFO_SKIPPED = 1;
+
+    /**
+     * Result code representing that the command is successfully completed.
+     */
+    public static final int RESULT_SUCCESS = 0;
+
+    /**
+     * Result code represents that call is ended with an unknown error.
+     */
+    public static final int RESULT_ERROR_UNKNOWN_ERROR = -1;
+
+    public static final Parcelable.Creator<Session2Command> CREATOR =
+            new Parcelable.Creator<Session2Command>() {
+                @Override
+                public Session2Command createFromParcel(Parcel in) {
+                    return new Session2Command(in);
+                }
+
+                @Override
+                public Session2Command[] newArray(int size) {
+                    return new Session2Command[size];
+                }
+            };
+
+    private final int mCommandCode;
+    // Nonnull if it's custom command
+    private final String mCustomCommand;
+    private final Bundle mExtras;
+
+    /**
+     * Constructor for creating a command predefined in AndroidX media2.
+     *
+     * @param commandCode A command code for a command predefined in AndroidX media2.
+     */
+    public Session2Command(int commandCode) {
+        if (commandCode == COMMAND_CODE_CUSTOM) {
+            throw new IllegalArgumentException("commandCode shouldn't be COMMAND_CODE_CUSTOM");
+        }
+        mCommandCode = commandCode;
+        mCustomCommand = null;
+        mExtras = null;
+    }
+
+    /**
+     * Constructor for creating a custom command.
+     *
+     * @param action The action of this custom command.
+     * @param extras An extra bundle for this custom command.
+     */
+    public Session2Command(@NonNull String action, @Nullable Bundle extras) {
+        if (action == null) {
+            throw new IllegalArgumentException("action shouldn't be null");
+        }
+        mCommandCode = COMMAND_CODE_CUSTOM;
+        mCustomCommand = action;
+        mExtras = extras;
+    }
+
+    /**
+     * Used by parcelable creator.
+     */
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    Session2Command(Parcel in) {
+        mCommandCode = in.readInt();
+        mCustomCommand = in.readString();
+        mExtras = in.readBundle();
+    }
+
+    /**
+     * Gets the command code of a predefined command.
+     * This will return {@link #COMMAND_CODE_CUSTOM} for a custom command.
+     */
+    public int getCommandCode() {
+        return mCommandCode;
+    }
+
+    /**
+     * Gets the action of a custom command.
+     * This will return {@code null} for a predefined command.
+     */
+    @Nullable
+    public String getCustomCommand() {
+        return mCustomCommand;
+    }
+
+    /**
+     * Gets the extra bundle of a custom command.
+     * This will return {@code null} for a predefined command.
+     */
+    @Nullable
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mCommandCode);
+        dest.writeString(mCustomCommand);
+        dest.writeBundle(mExtras);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof Session2Command)) {
+            return false;
+        }
+        Session2Command other = (Session2Command) obj;
+        return mCommandCode == other.mCommandCode
+                && TextUtils.equals(mCustomCommand, other.mCustomCommand);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mCustomCommand, mCommandCode);
+    }
+
+    /**
+     * Contains the result of {@link Session2Command}.
+     */
+    public static final class Result {
+        private final int mResultCode;
+        private final Bundle mResultData;
+
+        /**
+         * Constructor of {@link Result}.
+         *
+         * @param resultCode result code
+         * @param resultData result data
+         */
+        public Result(int resultCode, Bundle resultData) {
+            mResultCode = resultCode;
+            mResultData = resultData;
+        }
+
+        /**
+         * Returns the result code.
+         */
+        public int getResultCode() {
+            return mResultCode;
+        }
+
+        /**
+         * Returns the result data.
+         */
+        public Bundle getResultData() {
+            return mResultData;
+        }
+    }
+}
diff --git a/media/java/android/media/Session2CommandGroup.java b/media/java/android/media/Session2CommandGroup.java
new file mode 100644
index 0000000..122dfb1
--- /dev/null
+++ b/media/java/android/media/Session2CommandGroup.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import static android.media.Session2Command.COMMAND_CODE_CUSTOM;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A set of {@link Session2Command} which represents a command group.
+ * <p>
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
+ * for consistent behavior across all devices.
+ * </p>
+ * @hide
+ */
+public final class Session2CommandGroup implements Parcelable {
+    private static final String TAG = "Session2CommandGroup";
+
+    public static final Parcelable.Creator<Session2CommandGroup> CREATOR =
+            new Parcelable.Creator<Session2CommandGroup>() {
+                @Override
+                public Session2CommandGroup createFromParcel(Parcel in) {
+                    return new Session2CommandGroup(in);
+                }
+
+                @Override
+                public Session2CommandGroup[] newArray(int size) {
+                    return new Session2CommandGroup[size];
+                }
+            };
+
+    Set<Session2Command> mCommands = new HashSet<>();
+
+    /**
+     * Default Constructor.
+     */
+    public Session2CommandGroup() {}
+
+    /**
+     * Creates a new Session2CommandGroup with commands copied from another object.
+     *
+     * @param commands The collection of commands to copy.
+     */
+    public Session2CommandGroup(@Nullable Collection<Session2Command> commands) {
+        if (commands != null) {
+            mCommands.addAll(commands);
+        }
+    }
+
+    /**
+     * Used by parcelable creator.
+     */
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    Session2CommandGroup(Parcel in) {
+        Session2Command[] commands = in.readParcelableArray(
+                Session2Command.class.getClassLoader(), Session2Command.class);
+        if (commands != null) {
+            for (Session2Command command : commands) {
+                mCommands.add(command);
+            }
+        }
+    }
+
+    /**
+     * Checks whether this command group has a command that matches given {@code command}.
+     *
+     * @param command A command to find. Shouldn't be {@code null}.
+     */
+    public boolean hasCommand(@NonNull Session2Command command) {
+        if (command == null) {
+            throw new IllegalArgumentException("command shouldn't be null");
+        }
+        return mCommands.contains(command);
+    }
+
+    /**
+     * Checks whether this command group has a command that matches given {@code commandCode}.
+     *
+     * @param commandCode A command code to find.
+     *                    Shouldn't be {@link Session2Command#COMMAND_CODE_CUSTOM}.
+     */
+    public boolean hasCommand(int commandCode) {
+        if (commandCode == COMMAND_CODE_CUSTOM) {
+            throw new IllegalArgumentException("Use hasCommand(Command) for custom command");
+        }
+        for (Session2Command command : mCommands) {
+            if (command.getCommandCode() == commandCode) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Gets all commands of this command group.
+     */
+    @NonNull
+    public Set<Session2Command> getCommands() {
+        return new HashSet<>(mCommands);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelableArray((Session2Command[]) mCommands.toArray(), 0);
+    }
+
+    /**
+     * Builds a {@link Session2CommandGroup} object.
+     */
+    public static final class Builder {
+        private Set<Session2Command> mCommands;
+
+        public Builder() {
+            mCommands = new HashSet<>();
+        }
+
+        /**
+         * Creates a new builder for {@link Session2CommandGroup} with commands copied from another
+         * {@link Session2CommandGroup} object.
+         * @param commandGroup
+         */
+        public Builder(@NonNull Session2CommandGroup commandGroup) {
+            mCommands = commandGroup.getCommands();
+        }
+
+        /**
+         * Adds a command to this command group.
+         *
+         * @param command A command to add. Shouldn't be {@code null}.
+         */
+        @NonNull
+        public Builder addCommand(@NonNull Session2Command command) {
+            if (command == null) {
+                throw new IllegalArgumentException("command shouldn't be null");
+            }
+            mCommands.add(command);
+            return this;
+        }
+
+        /**
+         * Adds a predefined command with given {@code commandCode} to this command group.
+         *
+         * @param commandCode A command code to add.
+         *                    Shouldn't be {@link Session2Command#COMMAND_CODE_CUSTOM}.
+         */
+        @NonNull
+        public Builder addCommand(int commandCode) {
+            if (commandCode == COMMAND_CODE_CUSTOM) {
+                throw new IllegalArgumentException(
+                        "Use addCommand(Session2Command) for COMMAND_CODE_CUSTOM.");
+            }
+            mCommands.add(new Session2Command(commandCode));
+            return this;
+        }
+
+        /**
+         * Removes a command from this group which matches given {@code command}.
+         *
+         * @param command A command to find. Shouldn't be {@code null}.
+         */
+        @NonNull
+        public Builder removeCommand(@NonNull Session2Command command) {
+            if (command == null) {
+                throw new IllegalArgumentException("command shouldn't be null");
+            }
+            mCommands.remove(command);
+            return this;
+        }
+
+        /**
+         * Removes a command from this group which matches given {@code commandCode}.
+         *
+         * @param commandCode A command code to find.
+         *                    Shouldn't be {@link Session2Command#COMMAND_CODE_CUSTOM}.
+         */
+        @NonNull
+        public Builder removeCommand(int commandCode) {
+            if (commandCode == COMMAND_CODE_CUSTOM) {
+                throw new IllegalArgumentException("commandCode shouldn't be COMMAND_CODE_CUSTOM");
+            }
+            mCommands.remove(new Session2Command(commandCode));
+            return this;
+        }
+
+        /**
+         * Builds {@link Session2CommandGroup}.
+         *
+         * @return a new {@link Session2CommandGroup}.
+         */
+        @NonNull
+        public Session2CommandGroup build() {
+            return new Session2CommandGroup(mCommands);
+        }
+    }
+}
diff --git a/media/java/android/media/Session2Link.java b/media/java/android/media/Session2Link.java
new file mode 100644
index 0000000..5fe558d
--- /dev/null
+++ b/media/java/android/media/Session2Link.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.NonNull;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+import java.util.Objects;
+
+/**
+ * Handles incoming commands from {@link MediaController2} to {@link MediaSession2}.
+ * @hide
+ */
+// @SystemApi
+public final class Session2Link implements Parcelable {
+    private static final String TAG = "Session2Link";
+    private static final boolean DEBUG = MediaSession2.DEBUG;
+
+    public static final Parcelable.Creator<Session2Link> CREATOR =
+            new Parcelable.Creator<Session2Link>() {
+                @Override
+                public Session2Link createFromParcel(Parcel in) {
+                    return new Session2Link(in);
+                }
+
+                @Override
+                public Session2Link[] newArray(int size) {
+                    return new Session2Link[size];
+                }
+            };
+
+    private final MediaSession2 mSession;
+    private final IMediaSession2 mISession;
+
+    public Session2Link(MediaSession2 session) {
+        mSession = session;
+        mISession = new Session2Stub();
+    }
+
+    Session2Link(Parcel in) {
+        mSession = null;
+        mISession = IMediaSession2.Stub.asInterface(in.readStrongBinder());
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeStrongBinder(mISession.asBinder());
+    }
+
+    @Override
+    public int hashCode() {
+        return mISession.asBinder().hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof Session2Link)) {
+            return false;
+        }
+        Session2Link other = (Session2Link) obj;
+        return Objects.equals(mISession.asBinder(), other.mISession.asBinder());
+    }
+
+    /** Link to death with mISession */
+    public void linkToDeath(@NonNull IBinder.DeathRecipient recipient, int flags) {
+        if (mISession != null) {
+            try {
+                mISession.asBinder().linkToDeath(recipient, flags);
+            } catch (RemoteException e) {
+                if (DEBUG) {
+                    Log.d(TAG, "Session died too early.", e);
+                }
+            }
+        }
+    }
+
+    /** Unlink to death with mISession */
+    public boolean unlinkToDeath(@NonNull IBinder.DeathRecipient recipient, int flags) {
+        if (mISession != null) {
+            return mISession.asBinder().unlinkToDeath(recipient, flags);
+        }
+        return true;
+    }
+
+    /** Interface method for IMediaSession2.connect */
+    public void connect(final Controller2Link caller, int seq, Bundle connectionRequest) {
+        try {
+            mISession.connect(caller, seq, connectionRequest);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** Interface method for IMediaSession2.disconnect */
+    public void disconnect(final Controller2Link caller, int seq) {
+        try {
+            mISession.disconnect(caller, seq);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** Interface method for IMediaSession2.sendSessionCommand */
+    public void sendSessionCommand(final Controller2Link caller, final int seq,
+            final Session2Command command, final Bundle args, ResultReceiver resultReceiver) {
+        try {
+            mISession.sendSessionCommand(caller, seq, command, args, resultReceiver);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** Interface method for IMediaSession2.sendSessionCommand */
+    public void cancelSessionCommand(final Controller2Link caller, final int seq) {
+        try {
+            mISession.cancelSessionCommand(caller, seq);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** Stub implementation for IMediaSession2.connect */
+    public void onConnect(final Controller2Link caller, int seq, Bundle connectionRequest) {
+        mSession.onConnect(caller, seq, connectionRequest);
+    }
+
+    /** Stub implementation for IMediaSession2.disconnect */
+    public void onDisconnect(final Controller2Link caller, int seq) {
+        mSession.onDisconnect(caller, seq);
+    }
+
+    /** Stub implementation for IMediaSession2.sendSessionCommand */
+    public void onSessionCommand(final Controller2Link caller, final int seq,
+            final Session2Command command, final Bundle args, ResultReceiver resultReceiver) {
+        mSession.onSessionCommand(caller, seq, command, args, resultReceiver);
+    }
+
+    /** Stub implementation for IMediaSession2.cancelSessionCommand */
+    public void onCancelCommand(final Controller2Link caller, final int seq) {
+        mSession.onCancelCommand(caller, seq);
+    }
+
+    private class Session2Stub extends IMediaSession2.Stub {
+        @Override
+        public void connect(final Controller2Link caller, int seq, Bundle connectionRequest) {
+            Session2Link.this.onConnect(caller, seq, connectionRequest);
+        }
+
+        @Override
+        public void disconnect(final Controller2Link caller, int seq) {
+            Session2Link.this.onDisconnect(caller, seq);
+        }
+
+        @Override
+        public void sendSessionCommand(final Controller2Link caller, final int seq,
+                final Session2Command command, final Bundle args, ResultReceiver resultReceiver) {
+            Session2Link.this.onSessionCommand(caller, seq, command, args, resultReceiver);
+        }
+
+        @Override
+        public void cancelSessionCommand(final Controller2Link caller, final int seq) {
+            Session2Link.this.onCancelCommand(caller, seq);
+        }
+    }
+}
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/media/java/android/media/Session2Token.aidl
similarity index 76%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to media/java/android/media/Session2Token.aidl
index b7a7920..c5980e9 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/media/java/android/media/Session2Token.aidl
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.media;
 
-parcelable UsbPort;
+parcelable Session2Token;
diff --git a/media/java/android/media/Session2Token.java b/media/java/android/media/Session2Token.java
new file mode 100644
index 0000000..4634c69
--- /dev/null
+++ b/media/java/android/media/Session2Token.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents an ongoing {@link MediaSession2} or a {@link MediaSession2Service}.
+ * If it's representing a session service, it may not be ongoing.
+ * <p>
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
+ * for consistent behavior across all devices.
+ * <p>
+ * This may be passed to apps by the session owner to allow them to create a
+ * {@link MediaController2} to communicate with the session.
+ * <p>
+ * It can be also obtained by {@link MediaSessionManager}.
+ *
+ * @hide
+ */
+// New version of MediaSession2.Token for following reasons
+//   - Stop implementing Parcelable for updatable support
+//   - Represent session and library service (formerly browser service) in one class.
+//     Previously MediaSession2.Token was for session and ComponentName was for service.
+//     This helps controller apps to keep target of dispatching media key events in uniform way.
+//     For details about the reason, see following. (Android O+)
+//         android.media.session.MediaSessionManager.Callback#onAddressedPlayerChanged
+public final class Session2Token implements Parcelable {
+    private static final String TAG = "Session2Token";
+
+    public static final Creator<Session2Token> CREATOR = new Creator<Session2Token>() {
+        @Override
+        public Session2Token createFromParcel(Parcel p) {
+            return new Session2Token(p);
+        }
+
+        @Override
+        public Session2Token[] newArray(int size) {
+            return new Session2Token[size];
+        }
+    };
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "TYPE_", value = {TYPE_SESSION, TYPE_SESSION_SERVICE, TYPE_LIBRARY_SERVICE})
+    public @interface TokenType {
+    }
+
+    /**
+     * Type for {@link MediaSession2}.
+     */
+    public static final int TYPE_SESSION = 0;
+
+    /**
+     * Type for {@link MediaSession2Service}.
+     */
+    public static final int TYPE_SESSION_SERVICE = 1;
+
+    /**
+     * Type for {@link MediaLibrary2Service}.
+     */
+    public static final int TYPE_LIBRARY_SERVICE = 2;
+
+    private final int mUid;
+    private final @TokenType int mType;
+    private final String mPackageName;
+    private final String mServiceName;
+    private final Session2Link mSessionLink;
+    private final ComponentName mComponentName;
+
+    /**
+     * Constructor for the token with type {@link #TYPE_SESSION_SERVICE} or
+     * {@link #TYPE_LIBRARY_SERVICE}.
+     *
+     * @param context The context.
+     * @param serviceComponent The component name of the service.
+     */
+    public Session2Token(@NonNull Context context, @NonNull ComponentName serviceComponent) {
+        if (context == null) {
+            throw new IllegalArgumentException("context shouldn't be null");
+        }
+        if (serviceComponent == null) {
+            throw new IllegalArgumentException("serviceComponent shouldn't be null");
+        }
+
+        final PackageManager manager = context.getPackageManager();
+        final int uid = getUid(manager, serviceComponent.getPackageName());
+
+        // TODO: Uncomment below to stop hardcode type.
+        final int type = TYPE_SESSION_SERVICE;
+//        final int type;
+//        if (isInterfaceDeclared(manager, MediaLibraryService2.SERVICE_INTERFACE,
+//                serviceComponent)) {
+//            type = TYPE_LIBRARY_SERVICE;
+//        } else if (isInterfaceDeclared(manager, MediaSessionService2.SERVICE_INTERFACE,
+//                    serviceComponent)) {
+//            type = TYPE_SESSION_SERVICE;
+//        } else if (isInterfaceDeclared(manager,
+//                        MediaBrowserServiceCompat.SERVICE_INTERFACE, serviceComponent)) {
+//            type = TYPE_BROWSER_SERVICE_LEGACY;
+//        } else {
+//            throw new IllegalArgumentException(serviceComponent + " doesn't implement none of"
+//                    + " MediaSessionService2, MediaLibraryService2, MediaBrowserService nor"
+//                    + " MediaBrowserServiceCompat. Use service's full name.");
+//        }
+        mComponentName = serviceComponent;
+        mPackageName = serviceComponent.getPackageName();
+        mServiceName = serviceComponent.getClassName();
+        mUid = uid;
+        mType = type;
+        mSessionLink = null;
+    }
+
+    Session2Token(int uid, int type, String packageName, Session2Link sessionLink) {
+        mUid = uid;
+        mType = type;
+        mPackageName = packageName;
+        mServiceName = null;
+        mComponentName = null;
+        mSessionLink = sessionLink;
+    }
+
+    Session2Token(Parcel in) {
+        mUid = in.readInt();
+        mType = in.readInt();
+        mPackageName = in.readString();
+        mServiceName = in.readString();
+        // TODO: Uncomment below and stop hardcode mSessionLink
+        mSessionLink = null;
+        //mSessionLink = ISession.Stub.asInterface(in.readStrongBinder());
+        mComponentName = ComponentName.unflattenFromString(in.readString());
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mUid);
+        dest.writeInt(mType);
+        dest.writeString(mPackageName);
+        dest.writeString(mServiceName);
+        // TODO: Uncomment below
+        //dest.writeStrongBinder(mSessionLink.asBinder());
+        dest.writeString(mComponentName == null ? "" : mComponentName.flattenToString());
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mType, mUid, mPackageName, mServiceName, mSessionLink);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof Session2Token)) {
+            return false;
+        }
+        Session2Token other = (Session2Token) obj;
+        return mUid == other.mUid
+                && TextUtils.equals(mPackageName, other.mPackageName)
+                && TextUtils.equals(mServiceName, other.mServiceName)
+                && mType == other.mType
+                && Objects.equals(mSessionLink, other.mSessionLink);
+    }
+
+    @Override
+    public String toString() {
+        return "Session2Token {pkg=" + mPackageName + " type=" + mType
+                + " service=" + mServiceName + " Session2Link=" + mSessionLink + "}";
+    }
+
+    /**
+     * @return uid of the session
+     */
+    public int getUid() {
+        return mUid;
+    }
+
+    /**
+     * @return package name of the session
+     */
+    @NonNull
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * @return service name of the session. Can be {@code null} for {@link #TYPE_SESSION}.
+     */
+    @Nullable
+    public String getServiceName() {
+        return mServiceName;
+    }
+
+    /**
+     * @hide
+     * @return component name of the session. Can be {@code null} for {@link #TYPE_SESSION}.
+     */
+    public ComponentName getComponentName() {
+        return mComponentName;
+    }
+
+    /**
+     * @return type of the token
+     * @see #TYPE_SESSION
+     * @see #TYPE_SESSION_SERVICE
+     * @see #TYPE_LIBRARY_SERVICE
+     */
+    public @TokenType int getType() {
+        return mType;
+    }
+
+    /**
+     * @hide
+     */
+    public Session2Link getSessionLink() {
+        return mSessionLink;
+    }
+
+    private static boolean isInterfaceDeclared(PackageManager manager, String serviceInterface,
+            ComponentName serviceComponent) {
+        Intent serviceIntent = new Intent(serviceInterface);
+        // Use queryIntentServices to find services with MediaLibraryService2.SERVICE_INTERFACE.
+        // We cannot use resolveService with intent specified class name, because resolveService
+        // ignores actions if Intent.setClassName() is specified.
+        serviceIntent.setPackage(serviceComponent.getPackageName());
+
+        List<ResolveInfo> list = manager.queryIntentServices(
+                serviceIntent, PackageManager.GET_META_DATA);
+        if (list != null) {
+            for (int i = 0; i < list.size(); i++) {
+                ResolveInfo resolveInfo = list.get(i);
+                if (resolveInfo == null || resolveInfo.serviceInfo == null) {
+                    continue;
+                }
+                if (TextUtils.equals(
+                        resolveInfo.serviceInfo.name, serviceComponent.getClassName())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private static int getUid(PackageManager manager, String packageName) {
+        try {
+            return manager.getApplicationInfo(packageName, 0).uid;
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalArgumentException("Cannot find package " + packageName);
+        }
+    }
+}
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index fd14060..ccf49bd 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -16,33 +16,54 @@
 
 package android.media;
 
+import static android.media.MediaMetadataRetriever.METADATA_KEY_DURATION;
+import static android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT;
+import static android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH;
+import static android.media.MediaMetadataRetriever.OPTION_CLOSEST_SYNC;
+import static android.os.Environment.MEDIA_UNKNOWN;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.ContentResolver;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
+import android.graphics.ImageDecoder;
+import android.graphics.ImageDecoder.ImageInfo;
+import android.graphics.ImageDecoder.Source;
 import android.graphics.Matrix;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.net.Uri;
+import android.os.Build;
+import android.os.CancellationSignal;
+import android.os.Environment;
 import android.os.ParcelFileDescriptor;
-import android.provider.MediaStore.Images;
+import android.provider.MediaStore.ThumbnailConstants;
 import android.util.Log;
+import android.util.Size;
 
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
+import com.android.internal.util.ArrayUtils;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.function.ToIntFunction;
 
 /**
- * Thumbnail generation routines for media provider.
+ * Utilities for generating visual thumbnails from files.
  */
-
 public class ThumbnailUtils {
     private static final String TAG = "ThumbnailUtils";
 
-    /* Maximum pixels size for created bitmap. */
-    private static final int MAX_NUM_PIXELS_THUMBNAIL = 512 * 384;
-    private static final int MAX_NUM_PIXELS_MICRO_THUMBNAIL = 160 * 120;
-    private static final int UNCONSTRAINED = -1;
+    /** @hide */
+    @Deprecated
+    @UnsupportedAppUsage
+    public static final int TARGET_SIZE_MICRO_THUMBNAIL = 96;
 
     /* Options used internally. */
     private static final int OPTIONS_NONE = 0x0;
@@ -54,153 +75,252 @@
      */
     public static final int OPTIONS_RECYCLE_INPUT = 0x2;
 
-    /**
-     * Constant used to indicate the dimension of mini thumbnail.
-     * @hide Only used by media framework and media provider internally.
-     */
-    public static final int TARGET_SIZE_MINI_THUMBNAIL = 320;
+    private static Size convertKind(int kind) {
+        if (kind == ThumbnailConstants.MICRO_KIND) {
+            return Point.convert(ThumbnailConstants.MICRO_SIZE);
+        } else if (kind == ThumbnailConstants.FULL_SCREEN_KIND) {
+            return Point.convert(ThumbnailConstants.FULL_SCREEN_SIZE);
+        } else if (kind == ThumbnailConstants.MINI_KIND) {
+            return Point.convert(ThumbnailConstants.MINI_SIZE);
+        } else {
+            throw new IllegalArgumentException("Unsupported kind: " + kind);
+        }
+    }
+
+    private static class Resizer implements ImageDecoder.OnHeaderDecodedListener {
+        private final Size size;
+        private final CancellationSignal signal;
+
+        public Resizer(Size size, CancellationSignal signal) {
+            this.size = size;
+            this.signal = signal;
+        }
+
+        @Override
+        public void onHeaderDecoded(ImageDecoder decoder, ImageInfo info, Source source) {
+            // One last-ditch check to see if we've been canceled.
+            if (signal != null) signal.throwIfCanceled();
+
+            // We don't know how clients will use the decoded data, so we have
+            // to default to the more flexible "software" option.
+            decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+
+            // We requested a rough thumbnail size, but the remote size may have
+            // returned something giant, so defensively scale down as needed.
+            final int widthSample = info.getSize().getWidth() / size.getWidth();
+            final int heightSample = info.getSize().getHeight() / size.getHeight();
+            final int sample = Math.max(widthSample, heightSample);
+            if (sample > 1) {
+                decoder.setTargetSampleSize(sample);
+            }
+        }
+    }
 
     /**
-     * Constant used to indicate the dimension of micro thumbnail.
-     * @hide Only used by media framework and media provider internally.
+     * Create a thumbnail for given audio file.
+     *
+     * @param filePath The audio file.
+     * @param kind The desired thumbnail kind, such as
+     *            {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND}.
      */
-    @UnsupportedAppUsage
-    public static final int TARGET_SIZE_MICRO_THUMBNAIL = 96;
+    @Deprecated
+    public static @Nullable Bitmap createAudioThumbnail(@NonNull String filePath, int kind) {
+        try {
+            return createAudioThumbnail(new File(filePath), convertKind(kind), null);
+        } catch (IOException e) {
+            Log.w(TAG, e);
+            return null;
+        }
+    }
 
     /**
-     * This method first examines if the thumbnail embedded in EXIF is bigger than our target
-     * size. If not, then it'll create a thumbnail from original image. Due to efficiency
-     * consideration, we want to let MediaThumbRequest avoid calling this method twice for
-     * both kinds, so it only requests for MICRO_KIND and set saveImage to true.
+     * Create a thumbnail for given audio file.
      *
-     * This method always returns a "square thumbnail" for MICRO_KIND thumbnail.
-     *
-     * @param filePath the path of image file
-     * @param kind could be MINI_KIND or MICRO_KIND
-     * @return Bitmap, or null on failures
-     *
-     * @hide This method is only used by media framework and media provider internally.
+     * @param file The audio file.
+     * @param size The desired thumbnail size.
+     * @throws IOException If any trouble was encountered while generating or
+     *             loading the thumbnail, or if
+     *             {@link CancellationSignal#cancel()} was invoked.
      */
-    @UnsupportedAppUsage
-    public static Bitmap createImageThumbnail(String filePath, int kind) {
-        boolean wantMini = (kind == Images.Thumbnails.MINI_KIND);
-        int targetSize = wantMini
-                ? TARGET_SIZE_MINI_THUMBNAIL
-                : TARGET_SIZE_MICRO_THUMBNAIL;
-        int maxPixels = wantMini
-                ? MAX_NUM_PIXELS_THUMBNAIL
-                : MAX_NUM_PIXELS_MICRO_THUMBNAIL;
-        SizedThumbnailBitmap sizedThumbnailBitmap = new SizedThumbnailBitmap();
-        Bitmap bitmap = null;
-        String mimeType = MediaFile.getMimeTypeForFile(filePath);
+    public static @NonNull Bitmap createAudioThumbnail(@NonNull File file, @NonNull Size size,
+            @Nullable CancellationSignal signal) throws IOException {
+        // Checkpoint before going deeper
+        if (signal != null) signal.throwIfCanceled();
+
+        final Resizer resizer = new Resizer(size, signal);
+        try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) {
+            retriever.setDataSource(file.getAbsolutePath());
+            final byte[] raw = retriever.getEmbeddedPicture();
+            if (raw != null) {
+                return ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer);
+            }
+        } catch (RuntimeException e) {
+            throw new IOException("Failed to create thumbnail", e);
+        }
+
+        // Only poke around for files on external storage
+        if (MEDIA_UNKNOWN.equals(Environment.getExternalStorageState(file))) {
+            throw new IOException("No embedded album art found");
+        }
+
+        // Ignore "Downloads" or top-level directories
+        final File parent = file.getParentFile();
+        final File grandParent = parent != null ? parent.getParentFile() : null;
+        if (parent != null
+                && parent.getName().equals(Environment.DIRECTORY_DOWNLOADS)) {
+            throw new IOException("No thumbnails in Downloads directories");
+        }
+        if (grandParent != null
+                && MEDIA_UNKNOWN.equals(Environment.getExternalStorageState(grandParent))) {
+            throw new IOException("No thumbnails in top-level directories");
+        }
+
+        // If no embedded image found, look around for best standalone file
+        final File[] found = ArrayUtils
+                .defeatNullable(file.getParentFile().listFiles((dir, name) -> {
+                    final String lower = name.toLowerCase();
+                    return (lower.endsWith(".jpg") || lower.endsWith(".png"));
+                }));
+
+        final ToIntFunction<File> score = (f) -> {
+            final String lower = f.getName().toLowerCase();
+            if (lower.equals("albumart.jpg")) return 4;
+            if (lower.startsWith("albumart") && lower.endsWith(".jpg")) return 3;
+            if (lower.contains("albumart") && lower.endsWith(".jpg")) return 2;
+            if (lower.endsWith(".jpg")) return 1;
+            return 0;
+        };
+        final Comparator<File> bestScore = (a, b) -> {
+            return score.applyAsInt(a) - score.applyAsInt(b);
+        };
+
+        final File bestFile = Arrays.asList(found).stream().max(bestScore).orElse(null);
+        if (bestFile == null) {
+            throw new IOException("No album art found");
+        }
+
+        // Checkpoint before going deeper
+        if (signal != null) signal.throwIfCanceled();
+
+        return ImageDecoder.decodeBitmap(ImageDecoder.createSource(bestFile), resizer);
+    }
+
+    /**
+     * Create a thumbnail for given image file.
+     *
+     * @param filePath The image file.
+     * @param kind The desired thumbnail kind, such as
+     *            {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND}.
+     */
+    @Deprecated
+    public static @Nullable Bitmap createImageThumbnail(@NonNull String filePath, int kind) {
+        try {
+            return createImageThumbnail(new File(filePath), convertKind(kind), null);
+        } catch (IOException e) {
+            Log.w(TAG, e);
+            return null;
+        }
+    }
+
+    /**
+     * Create a thumbnail for given image file.
+     *
+     * @param file The audio file.
+     * @param size The desired thumbnail size.
+     * @throws IOException If any trouble was encountered while generating or
+     *             loading the thumbnail, or if
+     *             {@link CancellationSignal#cancel()} was invoked.
+     */
+    public static @NonNull Bitmap createImageThumbnail(@NonNull File file, @NonNull Size size,
+            @Nullable CancellationSignal signal) throws IOException {
+        // Checkpoint before going deeper
+        if (signal != null) signal.throwIfCanceled();
+
+        final Resizer resizer = new Resizer(size, signal);
+        final String mimeType = MediaFile.getMimeTypeForFile(file.getName());
         if (mimeType.equals("image/heif")
                 || mimeType.equals("image/heif-sequence")
                 || mimeType.equals("image/heic")
                 || mimeType.equals("image/heic-sequence")) {
-            bitmap = createThumbnailFromMetadataRetriever(filePath, targetSize, maxPixels);
-        } else if (MediaFile.isExifMimeType(mimeType)) {
-            createThumbnailFromEXIF(filePath, targetSize, maxPixels, sizedThumbnailBitmap);
-            bitmap = sizedThumbnailBitmap.mBitmap;
-        }
-
-        if (bitmap == null) {
-            FileInputStream stream = null;
-            try {
-                stream = new FileInputStream(filePath);
-                FileDescriptor fd = stream.getFD();
-                BitmapFactory.Options options = new BitmapFactory.Options();
-                options.inSampleSize = 1;
-                options.inJustDecodeBounds = true;
-                BitmapFactory.decodeFileDescriptor(fd, null, options);
-                if (options.mCancel || options.outWidth == -1
-                        || options.outHeight == -1) {
-                    return null;
-                }
-                options.inSampleSize = computeSampleSize(
-                        options, targetSize, maxPixels);
-                options.inJustDecodeBounds = false;
-
-                options.inDither = false;
-                options.inPreferredConfig = Bitmap.Config.ARGB_8888;
-                bitmap = BitmapFactory.decodeFileDescriptor(fd, null, options);
-            } catch (IOException ex) {
-                Log.e(TAG, "", ex);
-            } catch (OutOfMemoryError oom) {
-                Log.e(TAG, "Unable to decode file " + filePath + ". OutOfMemoryError.", oom);
-            } finally {
-                try {
-                    if (stream != null) {
-                        stream.close();
-                    }
-                } catch (IOException ex) {
-                    Log.e(TAG, "", ex);
-                }
+            try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) {
+                retriever.setDataSource(file.getAbsolutePath());
+                return retriever.getThumbnailImageAtIndex(-1,
+                        new MediaMetadataRetriever.BitmapParams(), size.getWidth(),
+                        size.getWidth() * size.getHeight());
+            } catch (RuntimeException e) {
+                throw new IOException("Failed to create thumbnail", e);
             }
-
+        } else if (MediaFile.isExifMimeType(mimeType)) {
+            final ExifInterface exif = new ExifInterface(file);
+            final byte[] raw = exif.getThumbnailBytes();
+            if (raw != null) {
+                return ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer);
+            }
         }
 
-        if (kind == Images.Thumbnails.MICRO_KIND) {
-            // now we make it a "square thumbnail" for MICRO_KIND thumbnail
-            bitmap = extractThumbnail(bitmap,
-                    TARGET_SIZE_MICRO_THUMBNAIL,
-                    TARGET_SIZE_MICRO_THUMBNAIL, OPTIONS_RECYCLE_INPUT);
-        }
-        return bitmap;
+        // Checkpoint before going deeper
+        if (signal != null) signal.throwIfCanceled();
+
+        return ImageDecoder.decodeBitmap(ImageDecoder.createSource(file), resizer);
     }
 
     /**
-     * Create a video thumbnail for a video. May return null if the video is
-     * corrupt or the format is not supported.
+     * Create a thumbnail for given video file.
      *
-     * @param filePath the path of video file
-     * @param kind could be MINI_KIND or MICRO_KIND
+     * @param filePath The video file.
+     * @param kind The desired thumbnail kind, such as
+     *            {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND}.
      */
-    public static Bitmap createVideoThumbnail(String filePath, int kind) {
-        Bitmap bitmap = null;
-        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+    @Deprecated
+    public static @Nullable Bitmap createVideoThumbnail(@NonNull String filePath, int kind) {
         try {
-            retriever.setDataSource(filePath);
-            // First retrieve album art in metadata if set.
-            byte[] embeddedPicture = retriever.getEmbeddedPicture();
-            if (embeddedPicture != null && embeddedPicture.length > 0) {
-                bitmap = BitmapFactory.decodeByteArray(embeddedPicture, 0, embeddedPicture.length);
-            }
-            // Fall back to first frame of the video.
-            if (bitmap == null) {
-                bitmap = retriever.getFrameAtTime(-1);
-            }
-        } catch (IllegalArgumentException ex) {
-            // Assume this is a corrupt video file
-        } catch (RuntimeException ex) {
-            // Assume this is a corrupt video file.
-        } finally {
-            try {
-                retriever.release();
-            } catch (RuntimeException ex) {
-                // Ignore failures while cleaning up.
-            }
+            return createVideoThumbnail(new File(filePath), convertKind(kind), null);
+        } catch (IOException e) {
+            Log.w(TAG, e);
+            return null;
         }
+    }
 
-        if (bitmap == null) return null;
+    /**
+     * Create a thumbnail for given video file.
+     *
+     * @param file The video file.
+     * @param size The desired thumbnail size.
+     * @throws IOException If any trouble was encountered while generating or
+     *             loading the thumbnail, or if
+     *             {@link CancellationSignal#cancel()} was invoked.
+     */
+    public static @NonNull Bitmap createVideoThumbnail(@NonNull File file, @NonNull Size size,
+            @Nullable CancellationSignal signal) throws IOException {
+        // Checkpoint before going deeper
+        if (signal != null) signal.throwIfCanceled();
 
-        if (kind == Images.Thumbnails.MINI_KIND) {
-            // Scale down the bitmap if it's too large.
-            int width = bitmap.getWidth();
-            int height = bitmap.getHeight();
-            int max = Math.max(width, height);
-            if (max > 512) {
-                float scale = 512f / max;
-                int w = Math.round(scale * width);
-                int h = Math.round(scale * height);
-                bitmap = Bitmap.createScaledBitmap(bitmap, w, h, true);
+        final Resizer resizer = new Resizer(size, signal);
+        try (MediaMetadataRetriever mmr = new MediaMetadataRetriever()) {
+            mmr.setDataSource(file.getAbsolutePath());
+
+            // Try to retrieve thumbnail from metadata
+            final byte[] raw = mmr.getEmbeddedPicture();
+            if (raw != null) {
+                return ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer);
             }
-        } else if (kind == Images.Thumbnails.MICRO_KIND) {
-            bitmap = extractThumbnail(bitmap,
-                    TARGET_SIZE_MICRO_THUMBNAIL,
-                    TARGET_SIZE_MICRO_THUMBNAIL,
-                    OPTIONS_RECYCLE_INPUT);
+
+            // Fall back to middle of video
+            final int width = Integer.parseInt(mmr.extractMetadata(METADATA_KEY_VIDEO_WIDTH));
+            final int height = Integer.parseInt(mmr.extractMetadata(METADATA_KEY_VIDEO_HEIGHT));
+            final long duration = Long.parseLong(mmr.extractMetadata(METADATA_KEY_DURATION));
+
+            // If we're okay with something larger than native format, just
+            // return a frame without up-scaling it
+            if (size.getWidth() > width && size.getHeight() > height) {
+                return mmr.getFrameAtTime(duration / 2, OPTION_CLOSEST_SYNC);
+            } else {
+                return mmr.getScaledFrameAtTime(duration / 2, OPTION_CLOSEST_SYNC,
+                        size.getWidth(), size.getHeight());
+            }
+        } catch (RuntimeException e) {
+            throw new IOException("Failed to create thumbnail", e);
         }
-        return bitmap;
     }
 
     /**
@@ -242,123 +362,28 @@
         return thumbnail;
     }
 
-    /*
-     * Compute the sample size as a function of minSideLength
-     * and maxNumOfPixels.
-     * minSideLength is used to specify that minimal width or height of a
-     * bitmap.
-     * maxNumOfPixels is used to specify the maximal size in pixels that is
-     * tolerable in terms of memory usage.
-     *
-     * The function returns a sample size based on the constraints.
-     * Both size and minSideLength can be passed in as IImage.UNCONSTRAINED,
-     * which indicates no care of the corresponding constraint.
-     * The functions prefers returning a sample size that
-     * generates a smaller bitmap, unless minSideLength = IImage.UNCONSTRAINED.
-     *
-     * Also, the function rounds up the sample size to a power of 2 or multiple
-     * of 8 because BitmapFactory only honors sample size this way.
-     * For example, BitmapFactory downsamples an image by 2 even though the
-     * request is 3. So we round up the sample size to avoid OOM.
-     */
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static int computeSampleSize(BitmapFactory.Options options,
             int minSideLength, int maxNumOfPixels) {
-        int initialSize = computeInitialSampleSize(options, minSideLength,
-                maxNumOfPixels);
-
-        int roundedSize;
-        if (initialSize <= 8 ) {
-            roundedSize = 1;
-            while (roundedSize < initialSize) {
-                roundedSize <<= 1;
-            }
-        } else {
-            roundedSize = (initialSize + 7) / 8 * 8;
-        }
-
-        return roundedSize;
+        return 1;
     }
 
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static int computeInitialSampleSize(BitmapFactory.Options options,
             int minSideLength, int maxNumOfPixels) {
-        double w = options.outWidth;
-        double h = options.outHeight;
-
-        int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 :
-                (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
-        int upperBound = (minSideLength == UNCONSTRAINED) ? 128 :
-                (int) Math.min(Math.floor(w / minSideLength),
-                Math.floor(h / minSideLength));
-
-        if (upperBound < lowerBound) {
-            // return the larger one when there is no overlapping zone.
-            return lowerBound;
-        }
-
-        if ((maxNumOfPixels == UNCONSTRAINED) &&
-                (minSideLength == UNCONSTRAINED)) {
-            return 1;
-        } else if (minSideLength == UNCONSTRAINED) {
-            return lowerBound;
-        } else {
-            return upperBound;
-        }
+        return 1;
     }
 
-    /**
-     * Make a bitmap from a given Uri, minimal side length, and maximum number of pixels.
-     * The image data will be read from specified pfd if it's not null, otherwise
-     * a new input stream will be created using specified ContentResolver.
-     *
-     * Clients are allowed to pass their own BitmapFactory.Options used for bitmap decoding. A
-     * new BitmapFactory.Options will be created if options is null.
-     */
-    private static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels,
-            Uri uri, ContentResolver cr, ParcelFileDescriptor pfd,
-            BitmapFactory.Options options) {
-        Bitmap b = null;
-        try {
-            if (pfd == null) pfd = makeInputStream(uri, cr);
-            if (pfd == null) return null;
-            if (options == null) options = new BitmapFactory.Options();
-
-            FileDescriptor fd = pfd.getFileDescriptor();
-            options.inSampleSize = 1;
-            options.inJustDecodeBounds = true;
-            BitmapFactory.decodeFileDescriptor(fd, null, options);
-            if (options.mCancel || options.outWidth == -1
-                    || options.outHeight == -1) {
-                return null;
-            }
-            options.inSampleSize = computeSampleSize(
-                    options, minSideLength, maxNumOfPixels);
-            options.inJustDecodeBounds = false;
-
-            options.inDither = false;
-            options.inPreferredConfig = Bitmap.Config.ARGB_8888;
-            b = BitmapFactory.decodeFileDescriptor(fd, null, options);
-        } catch (OutOfMemoryError ex) {
-            Log.e(TAG, "Got oom exception ", ex);
-            return null;
-        } finally {
-            closeSilently(pfd);
-        }
-        return b;
-    }
-
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static void closeSilently(ParcelFileDescriptor c) {
-      if (c == null) return;
-      try {
-          c.close();
-      } catch (Throwable t) {
-          // do nothing
-      }
+        IoUtils.closeQuietly(c);
     }
 
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static ParcelFileDescriptor makeInputStream(
             Uri uri, ContentResolver cr) {
         try {
@@ -371,7 +396,8 @@
     /**
      * Transform source Bitmap to targeted width and height.
      */
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static Bitmap transform(Matrix scaler,
             Bitmap source,
             int targetWidth,
@@ -468,14 +494,7 @@
         return b2;
     }
 
-    /**
-     * SizedThumbnailBitmap contains the bitmap, which is downsampled either from
-     * the thumbnail in exif or the full image.
-     * mThumbnailData, mThumbnailWidth and mThumbnailHeight are set together only if mThumbnail
-     * is not null.
-     *
-     * The width/height of the sized bitmap may be different from mThumbnailWidth/mThumbnailHeight.
-     */
+    @Deprecated
     private static class SizedThumbnailBitmap {
         public byte[] mThumbnailData;
         public Bitmap mBitmap;
@@ -483,81 +502,9 @@
         public int mThumbnailHeight;
     }
 
-    /**
-     * Creates a bitmap by either downsampling from the thumbnail in EXIF or the full image.
-     * The functions returns a SizedThumbnailBitmap,
-     * which contains a downsampled bitmap and the thumbnail data in EXIF if exists.
-     */
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static void createThumbnailFromEXIF(String filePath, int targetSize,
             int maxPixels, SizedThumbnailBitmap sizedThumbBitmap) {
-        if (filePath == null) return;
-
-        ExifInterface exif = null;
-        byte [] thumbData = null;
-        try {
-            exif = new ExifInterface(filePath);
-            thumbData = exif.getThumbnail();
-        } catch (IOException ex) {
-            Log.w(TAG, ex);
-        }
-
-        BitmapFactory.Options fullOptions = new BitmapFactory.Options();
-        BitmapFactory.Options exifOptions = new BitmapFactory.Options();
-        int exifThumbWidth = 0;
-        int fullThumbWidth = 0;
-
-        // Compute exifThumbWidth.
-        if (thumbData != null) {
-            exifOptions.inJustDecodeBounds = true;
-            BitmapFactory.decodeByteArray(thumbData, 0, thumbData.length, exifOptions);
-            exifOptions.inSampleSize = computeSampleSize(exifOptions, targetSize, maxPixels);
-            exifThumbWidth = exifOptions.outWidth / exifOptions.inSampleSize;
-        }
-
-        // Compute fullThumbWidth.
-        fullOptions.inJustDecodeBounds = true;
-        BitmapFactory.decodeFile(filePath, fullOptions);
-        fullOptions.inSampleSize = computeSampleSize(fullOptions, targetSize, maxPixels);
-        fullThumbWidth = fullOptions.outWidth / fullOptions.inSampleSize;
-
-        // Choose the larger thumbnail as the returning sizedThumbBitmap.
-        if (thumbData != null && exifThumbWidth >= fullThumbWidth) {
-            int width = exifOptions.outWidth;
-            int height = exifOptions.outHeight;
-            exifOptions.inJustDecodeBounds = false;
-            sizedThumbBitmap.mBitmap = BitmapFactory.decodeByteArray(thumbData, 0,
-                    thumbData.length, exifOptions);
-            if (sizedThumbBitmap.mBitmap != null) {
-                sizedThumbBitmap.mThumbnailData = thumbData;
-                sizedThumbBitmap.mThumbnailWidth = width;
-                sizedThumbBitmap.mThumbnailHeight = height;
-            }
-        } else {
-            fullOptions.inJustDecodeBounds = false;
-            sizedThumbBitmap.mBitmap = BitmapFactory.decodeFile(filePath, fullOptions);
-        }
-    }
-
-    private static Bitmap createThumbnailFromMetadataRetriever(
-            String filePath, int targetSize, int maxPixels) {
-        if (filePath == null) {
-            return null;
-        }
-        Bitmap thumbnail = null;
-        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
-        try {
-            retriever.setDataSource(filePath);
-            MediaMetadataRetriever.BitmapParams params = new MediaMetadataRetriever.BitmapParams();
-            params.setPreferredConfig(Bitmap.Config.ARGB_8888);
-            thumbnail = retriever.getThumbnailImageAtIndex(-1, params, targetSize, maxPixels);
-        } catch (RuntimeException ex) {
-            // Assume this is a corrupt video file.
-        } finally {
-            if (retriever != null) {
-                retriever.release();
-            }
-        }
-        return thumbnail;
     }
 }
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index 7fb3aa6..1c6210e 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -165,6 +165,20 @@
     }
 
     /** @hide */
+    public boolean isRoutedToDevice(int deviceType, @NonNull String deviceAddress) {
+        if ((mRouteFlags & ROUTE_FLAG_RENDER) != ROUTE_FLAG_RENDER) {
+            return false;
+        }
+        if (deviceType != mDeviceSystemType) {
+            return false;
+        }
+        if (!deviceAddress.equals(mDeviceAddress)) {
+            return false;
+        }
+        return true;
+    }
+
+    /** @hide */
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 6103f557..65f3294 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.media.AudioAttributes;
+import android.media.AudioDeviceInfo;
 import android.media.AudioFocusInfo;
 import android.media.AudioFormat;
 import android.media.AudioManager;
@@ -323,6 +324,80 @@
         }
     }
 
+    /**
+     * @hide
+     * Configures the audio framework so that all audio stream originating from the given UID
+     * can only come from a set of audio devices.
+     * For this routing to be operational, a number of {@link AudioMix} instances must have been
+     * previously registered on this policy, and routed to a super-set of the given audio devices
+     * with {@link AudioMix.Builder#setDevice(android.media.AudioDeviceInfo)}. Note that having
+     * multiple devices in the list doesn't imply the signals will be duplicated on the different
+     * audio devices, final routing will depend on the {@link AudioAttributes} of the sounds being
+     * played.
+     * @param uid UID of the application to affect.
+     * @param devices list of devices to which the audio stream of the application may be routed.
+     * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
+     *          otherwise.
+     */
+    @SystemApi
+    public int setUidDeviceAffinity(int uid, @NonNull List<AudioDeviceInfo> devices) {
+        if (devices == null) {
+            throw new IllegalArgumentException("Illegal null list of audio devices");
+        }
+        synchronized (mLock) {
+            if (mStatus != POLICY_STATUS_REGISTERED) {
+                throw new IllegalStateException("Cannot use unregistered AudioPolicy");
+            }
+            final int[] deviceTypes = new int[devices.size()];
+            final String[] deviceAdresses = new String[devices.size()];
+            int i = 0;
+            for (AudioDeviceInfo device : devices) {
+                if (device == null) {
+                    throw new IllegalArgumentException(
+                            "Illegal null AudioDeviceInfo in setUidDeviceAffinity");
+                }
+                deviceTypes[i] =
+                        AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType());
+                deviceAdresses[i] = device.getAddress();
+                i++;
+            }
+            final IAudioService service = getService();
+            try {
+                final int status = service.setUidDeviceAffinity(this.cb(),
+                        uid, deviceTypes, deviceAdresses);
+                return status;
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in setUidDeviceAffinity", e);
+                return AudioManager.ERROR;
+            }
+        }
+    }
+
+    /**
+     * @hide
+     * Removes audio device affinity previously set by
+     * {@link #setUidDeviceAffinity(int, java.util.List)}.
+     * @param uid UID of the application affected.
+     * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
+     *          otherwise.
+     */
+    @SystemApi
+    public int removeUidDeviceAffinity(int uid) {
+        synchronized (mLock) {
+            if (mStatus != POLICY_STATUS_REGISTERED) {
+                throw new IllegalStateException("Cannot use unregistered AudioPolicy");
+            }
+            final IAudioService service = getService();
+            try {
+                final int status = service.removeUidDeviceAffinity(this.cb(), uid);
+                return status;
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in removeUidDeviceAffinity", e);
+                return AudioManager.ERROR;
+            }
+        }
+    }
+
     public void setRegistration(String regId) {
         synchronized (mLock) {
             mRegistrationId = regId;
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index bd0019f..bfc05fa 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -39,7 +39,7 @@
     void destroy();
 
     // These commands are for the TransportPerformer
-    void setMetadata(in MediaMetadata metadata);
+    void setMetadata(in MediaMetadata metadata, long duration, String metadataDescription);
     void setPlaybackState(in PlaybackState state);
     void setQueue(in ParceledListSlice queue);
     void setQueueTitle(CharSequence title);
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index 861a8ce..e61bf5b 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -48,9 +48,10 @@
     PendingIntent getLaunchPendingIntent();
     long getFlags();
     ParcelableVolumeInfo getVolumeAttributes();
-    void adjustVolume(String packageName, ISessionControllerCallback caller,
-            boolean asSystemService, int direction, int flags);
-    void setVolumeTo(String packageName, ISessionControllerCallback caller,
+    void adjustVolume(String packageName, String opPackageName,
+            in ISessionControllerCallback caller, boolean asSystemService, int direction,
+            int flags);
+    void setVolumeTo(String packageName, String opPackageName, in ISessionControllerCallback caller,
             int value, int flags);
 
     // These commands are for the TransportControls
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 4ced7be..51148e2 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -17,6 +17,7 @@
 
 import android.content.ComponentName;
 import android.media.IRemoteVolumeController;
+import android.media.Session2Token;
 import android.media.session.IActiveSessionsListener;
 import android.media.session.ICallback;
 import android.media.session.IOnMediaKeyListener;
@@ -32,12 +33,14 @@
  */
 interface ISessionManager {
     ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId);
+    void notifySession2Created(in Session2Token sessionToken);
     List<IBinder> getSessions(in ComponentName compName, int userId);
     void dispatchMediaKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent,
             boolean needWakeLock);
-    void dispatchVolumeKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent,
-            int stream, boolean musicOnly);
-    void dispatchAdjustVolume(String packageName, int suggestedStream, int delta, int flags);
+    void dispatchVolumeKeyEvent(String packageName, String opPackageName, boolean asSystemService,
+            in KeyEvent keyEvent, int stream, boolean musicOnly);
+    void dispatchAdjustVolume(String packageName, String opPackageName, int suggestedStream,
+            int delta, int flags);
     void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName,
             int userId);
     void removeSessionsListener(in IActiveSessionsListener listener);
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index b457357..acbc828 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -153,7 +153,7 @@
             return false;
         }
         try {
-            return mSessionBinder.sendMediaButton(mContext.getOpPackageName(), mCbStub,
+            return mSessionBinder.sendMediaButton(mContext.getPackageName(), mCbStub,
                     asSystemService, keyEvent);
         } catch (RemoteException e) {
             // System is dead. =(
@@ -186,8 +186,12 @@
                         break;
                 }
                 try {
-                    mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, true,
-                            direction, AudioManager.FLAG_SHOW_UI);
+                    // Note: Need both package name and OP package name. Package name is used for
+                    //       RemoteUserInfo, and OP package name is used for AudioService's internal
+                    //       AppOpsManager usages.
+                    mSessionBinder.adjustVolume(mContext.getPackageName(),
+                            mContext.getOpPackageName(), mCbStub, true, direction,
+                            AudioManager.FLAG_SHOW_UI);
                 } catch (RemoteException e) {
                     Log.wtf(TAG, "Error calling adjustVolumeBy", e);
                 }
@@ -197,8 +201,11 @@
                 final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE
                         | AudioManager.FLAG_FROM_KEY;
                 try {
-                    mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, true, 0,
-                            flags);
+                    // Note: Need both package name and OP package name. Package name is used for
+                    //       RemoteUserInfo, and OP package name is used for AudioService's internal
+                    //       AppOpsManager usages.
+                    mSessionBinder.adjustVolume(mContext.getPackageName(),
+                            mContext.getOpPackageName(), mCbStub, true, 0, flags);
                 } catch (RemoteException e) {
                     Log.wtf(TAG, "Error calling adjustVolumeBy", e);
                 }
@@ -367,7 +374,11 @@
      */
     public void setVolumeTo(int value, int flags) {
         try {
-            mSessionBinder.setVolumeTo(mContext.getOpPackageName(), mCbStub, value, flags);
+            // Note: Need both package name and OP package name. Package name is used for
+            //       RemoteUserInfo, and OP package name is used for AudioService's internal
+            //       AppOpsManager usages.
+            mSessionBinder.setVolumeTo(mContext.getPackageName(), mContext.getOpPackageName(),
+                    mCbStub, value, flags);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Error calling setVolumeTo.", e);
         }
@@ -388,8 +399,11 @@
      */
     public void adjustVolume(int direction, int flags) {
         try {
-            mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, false, direction,
-                    flags);
+            // Note: Need both package name and OP package name. Package name is used for
+            //       RemoteUserInfo, and OP package name is used for AudioService's internal
+            //       AppOpsManager usages.
+            mSessionBinder.adjustVolume(mContext.getPackageName(), mContext.getOpPackageName(),
+                    mCbStub, false, direction, flags);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
         }
@@ -455,7 +469,7 @@
             throw new IllegalArgumentException("command cannot be null or empty");
         }
         try {
-            mSessionBinder.sendCommand(mContext.getOpPackageName(), mCbStub, command, args, cb);
+            mSessionBinder.sendCommand(mContext.getPackageName(), mCbStub, command, args, cb);
         } catch (RemoteException e) {
             Log.d(TAG, "Dead object in sendCommand.", e);
         }
@@ -521,7 +535,7 @@
 
         if (!mCbRegistered) {
             try {
-                mSessionBinder.registerCallbackListener(mContext.getOpPackageName(), mCbStub);
+                mSessionBinder.registerCallbackListener(mContext.getPackageName(), mCbStub);
                 mCbRegistered = true;
             } catch (RemoteException e) {
                 Log.e(TAG, "Dead object in registerCallback", e);
@@ -668,7 +682,7 @@
          */
         public void prepare() {
             try {
-                mSessionBinder.prepare(mContext.getOpPackageName(), mCbStub);
+                mSessionBinder.prepare(mContext.getPackageName(), mCbStub);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling prepare.", e);
             }
@@ -692,7 +706,7 @@
                         "You must specify a non-empty String for prepareFromMediaId.");
             }
             try {
-                mSessionBinder.prepareFromMediaId(mContext.getOpPackageName(), mCbStub, mediaId,
+                mSessionBinder.prepareFromMediaId(mContext.getPackageName(), mCbStub, mediaId,
                         extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling prepare(" + mediaId + ").", e);
@@ -719,7 +733,7 @@
                 query = "";
             }
             try {
-                mSessionBinder.prepareFromSearch(mContext.getOpPackageName(), mCbStub, query,
+                mSessionBinder.prepareFromSearch(mContext.getPackageName(), mCbStub, query,
                         extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling prepare(" + query + ").", e);
@@ -744,7 +758,7 @@
                         "You must specify a non-empty Uri for prepareFromUri.");
             }
             try {
-                mSessionBinder.prepareFromUri(mContext.getOpPackageName(), mCbStub, uri, extras);
+                mSessionBinder.prepareFromUri(mContext.getPackageName(), mCbStub, uri, extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling prepare(" + uri + ").", e);
             }
@@ -755,7 +769,7 @@
          */
         public void play() {
             try {
-                mSessionBinder.play(mContext.getOpPackageName(), mCbStub);
+                mSessionBinder.play(mContext.getPackageName(), mCbStub);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling play.", e);
             }
@@ -774,7 +788,7 @@
                         "You must specify a non-empty String for playFromMediaId.");
             }
             try {
-                mSessionBinder.playFromMediaId(mContext.getOpPackageName(), mCbStub, mediaId,
+                mSessionBinder.playFromMediaId(mContext.getPackageName(), mCbStub, mediaId,
                         extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling play(" + mediaId + ").", e);
@@ -797,7 +811,7 @@
                 query = "";
             }
             try {
-                mSessionBinder.playFromSearch(mContext.getOpPackageName(), mCbStub, query, extras);
+                mSessionBinder.playFromSearch(mContext.getPackageName(), mCbStub, query, extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling play(" + query + ").", e);
             }
@@ -816,7 +830,7 @@
                         "You must specify a non-empty Uri for playFromUri.");
             }
             try {
-                mSessionBinder.playFromUri(mContext.getOpPackageName(), mCbStub, uri, extras);
+                mSessionBinder.playFromUri(mContext.getPackageName(), mCbStub, uri, extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling play(" + uri + ").", e);
             }
@@ -828,7 +842,7 @@
          */
         public void skipToQueueItem(long id) {
             try {
-                mSessionBinder.skipToQueueItem(mContext.getOpPackageName(), mCbStub, id);
+                mSessionBinder.skipToQueueItem(mContext.getPackageName(), mCbStub, id);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e);
             }
@@ -840,7 +854,7 @@
          */
         public void pause() {
             try {
-                mSessionBinder.pause(mContext.getOpPackageName(), mCbStub);
+                mSessionBinder.pause(mContext.getPackageName(), mCbStub);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling pause.", e);
             }
@@ -852,7 +866,7 @@
          */
         public void stop() {
             try {
-                mSessionBinder.stop(mContext.getOpPackageName(), mCbStub);
+                mSessionBinder.stop(mContext.getPackageName(), mCbStub);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling stop.", e);
             }
@@ -865,7 +879,7 @@
          */
         public void seekTo(long pos) {
             try {
-                mSessionBinder.seekTo(mContext.getOpPackageName(), mCbStub, pos);
+                mSessionBinder.seekTo(mContext.getPackageName(), mCbStub, pos);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling seekTo.", e);
             }
@@ -877,7 +891,7 @@
          */
         public void fastForward() {
             try {
-                mSessionBinder.fastForward(mContext.getOpPackageName(), mCbStub);
+                mSessionBinder.fastForward(mContext.getPackageName(), mCbStub);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling fastForward.", e);
             }
@@ -888,7 +902,7 @@
          */
         public void skipToNext() {
             try {
-                mSessionBinder.next(mContext.getOpPackageName(), mCbStub);
+                mSessionBinder.next(mContext.getPackageName(), mCbStub);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling next.", e);
             }
@@ -900,7 +914,7 @@
          */
         public void rewind() {
             try {
-                mSessionBinder.rewind(mContext.getOpPackageName(), mCbStub);
+                mSessionBinder.rewind(mContext.getPackageName(), mCbStub);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling rewind.", e);
             }
@@ -911,7 +925,7 @@
          */
         public void skipToPrevious() {
             try {
-                mSessionBinder.previous(mContext.getOpPackageName(), mCbStub);
+                mSessionBinder.previous(mContext.getPackageName(), mCbStub);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling previous.", e);
             }
@@ -926,7 +940,7 @@
          */
         public void setRating(Rating rating) {
             try {
-                mSessionBinder.rate(mContext.getOpPackageName(), mCbStub, rating);
+                mSessionBinder.rate(mContext.getPackageName(), mCbStub, rating);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling rate.", e);
             }
@@ -961,7 +975,7 @@
                 throw new IllegalArgumentException("CustomAction cannot be null.");
             }
             try {
-                mSessionBinder.sendCustomAction(mContext.getOpPackageName(), mCbStub, action, args);
+                mSessionBinder.sendCustomAction(mContext.getPackageName(), mCbStub, action, args);
             } catch (RemoteException e) {
                 Log.d(TAG, "Dead object in sendCustomAction.", e);
             }
@@ -1140,7 +1154,7 @@
         private boolean mRegistered = false;
 
         public MessageHandler(Looper looper, MediaController.Callback cb) {
-            super(looper, null, true);
+            super(looper);
             mCallback = cb;
         }
 
@@ -1179,6 +1193,7 @@
 
         public void post(int what, Object obj, Bundle data) {
             Message msg = obtainMessage(what, obj);
+            msg.setAsynchronous(true);
             msg.setData(data);
             msg.sendToTarget();
         }
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index d43cd30..df8cc35 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -30,6 +30,7 @@
 import android.media.MediaMetadata;
 import android.media.Rating;
 import android.media.VolumeProvider;
+import android.media.session.MediaSessionManager.RemoteUserInfo;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -40,7 +41,6 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
-import android.media.session.MediaSessionManager.RemoteUserInfo;
 import android.service.media.MediaBrowserService;
 import android.text.TextUtils;
 import android.util.Log;
@@ -434,11 +434,21 @@
      * @see android.media.MediaMetadata.Builder#putBitmap
      */
     public void setMetadata(@Nullable MediaMetadata metadata) {
+        long duration = -1;
+        int fields = 0;
+        MediaDescription description = null;
         if (metadata != null) {
             metadata = (new MediaMetadata.Builder(metadata, mMaxBitmapSize)).build();
+            if (metadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) {
+                duration = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
+            }
+            fields = metadata.size();
+            description = metadata.getDescription();
         }
+        String metadataDescription = "size=" + fields + ", description=" + description;
+
         try {
-            mBinder.setMetadata(metadata);
+            mBinder.setMetadata(metadata, duration, metadataDescription);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Dead object in setPlaybackState.", e);
         }
@@ -1443,7 +1453,7 @@
         private RemoteUserInfo mCurrentControllerInfo;
 
         public CallbackMessageHandler(Looper looper, MediaSession.Callback callback) {
-            super(looper, null, true);
+            super(looper);
             mCallback = callback;
             mCallback.mHandler = this;
         }
@@ -1451,6 +1461,7 @@
         public void post(RemoteUserInfo caller, int what, Object obj, Bundle data, long delayMs) {
             Pair<RemoteUserInfo, Object> objWithCaller = Pair.create(caller, obj);
             Message msg = obtainMessage(what, objWithCaller);
+            msg.setAsynchronous(true);
             msg.setData(data);
             if (delayMs > 0) {
                 sendMessageDelayed(msg, delayMs);
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 6a06dd0..ef5cf00 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -26,6 +26,8 @@
 import android.content.Context;
 import android.media.AudioManager;
 import android.media.IRemoteVolumeController;
+import android.media.MediaSession2;
+import android.media.Session2Token;
 import android.media.browse.MediaBrowser;
 import android.os.Handler;
 import android.os.IBinder;
@@ -102,6 +104,30 @@
     }
 
     /**
+     * Notifies that a new {@link MediaSession2} with type {@link Session2Token#TYPE_SESSION} is
+     * created.
+     * <p>
+     * Do not use this API directly, but create a new instance through the
+     * {@link MediaSession2.Builder} instead.
+     *
+     * @param token newly created session2 token
+     * @hide
+     */
+    public void notifySession2Created(@NonNull Session2Token token) {
+        if (token == null) {
+            throw new IllegalArgumentException("token shouldn't be null");
+        }
+        if (token.getType() != Session2Token.TYPE_SESSION) {
+            throw new IllegalArgumentException("token's type should be TYPE_SESSION");
+        }
+        try {
+            mService.notifySession2Created(token);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Get a list of controllers for all ongoing sessions. The controllers will
      * be provided in priority order with the most important controller at index
      * 0.
@@ -312,7 +338,7 @@
     private void dispatchMediaKeyEventInternal(boolean asSystemService, @NonNull KeyEvent keyEvent,
             boolean needWakeLock) {
         try {
-            mService.dispatchMediaKeyEvent(mContext.getOpPackageName(), asSystemService, keyEvent,
+            mService.dispatchMediaKeyEvent(mContext.getPackageName(), asSystemService, keyEvent,
                     needWakeLock);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to send key event.", e);
@@ -348,8 +374,8 @@
     private void dispatchVolumeKeyEventInternal(boolean asSystemService, @NonNull KeyEvent keyEvent,
             int stream, boolean musicOnly) {
         try {
-            mService.dispatchVolumeKeyEvent(mContext.getOpPackageName(), asSystemService, keyEvent,
-                    stream, musicOnly);
+            mService.dispatchVolumeKeyEvent(mContext.getPackageName(), mContext.getOpPackageName(),
+                    asSystemService, keyEvent, stream, musicOnly);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to send volume key event.", e);
         }
@@ -369,8 +395,8 @@
      */
     public void dispatchAdjustVolume(int suggestedStream, int direction, int flags) {
         try {
-            mService.dispatchAdjustVolume(mContext.getOpPackageName(), suggestedStream, direction,
-                    flags);
+            mService.dispatchAdjustVolume(mContext.getPackageName(), mContext.getOpPackageName(),
+                    suggestedStream, direction, flags);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to send adjust volume.", e);
         }
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index e25e6a5..fda0fc4 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -95,19 +95,23 @@
     ],
 
     shared_libs: [
-        "android.hardware.cas@1.0",  // for CasManager. VNDK???
-        "android.hardware.cas.native@1.0",  // CasManager. VNDK???
-        "libbinder",
-        "libgui",  // for VideoFrameScheduler
-        "libhidlallocatorutils",
-        "libhidlbase",  // VNDK???
-        "libpowermanager",  // for JWakeLock. to be removed
+        // MediaCas
+        "android.hardware.cas@1.0",
+        "android.hardware.cas.native@1.0",
+        "android.hidl.allocator@1.0",
+        "libhidlbase",
+        "libhidlmemory",
+
+        "libpowermanager",  // Used by JWakeLock. Will be replace with public SDK API.
+        "libmediametrics",  // Used by MediaMetrics. Will be replaced with stable C API.
+        "libbinder",  // Used by JWakeLock and MediaMetrics.
 
         "libutils",  // Have to use shared lib to make libandroid_runtime behave correctly.
                      // Otherwise, AndroidRuntime::getJNIEnv() will return NULL.
 
         // NDK or NDK-compliant
         "libandroid",
+        "libbinder_ndk",
         "libmediandk",
         "libnativehelper_compat_libc++",
         "liblog",
@@ -123,7 +127,6 @@
         "libmedia_helper",
         "libmedia_player2_util",
         "libmediaextractor",
-        "libmediametrics",
         "libmediaplayer2",
         "libmediaplayer2-protos",
         "libmediandk_utils",
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 5dd01b0..76bbce7 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -39,7 +39,6 @@
 #include "utils/Errors.h"  // for status_t
 #include "utils/KeyedVector.h"
 #include "utils/String8.h"
-#include "android_media_BufferingParams.h"
 #include "android_media_MediaDataSource.h"
 #include "android_media_MediaMetricsJNI.h"
 #include "android_media_PlaybackParams.h"
@@ -94,7 +93,6 @@
 };
 static fields_t fields;
 
-static BufferingParams::fields_t gBufferingParamsFields;
 static PlaybackParams::fields_t gPlaybackParamsFields;
 static SyncParams::fields_t gSyncParamsFields;
 static VolumeShaperHelper::fields_t gVolumeShaperFields;
@@ -370,50 +368,6 @@
     setVideoSurface(env, thiz, jsurface, true /* mediaPlayerMustBeAlive */);
 }
 
-static jobject
-android_media_MediaPlayer_getBufferingParams(JNIEnv *env, jobject thiz)
-{
-    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
-    if (mp == NULL) {
-        jniThrowException(env, "java/lang/IllegalStateException", NULL);
-        return NULL;
-    }
-
-    BufferingParams bp;
-    BufferingSettings &settings = bp.settings;
-    process_media_player_call(
-            env, thiz, mp->getBufferingSettings(&settings),
-            "java/lang/IllegalStateException", "unexpected error");
-    if (env->ExceptionCheck()) {
-        return nullptr;
-    }
-    ALOGV("getBufferingSettings:{%s}", settings.toString().string());
-
-    return bp.asJobject(env, gBufferingParamsFields);
-}
-
-static void
-android_media_MediaPlayer_setBufferingParams(JNIEnv *env, jobject thiz, jobject params)
-{
-    if (params == NULL) {
-        return;
-    }
-
-    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
-    if (mp == NULL) {
-        jniThrowException(env, "java/lang/IllegalStateException", NULL);
-        return;
-    }
-
-    BufferingParams bp;
-    bp.fillFromJobject(env, gBufferingParamsFields, params);
-    ALOGV("setBufferingParams:{%s}", bp.settings.toString().string());
-
-    process_media_player_call(
-            env, thiz, mp->setBufferingSettings(bp.settings),
-            "java/lang/IllegalStateException", "unexpected error");
-}
-
 static void
 android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)
 {
@@ -976,8 +930,6 @@
 
     env->DeleteLocalRef(clazz);
 
-    gBufferingParamsFields.init(env);
-
     // Modular DRM
     FIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException");
     if (clazz) {
@@ -1426,8 +1378,6 @@
     {"_setDataSource",      "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},
     {"_setDataSource",      "(Landroid/media/MediaDataSource;)V",(void *)android_media_MediaPlayer_setDataSourceCallback },
     {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)android_media_MediaPlayer_setVideoSurface},
-    {"getBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer_getBufferingParams},
-    {"setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer_setBufferingParams},
     {"_prepare",            "()V",                              (void *)android_media_MediaPlayer_prepare},
     {"prepareAsync",        "()V",                              (void *)android_media_MediaPlayer_prepareAsync},
     {"_start",              "()V",                              (void *)android_media_MediaPlayer_start},
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 8b6009e..7e6a8ab 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -820,7 +820,7 @@
 }
 
 static jlong
-android_media_MediaPlayer2_getDuration(JNIEnv *env, jobject thiz)
+android_media_MediaPlayer2_getDuration(JNIEnv *env, jobject thiz, jlong srcId)
 {
     sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
     if (mp == NULL ) {
@@ -828,7 +828,7 @@
         return 0;
     }
     int64_t msec;
-    process_media_player_call( env, thiz, mp->getDuration(&msec), NULL, NULL );
+    process_media_player_call( env, thiz, mp->getDuration(srcId, &msec), NULL, NULL );
     ALOGV("getDuration: %lld (msec)", (long long)msec);
     return (jlong) msec;
 }
@@ -1408,7 +1408,7 @@
     {"native_seekTo",       "(JI)V",                            (void *)android_media_MediaPlayer2_seekTo},
     {"native_pause",        "()V",                              (void *)android_media_MediaPlayer2_pause},
     {"getCurrentPosition",  "()J",                              (void *)android_media_MediaPlayer2_getCurrentPosition},
-    {"getDuration",         "()J",                              (void *)android_media_MediaPlayer2_getDuration},
+    {"native_getDuration",  "(J)J",                             (void *)android_media_MediaPlayer2_getDuration},
     {"native_release",      "()V",                              (void *)android_media_MediaPlayer2_release},
     {"native_reset",        "()V",                              (void *)android_media_MediaPlayer2_reset},
     {"native_setAudioAttributes", "(Landroid/media/AudioAttributes;)Z", (void *)android_media_MediaPlayer2_setAudioAttributes},
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index b3a8b21..ca30f32 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -763,6 +763,20 @@
     }
     return jStatus;
 }
+
+static jint android_media_MediaRecord_getPortId(JNIEnv *env,  jobject thiz) {
+    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
+    if (mr == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return (jint)AUDIO_PORT_HANDLE_NONE;
+    }
+
+    audio_port_handle_t portId;
+    process_media_recorder_call(env, mr->getPortId(&portId),
+                                "java/lang/RuntimeException", "getPortId failed.");
+    return (jint)portId;
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gMethods[] = {
@@ -801,6 +815,7 @@
     {"native_enableDeviceCallback", "(Z)V",                     (void *)android_media_MediaRecorder_enableDeviceCallback},
 
     {"native_getActiveMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_MediaRecord_getActiveMicrophones},
+    {"native_getPortId", "()I", (void *)android_media_MediaRecord_getPortId},
 };
 
 // This function only registers the native methods, and is called from
diff --git a/media/tests/MtpTests/Android.mk b/media/tests/MtpTests/Android.mk
index 6375ed3..4cee62e 100644
--- a/media/tests/MtpTests/Android.mk
+++ b/media/tests/MtpTests/Android.mk
@@ -5,7 +5,7 @@
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
 
 LOCAL_PACKAGE_NAME := MtpTests
 LOCAL_PRIVATE_PLATFORM_APIS := true
diff --git a/media/tests/MtpTests/AndroidManifest.xml b/media/tests/MtpTests/AndroidManifest.xml
index 21e2b01..72e03f1 100644
--- a/media/tests/MtpTests/AndroidManifest.xml
+++ b/media/tests/MtpTests/AndroidManifest.xml
@@ -25,7 +25,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.mtp"
                      android:label="MtpTests"/>
 </manifest>
diff --git a/media/tests/MtpTests/AndroidTest.xml b/media/tests/MtpTests/AndroidTest.xml
index a61a3b4..22638bc 100644
--- a/media/tests/MtpTests/AndroidTest.xml
+++ b/media/tests/MtpTests/AndroidTest.xml
@@ -10,6 +10,6 @@
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
         <option name="package" value="android.mtp"/>
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
     </test>
 </configuration>
\ No newline at end of file
diff --git a/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java b/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
index 566d1c6..1f58bde 100644
--- a/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
+++ b/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
@@ -18,20 +18,19 @@
 import android.os.FileUtils;
 import android.os.UserHandle;
 import android.os.storage.StorageVolume;
-import android.support.test.filters.SmallTest;
-import android.support.test.InstrumentationRegistry;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
 import org.junit.After;
-import org.junit.AfterClass;
 import org.junit.Assert;
 import org.junit.Before;
-import org.junit.BeforeClass;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
-import org.junit.runners.MethodSorters;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import org.junit.runners.MethodSorters;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -39,8 +38,6 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
-import java.util.function.Predicate;
-import java.util.stream.Stream;
 
 /**
  * Tests for MtpStorageManager functionality.
diff --git a/native/android/net.c b/native/android/net.c
index e32b787..4cac371 100644
--- a/native/android/net.c
+++ b/native/android/net.c
@@ -84,8 +84,7 @@
     return android_getaddrinfofornet(node, service, hints, netid, 0, res);
 }
 
-int android_res_nquery(net_handle_t network,
-        const char *dname, int ns_class, int ns_type) {
+int android_res_nquery(net_handle_t network, const char *dname, int ns_class, int ns_type) {
     unsigned netid;
     if (!getnetidfromhandle(network, &netid)) {
         return -ENONET;
@@ -94,12 +93,11 @@
     return resNetworkQuery(netid, dname, ns_class, ns_type);
 }
 
-int android_res_nresult(int fd, int *rcode, unsigned char *answer, int anslen) {
+int android_res_nresult(int fd, int *rcode, uint8_t *answer, size_t anslen) {
     return resNetworkResult(fd, rcode, answer, anslen);
 }
 
-int android_res_nsend(net_handle_t network,
-        const unsigned char *msg, int msglen) {
+int android_res_nsend(net_handle_t network, const uint8_t *msg, size_t msglen) {
     unsigned netid;
     if (!getnetidfromhandle(network, &netid)) {
         return -ENONET;
diff --git a/native/webview/plat_support/Android.bp b/native/webview/plat_support/Android.bp
index 96c9c1c..0936256 100644
--- a/native/webview/plat_support/Android.bp
+++ b/native/webview/plat_support/Android.bp
@@ -22,6 +22,7 @@
     name: "libwebviewchromium_plat_support",
 
     srcs: [
+        "draw_functor.cpp",
         "draw_gl_functor.cpp",
         "draw_vk_functor.cpp",
         "functor_utils.cpp",
diff --git a/native/webview/plat_support/draw_fn.h b/native/webview/plat_support/draw_fn.h
new file mode 100644
index 0000000..6afd883
--- /dev/null
+++ b/native/webview/plat_support/draw_fn.h
@@ -0,0 +1,197 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+//******************************************************************************
+// This is a copy of the coresponding android_webview/public/browser header.
+// Any changes to the interface should be made there as well.
+//******************************************************************************
+
+#ifndef ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_FN_H_
+#define ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_FN_H_
+
+#include <vulkan/vulkan.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// In order to make small changes backwards compatible, all structs passed from
+// android to chromium are versioned.
+//
+// 1 is Android Q. This matches kAwDrawGLInfoVersion version 3.
+static const int kAwDrawFnVersion = 1;
+
+struct AwDrawFn_OnSyncParams {
+  int version;
+
+  bool apply_force_dark;
+};
+
+struct AwDrawFn_DrawGLParams {
+  int version;
+
+  // Input: current clip rect in surface coordinates. Reflects the current state
+  // of the OpenGL scissor rect. Both the OpenGL scissor rect and viewport are
+  // set by the caller of the draw function and updated during View animations.
+  int clip_left;
+  int clip_top;
+  int clip_right;
+  int clip_bottom;
+
+  // Input: current width/height of destination surface.
+  int width;
+  int height;
+
+  // Input: is the View rendered into an independent layer.
+  // If false, the surface is likely to hold to the full screen contents, with
+  // the scissor box set by the caller to the actual View location and size.
+  // Also the transformation matrix will contain at least a translation to the
+  // position of the View to render, plus any other transformations required as
+  // part of any ongoing View animation. View translucency (alpha) is ignored,
+  // although the framework will set is_layer to true for non-opaque cases.
+  // Can be requested via the View.setLayerType(View.LAYER_TYPE_NONE, ...)
+  // Android API method.
+  //
+  // If true, the surface is dedicated to the View and should have its size.
+  // The viewport and scissor box are set by the caller to the whole surface.
+  // Animation transformations are handled by the caller and not reflected in
+  // the provided transformation matrix. Translucency works normally.
+  // Can be requested via the View.setLayerType(View.LAYER_TYPE_HARDWARE, ...)
+  // Android API method.
+  bool is_layer;
+
+  // Input: current transformation matrix in surface pixels.
+  // Uses the column-based OpenGL matrix format.
+  float transform[16];
+};
+
+struct AwDrawFn_InitVkParams {
+  int version;
+  VkInstance instance;
+  VkPhysicalDevice physical_device;
+  VkDevice device;
+  VkQueue queue;
+  uint32_t graphics_queue_index;
+  uint32_t instance_version;
+  const char* const* enabled_extension_names;
+  // Only one of device_features and device_features_2 should be non-null.
+  // If both are null then no features are enabled.
+  VkPhysicalDeviceFeatures* device_features;
+  VkPhysicalDeviceFeatures2* device_features_2;
+};
+
+struct AwDrawFn_DrawVkParams {
+  int version;
+
+  // Input: current width/height of destination surface.
+  int width;
+  int height;
+
+  // Input: is the render target a FBO
+  bool is_layer;
+
+  // Input: current transform matrix
+  float transform[16];
+
+  // Input WebView should do its main compositing draws into this. It cannot do
+  // anything that would require stopping the render pass.
+  VkCommandBuffer secondary_command_buffer;
+
+  // Input: The main color attachment index where secondary_command_buffer will
+  // eventually be submitted.
+  uint32_t color_attachment_index;
+
+  // Input: A render pass which will be compatible to the one which the
+  // secondary_command_buffer will be submitted into.
+  VkRenderPass compatible_render_pass;
+
+  // Input: Format of the destination surface.
+  VkFormat format;
+
+  // Input: Color space transformation from linear RGB to D50-adapted XYZ
+  float matrix[9];
+
+  // Input: current clip rect
+  int clip_left;
+  int clip_top;
+  int clip_right;
+  int clip_bottom;
+};
+
+struct AwDrawFn_PostDrawVkParams {
+  int version;
+
+  // Input: Fence for the composite command buffer to signal it has finished its
+  // work on the GPU.
+  int fd;
+};
+
+// Called on render thread while UI thread is blocked. Called for both GL and
+// VK.
+typedef void AwDrawFn_OnSync(int functor, void* data, AwDrawFn_OnSyncParams* params);
+
+// Called on render thread when either the context is destroyed _or_ when the
+// functor's last reference goes away. Will always be called with an active
+// context. Called for both GL and VK.
+typedef void AwDrawFn_OnContextDestroyed(int functor, void* data);
+
+// Called on render thread when the last reference to the handle goes away and
+// the handle is considered irrevocably destroyed. Will always be preceded by
+// a call to OnContextDestroyed if this functor had ever been drawn. Called for
+// both GL and VK.
+typedef void AwDrawFn_OnDestroyed(int functor, void* data);
+
+// Only called for GL.
+typedef void AwDrawFn_DrawGL(int functor, void* data, AwDrawFn_DrawGLParams* params);
+
+// Initialize vulkan state. Needs to be called again after any
+// OnContextDestroyed. Only called for Vulkan.
+typedef void AwDrawFn_InitVk(int functor, void* data, AwDrawFn_InitVkParams* params);
+
+// Only called for Vulkan.
+typedef void AwDrawFn_DrawVk(int functor, void* data, AwDrawFn_DrawVkParams* params);
+
+// Only called for Vulkan.
+typedef void AwDrawFn_PostDrawVk(int functor, void* data,
+                                 AwDrawFn_PostDrawVkParams* params);
+
+struct AwDrawFnFunctorCallbacks {
+  // No version here since this is passed from chromium to android.
+  AwDrawFn_OnSync* on_sync;
+  AwDrawFn_OnContextDestroyed* on_context_destroyed;
+  AwDrawFn_OnDestroyed* on_destroyed;
+  AwDrawFn_DrawGL* draw_gl;
+  AwDrawFn_InitVk* init_vk;
+  AwDrawFn_DrawVk* draw_vk;
+  AwDrawFn_PostDrawVk* post_draw_vk;
+};
+
+enum AwDrawFnRenderMode {
+  AW_DRAW_FN_RENDER_MODE_OPENGL_ES = 0,
+  AW_DRAW_FN_RENDER_MODE_VULKAN = 1,
+};
+
+// Get the render mode. Result is static for the process.
+typedef AwDrawFnRenderMode AwDrawFn_QueryRenderMode(void);
+
+// Create a functor. |functor_callbacks| should be valid until OnDestroyed.
+typedef int AwDrawFn_CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks);
+
+// May be called on any thread to signal that the functor should be destroyed.
+// The functor will receive an onDestroyed when the last usage of it is
+// released, and it should be considered alive & active until that point.
+typedef void AwDrawFn_ReleaseFunctor(int functor);
+
+struct AwDrawFnFunctionTable {
+  int version;
+  AwDrawFn_QueryRenderMode* query_render_mode;
+  AwDrawFn_CreateFunctor* create_functor;
+  AwDrawFn_ReleaseFunctor* release_functor;
+};
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_FN_H_
diff --git a/native/webview/plat_support/draw_functor.cpp b/native/webview/plat_support/draw_functor.cpp
new file mode 100644
index 0000000..820bac5
--- /dev/null
+++ b/native/webview/plat_support/draw_functor.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "draw_fn.h"
+
+#include <jni.h>
+#include <private/hwui/WebViewFunctor.h>
+#include <utils/Log.h>
+
+#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+#define COMPILE_ASSERT(expr, err) \
+__unused static const char (err)[(expr) ? 1 : -1] = "";
+
+namespace android {
+namespace {
+
+struct SupportData {
+  void* const data;
+  AwDrawFnFunctorCallbacks callbacks;
+};
+
+void onSync(int functor, void* data,
+            const uirenderer::WebViewSyncData& syncData) {
+  AwDrawFn_OnSyncParams params = {
+      .version = kAwDrawFnVersion,
+      .apply_force_dark = syncData.applyForceDark,
+  };
+  SupportData* support = static_cast<SupportData*>(data);
+  support->callbacks.on_sync(functor, support->data, &params);
+}
+
+void onContextDestroyed(int functor, void* data) {
+  SupportData* support = static_cast<SupportData*>(data);
+  support->callbacks.on_context_destroyed(functor, support->data);
+}
+
+void onDestroyed(int functor, void* data) {
+  SupportData* support = static_cast<SupportData*>(data);
+  support->callbacks.on_destroyed(functor, support->data);
+  delete support;
+}
+
+void draw_gl(int functor, void* data,
+             const uirenderer::DrawGlInfo& draw_gl_params) {
+  AwDrawFn_DrawGLParams params = {
+      .version = kAwDrawFnVersion,
+      .clip_left = draw_gl_params.clipLeft,
+      .clip_top = draw_gl_params.clipTop,
+      .clip_right = draw_gl_params.clipRight,
+      .clip_bottom = draw_gl_params.clipBottom,
+      .width = draw_gl_params.width,
+      .height = draw_gl_params.height,
+      .is_layer = draw_gl_params.isLayer,
+  };
+  COMPILE_ASSERT(NELEM(params.transform) == NELEM(draw_gl_params.transform),
+                 mismatched_transform_matrix_sizes);
+  for (int i = 0; i < NELEM(params.transform); ++i) {
+    params.transform[i] = draw_gl_params.transform[i];
+  }
+  SupportData* support = static_cast<SupportData*>(data);
+  support->callbacks.draw_gl(functor, support->data, &params);
+}
+
+int CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks) {
+  static bool callbacks_initialized = false;
+  static uirenderer::WebViewFunctorCallbacks webview_functor_callbacks = {
+      .onSync = &onSync,
+      .onContextDestroyed = &onContextDestroyed,
+      .onDestroyed = &onDestroyed,
+  };
+  if (!callbacks_initialized) {
+    switch (uirenderer::WebViewFunctor_queryPlatformRenderMode()) {
+      case uirenderer::RenderMode::OpenGL_ES:
+        webview_functor_callbacks.gles.draw = &draw_gl;
+        break;
+      case uirenderer::RenderMode::Vulkan:
+        break;
+    }
+    callbacks_initialized = true;
+  }
+  SupportData* support = new SupportData{
+      .data = data,
+      .callbacks = *functor_callbacks,
+  };
+  int functor = uirenderer::WebViewFunctor_create(
+      support, webview_functor_callbacks,
+      uirenderer::WebViewFunctor_queryPlatformRenderMode());
+  if (functor <= 0) delete support;
+  return functor;
+}
+
+void ReleaseFunctor(int functor) {
+  uirenderer::WebViewFunctor_release(functor);
+}
+
+AwDrawFnRenderMode QueryRenderMode(void) {
+  switch (uirenderer::WebViewFunctor_queryPlatformRenderMode()) {
+    case uirenderer::RenderMode::OpenGL_ES:
+      return AW_DRAW_FN_RENDER_MODE_OPENGL_ES;
+    case uirenderer::RenderMode::Vulkan:
+      return AW_DRAW_FN_RENDER_MODE_VULKAN;
+  }
+}
+
+jlong GetDrawFnFunctionTable() {
+  static AwDrawFnFunctionTable function_table = {
+    .version = kAwDrawFnVersion,
+    .query_render_mode = &QueryRenderMode,
+    .create_functor = &CreateFunctor,
+    .release_functor = &ReleaseFunctor,
+  };
+  return reinterpret_cast<intptr_t>(&function_table);
+}
+
+const char kClassName[] = "com/android/webview/chromium/DrawFunctor";
+const JNINativeMethod kJniMethods[] = {
+    {"nativeGetFunctionTable", "()J",
+     reinterpret_cast<void*>(GetDrawFnFunctionTable)},
+};
+
+}  // namespace
+
+void RegisterDrawFunctor(JNIEnv* env) {
+  jclass clazz = env->FindClass(kClassName);
+  LOG_ALWAYS_FATAL_IF(!clazz, "Unable to find class '%s'", kClassName);
+
+  int res = env->RegisterNatives(clazz, kJniMethods, NELEM(kJniMethods));
+  LOG_ALWAYS_FATAL_IF(res < 0, "register native methods failed: res=%d", res);
+}
+
+}  // namespace android
diff --git a/native/webview/plat_support/draw_vk_functor.cpp b/native/webview/plat_support/draw_vk_functor.cpp
index 1ba559d..eab1340 100644
--- a/native/webview/plat_support/draw_vk_functor.cpp
+++ b/native/webview/plat_support/draw_vk_functor.cpp
@@ -20,6 +20,7 @@
 
 #define LOG_TAG "webviewchromium_plat_support"
 
+#include "draw_fn.h"
 #include "draw_vk.h"
 
 #include <jni.h>
diff --git a/native/webview/plat_support/jni_entry_point.cpp b/native/webview/plat_support/jni_entry_point.cpp
index 4771be1..9599fa6 100644
--- a/native/webview/plat_support/jni_entry_point.cpp
+++ b/native/webview/plat_support/jni_entry_point.cpp
@@ -21,6 +21,7 @@
 
 namespace android {
 
+void RegisterDrawFunctor(JNIEnv* env);
 void RegisterDrawGLFunctor(JNIEnv* env);
 void RegisterGraphicsUtils(JNIEnv* env);
 
@@ -30,6 +31,7 @@
   JNIEnv* env = NULL;
   jint ret = vm->AttachCurrentThread(&env, NULL);
   LOG_ALWAYS_FATAL_IF(ret != JNI_OK, "AttachCurrentThread failed");
+  android::RegisterDrawFunctor(env);
   android::RegisterDrawGLFunctor(env);
   android::RegisterGraphicsUtils(env);
 
diff --git a/packages/BackupRestoreConfirmation/res/values-as/strings.xml b/packages/BackupRestoreConfirmation/res/values-as/strings.xml
index 4a86d64..76ec60b 100644
--- a/packages/BackupRestoreConfirmation/res/values-as/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-as/strings.xml
@@ -32,8 +32,8 @@
     <string name="backup_enc_password_required" msgid="7889652203371654149">"যিহেতু আপোনাৰ ডিভাইচটো এনক্ৰিপ্ট কৰা হৈছে, আপুনি আপোনাৰ বেকআপ এনক্ৰিপ্ট কৰাৰ প্ৰয়োজন। অনুগ্ৰহ কৰি তলত এটা পাছৱৰ্ড দিয়ক:"</string>
     <string name="restore_enc_password_text" msgid="6140898525580710823">"যদি পুনঃস্থাপন কৰিবলগীয়া ডেটা এনক্ৰিপ্ট কৰা আছে, তেন্তে তলত পাছৱৰ্ড দিয়ক:"</string>
     <string name="toast_backup_started" msgid="550354281452756121">"বেকআপ লোৱা কাৰ্য আৰম্ভ কৰি আছে..."</string>
-    <string name="toast_backup_ended" msgid="3818080769548726424">"বেকআপ লোৱা সম্পূৰ্ণ হ\'ল"</string>
+    <string name="toast_backup_ended" msgid="3818080769548726424">"বেকআপ লোৱা সম্পূৰ্ণ হ’ল"</string>
     <string name="toast_restore_started" msgid="7881679218971277385">"পুনঃস্থাপন কাৰ্য আৰম্ভ কৰি আছে..."</string>
-    <string name="toast_restore_ended" msgid="1764041639199696132">"পুনঃস্থাপন কাৰ্য সমাপ্ত হ\'ল"</string>
+    <string name="toast_restore_ended" msgid="1764041639199696132">"পুনঃস্থাপন কাৰ্য সমাপ্ত হ’ল"</string>
     <string name="toast_timeout" msgid="5276598587087626877">"কাৰ্যটোৰ সময়সীমা উকলি গ\'ল"</string>
 </resources>
diff --git a/packages/BackupRestoreConfirmation/res/values-ne/strings.xml b/packages/BackupRestoreConfirmation/res/values-ne/strings.xml
index d01ad50..2296b9f 100644
--- a/packages/BackupRestoreConfirmation/res/values-ne/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-ne/strings.xml
@@ -29,7 +29,7 @@
     <string name="device_encryption_backup_text" msgid="5866590762672844664">"कृपया तल तपाईंको उपकरण एन्क्रिप्सन पासवर्ड प्रविष्टि गर्नुहोस्: यो ब्याकप सँग्रह एन्क्रिप्ट गर्न पनि प्रयोग हुने छ।"</string>
     <string name="backup_enc_password_text" msgid="4981585714795233099">"ब्याकप डेटालाई encrypt गर्न पासवर्ड प्रविष्टि गर्नुहोस्, यदि यो खालि छोडिएको खण्डमा तपाईको पुरानै पासवर्ड प्रयोग हुने छ।"</string>
     <string name="backup_enc_password_optional" msgid="1350137345907579306">"यदि तपाईं पूर्ण ब्याकअप डेटा इन्क्रिप्ट गर्न चाहनु हुन्छ भने तल पासवर्ड प्रविष्टि गर्नुहोस्।"</string>
-    <string name="backup_enc_password_required" msgid="7889652203371654149">"तपाईँको उपकरण गुप्तिकरण गरिए देखि, तपाईंले आफ्नो जगेडा गुप्तिकरण गर्न आवश्यक छ। कृपया तल पासवर्ड प्रविष्ट गर्नुहोस्:"</string>
+    <string name="backup_enc_password_required" msgid="7889652203371654149">"तपाईँको उपकरण गुप्तिकरण गरिए देखि, तपाईंले आफ्नो जगेडा गुप्तिकरण गर्न आवश्यक छ। कृपया तल पासवर्ड प्रविष्टि गर्नुहोस्:"</string>
     <string name="restore_enc_password_text" msgid="6140898525580710823">"यदि पुनःबहाली डेटा इन्क्रिप्ट छ भने कृपया तल पासवर्ड प्रविष्टि गर्नुहोस्:"</string>
     <string name="toast_backup_started" msgid="550354281452756121">"जगेडा राख्न सुरु हुँदै..."</string>
     <string name="toast_backup_ended" msgid="3818080769548726424">"ब्याकअप सकियो"</string>
diff --git a/packages/CaptivePortalLogin/res/values-gl/strings.xml b/packages/CaptivePortalLogin/res/values-gl/strings.xml
index 6419516..f6f4aea 100644
--- a/packages/CaptivePortalLogin/res/values-gl/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-gl/strings.xml
@@ -13,7 +13,7 @@
     <string name="ssl_error_mismatch" msgid="3060364165934822383">"O nome do sitio non coincide co nome que aparece no certificado."</string>
     <string name="ssl_error_expired" msgid="1501588340716182495">"Este certificado caducou."</string>
     <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Este certificado aínda non é válido."</string>
-    <string name="ssl_error_date_invalid" msgid="88425990680059223">"Este certificado ten unha data non válida."</string>
+    <string name="ssl_error_date_invalid" msgid="88425990680059223">"Este certificado ten unha data non-válida."</string>
     <string name="ssl_error_invalid" msgid="2540546515565633432">"Este certificado non é válido."</string>
     <string name="ssl_error_unknown" msgid="4405203446079465859">"Produciuse un erro descoñecido relacionado co certificado."</string>
     <string name="ssl_security_warning_title" msgid="8768539813847504404">"Advertencia de seguranza"</string>
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index 74d6605..9b6ad38 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -69,6 +69,7 @@
         ],
     },
     resource_dirs: [
+        "res-keyguard",
         "res",
     ],
 
@@ -80,4 +81,5 @@
         "com.android.keyguard",
     ],
 
+    annotation_processors: ["dagger2-compiler-2.19"],
 }
diff --git a/packages/CarSystemUI/res-keyguard/drawable/ic_backspace.xml b/packages/CarSystemUI/res-keyguard/drawable/ic_backspace.xml
new file mode 100644
index 0000000..f3a2f0f
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/drawable/ic_backspace.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2018, The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License")
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,15.59L12.59,12L9,8.41L10.41,7L14,10.59L17.59,7L19,8.41L15.41,12L19,15.59L17.59,17L14,13.41L10.41,17L9,15.59zM21,6H8l-4.5,6L8,18h13V6M21,4c1.1,0 2,0.9 2,2v12c0,1.1 -0.9,2 -2,2H8c-0.63,0 -1.22,-0.3 -1.6,-0.8L1,12l5.4,-7.2C6.78,4.3 7.37,4 8,4H21L21,4z"/>
+</vector>
diff --git a/packages/CarSystemUI/res-keyguard/drawable/ic_done.xml b/packages/CarSystemUI/res-keyguard/drawable/ic_done.xml
new file mode 100644
index 0000000..ef0aac2
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/drawable/ic_done.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2018, The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License")
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,16.2l-3.5,-3.5a0.984,0.984 0,0 0,-1.4 0,0.984 0.984,0 0,0 0,1.4l4.19,4.19c0.39,0.39 1.02,0.39 1.41,0L20.3,7.7a0.984,0.984 0,0 0,0 -1.4,0.984 0.984,0 0,0 -1.4,0L9,16.2z"/>
+</vector>
diff --git a/packages/CarSystemUI/res-keyguard/drawable/keyguard_button_background.xml b/packages/CarSystemUI/res-keyguard/drawable/keyguard_button_background.xml
new file mode 100644
index 0000000..b428931
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/drawable/keyguard_button_background.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2018, The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License")
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true">
+        <shape android:shape="rectangle">
+            <corners android:radius="@dimen/car_button_radius"/>
+            <solid android:color="#131315"/>
+        </shape>
+    </item>
+    <item>
+        <shape android:shape="rectangle">
+            <corners android:radius="@dimen/car_button_radius"/>
+            <solid android:color="@color/button_background"/>
+        </shape>
+    </item>
+</selector>
diff --git a/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml b/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml
new file mode 100644
index 0000000..b115a1f
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- Car customizations
+     - Added title "Enter your Pattern" at the top
+     - Hid the emergency call at the bottom
+-->
+
+<com.android.keyguard.KeyguardPatternView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/keyguard_pattern_view"
+    android:orientation="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingHorizontal="@dimen/car_margin">
+
+    <FrameLayout
+        android:layout_height="match_parent"
+        android:layout_width="0dp"
+        android:layout_weight="1">
+
+        <com.android.internal.widget.LockPatternView
+            android:id="@+id/lockPatternView"
+            android:layout_width="@dimen/keyguard_pattern_dimension"
+            android:layout_height="@dimen/keyguard_pattern_dimension"
+            android:layout_gravity="center"/>
+    </FrameLayout>
+
+    <LinearLayout
+        android:id="@+id/container"
+        android:layout_height="match_parent"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:orientation="vertical"
+        android:gravity="center_vertical">
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/car_padding_2"
+            android:gravity="center"
+            android:textColor="@android:color/white"
+            android:textSize="@dimen/car_body1_size"
+            android:text="@string/keyguard_enter_your_pattern" />
+
+        <include layout="@layout/keyguard_message_area" />
+
+        <Button
+            android:id="@+id/cancel_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            style="@style/KeyguardButton"
+            android:text="@string/cancel"/>
+
+        <include layout="@layout/keyguard_eca"
+            android:id="@+id/keyguard_selector_fade_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:layout_gravity="bottom|center_horizontal"
+            android:gravity="center_horizontal"
+            android:visibility="gone" />
+    </LinearLayout>
+
+</com.android.keyguard.KeyguardPatternView>
diff --git a/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pin_view.xml b/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pin_view.xml
new file mode 100644
index 0000000..ed88c62
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pin_view.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2018, The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License")
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<!-- Car customizations
+     - Added title "Enter your PIN" under the entry field
+     - Put backspace and enter buttons in row 4
+     - PIN pad is on start side while entry field and title are on the end side
+     -  Hid the emergency call at the bottom
+-->
+
+<com.android.keyguard.KeyguardPINView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/keyguard_pin_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:paddingHorizontal="@dimen/car_margin">
+
+    <FrameLayout
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:layout_height="match_parent">
+
+        <GridLayout
+            android:id="@+id/container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:columnCount="3">
+
+            <include layout="@layout/num_pad_keys"/>
+        </GridLayout>
+    </FrameLayout>
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:gravity="center"
+        android:orientation="vertical">
+
+        <com.android.keyguard.PasswordTextView
+            android:id="@+id/pinEntry"
+            android:layout_width="@dimen/keyguard_security_width"
+            android:layout_height="@dimen/pin_entry_height"
+            android:gravity="center"
+            app:scaledTextSize="@integer/password_text_view_scale"
+            android:contentDescription="@string/keyguard_accessibility_pin_area" />
+
+        <View
+            android:id="@+id/divider"
+            android:layout_width="@dimen/keyguard_security_width"
+            android:layout_height="@dimen/divider_height"
+            android:background="@android:color/white" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/car_padding_2"
+            android:gravity="center"
+            android:textColor="@android:color/white"
+            android:textSize="@dimen/car_body1_size"
+            android:text="@string/keyguard_enter_your_pin" />
+
+        <include layout="@layout/keyguard_message_area" />
+
+        <Button
+            android:id="@+id/cancel_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            style="@style/KeyguardButton"
+            android:text="@string/cancel"/>
+    </LinearLayout>
+
+    <!-- KeyguardPinView references these resources ids in code so removing them will cause the
+         keyguard to crash. Instead put them down here where they are out of the way and set their
+         visibility to gone. -->
+    <com.android.keyguard.AlphaOptimizedRelativeLayout
+        android:id="@+id/row0"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone" />
+    <LinearLayout
+        android:id="@+id/row1"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone" />
+    <LinearLayout
+        android:id="@+id/row2"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone" />
+    <LinearLayout
+        android:id="@+id/row3"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone" />
+    <LinearLayout
+        android:id="@+id/row4"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone" />
+
+    <include layout="@layout/keyguard_eca"
+         android:id="@+id/keyguard_selector_fade_container"
+         android:layout_width="0dp"
+         android:layout_height="0dp"
+         android:visibility="gone" />
+</com.android.keyguard.KeyguardPINView>
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_bouncer.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_bouncer.xml
new file mode 100644
index 0000000..062f7bd
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_bouncer.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<!-- Car customizations
+     Car has solid black background instead of a transparent one
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/black"
+    android:fitsSystemWindows="true">
+
+    <include
+        style="@style/BouncerSecurityContainer"
+        layout="@layout/keyguard_host_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+</FrameLayout>
+
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_message_area.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_message_area.xml
new file mode 100644
index 0000000..c230414
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_message_area.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<com.android.keyguard.KeyguardMessageArea
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center"
+    style="@style/Keyguard.TextView"
+    android:id="@+id/keyguard_message_area"
+    android:singleLine="true"
+    android:ellipsize="marquee"
+    android:focusable="true"
+    android:layout_marginBottom="@dimen/car_padding_4"
+    android:textSize="@dimen/car_body2_size" />
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_num_pad_key.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_num_pad_key.xml
new file mode 100644
index 0000000..c7eda38
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_num_pad_key.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<!-- Car customizations
+     The mnemonics is not shown for car but KeyguardPinView references the resource id in code.
+     Removing it will cause the keyguard to crash. Hide them instead
+-->
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+    <TextView
+        android:id="@+id/digit_text"
+        style="@style/Widget.TextView.NumPadKey"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        />
+    <!-- The mnemonics is not shown for car but KeyguardPinView references the resource id in code.
+         Removing it will cause the keyguard to crash. Hide them instead -->
+    <TextView
+        android:id="@+id/klondike_text"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone"
+        />
+</merge>
+
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml
new file mode 100644
index 0000000..e701fdb
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- Car customizations
+     - Added title "Enter your Password" below the password field
+     - Hid the emergency call at the bottom
+-->
+
+<com.android.keyguard.KeyguardPasswordView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/keyguard_password_view"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    androidprv:layout_maxWidth="@dimen/keyguard_security_width"
+    androidprv:layout_maxHeight="@dimen/keyguard_security_height"
+    android:gravity="center">
+
+    <include layout="@layout/keyguard_message_area" />
+
+    <!-- Password entry field -->
+    <LinearLayout
+         android:layout_height="wrap_content"
+         android:layout_width="wrap_content"
+         android:layout_gravity="center_horizontal"
+         android:orientation="vertical"
+         android:theme="?attr/passwordStyle">
+
+        <EditText
+            android:id="@+id/passwordEntry"
+            android:layout_width="@dimen/password_field_width"
+            android:layout_height="wrap_content"
+            android:gravity="center_horizontal"
+            android:singleLine="true"
+            android:textStyle="normal"
+            android:inputType="textPassword"
+            android:textSize="@dimen/car_body1_size"
+            android:textColor="?attr/wallpaperTextColor"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:imeOptions="flagForceAscii|actionDone"
+            android:maxLength="@integer/password_text_view_scale"
+         />
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/car_padding_2"
+            android:gravity="center"
+            android:textColor="@android:color/white"
+            android:textSize="@dimen/car_body1_size"
+            android:text="@string/keyguard_enter_your_password" />
+
+        <Button
+            android:id="@+id/cancel_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            style="@style/KeyguardButton"
+            android:text="@string/cancel"/>
+
+        <ImageView android:id="@+id/switch_ime_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="12dp"
+            android:src="@drawable/ic_lockscreen_ime"
+            android:contentDescription="@string/accessibility_ime_switch_button"
+            android:clickable="true"
+            android:padding="8dp"
+            android:tint="@color/background_protected"
+            android:layout_gravity="end|center_vertical"
+            android:background="?android:attr/selectableItemBackground"
+            android:visibility="gone"
+        />
+    </LinearLayout>
+
+    <include layout="@layout/keyguard_eca"
+        android:id="@+id/keyguard_selector_fade_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="12dp"
+        android:orientation="vertical"
+        android:layout_gravity="bottom|center_horizontal"
+        android:gravity="center_horizontal"
+        android:visibility="gone"
+    />
+
+</com.android.keyguard.KeyguardPasswordView>
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_pattern_view.xml
new file mode 100644
index 0000000..00333a8
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_pattern_view.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- Car customizations
+     - Added title "Enter your Pattern" at the top
+     - Hid the emergency call at the bottom
+-->
+
+<com.android.keyguard.KeyguardPatternView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/keyguard_pattern_view"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    androidprv:layout_maxWidth="@dimen/keyguard_security_width"
+    android:gravity="center_horizontal">
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <LinearLayout
+            android:id="@+id/container"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:orientation="vertical"
+            android:layout_gravity="center">
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_margin="@dimen/car_padding_2"
+                android:gravity="center"
+                android:textColor="@android:color/white"
+                android:textSize="@dimen/car_body1_size"
+                android:text="@string/keyguard_enter_your_pattern" />
+
+            <include layout="@layout/keyguard_message_area" />
+
+            <com.android.internal.widget.LockPatternView
+                android:id="@+id/lockPatternView"
+                android:layout_width="@dimen/keyguard_pattern_dimension"
+                android:layout_height="@dimen/keyguard_pattern_dimension"
+                android:layout_marginVertical="@dimen/pin_pattern_pad_margin_vertical"
+                android:layout_gravity="center_horizontal"
+                android:gravity="center" />
+
+            <Button
+                android:id="@+id/cancel_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                style="@style/KeyguardButton"
+                android:text="@string/cancel"/>
+
+          <include layout="@layout/keyguard_eca"
+              android:id="@+id/keyguard_selector_fade_container"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:orientation="vertical"
+              android:layout_gravity="bottom|center_horizontal"
+              android:gravity="center_horizontal"
+              android:visibility="gone" />
+        </LinearLayout>
+    </FrameLayout>
+
+</com.android.keyguard.KeyguardPatternView>
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_pin_view.xml
new file mode 100644
index 0000000..1662251
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2018, The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License")
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<!-- Car customizations
+     - Added title "Enter your PIN" under the entry field
+     - Put backspace and enter buttons in row 4
+     - Hid the emergency call at the bottom
+-->
+
+<com.android.keyguard.KeyguardPINView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto"
+        android:id="@+id/keyguard_pin_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginLeft="@dimen/num_pad_margin_left"
+        android:layout_marginRight="@dimen/num_pad_margin_right"
+        android:orientation="vertical"
+        android:gravity="center">
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:orientation="vertical">
+
+            <com.android.keyguard.PasswordTextView
+                android:id="@+id/pinEntry"
+                android:layout_width="@dimen/keyguard_security_width"
+                android:layout_height="@dimen/pin_entry_height"
+                android:gravity="center"
+                app:scaledTextSize="@integer/password_text_view_scale"
+                android:contentDescription="@string/keyguard_accessibility_pin_area" />
+
+            <View
+                android:id="@+id/divider"
+                android:layout_width="@dimen/keyguard_security_width"
+                android:layout_height="@dimen/divider_height"
+                android:background="@android:color/white" />
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_margin="@dimen/car_padding_2"
+                android:gravity="center"
+                android:textColor="@android:color/white"
+                android:textSize="@dimen/car_body1_size"
+                android:text="@string/keyguard_enter_your_pin" />
+
+            <include layout="@layout/keyguard_message_area" />
+
+        </LinearLayout>
+
+        <GridLayout
+            android:id="@+id/container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginVertical="@dimen/pin_pattern_pad_margin_vertical"
+            android:columnCount="3">
+
+            <include layout="@layout/num_pad_keys"/>
+        </GridLayout>
+
+        <Button
+            android:id="@+id/cancel_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            style="@style/KeyguardButton"
+            android:text="@string/cancel"/>
+
+    </LinearLayout>
+
+    <!-- KeyguardPinView references these resources ids in code so removing them will cause the
+         keyguard to crash. Instead put them down here where they are out of the way and set their
+         visibility to gone. -->
+    <com.android.keyguard.AlphaOptimizedRelativeLayout
+        android:id="@+id/row0"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone" />
+    <LinearLayout
+        android:id="@+id/row1"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone" />
+    <LinearLayout
+        android:id="@+id/row2"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone" />
+    <LinearLayout
+        android:id="@+id/row3"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone" />
+    <LinearLayout
+        android:id="@+id/row4"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone" />
+
+    <include
+        layout="@layout/keyguard_eca"
+        android:id="@+id/keyguard_selector_fade_container"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone" />
+</com.android.keyguard.KeyguardPINView>
diff --git a/packages/CarSystemUI/res-keyguard/layout/num_pad_keys.xml b/packages/CarSystemUI/res-keyguard/layout/num_pad_keys.xml
new file mode 100644
index 0000000..8306cb4
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/num_pad_keys.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+       xmlns:app="http://schemas.android.com/apk/res-auto">
+    <!-- Row 1 -->
+    <com.android.keyguard.NumPadKey
+        android:id="@+id/key1"
+        style="@style/NumPadKeyButton"
+        app:digit="1" />
+    <com.android.keyguard.NumPadKey
+        android:id="@+id/key2"
+        style="@style/NumPadKeyButton.MiddleColumn"
+        app:digit="2" />
+    <com.android.keyguard.NumPadKey
+        android:id="@+id/key3"
+        style="@style/NumPadKeyButton"
+        app:digit="3" />
+
+    <!-- Row 2 -->
+    <com.android.keyguard.NumPadKey
+        android:id="@+id/key4"
+        style="@style/NumPadKeyButton"
+        app:digit="4" />
+    <com.android.keyguard.NumPadKey
+        android:id="@+id/key5"
+        style="@style/NumPadKeyButton.MiddleColumn"
+        app:digit="5" />
+    <com.android.keyguard.NumPadKey
+        android:id="@+id/key6"
+        style="@style/NumPadKeyButton"
+        app:digit="6" />
+
+    <!-- Row 3 -->
+    <com.android.keyguard.NumPadKey
+        android:id="@+id/key7"
+        style="@style/NumPadKeyButton"
+        app:digit="7" />
+    <com.android.keyguard.NumPadKey
+        android:id="@+id/key8"
+        style="@style/NumPadKeyButton.MiddleColumn"
+        app:digit="8" />
+    <com.android.keyguard.NumPadKey
+        android:id="@+id/key9"
+        style="@style/NumPadKeyButton"
+        app:digit="9" />
+
+    <!-- Row 4 -->
+    <ImageButton
+        android:id="@+id/delete_button"
+        style="@style/NumPadKeyButton.LastRow"
+        android:gravity="center_vertical"
+        android:src="@drawable/ic_backspace"
+        android:clickable="true"
+        android:tint="@android:color/white"
+        android:background="@drawable/ripple_drawable"
+        android:contentDescription="@string/keyboardview_keycode_delete" />
+    <com.android.keyguard.NumPadKey
+        android:id="@+id/key0"
+        style="@style/NumPadKeyButton.LastRow.MiddleColumn"
+        app:digit="0" />
+    <ImageButton
+        android:id="@+id/key_enter"
+        style="@style/NumPadKeyButton.LastRow"
+        android:src="@drawable/ic_done"
+        android:tint="@android:color/white"
+        android:background="@drawable/ripple_drawable"
+        android:contentDescription="@string/keyboardview_keycode_enter" />
+</merge>
+
diff --git a/core/res/res/values-port/dimens_permission_controller.xml b/packages/CarSystemUI/res-keyguard/values-h1000dp/dimens.xml
similarity index 62%
copy from core/res/res/values-port/dimens_permission_controller.xml
copy to packages/CarSystemUI/res-keyguard/values-h1000dp/dimens.xml
index af28713..d055efa 100644
--- a/core/res/res/values-port/dimens_permission_controller.xml
+++ b/packages/CarSystemUI/res-keyguard/values-h1000dp/dimens.xml
@@ -1,23 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  Copyright (C) 2018 The Android Open Source Project
+  Copyright 2018, The Android Open Source Project
 
-  Licensed under the Apache License, Version 2.0 (the "License");
+  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
+      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.
-  -->
-
-<!-- portrait dimensions for the permission grant dialog. -->
+-->
 <resources>
-    <!-- This yields 95% width -->
-    <dimen name="permissionGrantDialogWeight">380</dimen>
-    <dimen name="permissionGrantDialogWidth">0dp</dimen>
+    <dimen name="pin_pattern_pad_margin_vertical">178dp</dimen>
 </resources>
diff --git a/core/res/res/values-port/dimens_permission_controller.xml b/packages/CarSystemUI/res-keyguard/values-land/dimens.xml
similarity index 62%
copy from core/res/res/values-port/dimens_permission_controller.xml
copy to packages/CarSystemUI/res-keyguard/values-land/dimens.xml
index af28713..805a134 100644
--- a/core/res/res/values-port/dimens_permission_controller.xml
+++ b/packages/CarSystemUI/res-keyguard/values-land/dimens.xml
@@ -1,23 +1,20 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  Copyright (C) 2018 The Android Open Source Project
+  Copyright 2018, The Android Open Source Project
 
-  Licensed under the Apache License, Version 2.0 (the "License");
+  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
+      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.
-  -->
-
-<!-- portrait dimensions for the permission grant dialog. -->
+-->
 <resources>
-    <!-- This yields 95% width -->
-    <dimen name="permissionGrantDialogWeight">380</dimen>
-    <dimen name="permissionGrantDialogWidth">0dp</dimen>
+    <dimen name="num_pad_key_margin_horizontal">@dimen/car_padding_5</dimen>
+    <dimen name="num_pad_key_margin_bottom">@dimen/car_padding_4</dimen>
 </resources>
diff --git a/core/res/res/values-port/dimens_permission_controller.xml b/packages/CarSystemUI/res-keyguard/values/colors.xml
similarity index 62%
copy from core/res/res/values-port/dimens_permission_controller.xml
copy to packages/CarSystemUI/res-keyguard/values/colors.xml
index af28713..e6edbea3 100644
--- a/core/res/res/values-port/dimens_permission_controller.xml
+++ b/packages/CarSystemUI/res-keyguard/values/colors.xml
@@ -1,23 +1,21 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  Copyright (C) 2018 The Android Open Source Project
+  Copyright 2018, The Android Open Source Project
 
-  Licensed under the Apache License, Version 2.0 (the "License");
+  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
+      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.
-  -->
+-->
 
-<!-- portrait dimensions for the permission grant dialog. -->
 <resources>
-    <!-- This yields 95% width -->
-    <dimen name="permissionGrantDialogWeight">380</dimen>
-    <dimen name="permissionGrantDialogWidth">0dp</dimen>
-</resources>
+    <color name="button_background">@color/car_dark_blue_grey_600</color>
+    <color name="button_text">@color/car_action1_light</color>
+</resources>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res-keyguard/values/dimens.xml b/packages/CarSystemUI/res-keyguard/values/dimens.xml
new file mode 100644
index 0000000..9424dc3
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/values/dimens.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2017, The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License")
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<resources>
+    <dimen name="num_pad_margin_left">112dp</dimen>
+    <dimen name="num_pad_margin_right">144dp</dimen>
+    <dimen name="num_pad_key_width">80dp</dimen>
+    <dimen name="num_pad_key_height">80dp</dimen>
+    <dimen name="num_pad_key_margin_horizontal">@dimen/car_padding_6</dimen>
+    <dimen name="num_pad_key_margin_bottom">@dimen/car_padding_5</dimen>
+    <dimen name="pin_entry_height">@dimen/num_pad_key_height</dimen>
+    <dimen name="divider_height">1dp</dimen>
+    <dimen name="key_enter_margin_top">128dp</dimen>
+    <dimen name="keyguard_pattern_dimension">400dp</dimen>
+    <dimen name="password_field_width">350dp</dimen>
+    <dimen name="pin_pattern_pad_margin_vertical">0dp</dimen>
+</resources>
diff --git a/core/res/res/values-port/dimens_permission_controller.xml b/packages/CarSystemUI/res-keyguard/values/integers.xml
similarity index 62%
copy from core/res/res/values-port/dimens_permission_controller.xml
copy to packages/CarSystemUI/res-keyguard/values/integers.xml
index af28713..bad1346 100644
--- a/core/res/res/values-port/dimens_permission_controller.xml
+++ b/packages/CarSystemUI/res-keyguard/values/integers.xml
@@ -1,23 +1,20 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  Copyright (C) 2018 The Android Open Source Project
+  Copyright 2017, The Android Open Source Project
 
-  Licensed under the Apache License, Version 2.0 (the "License");
+  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
+      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.
-  -->
-
-<!-- portrait dimensions for the permission grant dialog. -->
+-->
 <resources>
-    <!-- This yields 95% width -->
-    <dimen name="permissionGrantDialogWeight">380</dimen>
-    <dimen name="permissionGrantDialogWidth">0dp</dimen>
+    <integer name="password_text_view_scale">40</integer>
+    <integer name="password_max_length">500</integer>
 </resources>
diff --git a/packages/CarSystemUI/res-keyguard/values/styles.xml b/packages/CarSystemUI/res-keyguard/values/styles.xml
new file mode 100644
index 0000000..b39e6e6
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/values/styles.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2017, The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License")
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <!-- The style for the volume icons in the volume dialog. This style makes the icon scale to
+         fit its container since auto wants the icon to be larger. The padding is added to make it
+         so the icon does not press along the edges of the dialog. -->
+    <style name="NumPadKeyButton">
+        <item name="android:layout_width">@dimen/num_pad_key_width</item>
+        <item name="android:layout_height">@dimen/num_pad_key_height</item>
+        <item name="android:layout_marginBottom">@dimen/num_pad_key_margin_bottom</item>
+        <item name="textView">@id/pinEntry</item>
+    </style>
+
+    <style name="NumPadKeyButton.MiddleColumn">
+        <item name="android:layout_marginStart">@dimen/num_pad_key_margin_horizontal</item>
+        <item name="android:layout_marginEnd">@dimen/num_pad_key_margin_horizontal</item>
+    </style>
+
+    <style name="NumPadKeyButton.LastRow">
+        <item name="android:layout_marginBottom">0dp</item>
+    </style>
+
+    <style name="NumPadKeyButton.LastRow.MiddleColumn">
+        <item name="android:layout_marginStart">@dimen/num_pad_key_margin_horizontal</item>
+        <item name="android:layout_marginEnd">@dimen/num_pad_key_margin_horizontal</item>
+    </style>
+
+    <style name="KeyguardButton" parent="Widget.Car.Button">
+        <item name="android:background">@drawable/keyguard_button_background</item>
+        <item name="android:textColor">@color/button_text</item>
+        <item name="android:textAllCaps">false</item>
+    </style>
+
+    <style name="Widget.TextView.NumPadKey" parent="@android:style/Widget.TextView">
+        <!-- Only replaces the text size. -->
+        <item name="android:textSize">@dimen/car_body1_size</item>
+    </style>
+</resources>
diff --git a/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
index 1d67286..a2a628d 100644
--- a/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
+++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
@@ -25,13 +25,15 @@
     android:orientation="vertical"
     android:gravity="center">
 
-    <ImageView android:id="@+id/user_avatar"
+    <ImageView
+        android:id="@+id/user_avatar"
         android:layout_width="@dimen/car_user_switcher_image_avatar_size"
         android:layout_height="@dimen/car_user_switcher_image_avatar_size"
-        android:background="@drawable/car_button_ripple_background_light"
+        android:background="?android:attr/selectableItemBackground"
         android:gravity="center"/>
 
-    <TextView android:id="@+id/user_name"
+    <TextView
+        android:id="@+id/user_name"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/car_user_switcher_vertical_spacing_between_name_and_avatar"
diff --git a/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
index 6cd70d6..e8c5134cd 100644
--- a/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
+++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
@@ -19,12 +19,10 @@
         android:fitsSystemWindows="true"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:background="@color/car_user_switcher_background_color"
         android:visibility="gone">
 
     <LinearLayout
         android:id="@+id/container"
-        android:background="@color/car_user_switcher_background_color"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="vertical">
@@ -38,7 +36,7 @@
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:layout_marginTop="@dimen/car_user_switcher_margin_top"
-            android:theme="@style/Theme.Car.Light.List"
+            android:theme="@style/PagedListTheme"
             app:verticallyCenterListContent="true"
             app:showPagedListViewDivider="false"
             app:gutter="both"
diff --git a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
index 141b28a..72ec8d8 100644
--- a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
@@ -68,7 +68,7 @@
         android:orientation="vertical">
 
         <com.android.keyguard.AlphaOptimizedImageButton
-            android:id="@+id/notifications"
+            android:id="@+id/note"
             android:layout_height="wrap_content"
             android:layout_width="match_parent"
             android:src="@drawable/car_ic_notification"
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
index b67ce15..052566d 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
@@ -20,7 +20,7 @@
     xmlns:systemui="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@android:color/black"
+    android:background="@drawable/system_bar_background"
     android:orientation="vertical">
     <LinearLayout
         android:id="@id/nav_buttons"
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
index 46e60db..4fa877f 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
@@ -20,7 +20,7 @@
     xmlns:systemui="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@android:color/black"
+    android:background="@drawable/system_bar_background"
     android:orientation="vertical">
 
     <LinearLayout
diff --git a/packages/CarSystemUI/res/layout/car_qs_footer.xml b/packages/CarSystemUI/res/layout/car_qs_footer.xml
index 6f19cfc..bf96c00 100644
--- a/packages/CarSystemUI/res/layout/car_qs_footer.xml
+++ b/packages/CarSystemUI/res/layout/car_qs_footer.xml
@@ -35,7 +35,7 @@
         android:layout_centerVertical="true"
         android:layout_width="@dimen/car_qs_footer_icon_width"
         android:layout_height="@dimen/car_qs_footer_icon_height"
-        android:background="@drawable/ripple_drawable"
+        android:background="?android:attr/selectableItemBackground"
         android:focusable="true">
 
         <ImageView
diff --git a/packages/CarSystemUI/res/layout/car_qs_panel.xml b/packages/CarSystemUI/res/layout/car_qs_panel.xml
index dfa48c3..d923e0f 100644
--- a/packages/CarSystemUI/res/layout/car_qs_panel.xml
+++ b/packages/CarSystemUI/res/layout/car_qs_panel.xml
@@ -21,8 +21,7 @@
     android:layout_height="wrap_content"
     android:background="@color/car_qs_background_primary"
     android:orientation="vertical"
-    android:elevation="4dp"
-    android:theme="@android:style/Theme">
+    android:elevation="4dp">
 
     <include layout="@layout/car_status_bar_header"/>
     <include layout="@layout/car_qs_footer"/>
@@ -39,7 +38,7 @@
             android:id="@+id/user_grid"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:theme="@style/Theme.Car.Light.List"
+            android:theme="@style/PagedListTheme"
             app:showPagedListViewDivider="false"
             app:gutter="both"
             app:itemSpacing="@dimen/car_user_switcher_vertical_spacing_between_users"/>
diff --git a/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
index 141b28a..72ec8d8 100644
--- a/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
@@ -68,7 +68,7 @@
         android:orientation="vertical">
 
         <com.android.keyguard.AlphaOptimizedImageButton
-            android:id="@+id/notifications"
+            android:id="@+id/note"
             android:layout_height="wrap_content"
             android:layout_width="match_parent"
             android:src="@drawable/car_ic_notification"
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
index 7b3333e..2fe740d 100644
--- a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
@@ -21,7 +21,7 @@
     android:id="@+id/car_top_bar"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@android:color/black"
+    android:background="@drawable/system_bar_background"
     android:orientation="vertical">
 
     <RelativeLayout
@@ -76,7 +76,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:background="@null"
-                systemui:intent="intent:#Intent;component=com.android.car.settings/.common.CarSettingActivity;launchFlags=0x14008000;end"
+                systemui:intent="intent:#Intent;component=com.android.car.settings/.common.CarSettingActivity;launchFlags=0x24000000;end"
             />
             <com.android.systemui.statusbar.policy.Clock
                 android:id="@+id/clock"
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index 572737f..2e05c38 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -32,7 +32,7 @@
          SystemUi b/c it can't be overlayed at this level for now
     -->
     <string-array name="config_systemUIServiceComponents" translatable="false">
-        <item>com.android.systemui.Dependency</item>
+        <item>com.android.systemui.Dependency$DependencyCreator</item>
         <item>com.android.systemui.util.NotificationChannels</item>
         <item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item>
         <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
@@ -51,7 +51,6 @@
         <item>com.android.systemui.LatencyTester</item>
         <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
         <item>com.android.systemui.ScreenDecorations</item>
-        <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item>
         <item>com.android.systemui.SliceBroadcastRelayHandler</item>
         <item>com.android.systemui.notifications.NotificationsUI</item>
     </string-array>
diff --git a/core/java/android/service/contentcapture/InteractionContext.aidl b/packages/CarSystemUI/res/values/themes.xml
similarity index 70%
copy from core/java/android/service/contentcapture/InteractionContext.aidl
copy to packages/CarSystemUI/res/values/themes.xml
index 982e095..8a5961e 100644
--- a/core/java/android/service/contentcapture/InteractionContext.aidl
+++ b/packages/CarSystemUI/res/values/themes.xml
@@ -1,3 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
 /**
  * Copyright (c) 2018, The Android Open Source Project
  *
@@ -13,7 +15,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+-->
 
-package android.service.contentcapture;
-
-parcelable InteractionContext;
+<resources>
+    <!--This Theme contains attributes required for components from the car support lib -->
+    <style name="PagedListTheme" parent="Theme.CarSupportWrapper.NoActionBar">
+    </style>
+</resources>
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
index f57f26d..0c35204 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
@@ -17,25 +17,42 @@
 package com.android.systemui;
 
 import android.content.Context;
-import android.util.ArrayMap;
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.Dependency.DependencyProvider;
-import com.android.systemui.car.CarNotificationEntryManager;
+import com.android.systemui.car.CarNotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.car.CarFacetButtonController;
 import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.car.hvac.HvacController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.volume.CarVolumeDialogComponent;
 import com.android.systemui.volume.VolumeDialogComponent;
 
+import javax.inject.Singleton;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+
 /**
  * Class factory to provide car specific SystemUI components.
  */
 public class CarSystemUIFactory extends SystemUIFactory {
 
+    private CarDependencyComponent mCarDependencyComponent;
+
+    @Override
+    protected void init(Context context) {
+        super.init(context);
+        mCarDependencyComponent = DaggerCarSystemUIFactory_CarDependencyComponent.builder()
+                .contextHolder(new ContextHolder(context))
+                .build();
+    }
+
+    public CarDependencyComponent getCarDependencyComponent() {
+        return mCarDependencyComponent;
+    }
+
     public StatusBarKeyguardViewManager createStatusBarKeyguardViewManager(Context context,
         ViewMediatorCallback viewMediatorCallback, LockPatternUtils lockPatternUtils) {
         return new CarStatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
@@ -46,12 +63,33 @@
     }
 
     @Override
-    public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
-        Context context) {
-        super.injectDependencies(providers, context);
-        providers.put(NotificationEntryManager.class,
-            () -> new CarNotificationEntryManager(context));
-        providers.put(CarFacetButtonController.class, () -> new CarFacetButtonController(context));
-        providers.put(HvacController.class, () -> new HvacController(context));
+    public NotificationInterruptionStateProvider provideNotificationInterruptionStateProvider(
+            Context context) {
+        return new CarNotificationInterruptionStateProvider(context);
+    }
+
+    @Override
+    public boolean provideAllowNotificationLongPress() {
+        return false;
+    }
+
+    @Module
+    protected static class ContextHolder {
+        private Context mContext;
+
+        public ContextHolder(Context context) {
+            mContext = context;
+        }
+
+        @Provides
+        public Context provideContext() {
+            return mContext;
+        }
+    }
+
+    @Singleton
+    @Component(modules = ContextHolder.class)
+    public interface CarDependencyComponent {
+        CarFacetButtonController getCarFacetButtonController();
     }
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
deleted file mode 100644
index 0563418..0000000
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.car;
-
-import android.content.Context;
-
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-
-public class CarNotificationEntryManager extends NotificationEntryManager {
-    public CarNotificationEntryManager(Context context) {
-        super(context);
-    }
-
-    /**
-     * Returns the
-     * {@link ExpandableNotificationRow.LongPressListener} that will
-     * be triggered when a notification card is long-pressed.
-     */
-    @Override
-    public ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
-        // For the automative use case, we do not want to the user to be able to interact with
-        // a notification other than a regular click. As a result, just return null for the
-        // long click listener.
-        return null;
-    }
-
-    @Override
-    public boolean shouldHeadsUp(NotificationData.Entry entry) {
-        // Because space is usually constrained in the auto use-case, there should not be a
-        // pinned notification when the shade has been expanded. Ensure this by not pinning any
-        // notification if the shade is already opened.
-        if (!getPresenter().isPresenterFullyCollapsed()) {
-            return false;
-        }
-
-        return super.shouldHeadsUp(entry);
-    }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
new file mode 100644
index 0000000..6d960d7
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.car;
+
+import android.content.Context;
+
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+/** Auto-specific implementation of {@link NotificationInterruptionStateProvider}. */
+public class CarNotificationInterruptionStateProvider extends
+        NotificationInterruptionStateProvider {
+    public CarNotificationInterruptionStateProvider(Context context) {
+        super(context);
+    }
+
+    @Override
+    public boolean shouldHeadsUp(NotificationEntry entry) {
+        // Because space is usually constrained in the auto use-case, there should not be a
+        // pinned notification when the shade has been expanded. Ensure this by not pinning any
+        // notification if the shade is already opened.
+        if (!getPresenter().isPresenterFullyCollapsed()) {
+            return false;
+        }
+
+        return super.shouldHeadsUp(entry);
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java b/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java
index cb92c42..5e63b90 100644
--- a/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java
+++ b/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.notifications;
 
+import android.app.ActivityManager;
 import android.car.Car;
 import android.car.CarNotConnectedException;
 import android.car.drivingstate.CarUxRestrictionsManager;
@@ -24,6 +25,7 @@
 import android.content.ServiceConnection;
 import android.graphics.PixelFormat;
 import android.os.IBinder;
+import android.os.ServiceManager;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -31,8 +33,10 @@
 
 import com.android.car.notification.CarNotificationListener;
 import com.android.car.notification.CarUxRestrictionManagerWrapper;
+import com.android.car.notification.NotificationClickHandlerFactory;
 import com.android.car.notification.NotificationViewController;
 import com.android.car.notification.PreprocessingManager;
+import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
 
@@ -44,6 +48,7 @@
     private static final String TAG = "NotificationsUI";
     private CarNotificationListener mCarNotificationListener;
     private CarUxRestrictionsManager mCarUxRestrictionsManager;
+    private NotificationClickHandlerFactory mClickHandlerFactory;
     private Car mCar;
     private ViewGroup mCarNotificationWindow;
     private NotificationViewController mNotificationViewController;
@@ -60,7 +65,17 @@
         WindowManager windowManager =
                 (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         mCarNotificationListener = new CarNotificationListener();
-        mCarNotificationListener.registerAsSystemService(mContext, mCarUxRestrictionManagerWrapper);
+        mClickHandlerFactory = new NotificationClickHandlerFactory(
+                IStatusBarService.Stub.asInterface(
+                        ServiceManager.getService(Context.STATUS_BAR_SERVICE)),
+                launchResult -> {
+                    if (launchResult == ActivityManager.START_TASK_TO_FRONT
+                            || launchResult == ActivityManager.START_SUCCESS) {
+                        closeCarNotifications();
+                    }
+                });
+        mCarNotificationListener.registerAsSystemService(mContext, mCarUxRestrictionManagerWrapper,
+                mClickHandlerFactory);
         mCar = Car.createCar(mContext, mCarConnectionListener);
         mCar.connect();
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
index d5dd3c3..4ef926f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
+++ b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
@@ -28,7 +28,7 @@
 import com.android.settingslib.Utils;
 import com.android.systemui.BatteryMeterView;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher;
 
 /**
  * A view that forms the header of the notification panel. This view will ensure that any
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/AssitantButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/AssitantButton.java
index 5bf30ca..b36a7da 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/AssitantButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/AssitantButton.java
@@ -45,9 +45,7 @@
                     Log.d(TAG, "IVoiceInteractionSessionShowCallback onShown()");
                 }
             };
-
-    private static final String EXTRA_CAR_PUSH_TO_TALK =
-            "com.android.car.input.EXTRA_CAR_PUSH_TO_TALK";
+    
     private final AssistUtils mAssistUtils;
 
     public AssitantButton(Context context, AttributeSet attrs) {
@@ -60,7 +58,6 @@
 
     private void showAssistant() {
         final Bundle args = new Bundle();
-        args.putBoolean(EXTRA_CAR_PUSH_TO_TALK, true);
         mAssistUtils.showSessionForActiveService(args,
                 SHOW_SOURCE_ASSIST_GESTURE, mShowCallback, /*activityToken=*/ null);
     }
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
index cea4ab0..0a20eaa 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
@@ -26,8 +26,9 @@
 import android.widget.LinearLayout;
 
 import com.android.keyguard.AlphaOptimizedImageButton;
-import com.android.systemui.Dependency;
+import com.android.systemui.CarSystemUIFactory;
 import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
 
 /**
  * CarFacetButton is a ui component designed to be used as a shortcut for an app of a defined
@@ -76,8 +77,9 @@
         TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton);
         setupIntents(typedArray);
         setupIcons(typedArray);
-        CarFacetButtonController carFacetButtonController = Dependency.get(
-                CarFacetButtonController.class);
+        CarSystemUIFactory factory = SystemUIFactory.getInstance();
+        CarFacetButtonController carFacetButtonController = factory.getCarDependencyComponent()
+                .getCarFacetButtonController();
         carFacetButtonController.addFacetButton(this);
     }
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
index 56db242..7811a1c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
@@ -29,11 +29,15 @@
 import java.util.List;
 import java.util.Set;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * CarFacetButtons placed on the nav bar are designed to have visual indication that the active
  * application on screen is associated with it. This is basically a similar concept to a radio
  * button group.
  */
+@Singleton
 public class CarFacetButtonController {
 
     protected HashMap<String, CarFacetButton> mButtonsByCategory = new HashMap<>();
@@ -42,6 +46,7 @@
     protected CarFacetButton mSelectedFacetButton;
     protected Context mContext;
 
+    @Inject
     public CarFacetButtonController(Context context) {
         mContext = context;
     }
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 5da236c..dbddf71 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -29,9 +29,11 @@
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.BatteryMeterView;
+import com.android.systemui.CarSystemUIFactory;
 import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
 import com.android.systemui.classifier.FalsingLog;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.fragments.FragmentHostManager;
@@ -102,7 +104,9 @@
 
         mHvacController.connectToCarService();
 
-        mCarFacetButtonController = Dependency.get(CarFacetButtonController.class);
+        CarSystemUIFactory factory = SystemUIFactory.getInstance();
+        mCarFacetButtonController = factory.getCarDependencyComponent()
+                .getCarFacetButtonController();
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
         mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
         if (!mDeviceIsProvisioned) {
@@ -239,7 +243,7 @@
     @Override
     protected void makeStatusBarView() {
         super.makeStatusBarView();
-        mHvacController = Dependency.get(HvacController.class);
+        mHvacController = new HvacController(mContext);
 
         mNotificationPanelBackground = getDefaultWallpaper();
         mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
@@ -276,7 +280,9 @@
         buildNavBarContent();
         attachNavBarWindows();
 
-        mNavigationBarController.createNavigationBars();
+        // There has been a car customized nav bar on the default display, so just create nav bars
+        // on external displays.
+        mNavigationBarController.createNavigationBars(false /* includeDefaultDisplay */);
     }
 
     private void buildNavBarContent() {
@@ -443,12 +449,6 @@
         }
     }
 
-
-    @Override
-    public View getNavigationBarWindow() {
-        return mNavigationBarWindow;
-    }
-
     @Override
     protected View.OnTouchListener getStatusBarWindowTouchListener() {
         // Usually, a touch on the background window will dismiss the notification shade. However,
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
index 730c3e3..a442426 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
@@ -52,6 +52,9 @@
      * is idling or moving, {@code false} otherwise.
      */
     public boolean isCurrentlyDriving() {
+        if (mDrivingStateManager == null) {
+            return false;
+        }
         try {
             CarDrivingStateEvent currentState = mDrivingStateManager.getCurrentCarDrivingState();
             if (currentState != null) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
index aec31ee..30429ed 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
@@ -16,7 +16,11 @@
 
 package com.android.systemui.statusbar.car.hvac;
 
+import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
+import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS;
+
 import android.car.Car;
+import android.car.VehicleUnit;
 import android.car.hardware.CarPropertyValue;
 import android.car.hardware.hvac.CarHvacManager;
 import android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback;
@@ -167,7 +171,17 @@
     private void initComponent(TemperatureView view) {
         int id = view.getPropertyId();
         int zone = view.getAreaId();
+
         try {
+            if (mHvacManager != null
+                    && mHvacManager.isPropertyAvailable(HVAC_TEMPERATURE_DISPLAY_UNITS,
+                            VEHICLE_AREA_TYPE_GLOBAL)) {
+                if (mHvacManager.getIntProperty(HVAC_TEMPERATURE_DISPLAY_UNITS,
+                        VEHICLE_AREA_TYPE_GLOBAL) == VehicleUnit.FAHRENHEIT) {
+                    view.setDisplayInFahrenheit(true);
+                }
+
+            }
             if (mHvacManager == null || !mHvacManager.isPropertyAvailable(id, zone)) {
                 view.setTemp(Float.NaN);
                 return;
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
index 507c60f..17ef3c0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
@@ -36,6 +36,7 @@
     private final int mAreaId;
     private final int mPropertyId;
     private final String mTempFormat;
+    private boolean mDisplayFahrenheit = false;
 
     public TemperatureTextView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -57,9 +58,17 @@
             setText("--");
             return;
         }
+        if (mDisplayFahrenheit) {
+            temp = convertToFahrenheit(temp);
+        }
         setText(String.format(mTempFormat, temp));
     }
 
+    @Override
+    public void setDisplayInFahrenheit(boolean displayFahrenheit) {
+        mDisplayFahrenheit = displayFahrenheit;
+    }
+
     /**
      * @return propertiyId  Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385)
      */
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
index 7651356..c17da18 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
@@ -23,10 +23,26 @@
     /**
      * Formats the float for display
      *
-     * @param temp - The current temp or NaN
+     * @param temp - The current temp in Celsius or NaN
      */
     void setTemp(float temp);
 
+    /**
+     * Render the displayed temperature in Fahrenheit
+     *
+     * @param displayFahrenheit - True if temperature should be displayed in Fahrenheit
+     */
+    void setDisplayInFahrenheit(boolean displayFahrenheit);
+
+    /**
+     * Convert the given temperature in Celsius into Fahrenheit
+     *
+     * @param realTemp - The temperature in Celsius
+     * @return Temperature in Fahrenheit.
+     */
+    default float convertToFahrenheit(float realTemp) {
+        return (realTemp * 9f / 5f) + 32;
+    }
 
     /**
      * @return propertiyId  Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385)
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java
index 0467bff..76126fc 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java
@@ -99,6 +99,7 @@
     private final TemperatureColorStore mColorStore = new TemperatureColorStore();
     private final TemperatureBackgroundAnimator mBackgroundAnimator;
     private final TemperatureTextAnimator mTextAnimator;
+    boolean mDisplayInFahrenheit = false;
 
     public AnimatedTemperatureView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -193,6 +194,9 @@
      */
     @Override
     public void setTemp(float temp) {
+        if (mDisplayInFahrenheit) {
+            temp = convertToFahrenheit(temp);
+        }
         mTextAnimator.setTemp(temp);
         if (Float.isNaN(temp)) {
             mBackgroundAnimator.hideCircle();
@@ -219,6 +223,11 @@
         mBackgroundAnimator.animateOpen();
     }
 
+    @Override
+    public void setDisplayInFahrenheit(boolean displayInFahrenheit) {
+        mDisplayInFahrenheit = displayInFahrenheit;
+    }
+
     boolean isMinValue(float temp) {
         return !Float.isNaN(mMinValue) && isApproxEqual(temp, mMinValue);
     }
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index 1737b64..85dab57 100644
--- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -107,36 +107,44 @@
     private CarAudioManager mCarAudioManager;
     private final CarAudioManager.CarVolumeCallback mVolumeChangeCallback =
             new CarAudioManager.CarVolumeCallback() {
-        @Override
-        public void onGroupVolumeChanged(int zoneId, int groupId, int flags) {
-            // TODO: Include zoneId into consideration.
-            // For instance
-            // - single display + single-zone, ignore zoneId
-            // - multi-display + single-zone, zoneId is fixed, may show volume bar on all displays
-            // - single-display + multi-zone, may show volume bar on primary display only
-            // - multi-display + multi-zone, may show volume bar on display specified by zoneId
-            VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
-            int value = getSeekbarValue(mCarAudioManager, groupId);
-            // Do not update the progress if it is the same as before. When car audio manager sets
-            // its group volume caused by the seekbar progress changed, it also triggers this
-            // callback. Updating the seekbar at the same time could block the continuous seeking.
-            if (value != volumeItem.progress) {
-                volumeItem.listItem.setProgress(value);
-                volumeItem.progress = value;
-            }
-            if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
-                mHandler.obtainMessage(H.SHOW, Events.SHOW_REASON_VOLUME_CHANGED).sendToTarget();
-            }
-        }
+                @Override
+                public void onGroupVolumeChanged(int zoneId, int groupId, int flags) {
+                    // TODO: Include zoneId into consideration.
+                    // For instance
+                    // - single display + single-zone, ignore zoneId
+                    // - multi-display + single-zone, zoneId is fixed, may show volume bar on all
+                    // displays
+                    // - single-display + multi-zone, may show volume bar on primary display only
+                    // - multi-display + multi-zone, may show volume bar on display specified by
+                    // zoneId
+                    VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
+                    int value = getSeekbarValue(mCarAudioManager, groupId);
+                    // Do not update the progress if it is the same as before. When car audio
+                    // manager sets
+                    // its group volume caused by the seekbar progress changed, it also triggers
+                    // this
+                    // callback. Updating the seekbar at the same time could block the continuous
+                    // seeking.
+                    if (value != volumeItem.progress) {
+                        volumeItem.listItem.setProgress(value);
+                        volumeItem.progress = value;
+                    }
+                    if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
+                        mHandler.obtainMessage(H.SHOW,
+                                Events.SHOW_REASON_VOLUME_CHANGED).sendToTarget();
+                    }
+                }
 
-        @Override
-        public void onMasterMuteChanged(int zoneId, int flags) {
-            // ignored
-        }
-    };
+                @Override
+                public void onMasterMuteChanged(int zoneId, int flags) {
+                    // ignored
+                }
+            };
     private boolean mHovering;
     private boolean mShowing;
     private boolean mExpanded;
+    private View mExpandIcon;
+    private VolumeItem mDefaultVolumeItem;
     private final ServiceConnection mServiceConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
@@ -151,10 +159,8 @@
                     mAvailableVolumeItems.add(volumeItem);
                     // The first one is the default item.
                     if (groupId == 0) {
-                        volumeItem.defaultItem = true;
-                        addSeekbarListItem(volumeItem, groupId,
-                                R.drawable.car_ic_keyboard_arrow_down,
-                                new ExpandIconListener());
+                        mDefaultVolumeItem = volumeItem;
+                        setupDefaultListItem();
                     }
                 }
 
@@ -178,6 +184,13 @@
         }
     };
 
+    private void setupDefaultListItem() {
+        mDefaultVolumeItem.defaultItem = true;
+        addSeekbarListItem(mDefaultVolumeItem, /* volumeGroupId = */0,
+                R.drawable.car_ic_keyboard_arrow_down, new ExpandIconListener()
+        );
+    }
+
     public CarVolumeDialogImpl(Context context) {
         mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
         mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
@@ -294,7 +307,9 @@
             return;
         }
         mShowing = true;
-
+        if (mVolumeLineItems.isEmpty()) {
+            setupDefaultListItem();
+        }
         mDialog.show();
         Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
     }
@@ -340,6 +355,13 @@
                     }
                     mDialog.dismiss();
                     mShowing = false;
+                    mShowing = false;
+                    // if mExpandIcon is null that means user never clicked on the expanded arrow
+                    // which implies that the dialog is still not expanded. In that case we do
+                    // not want to reset the state
+                    if (mExpandIcon != null && mExpanded) {
+                        toggleDialogExpansion(/* isClicked = */ false);
+                    }
                 }, DISMISS_DELAY_IN_MILLIS))
                 .start();
 
@@ -517,52 +539,62 @@
     }
 
     private final class ExpandIconListener implements View.OnClickListener {
-
         @Override
         public void onClick(final View v) {
-            mExpanded = !mExpanded;
-            Animator inAnimator;
-            if (mExpanded) {
-                for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) {
-                    // Adding the items which are not coming from the default item.
-                    VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
-                    if (volumeItem.defaultItem) {
-                        // Set progress here due to the progress of seekbar may not be updated.
-                        volumeItem.listItem.setProgress(volumeItem.progress);
-                    } else {
-                        addSeekbarListItem(volumeItem, groupId, 0, null);
-                    }
-                }
-                inAnimator = AnimatorInflater.loadAnimator(
-                        mContext, R.anim.car_arrow_fade_in_rotate_up);
-            } else {
-                // Only keeping the default stream if it is not expended.
-                Iterator itr = mVolumeLineItems.iterator();
-                while (itr.hasNext()) {
-                    SeekbarListItem seekbarListItem = (SeekbarListItem) itr.next();
-                    VolumeItem volumeItem = findVolumeItem(seekbarListItem);
-                    if (!volumeItem.defaultItem) {
-                        itr.remove();
-                    } else {
-                        // Set progress here due to the progress of seekbar may not be updated.
-                        seekbarListItem.setProgress(volumeItem.progress);
-                    }
-                }
-                inAnimator = AnimatorInflater.loadAnimator(
-                        mContext, R.anim.car_arrow_fade_in_rotate_down);
-            }
-
-            Animator outAnimator = AnimatorInflater.loadAnimator(
-                    mContext, R.anim.car_arrow_fade_out);
-            inAnimator.setStartDelay(ARROW_FADE_IN_START_DELAY_IN_MILLIS);
-            AnimatorSet animators = new AnimatorSet();
-            animators.playTogether(outAnimator, inAnimator);
-            animators.setTarget(v);
-            animators.start();
-            mPagedListAdapter.notifyDataSetChanged();
+            mExpandIcon = v;
+            toggleDialogExpansion(true);
         }
     }
 
+    private void toggleDialogExpansion(boolean isClicked) {
+        mExpanded = !mExpanded;
+        Animator inAnimator;
+        if (mExpanded) {
+            for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) {
+                // Adding the items which are not coming from the default item.
+                VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
+                if (volumeItem.defaultItem) {
+                    // Set progress here due to the progress of seekbar may not be updated.
+                    volumeItem.listItem.setProgress(volumeItem.progress);
+                } else {
+                    addSeekbarListItem(volumeItem, groupId, 0, null);
+                }
+            }
+            inAnimator = AnimatorInflater.loadAnimator(
+                    mContext, R.anim.car_arrow_fade_in_rotate_up);
+
+        } else {
+            // Only keeping the default stream if it is not expended.
+            Iterator itr = mVolumeLineItems.iterator();
+            while (itr.hasNext()) {
+                SeekbarListItem seekbarListItem = (SeekbarListItem) itr.next();
+                VolumeItem volumeItem = findVolumeItem(seekbarListItem);
+                if (!volumeItem.defaultItem) {
+                    itr.remove();
+                } else {
+                    // Set progress here due to the progress of seekbar may not be updated.
+                    seekbarListItem.setProgress(volumeItem.progress);
+                }
+            }
+            inAnimator = AnimatorInflater.loadAnimator(
+                    mContext, R.anim.car_arrow_fade_in_rotate_down);
+        }
+
+        Animator outAnimator = AnimatorInflater.loadAnimator(
+                mContext, R.anim.car_arrow_fade_out);
+        inAnimator.setStartDelay(ARROW_FADE_IN_START_DELAY_IN_MILLIS);
+        AnimatorSet animators = new AnimatorSet();
+        animators.playTogether(outAnimator, inAnimator);
+        if (!isClicked) {
+            // Do not animate when the state is called to reset the dialogs view and not clicked
+            // by user.
+            animators.setDuration(0);
+        }
+        animators.setTarget(mExpandIcon);
+        animators.start();
+        mPagedListAdapter.notifyDataSetChanged();
+    }
+
     private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener {
 
         private final int mVolumeGroupId;
@@ -601,4 +633,4 @@
         public void onStopTrackingTouch(SeekBar seekBar) {
         }
     }
-}
+}
\ No newline at end of file
diff --git a/packages/CarrierDefaultApp/AndroidManifest.xml b/packages/CarrierDefaultApp/AndroidManifest.xml
index 4d9aaec..f116546 100644
--- a/packages/CarrierDefaultApp/AndroidManifest.xml
+++ b/packages/CarrierDefaultApp/AndroidManifest.xml
@@ -31,7 +31,8 @@
     <application
         android:label="@string/app_name"
         android:directBootAware="true"
-        android:usesCleartextTraffic="true">
+        android:usesCleartextTraffic="true"
+        android:icon="@mipmap/ic_launcher_android">
         <receiver android:name="com.android.carrierdefaultapp.CarrierDefaultBroadcastReceiver">
             <intent-filter>
                 <action android:name="com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED" />
@@ -45,6 +46,7 @@
         <activity
             android:name="com.android.carrierdefaultapp.CaptivePortalLoginActivity"
             android:label="@string/action_bar_label"
+            android:exported="true"
             android:theme="@style/AppTheme"
             android:configChanges="keyboardHidden|orientation|screenSize">
             <intent-filter>
diff --git a/packages/CarrierDefaultApp/res/mipmap/ic_launcher_android.png b/packages/CarrierDefaultApp/res/mipmap/ic_launcher_android.png
new file mode 100644
index 0000000..2e9b196
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/mipmap/ic_launcher_android.png
Binary files differ
diff --git a/packages/CarrierDefaultApp/res/values-as/strings.xml b/packages/CarrierDefaultApp/res/values-as/strings.xml
index 8650171..4b36b06 100644
--- a/packages/CarrierDefaultApp/res/values-as/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-as/strings.xml
@@ -2,16 +2,16 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
-    <string name="android_system_label" msgid="2797790869522345065">"ম\'বাইল সেৱা প্ৰদান কৰা কোম্পানী"</string>
-    <string name="portal_notification_id" msgid="5155057562457079297">"ম\'বাইল ডেটা শেষ হৈছে"</string>
-    <string name="no_data_notification_id" msgid="668400731803969521">"আপোনাৰ ম\'বাইল ডেটা সেৱা নিষ্ক্ৰিয় কৰা হৈছে"</string>
+    <string name="android_system_label" msgid="2797790869522345065">"ম’বাইল সেৱা প্ৰদান কৰা কোম্পানী"</string>
+    <string name="portal_notification_id" msgid="5155057562457079297">"ম’বাইল ডেটা শেষ হৈছে"</string>
+    <string name="no_data_notification_id" msgid="668400731803969521">"আপোনাৰ ম’বাইল ডেটা সেৱা নিষ্ক্ৰিয় কৰা হৈছে"</string>
     <string name="portal_notification_detail" msgid="2295729385924660881">"%s ৱেবছাইটটোলৈ যাবলৈ টিপক"</string>
     <string name="no_data_notification_detail" msgid="3112125343857014825">"অনুগ্ৰহ কৰি আপোনাৰ সেৱা প্ৰদানকাৰী %sৰ সৈতে যোগাযোগ কৰক"</string>
-    <string name="no_mobile_data_connection_title" msgid="7449525772416200578">"কোনো ম\'বাইল ডেটা সংযোগ নাই"</string>
+    <string name="no_mobile_data_connection_title" msgid="7449525772416200578">"কোনো ম’বাইল ডেটা সংযোগ নাই"</string>
     <string name="no_mobile_data_connection" msgid="544980465184147010">"%sৰ যোগেৰে ডেটা বা ৰ\'মিঙৰ আঁচনি যোগ কৰক"</string>
-    <string name="mobile_data_status_notification_channel_name" msgid="833999690121305708">"ম\'বাইল ডেটাৰ স্থিতি"</string>
-    <string name="action_bar_label" msgid="4290345990334377177">"ম\'বাইল নেটৱৰ্কত ছাইন ইন কৰক"</string>
+    <string name="mobile_data_status_notification_channel_name" msgid="833999690121305708">"ম’বাইল ডেটাৰ স্থিতি"</string>
+    <string name="action_bar_label" msgid="4290345990334377177">"ম’বাইল নেটৱৰ্কত ছাইন ইন কৰক"</string>
     <string name="ssl_error_warning" msgid="3127935140338254180">"আপুনি সংযোগ কৰিবলৈ বিচৰা নেটৱৰ্কটোত সুৰক্ষাজনিত সমস্যা আছে।"</string>
-    <string name="ssl_error_example" msgid="6188711843183058764">"উদাহৰণ স্বৰূপে, আপোনাক দেখুওৱা লগ ইনৰ পৃষ্ঠাটো প্ৰতিষ্ঠানটোৰ নিজা নহ\'বও পাৰে।"</string>
+    <string name="ssl_error_example" msgid="6188711843183058764">"উদাহৰণস্বৰূপে, আপোনাক দেখুওৱা লগ ইনৰ পৃষ্ঠাটো প্ৰতিষ্ঠানটোৰ নিজা নহ\'বও পাৰে।"</string>
     <string name="ssl_error_continue" msgid="1138548463994095584">"তথাপিও ব্ৰাউজাৰৰ জৰিয়তে অব্যাহত ৰাখক"</string>
 </resources>
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index 4f67350..f36b4aa 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -195,19 +195,7 @@
         if (success) {
             // Trigger re-evaluation upon success http response code
             CarrierActionUtils.applyCarrierAction(
-                    CarrierActionUtils.CARRIER_ACTION_ENABLE_RADIO, getIntent(),
-                    getApplicationContext());
-            CarrierActionUtils.applyCarrierAction(
-                    CarrierActionUtils.CARRIER_ACTION_ENABLE_METERED_APNS, getIntent(),
-                    getApplicationContext());
-            CarrierActionUtils.applyCarrierAction(
-                    CarrierActionUtils.CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS, getIntent(),
-                    getApplicationContext());
-            CarrierActionUtils.applyCarrierAction(
-                    CarrierActionUtils.CARRIER_ACTION_DISABLE_DEFAULT_URL_HANDLER, getIntent(),
-                    getApplicationContext());
-            CarrierActionUtils.applyCarrierAction(
-                    CarrierActionUtils.CARRIER_ACTION_DEREGISTER_DEFAULT_NETWORK_AVAIL, getIntent(),
+                    CarrierActionUtils.CARRIER_ACTION_RESET_ALL, getIntent(),
                     getApplicationContext());
         }
         finishAndRemoveTask();
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
index 4518d79..3258d57 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
@@ -56,6 +56,7 @@
     public static final int CARRIER_ACTION_DISABLE_DEFAULT_URL_HANDLER       = 8;
     public static final int CARRIER_ACTION_REGISTER_DEFAULT_NETWORK_AVAIL    = 9;
     public static final int CARRIER_ACTION_DEREGISTER_DEFAULT_NETWORK_AVAIL  = 10;
+    public static final int CARRIER_ACTION_RESET_ALL                         = 11;
 
     public static void applyCarrierAction(int actionIdx, Intent intent, Context context) {
         switch (actionIdx) {
@@ -92,6 +93,9 @@
             case CARRIER_ACTION_DEREGISTER_DEFAULT_NETWORK_AVAIL:
                 onDeregisterDefaultNetworkAvail(intent, context);
                 break;
+            case CARRIER_ACTION_RESET_ALL:
+                onResetAllCarrierActions(intent, context);
+                break;
             default:
                 loge("unsupported carrier action index: " + actionIdx);
         }
@@ -196,6 +200,14 @@
         context.getSystemService(NotificationManager.class).cancelAll();
     }
 
+    private static void onResetAllCarrierActions(Intent intent, Context context) {
+        int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+                SubscriptionManager.getDefaultVoiceSubscriptionId());
+        logd("onResetAllCarrierActions subId: " + subId);
+        final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
+        telephonyMgr.carrierActionResetAll(subId);
+    }
+
     private static Notification getNotification(Context context, int titleId, int textId,
                                          PendingIntent pendingIntent) {
         final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml
index 34bc4eb..42885e8 100644
--- a/packages/CompanionDeviceManager/AndroidManifest.xml
+++ b/packages/CompanionDeviceManager/AndroidManifest.xml
@@ -25,7 +25,8 @@
 
     <uses-permission android:name="android.permission.BLUETOOTH"/>
     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
-    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
 
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 133d8ba..af52c00 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -117,7 +117,7 @@
         mPackageManager = ActivityThread.getPackageManager();
         mSettings = mSettingsFactory.createAndRegister(mHandler,
                 getApplicationContext().getContentResolver(), getUserId(), this::updateThresholds);
-        mSmartActionsHelper = new SmartActionsHelper();
+        mSmartActionsHelper = new SmartActionsHelper(getContext(), mSettings);
         mNotificationCategorizer = new NotificationCategorizer();
         mAgingHelper = new AgingHelper(getContext(),
                 mNotificationCategorizer,
@@ -215,10 +215,8 @@
             return null;
         }
         NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel);
-        ArrayList<Notification.Action> actions =
-                mSmartActionsHelper.suggestActions(this, entry, mSettings);
-        ArrayList<CharSequence> replies =
-                mSmartActionsHelper.suggestReplies(this, entry, mSettings);
+        ArrayList<Notification.Action> actions = mSmartActionsHelper.suggestActions(entry);
+        ArrayList<CharSequence> replies = mSmartActionsHelper.suggestReplies(entry);
         return createEnqueuedNotificationAdjustment(entry, actions, replies);
     }
 
@@ -294,7 +292,7 @@
             synchronized (mkeyToImpressions) {
                 ChannelImpressions ci = mkeyToImpressions.getOrDefault(key,
                         createChannelImpressionsWithThresholds());
-                if (stats.hasSeen()) {
+                if (stats != null && stats.hasSeen()) {
                     ci.incrementViews();
                     updatedImpressions = true;
                 }
@@ -343,6 +341,7 @@
                 if (entry != null) {
                     entry.setSeen();
                     mAgingHelper.onNotificationSeen(entry);
+                    mSmartActionsHelper.onNotificationSeen(entry);
                 }
             }
         } catch (Throwable e) {
@@ -351,34 +350,46 @@
     }
 
     @Override
-    public void onNotificationExpansionChanged(String key, boolean isUserAction,
+    public void onNotificationExpansionChanged(@NonNull String key, boolean isUserAction,
             boolean isExpanded) {
         if (DEBUG) {
-            Log.i(TAG,
-                    "onNotificationExpansionChanged " + key + ", isUserAction =" + isUserAction
-                            + ", isExpanded = isExpanded");
+            Log.d(TAG, "onNotificationExpansionChanged() called with: key = [" + key
+                    + "], isUserAction = [" + isUserAction + "], isExpanded = [" + isExpanded
+                    + "]");
+        }
+        NotificationEntry entry = mLiveNotifications.get(key);
+
+        if (entry != null) {
+            entry.setExpanded(isExpanded);
+            mSmartActionsHelper.onNotificationExpansionChanged(entry, isUserAction, isExpanded);
         }
     }
 
     @Override
-    public void onNotificationDirectReply(String key) {
+    public void onNotificationDirectReply(@NonNull String key) {
         if (DEBUG) Log.i(TAG, "onNotificationDirectReply " + key);
+        mSmartActionsHelper.onNotificationDirectReply(key);
     }
 
     @Override
-    public void onSuggestedReplySent(String key, CharSequence reply, int source) {
+    public void onSuggestedReplySent(@NonNull String key, @NonNull CharSequence reply,
+            @Source int source) {
         if (DEBUG) {
             Log.d(TAG, "onSuggestedReplySent() called with: key = [" + key + "], reply = [" + reply
                     + "], source = [" + source + "]");
         }
+        mSmartActionsHelper.onSuggestedReplySent(key, reply, source);
     }
 
     @Override
-    public void onActionClicked(String key, Notification.Action action, int source) {
+    public void onActionClicked(@NonNull String key, @NonNull Notification.Action action,
+            @Source int source) {
         if (DEBUG) {
-            Log.d(TAG, "onActionClicked() called with: key = [" + key + "], action = [" + action.title
-                    + "], source = [" + source + "]");
+            Log.d(TAG,
+                    "onActionClicked() called with: key = [" + key + "], action = [" + action.title
+                            + "], source = [" + source + "]");
         }
+        mSmartActionsHelper.onActionClicked(key, action, source);
     }
 
     @Override
diff --git a/packages/ExtServices/src/android/ext/services/notification/EntityTypeCounter.java b/packages/ExtServices/src/android/ext/services/notification/EntityTypeCounter.java
new file mode 100644
index 0000000..50cb0ab
--- /dev/null
+++ b/packages/ExtServices/src/android/ext/services/notification/EntityTypeCounter.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.ext.services.notification;
+
+import android.annotation.NonNull;
+import android.util.ArrayMap;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
+
+/**
+ * Counts the entity types for smart actions. Some entity types are considered the same
+ * type, like {@link TextClassifier#TYPE_DATE} and {@link TextClassifier#TYPE_DATE_TIME}.
+ */
+class EntityTypeCounter {
+
+    private static final ArrayMap<String, String> ENTITY_TYPE_MAPPING = new ArrayMap<>();
+
+    static {
+        ENTITY_TYPE_MAPPING.put(TextClassifier.TYPE_DATE_TIME, TextClassifier.TYPE_DATE);
+    }
+
+    private final ArrayMap<String, Integer> mEntityTypeCount = new ArrayMap<>();
+
+
+    void increment(@NonNull String entityType) {
+        entityType = convertToBaseEntityType(entityType);
+        if (mEntityTypeCount.containsKey(entityType)) {
+            mEntityTypeCount.put(entityType, mEntityTypeCount.get(entityType) + 1);
+        } else {
+            mEntityTypeCount.put(entityType, 1);
+        }
+    }
+
+    int getCount(@NonNull String entityType) {
+        entityType = convertToBaseEntityType(entityType);
+        return mEntityTypeCount.getOrDefault(entityType, 0);
+    }
+
+    @NonNull
+    private String convertToBaseEntityType(@NonNull String entityType) {
+        return ENTITY_TYPE_MAPPING.getOrDefault(entityType, entityType);
+    }
+
+    /**
+     * Given the links extracted from a piece of text, returns the frequency of each entity
+     * type.
+     */
+    @NonNull
+    static EntityTypeCounter fromTextLinks(@NonNull TextLinks links) {
+        EntityTypeCounter counter = new EntityTypeCounter();
+        for (TextLinks.TextLink link : links.getLinks()) {
+            if (link.getEntityCount() == 0) {
+                continue;
+            }
+            String entityType = link.getEntity(0);
+            counter.increment(entityType);
+        }
+        return counter;
+    }
+}
diff --git a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java b/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
index 6f437bd5..71fd9ce 100644
--- a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
+++ b/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
@@ -52,6 +52,8 @@
     private NotificationChannel mChannel;
     private int mImportance;
     private boolean mSeen;
+    private boolean mExpanded;
+    private boolean mIsShowActionEventLogged;
 
     public NotificationEntry(IPackageManager packageManager, StatusBarNotification sbn,
             NotificationChannel channel) {
@@ -216,10 +218,26 @@
         mSeen = true;
     }
 
+    public void setExpanded(boolean expanded) {
+        mExpanded = expanded;
+    }
+
+    public void setShowActionEventLogged() {
+        mIsShowActionEventLogged = true;
+    }
+
     public boolean hasSeen() {
         return mSeen;
     }
 
+    public boolean isExpanded() {
+        return mExpanded;
+    }
+
+    public boolean isShowActionEventLogged() {
+        return mIsShowActionEventLogged;
+    }
+
     public StatusBarNotification getSbn() {
         return mSbn;
     }
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index 38df9b0..f2db2da 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -24,12 +24,15 @@
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.Process;
+import android.service.notification.NotificationAssistantService;
 import android.text.TextUtils;
-import android.util.ArrayMap;
+import android.util.LruCache;
 import android.view.textclassifier.ConversationActions;
 import android.view.textclassifier.TextClassification;
+import android.view.textclassifier.TextClassificationContext;
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextClassifierEvent;
 import android.view.textclassifier.TextLinks;
 
 import java.time.Instant;
@@ -47,6 +50,7 @@
     private static final ArrayList<Notification.Action> EMPTY_ACTION_LIST = new ArrayList<>();
     private static final ArrayList<CharSequence> EMPTY_REPLY_LIST = new ArrayList<>();
 
+    private static final String KEY_ACTION_TYPE = "action_type";
     // If a notification has any of these flags set, it's inelgibile for actions being added.
     private static final int FLAG_MASK_INELGIBILE_FOR_ACTIONS =
             Notification.FLAG_ONGOING_EVENT
@@ -59,6 +63,7 @@
     private static final int MAX_SUGGESTED_REPLIES = 3;
     // TODO: Make this configurable.
     private static final int MAX_MESSAGES_TO_EXTRACT = 5;
+    private static final int MAX_RESULT_ID_TO_CACHE = 20;
 
     private static final ConversationActions.TypeConfig TYPE_CONFIG =
             new ConversationActions.TypeConfig.Builder().setIncludedTypes(
@@ -68,26 +73,36 @@
     private static final List<String> HINTS =
             Collections.singletonList(ConversationActions.HINT_FOR_NOTIFICATION);
 
-    SmartActionsHelper() {
+    private Context mContext;
+    @Nullable
+    private TextClassifier mTextClassifier;
+    @NonNull
+    private AssistantSettings mSettings;
+    private LruCache<String, String> mNotificationKeyToResultIdCache =
+            new LruCache<>(MAX_RESULT_ID_TO_CACHE);
+
+    SmartActionsHelper(Context context, AssistantSettings settings) {
+        mContext = context;
+        TextClassificationManager textClassificationManager =
+                mContext.getSystemService(TextClassificationManager.class);
+        if (textClassificationManager != null) {
+            mTextClassifier = textClassificationManager.getTextClassifier();
+        }
+        mSettings = settings;
     }
 
     /**
      * Adds action adjustments based on the notification contents.
      */
     @NonNull
-    ArrayList<Notification.Action> suggestActions(@Nullable Context context,
-            @NonNull NotificationEntry entry, @NonNull AssistantSettings settings) {
-        if (!settings.mGenerateActions) {
+    ArrayList<Notification.Action> suggestActions(@NonNull NotificationEntry entry) {
+        if (!mSettings.mGenerateActions) {
             return EMPTY_ACTION_LIST;
         }
         if (!isEligibleForActionAdjustment(entry)) {
             return EMPTY_ACTION_LIST;
         }
-        if (context == null) {
-            return EMPTY_ACTION_LIST;
-        }
-        TextClassificationManager tcm = context.getSystemService(TextClassificationManager.class);
-        if (tcm == null) {
+        if (mTextClassifier == null) {
             return EMPTY_ACTION_LIST;
         }
         List<ConversationActions.Message> messages = extractMessages(entry.getNotification());
@@ -96,22 +111,17 @@
         }
         // TODO: Move to TextClassifier.suggestConversationActions once it is ready.
         return suggestActionsFromText(
-                tcm, messages.get(messages.size() - 1).getText(), MAX_SMART_ACTIONS);
+                messages.get(messages.size() - 1).getText(), MAX_SMART_ACTIONS);
     }
 
-    ArrayList<CharSequence> suggestReplies(@Nullable Context context,
-            @NonNull NotificationEntry entry, @NonNull AssistantSettings settings) {
-        if (!settings.mGenerateReplies) {
+    ArrayList<CharSequence> suggestReplies(@NonNull NotificationEntry entry) {
+        if (!mSettings.mGenerateReplies) {
             return EMPTY_REPLY_LIST;
         }
         if (!isEligibleForReplyAdjustment(entry)) {
             return EMPTY_REPLY_LIST;
         }
-        if (context == null) {
-            return EMPTY_REPLY_LIST;
-        }
-        TextClassificationManager tcm = context.getSystemService(TextClassificationManager.class);
-        if (tcm == null) {
+        if (mTextClassifier == null) {
             return EMPTY_REPLY_LIST;
         }
         List<ConversationActions.Message> messages = extractMessages(entry.getNotification());
@@ -125,14 +135,122 @@
                         .setTypeConfig(TYPE_CONFIG)
                         .build();
 
-        TextClassifier textClassifier = tcm.getTextClassifier();
+        ConversationActions conversationActionsResult =
+                mTextClassifier.suggestConversationActions(request);
         List<ConversationActions.ConversationAction> conversationActions =
-                textClassifier.suggestConversationActions(request).getConversationActions();
-
-        return conversationActions.stream()
+                conversationActionsResult.getConversationActions();
+        ArrayList<CharSequence> replies = conversationActions.stream()
                 .map(conversationAction -> conversationAction.getTextReply())
                 .filter(textReply -> !TextUtils.isEmpty(textReply))
                 .collect(Collectors.toCollection(ArrayList::new));
+
+        String resultId = conversationActionsResult.getId();
+        if (resultId != null && !replies.isEmpty()) {
+            mNotificationKeyToResultIdCache.put(entry.getSbn().getKey(), resultId);
+        }
+        return replies;
+    }
+
+    void onNotificationSeen(@NonNull NotificationEntry entry) {
+        if (entry.isExpanded()) {
+            maybeSendActionShownEvent(entry);
+        }
+    }
+
+    void onNotificationExpansionChanged(@NonNull NotificationEntry entry, boolean isUserAction,
+            boolean isExpanded) {
+        // Notification can be expanded in the background, and thus the isUserAction check.
+        if (isUserAction && isExpanded) {
+            maybeSendActionShownEvent(entry);
+        }
+    }
+
+    void onNotificationDirectReply(@NonNull String key) {
+        if (mTextClassifier == null) {
+            return;
+        }
+        String resultId = mNotificationKeyToResultIdCache.get(key);
+        if (resultId == null) {
+            return;
+        }
+        TextClassifierEvent textClassifierEvent =
+                createTextClassifierEventBuilder(TextClassifierEvent.TYPE_MANUAL_REPLY, resultId)
+                        .build();
+        mTextClassifier.onTextClassifierEvent(textClassifierEvent);
+    }
+
+    void onSuggestedReplySent(@NonNull String key, @NonNull CharSequence reply,
+            @NotificationAssistantService.Source int source) {
+        if (mTextClassifier == null) {
+            return;
+        }
+        if (source != NotificationAssistantService.SOURCE_FROM_ASSISTANT) {
+            return;
+        }
+        String resultId = mNotificationKeyToResultIdCache.get(key);
+        if (resultId == null) {
+            return;
+        }
+        TextClassifierEvent textClassifierEvent =
+                createTextClassifierEventBuilder(TextClassifierEvent.TYPE_SMART_ACTION, resultId)
+                        .setEntityType(ConversationActions.TYPE_TEXT_REPLY)
+                        .build();
+        mTextClassifier.onTextClassifierEvent(textClassifierEvent);
+    }
+
+    void onActionClicked(@NonNull String key, @NonNull Notification.Action action,
+            @NotificationAssistantService.Source int source) {
+        if (mTextClassifier == null) {
+            return;
+        }
+        if (source != NotificationAssistantService.SOURCE_FROM_ASSISTANT) {
+            return;
+        }
+        String resultId = mNotificationKeyToResultIdCache.get(key);
+        if (resultId == null) {
+            return;
+        }
+        String actionType = action.getExtras().getString(KEY_ACTION_TYPE);
+        if (actionType == null) {
+            return;
+        }
+        TextClassifierEvent textClassifierEvent =
+                createTextClassifierEventBuilder(TextClassifierEvent.TYPE_SMART_ACTION, resultId)
+                        .setEntityType(actionType)
+                        .build();
+        mTextClassifier.onTextClassifierEvent(textClassifierEvent);
+    }
+
+    private TextClassifierEvent.Builder createTextClassifierEventBuilder(
+            int eventType, @NonNull String resultId) {
+        return new TextClassifierEvent.Builder(
+                TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS, eventType)
+                .setEventTime(System.currentTimeMillis())
+                .setEventContext(
+                        new TextClassificationContext.Builder(
+                                mContext.getPackageName(), TextClassifier.WIDGET_TYPE_NOTIFICATION)
+                        .build())
+                .setResultId(resultId);
+    }
+
+    private void maybeSendActionShownEvent(@NonNull NotificationEntry entry) {
+        if (mTextClassifier == null) {
+            return;
+        }
+        String resultId = mNotificationKeyToResultIdCache.get(entry.getSbn().getKey());
+        if (resultId == null) {
+            return;
+        }
+        // Only report if this is the first time the user sees these suggestions.
+        if (entry.isShowActionEventLogged()) {
+            return;
+        }
+        entry.setShowActionEventLogged();
+        TextClassifierEvent textClassifierEvent =
+                createTextClassifierEventBuilder(TextClassifierEvent.TYPE_ACTIONS_SHOWN, resultId)
+                        .build();
+        // TODO: If possible, report which replies / actions are actually seen by user.
+        mTextClassifier.onTextClassifierEvent(textClassifierEvent);
     }
 
     /**
@@ -220,13 +338,10 @@
     /** Returns a list of actions to act on entities in a given piece of text. */
     @NonNull
     private ArrayList<Notification.Action> suggestActionsFromText(
-            @NonNull TextClassificationManager tcm, @Nullable CharSequence text,
-            int maxSmartActions) {
+            @Nullable CharSequence text, int maxSmartActions) {
         if (TextUtils.isEmpty(text)) {
             return EMPTY_ACTION_LIST;
         }
-        TextClassifier textClassifier = tcm.getTextClassifier();
-
         // We want to process only text visible to the user to avoid confusing suggestions, so we
         // truncate the text to a reasonable length. This is particularly important for e.g.
         // email apps that sometimes include the text for the entire thread.
@@ -239,8 +354,8 @@
                                 Collections.singletonList(
                                         TextClassifier.HINT_TEXT_IS_NOT_EDITABLE)))
                 .build();
-        TextLinks links = textClassifier.generateLinks(textLinksRequest);
-        ArrayMap<String, Integer> entityTypeFrequency = getEntityTypeFrequency(links);
+        TextLinks links = mTextClassifier.generateLinks(textLinksRequest);
+        EntityTypeCounter entityTypeCounter = EntityTypeCounter.fromTextLinks(links);
 
         ArrayList<Notification.Action> actions = new ArrayList<>();
         for (TextLinks.TextLink link : links.getLinks()) {
@@ -248,25 +363,31 @@
             // case where a notification contains e.g. a list of phone numbers. In such cases, the
             // user likely wants to act on the whole list rather than an individual entity.
             if (link.getEntityCount() == 0
-                    || entityTypeFrequency.get(link.getEntity(0)) != 1) {
+                    || entityTypeCounter.getCount(link.getEntity(0)) != 1) {
                 continue;
             }
 
             // Generate the actions, and add the most prominent ones to the action bar.
             TextClassification classification =
-                    textClassifier.classifyText(
+                    mTextClassifier.classifyText(
                             new TextClassification.Request.Builder(
                                     text, link.getStart(), link.getEnd()).build());
+            if (classification.getEntityCount() == 0) {
+                continue;
+            }
             int numOfActions = Math.min(
                     MAX_ACTIONS_PER_LINK, classification.getActions().size());
             for (int i = 0; i < numOfActions; ++i) {
-                RemoteAction action = classification.getActions().get(i);
-                actions.add(
-                        new Notification.Action.Builder(
-                                action.getIcon(),
-                                action.getTitle(),
-                                action.getActionIntent())
-                                .build());
+                RemoteAction remoteAction = classification.getActions().get(i);
+                Notification.Action action = new Notification.Action.Builder(
+                        remoteAction.getIcon(),
+                        remoteAction.getTitle(),
+                        remoteAction.getActionIntent())
+                        .setContextual(true)
+                        .addExtras(Bundle.forPair(KEY_ACTION_TYPE, classification.getEntity(0)))
+                        .build();
+                actions.add(action);
+
                 // We have enough smart actions.
                 if (actions.size() >= maxSmartActions) {
                     return actions;
@@ -275,25 +396,4 @@
         }
         return actions;
     }
-
-    /**
-     * Given the links extracted from a piece of text, returns the frequency of each entity
-     * type.
-     */
-    @NonNull
-    private ArrayMap<String, Integer> getEntityTypeFrequency(@NonNull TextLinks links) {
-        ArrayMap<String, Integer> entityTypeCount = new ArrayMap<>();
-        for (TextLinks.TextLink link : links.getLinks()) {
-            if (link.getEntityCount() == 0) {
-                continue;
-            }
-            String entityType = link.getEntity(0);
-            if (entityTypeCount.containsKey(entityType)) {
-                entityTypeCount.put(entityType, entityTypeCount.get(entityType) + 1);
-            } else {
-                entityTypeCount.put(entityType, 1);
-            }
-        }
-        return entityTypeCount;
-    }
 }
diff --git a/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java b/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java
index ab718021..81a63dd 100644
--- a/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java
+++ b/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java
@@ -17,14 +17,10 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.database.Cursor;
 import android.database.CursorWindow;
-import android.net.Uri;
 import android.os.Bundle;
 import android.service.sms.FinancialSmsService;
-import android.util.Log;
 
-import java.util.ArrayList;
 /**
  * Service to provide financial apps access to sms messages.
  */
@@ -36,45 +32,6 @@
     @Nullable
     @Override
     public CursorWindow onGetSmsMessages(@NonNull Bundle params) {
-        ArrayList<String> columnNames = params.getStringArrayList(KEY_COLUMN_NAMES);
-        if (columnNames == null || columnNames.size() <= 0) {
-            return null;
-        }
-
-        Uri inbox = Uri.parse("content://sms/inbox");
-
-        try (Cursor cursor = getContentResolver().query(inbox, null, null, null, null);
-                CursorWindow window = new CursorWindow("FinancialSmsMessages")) {
-            int messageCount = cursor.getCount();
-            if (messageCount > 0 && cursor.moveToFirst()) {
-                window.setNumColumns(columnNames.size());
-                for (int row = 0; row < messageCount; row++) {
-                    if (!window.allocRow()) {
-                        Log.e(TAG, "CursorWindow ran out of memory.");
-                        return null;
-                    }
-                    for (int col = 0; col < columnNames.size(); col++) {
-                        String columnName = columnNames.get(col);
-                        int inboxColumnIndex = cursor.getColumnIndexOrThrow(columnName);
-                        String inboxColumnValue = cursor.getString(inboxColumnIndex);
-                        boolean addedToCursorWindow = window.putString(inboxColumnValue, row, col);
-                        if (!addedToCursorWindow) {
-                            Log.e(TAG, "Failed to add:"
-                                    + inboxColumnValue
-                                    + ";column:"
-                                    + columnName);
-                            return null;
-                        }
-                    }
-                    cursor.moveToNext();
-                }
-            } else {
-                Log.w(TAG, "No sms messages.");
-            }
-            return window;
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to get sms messages.");
-            return null;
-        }
+        return null;
     }
 }
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/EntityTypeCounterTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/EntityTypeCounterTest.java
new file mode 100644
index 0000000..2d29c7b
--- /dev/null
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/EntityTypeCounterTest.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.ext.services.notification;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.view.textclassifier.TextClassifier;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class EntityTypeCounterTest {
+    private EntityTypeCounter mCounter;
+
+    @Before
+    public void setup() {
+        mCounter = new EntityTypeCounter();
+    }
+
+    @Test
+    public void testIncrementAndGetCount() {
+        mCounter.increment(TextClassifier.TYPE_URL);
+        mCounter.increment(TextClassifier.TYPE_URL);
+        mCounter.increment(TextClassifier.TYPE_URL);
+
+        mCounter.increment(TextClassifier.TYPE_PHONE);
+        mCounter.increment(TextClassifier.TYPE_PHONE);
+
+        assertThat(mCounter.getCount(TextClassifier.TYPE_URL)).isEqualTo(3);
+        assertThat(mCounter.getCount(TextClassifier.TYPE_PHONE)).isEqualTo(2);
+        assertThat(mCounter.getCount(TextClassifier.TYPE_DATE_TIME)).isEqualTo(0);
+    }
+
+    @Test
+    public void testIncrementAndGetCount_typeDateAndDateTime() {
+        mCounter.increment(TextClassifier.TYPE_DATE_TIME);
+        mCounter.increment(TextClassifier.TYPE_DATE);
+
+        assertThat(mCounter.getCount(TextClassifier.TYPE_DATE_TIME)).isEqualTo(2);
+        assertThat(mCounter.getCount(TextClassifier.TYPE_DATE)).isEqualTo(2);
+    }
+}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
index 0352ebc..da382a0 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
@@ -28,12 +28,14 @@
 import android.app.Person;
 import android.content.Context;
 import android.os.Process;
+import android.service.notification.NotificationAssistantService;
 import android.service.notification.StatusBarNotification;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.textclassifier.ConversationActions;
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextClassifierEvent;
 
 import com.google.common.truth.FailureStrategy;
 import com.google.common.truth.Subject;
@@ -44,12 +46,14 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.time.Instant;
 import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -58,8 +62,14 @@
 
 @RunWith(AndroidJUnit4.class)
 public class SmartActionHelperTest {
+    private static final String NOTIFICATION_KEY = "key";
+    private static final String RESULT_ID = "id";
 
-    private SmartActionsHelper mSmartActionsHelper = new SmartActionsHelper();
+    private static final ConversationActions.ConversationAction REPLY_ACTION =
+            new ConversationActions.ConversationAction.Builder(
+                    ConversationActions.TYPE_TEXT_REPLY).setTextReply("Home").build();
+
+    private SmartActionsHelper mSmartActionsHelper;
     private Context mContext;
     @Mock private TextClassifier mTextClassifier;
     @Mock private NotificationEntry mNotificationEntry;
@@ -75,7 +85,7 @@
         mContext.getSystemService(TextClassificationManager.class)
                 .setTextClassifier(mTextClassifier);
         when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class)))
-                .thenReturn(new ConversationActions(Collections.emptyList(), null));
+                .thenReturn(new ConversationActions(Arrays.asList(REPLY_ACTION), RESULT_ID));
 
         when(mNotificationEntry.getSbn()).thenReturn(mStatusBarNotification);
         // The notification is eligible to have smart suggestions.
@@ -83,18 +93,20 @@
         when(mNotificationEntry.isMessaging()).thenReturn(true);
         when(mStatusBarNotification.getPackageName()).thenReturn("random.app");
         when(mStatusBarNotification.getUser()).thenReturn(Process.myUserHandle());
+        when(mStatusBarNotification.getKey()).thenReturn(NOTIFICATION_KEY);
         mNotificationBuilder = new Notification.Builder(mContext, "channel");
         mSettings = AssistantSettings.createForTesting(
                 null, null, Process.myUserHandle().getIdentifier(), null);
         mSettings.mGenerateActions = true;
         mSettings.mGenerateReplies = true;
+        mSmartActionsHelper = new SmartActionsHelper(mContext, mSettings);
     }
 
     @Test
     public void testSuggestReplies_notMessagingApp() {
         when(mNotificationEntry.isMessaging()).thenReturn(false);
         ArrayList<CharSequence> textReplies =
-                mSmartActionsHelper.suggestReplies(mContext, mNotificationEntry, mSettings);
+                mSmartActionsHelper.suggestReplies(mNotificationEntry);
         assertThat(textReplies).isEmpty();
     }
 
@@ -102,7 +114,7 @@
     public void testSuggestReplies_noInlineReply() {
         when(mNotificationEntry.hasInlineReply()).thenReturn(false);
         ArrayList<CharSequence> textReplies =
-                mSmartActionsHelper.suggestReplies(mContext, mNotificationEntry, mSettings);
+                mSmartActionsHelper.suggestReplies(mNotificationEntry);
         assertThat(textReplies).isEmpty();
     }
 
@@ -169,18 +181,128 @@
                         .build();
         when(mNotificationEntry.getNotification()).thenReturn(notification);
 
-        mSmartActionsHelper.suggestReplies(mContext, mNotificationEntry, mSettings);
+        mSmartActionsHelper.suggestReplies(mNotificationEntry);
 
         verify(mTextClassifier, never())
                 .suggestConversationActions(any(ConversationActions.Request.class));
     }
 
+    @Test
+    public void testOnSuggestedReplySent() {
+        final String message = "Where are you?";
+        Notification notification = mNotificationBuilder.setContentText(message).build();
+        when(mNotificationEntry.getNotification()).thenReturn(notification);
+
+        mSmartActionsHelper.suggestReplies(mNotificationEntry);
+        mSmartActionsHelper.onSuggestedReplySent(
+                NOTIFICATION_KEY, message, NotificationAssistantService.SOURCE_FROM_ASSISTANT);
+
+        ArgumentCaptor<TextClassifierEvent> argumentCaptor =
+                ArgumentCaptor.forClass(TextClassifierEvent.class);
+        verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture());
+        TextClassifierEvent textClassifierEvent = argumentCaptor.getValue();
+        assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_SMART_ACTION);
+    }
+
+    @Test
+    public void testOnSuggestedReplySent_anotherNotification() {
+        final String message = "Where are you?";
+        Notification notification = mNotificationBuilder.setContentText(message).build();
+        when(mNotificationEntry.getNotification()).thenReturn(notification);
+
+        mSmartActionsHelper.suggestReplies(mNotificationEntry);
+        mSmartActionsHelper.onSuggestedReplySent(
+                "something_else", message, NotificationAssistantService.SOURCE_FROM_ASSISTANT);
+
+        verify(mTextClassifier, never())
+                .onTextClassifierEvent(Mockito.any(TextClassifierEvent.class));
+    }
+
+    @Test
+    public void testOnSuggestedReplySent_missingResultId() {
+        when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class)))
+                .thenReturn(new ConversationActions(Collections.emptyList(), null));
+
+        final String message = "Where are you?";
+        Notification notification = mNotificationBuilder.setContentText(message).build();
+        when(mNotificationEntry.getNotification()).thenReturn(notification);
+
+        mSmartActionsHelper.suggestReplies(mNotificationEntry);
+        mSmartActionsHelper.onSuggestedReplySent(
+                "something_else", message, NotificationAssistantService.SOURCE_FROM_ASSISTANT);
+
+        verify(mTextClassifier, never())
+                .onTextClassifierEvent(Mockito.any(TextClassifierEvent.class));
+    }
+
+    @Test
+    public void testOnNotificationDirectReply() {
+        Notification notification = mNotificationBuilder.setContentText("Where are you?").build();
+        when(mNotificationEntry.getNotification()).thenReturn(notification);
+
+        mSmartActionsHelper.suggestReplies(mNotificationEntry);
+        mSmartActionsHelper.onNotificationDirectReply(NOTIFICATION_KEY);
+
+        ArgumentCaptor<TextClassifierEvent> argumentCaptor =
+                ArgumentCaptor.forClass(TextClassifierEvent.class);
+        verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture());
+        TextClassifierEvent textClassifierEvent = argumentCaptor.getValue();
+        assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_MANUAL_REPLY);
+    }
+
+    @Test
+    public void testOnNotificationExpansionChanged() {
+        final String message = "Where are you?";
+        Notification notification = mNotificationBuilder.setContentText(message).build();
+        when(mNotificationEntry.getNotification()).thenReturn(notification);
+
+        mSmartActionsHelper.suggestReplies(mNotificationEntry);
+        mSmartActionsHelper.onNotificationExpansionChanged(mNotificationEntry, true, true);
+
+        ArgumentCaptor<TextClassifierEvent> argumentCaptor =
+                ArgumentCaptor.forClass(TextClassifierEvent.class);
+        verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture());
+        TextClassifierEvent textClassifierEvent = argumentCaptor.getValue();
+        assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_SHOWN);
+    }
+
+    @Test
+    public void testOnNotificationsSeen_notExpanded() {
+        final String message = "Where are you?";
+        Notification notification = mNotificationBuilder.setContentText(message).build();
+        when(mNotificationEntry.getNotification()).thenReturn(notification);
+        when(mNotificationEntry.isExpanded()).thenReturn(false);
+
+        mSmartActionsHelper.suggestReplies(mNotificationEntry);
+        mSmartActionsHelper.onNotificationSeen(mNotificationEntry);
+
+        verify(mTextClassifier, never()).onTextClassifierEvent(
+                Mockito.any(TextClassifierEvent.class));
+    }
+
+    @Test
+    public void testOnNotificationsSeen_expanded() {
+        final String message = "Where are you?";
+        Notification notification = mNotificationBuilder.setContentText(message).build();
+        when(mNotificationEntry.getNotification()).thenReturn(notification);
+        when(mNotificationEntry.isExpanded()).thenReturn(true);
+
+        mSmartActionsHelper.suggestReplies(mNotificationEntry);
+        mSmartActionsHelper.onNotificationSeen(mNotificationEntry);
+
+        ArgumentCaptor<TextClassifierEvent> argumentCaptor =
+                ArgumentCaptor.forClass(TextClassifierEvent.class);
+        verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture());
+        TextClassifierEvent textClassifierEvent = argumentCaptor.getValue();
+        assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_SHOWN);
+    }
+
     private ZonedDateTime createZonedDateTimeFromMsUtc(long msUtc) {
         return ZonedDateTime.ofInstant(Instant.ofEpochMilli(msUtc), ZoneOffset.systemDefault());
     }
 
     private List<ConversationActions.Message> getMessagesInRequest() {
-        mSmartActionsHelper.suggestReplies(mContext, mNotificationEntry, mSettings);
+        mSmartActionsHelper.suggestReplies(mNotificationEntry);
 
         ArgumentCaptor<ConversationActions.Request> argumentCaptor =
                 ArgumentCaptor.forClass(ConversationActions.Request.class);
@@ -189,6 +311,17 @@
         return request.getConversation();
     }
 
+    private void assertTextClassifierEvent(
+            TextClassifierEvent textClassifierEvent, int expectedEventType) {
+        assertThat(textClassifierEvent.getEventCategory())
+                .isEqualTo(TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS);
+        assertThat(textClassifierEvent.getEventContext().getPackageName())
+                .isEqualTo(InstrumentationRegistry.getTargetContext().getPackageName());
+        assertThat(textClassifierEvent.getEventContext().getWidgetType())
+                .isEqualTo(TextClassifier.WIDGET_TYPE_NOTIFICATION);
+        assertThat(textClassifierEvent.getEventType()).isEqualTo(expectedEventType);
+    }
+
     private static final class MessageSubject
             extends Subject<MessageSubject, ConversationActions.Message> {
 
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 1eb4b74..8d04702 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -67,7 +67,7 @@
 
     private static final boolean DEBUG = false;
 
-    public static final String AUTHORITY = "com.android.externalstorage.documents";
+    public static final String AUTHORITY = DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY;
 
     private static final Uri BASE_URI =
             new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY).build();
@@ -96,7 +96,8 @@
         public boolean reportAvailableBytes = true;
     }
 
-    private static final String ROOT_ID_PRIMARY_EMULATED = "primary";
+    private static final String ROOT_ID_PRIMARY_EMULATED =
+            DocumentsContract.EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID;
     private static final String ROOT_ID_HOME = "home";
 
     private StorageManager mStorageManager;
diff --git a/packages/ExternalStorageProvider/tests/Android.bp b/packages/ExternalStorageProvider/tests/Android.bp
index 83427d4..04cf01a 100644
--- a/packages/ExternalStorageProvider/tests/Android.bp
+++ b/packages/ExternalStorageProvider/tests/Android.bp
@@ -14,7 +14,7 @@
     ],
 
     static_libs: [
-        "android-support-test",
+        "androidx.test.rules",
         "mockito-target",
         "truth-prebuilt",
     ],
diff --git a/packages/ExternalStorageProvider/tests/AndroidManifest.xml b/packages/ExternalStorageProvider/tests/AndroidManifest.xml
index 58b6e86..f1a6af0 100644
--- a/packages/ExternalStorageProvider/tests/AndroidManifest.xml
+++ b/packages/ExternalStorageProvider/tests/AndroidManifest.xml
@@ -6,7 +6,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.externalstorage"
                      android:label="ExternalStorageProvider Tests" />
 </manifest>
diff --git a/packages/ExternalStorageProvider/tests/AndroidTest.xml b/packages/ExternalStorageProvider/tests/AndroidTest.xml
index e5fa73f..f8438b2 100644
--- a/packages/ExternalStorageProvider/tests/AndroidTest.xml
+++ b/packages/ExternalStorageProvider/tests/AndroidTest.xml
@@ -23,7 +23,7 @@
     <option name="test-tag" value="ExternalStorageProviderTests" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.externalstorage.tests" />
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
         <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java b/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java
index a88b3e1..fbf2e4b 100644
--- a/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java
+++ b/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java
@@ -23,8 +23,9 @@
 import static org.mockito.Mockito.verify;
 
 import android.content.pm.ProviderInfo;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/packages/MtpDocumentsProvider/perf_tests/Android.mk b/packages/MtpDocumentsProvider/perf_tests/Android.mk
index e873157..d2f1c7a 100644
--- a/packages/MtpDocumentsProvider/perf_tests/Android.mk
+++ b/packages/MtpDocumentsProvider/perf_tests/Android.mk
@@ -3,7 +3,7 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
 LOCAL_PACKAGE_NAME := MtpDocumentsProviderPerfTests
 LOCAL_PRIVATE_PLATFORM_APIS := true
 LOCAL_INSTRUMENTATION_FOR := MtpDocumentsProvider
diff --git a/packages/MtpDocumentsProvider/perf_tests/AndroidManifest.xml b/packages/MtpDocumentsProvider/perf_tests/AndroidManifest.xml
index 26e109d..4367652 100644
--- a/packages/MtpDocumentsProvider/perf_tests/AndroidManifest.xml
+++ b/packages/MtpDocumentsProvider/perf_tests/AndroidManifest.xml
@@ -7,7 +7,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.mtp"
         android:label="Performance tests for MtpDocumentsProvider." />
 </manifest>
diff --git a/packages/MtpDocumentsProvider/perf_tests/src/com/android/mtp/AppFusePerfTest.java b/packages/MtpDocumentsProvider/perf_tests/src/com/android/mtp/AppFusePerfTest.java
index 36f6fe9..58b9c67 100644
--- a/packages/MtpDocumentsProvider/perf_tests/src/com/android/mtp/AppFusePerfTest.java
+++ b/packages/MtpDocumentsProvider/perf_tests/src/com/android/mtp/AppFusePerfTest.java
@@ -23,17 +23,15 @@
 import android.os.ProxyFileDescriptorCallback;
 import android.os.storage.StorageManager;
 import android.system.ErrnoException;
-import android.system.Os;
-import android.support.test.filters.LargeTest;
-import android.support.test.InstrumentationRegistry;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Arrays;
-import libcore.io.IoUtils;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
+import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
-import org.junit.Test;
+
+import java.io.IOException;
 
 @RunWith(JUnit4.class)
 public class AppFusePerfTest {
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
new file mode 100644
index 0000000..55bb517
--- /dev/null
+++ b/packages/NetworkStack/Android.bp
@@ -0,0 +1,37 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Library including the network stack, used to compile the network stack app, or linked into the
+// system server on devices that run the stack there
+java_library {
+    name: "NetworkStackLib",
+    installable: true,
+    srcs: [
+        "src/**/*.java",
+    ],
+}
+
+// Updatable network stack packaged as an application
+android_app {
+    name: "NetworkStack",
+    platform_apis: true,
+    certificate: "platform",
+    privileged: true,
+    static_libs: [
+        "NetworkStackLib"
+    ],
+    manifest: "AndroidManifest.xml",
+}
\ No newline at end of file
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
new file mode 100644
index 0000000..d1c5cb6
--- /dev/null
+++ b/packages/NetworkStack/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2014 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.google.android.networkstack"
+          android:sharedUserId="android.uid.networkstack">
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <!-- Launch captive portal app as specific user -->
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+    <application
+        android:label="NetworkStack"
+        android:defaultToDeviceProtectedStorage="true"
+        android:directBootAware="true"
+        android:usesCleartextTraffic="true">
+        <service android:name="com.android.server.NetworkStackService">
+            <intent-filter>
+                <action android:name="android.net.INetworkStackConnector"/>
+            </intent-filter>
+        </service>
+    </application>
+</manifest>
diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
new file mode 100644
index 0000000..5afaf58
--- /dev/null
+++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.os.Binder.getCallingUid;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Service;
+import android.content.Intent;
+import android.net.INetworkStackConnector;
+import android.os.IBinder;
+import android.os.Process;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Android service used to start the network stack when bound to via an intent.
+ *
+ * <p>The service returns a binder for the system server to communicate with the network stack.
+ */
+public class NetworkStackService extends Service {
+    private static final String TAG = NetworkStackService.class.getSimpleName();
+
+    /**
+     * Create a binder connector for the system server to communicate with the network stack.
+     *
+     * <p>On platforms where the network stack runs in the system server process, this method may
+     * be called directly instead of obtaining the connector by binding to the service.
+     */
+    public static IBinder makeConnector() {
+        return new NetworkStackConnector();
+    }
+
+    @NonNull
+    @Override
+    public IBinder onBind(Intent intent) {
+        return makeConnector();
+    }
+
+    private static class NetworkStackConnector extends INetworkStackConnector.Stub {
+        // TODO: makeDhcpServer(), etc. will go here.
+
+        @Override
+        protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
+                @Nullable String[] args) {
+            checkCaller();
+            fout.println("NetworkStack logs:");
+            // TODO: dump logs here
+        }
+    }
+
+    private static void checkCaller() {
+        // TODO: check that the calling PID is the system server.
+        if (getCallingUid() != Process.SYSTEM_UID && getCallingUid() != Process.ROOT_UID) {
+            throw new SecurityException("Invalid caller: " + getCallingUid());
+        }
+    }
+}
diff --git a/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java b/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java
index 0312b81..28b05396 100644
--- a/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java
+++ b/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java
@@ -39,12 +39,14 @@
 import android.webkit.WebViewClient;
 import android.widget.ProgressBar;
 
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
 import com.android.hotspot2.R;
 
 import java.net.MalformedURLException;
 import java.net.URL;
 
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
 
 /**
  * Online Sign Up Login Web View launched during Provision Process of Hotspot 2.0 rel2.
@@ -252,6 +254,10 @@
         @Override
         public void onReceivedError(WebView view, WebResourceRequest request,
                 WebResourceError error) {
+            if (request.getUrl().toString().startsWith("http://127.0.0.1")) {
+                view.stopLoading();
+            }
+
             if (request.isForMainFrame()) {
                 // This happens right after getting HTTP redirect response from an OSU server
                 // since no more Http request is allowed to send to the OSU server.
diff --git a/packages/PackageInstaller/res/values-af/strings.xml b/packages/PackageInstaller/res/values-af/strings.xml
index dfee4e8..ce007ef 100644
--- a/packages/PackageInstaller/res/values-af/strings.xml
+++ b/packages/PackageInstaller/res/values-af/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Wil jy hierdie program vir die gebruiker <xliff:g id="USERNAME">%1$s</xliff:g> deïnstalleer?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Vervang hierdie program met die fabriekweergawe? Alle data sal verwyder word."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Vervang hierdie program met die fabriekweergawe? Alle data sal verwyder word. Dit beïnvloed alle gebruikers van hierdie toestel, insluitend dié met werkprofiele."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Verwyder ook <xliff:g id="SIZE">%1$s</xliff:g> se geassosieerde medialêers."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Hou <xliff:g id="SIZE">%1$s</xliff:g> se programdata."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Voer tans deïnstallerings uit"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Mislukte deïnstallerings"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Deïnstalleer tans …"</string>
diff --git a/packages/PackageInstaller/res/values-am/strings.xml b/packages/PackageInstaller/res/values-am/strings.xml
index cfdeb59..21cdaa6 100644
--- a/packages/PackageInstaller/res/values-am/strings.xml
+++ b/packages/PackageInstaller/res/values-am/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"ይህን መተግበሪያ ለተጠቃሚ <xliff:g id="USERNAME">%1$s</xliff:g> ማራገፍ ይፈልጋሉ?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"ይህ መተግበሪያ በፋብሪካው ስሪት ይተካ? ሁሉም ውሂብ ይወገዳል።"</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"ይህ መተግበሪያ በፋብሪካው ስሪት ይተካ? ሁሉም ውሂብ ይወገዳል። እነዚያን የሥራ መገለጫዎች ያላቸውን ጨምሮ ሁሉንም በዚህ መሣሪያ ላይ ባሉ ተጠቃሚዎች ላይ ተጽዕኖ ያሳርፍባቸዋል።"</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"እንዲሁም <xliff:g id="SIZE">%1$s</xliff:g> ተጓዳኝ የሚዲያ ፋይሎችን አስወግድ"</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"ከመተግበሪያ ውሂብ <xliff:g id="SIZE">%1$s</xliff:g> አቆይ።"</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"በማሄድ ላይ ያሉ ማራገፎች"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"ያልተሳኩ ማራገፎች"</string>
     <string name="uninstalling" msgid="8709566347688966845">"በማራገፍ ላይ…"</string>
diff --git a/packages/PackageInstaller/res/values-ar/strings.xml b/packages/PackageInstaller/res/values-ar/strings.xml
index da91983..edbd178 100644
--- a/packages/PackageInstaller/res/values-ar/strings.xml
+++ b/packages/PackageInstaller/res/values-ar/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"هل تريد إزالة هذا التطبيق للمستخدم <xliff:g id="USERNAME">%1$s</xliff:g>؟"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"هل تريد استبدال هذا التطبيق بإصدار المصنع؟ ستتم إزالة جميع البيانات."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"هل تريد استبدال هذا التطبيق بإصدار المصنع؟ ستتم إزالة جميع البيانات. وسيؤثر هذا في جميع مستخدمي هذا الجهاز، بما في ذلك من لديهم ملفات شخصية للعمل."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"إزالة <xliff:g id="SIZE">%1$s</xliff:g> من ملفات الوسائط المرتبطة أيضًا."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"الاحتفاظ بالحجم <xliff:g id="SIZE">%1$s</xliff:g> من بيانات التطبيق."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"عمليات إلغاء التثبيت الجارية"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"عمليات إلغاء التثبيت غير الناجحة"</string>
     <string name="uninstalling" msgid="8709566347688966845">"جارٍ إلغاء التثبيت…"</string>
diff --git a/packages/PackageInstaller/res/values-as/strings.xml b/packages/PackageInstaller/res/values-as/strings.xml
index 2f2d867..d3429bf 100644
--- a/packages/PackageInstaller/res/values-as/strings.xml
+++ b/packages/PackageInstaller/res/values-as/strings.xml
@@ -22,10 +22,10 @@
     <string name="cancel" msgid="1018267193425558088">"বাতিল কৰক"</string>
     <string name="installing" msgid="4921993079741206516">"ইনষ্টল কৰি থকা হৈছে…"</string>
     <string name="installing_app" msgid="1165095864863849422">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ইনষ্টল কৰি থকা হৈছে…"</string>
-    <string name="install_done" msgid="5987363587661783896">"এপ্ ইনষ্টল কৰা হ\'ল।"</string>
+    <string name="install_done" msgid="5987363587661783896">"এপ্ ইনষ্টল কৰা হ’ল।"</string>
     <string name="install_confirm_question" msgid="8176284075816604590">"আপুনি এই এপ্লিকেশ্বনটো ইনষ্টল কৰিব বিচাৰেনে?"</string>
-    <string name="install_confirm_question_update" msgid="7942235418781274635">"আপুনি আগৰে পৰা থকা এপ্লিকেশ্বন আপডে\'ট কৰিব বিচাৰেনে? আপোনাৰ আগৰ ডেটা নেহেৰায়।"</string>
-    <string name="install_confirm_question_update_system" msgid="4713001702777910263">"আপুনি এই আগৰে পৰা থকা এপ্লিকেশ্বনটো আপডে\'ট কৰিব বিচাৰেনে? আপোনাৰ আগৰ ডেটা নেহেৰায়।"</string>
+    <string name="install_confirm_question_update" msgid="7942235418781274635">"আপুনি আগৰে পৰা থকা এপ্লিকেশ্বন আপডে’ট কৰিব বিচাৰেনে? আপোনাৰ আগৰ ডেটা নেহেৰায়।"</string>
+    <string name="install_confirm_question_update_system" msgid="4713001702777910263">"আপুনি এই আগৰে পৰা থকা এপ্লিকেশ্বনটো আপডে’ট কৰিব বিচাৰেনে? আপোনাৰ আগৰ ডেটা নেহেৰায়।"</string>
     <string name="install_failed" msgid="5777824004474125469">"এপ্ ইনষ্টল কৰা হোৱা নাই।"</string>
     <string name="install_failed_blocked" msgid="8512284352994752094">"পেকেজটোৰ ইনষ্টল অৱৰোধ কৰা হৈছে।"</string>
     <string name="install_failed_conflict" msgid="3493184212162521426">"এপটো ইনষ্টল কৰিব পৰা নগ\'ল কাৰণ ইয়াৰ সৈতে আগৰে পৰা থকা এটা পেকেজৰ সংঘাত হৈছে।"</string>
@@ -51,19 +51,21 @@
     <string name="generic_error_dlg_title" msgid="5863195085927067752">"আসোঁৱাহ"</string>
     <string name="generic_error_dlg_text" msgid="5287861443265795232">"এপ্ আনইনষ্টল কৰিব পৰা নগ\'ল।"</string>
     <string name="uninstall_application_title" msgid="4045420072401428123">"এপ্ আনইনষ্টল কৰক"</string>
-    <string name="uninstall_update_title" msgid="824411791011583031">"আপডে\'ট আনইনষ্টল কৰক"</string>
+    <string name="uninstall_update_title" msgid="824411791011583031">"আপডে’ট আনইনষ্টল কৰক"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> হৈছে তলৰ এপটোৰ এটা অংশ:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"আপুনি এই এপটো আনইনষ্টল কৰিব বিচাৰেনে?"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"আপুনি "<b>"সকলো"</b>" ব্যৱহাৰকাৰীৰ বাবে এই এপটো আনইনষ্টল কৰিব বিচাৰেনে? এপ্লিকেশ্বন আৰু ইয়াৰ ডেটা ডিভাইচটোত থকা "<b>"সকলো"</b>" ব্যৱহাৰকাৰীৰ পৰা আঁতৰোৱা হ\'ব৷"</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"আপুনি ব্যৱহাৰকাৰীৰ <xliff:g id="USERNAME">%1$s</xliff:g> বাবে এই এপটো আনইনষ্টল কৰিব বিচাৰেনে?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"এই এপটোৰ ফেক্টৰী সংস্কৰণ ব্যৱহাৰ কৰিব বিচাৰেনে? সকলো ডেটা মচা হ\'ব।"</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"এই এপটোৰ ফেক্টৰী সংস্কৰণ ব্যৱহাৰ কৰিব বিচাৰেনে? সকলো ডেটা মচা হ\'ব। কর্মস্থানৰ প্ৰফাইল থকা ব্যৱহাৰকাৰীৰ লগতে ডিভাইচটোৰ সকলো ব্যৱহাৰকাৰীৰ ওপৰত ইয়াৰ প্ৰভাৱ পৰিব।"</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"সংশ্লিষ্ট মিডিয়া ফাইলৰ পৰা <xliff:g id="SIZE">%1$s</xliff:g> আঁতৰাওক।"</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"এপৰ ডেটাৰ <xliff:g id="SIZE">%1$s</xliff:g> ৰাখক"</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"আনইনষ্টল কৰি থকা হৈছে"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"যিবোৰ আনইনষ্টল পৰা নগ\'ল"</string>
     <string name="uninstalling" msgid="8709566347688966845">"আনইনষ্টল কৰি থকা হৈছে…"</string>
     <string name="uninstalling_app" msgid="8866082646836981397">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> আনইনষ্টল কৰি থকা হৈছে…"</string>
     <string name="uninstall_done" msgid="439354138387969269">"আনইনষ্টল কৰা হ’ল।"</string>
-    <string name="uninstall_done_app" msgid="4588850984473605768">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> আনইনষ্টল কৰা হ\'ল"</string>
+    <string name="uninstall_done_app" msgid="4588850984473605768">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> আনইনষ্টল কৰা হ’ল"</string>
     <string name="uninstall_failed" msgid="1847750968168364332">"আনইনষ্টল কৰিব পৰা নগ\'ল।"</string>
     <string name="uninstall_failed_app" msgid="5506028705017601412">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> আনইনষ্টল কৰিব পৰা নগ\'ল।"</string>
     <string name="uninstall_failed_device_policy_manager" msgid="785293813665540305">"ডিভাইচৰ সক্ৰিয় প্ৰশাসক এপ্ আনইনষ্টল কৰিব নোৱাৰি"</string>
diff --git a/packages/PackageInstaller/res/values-az/strings.xml b/packages/PackageInstaller/res/values-az/strings.xml
index 478322a..caa6f7d 100644
--- a/packages/PackageInstaller/res/values-az/strings.xml
+++ b/packages/PackageInstaller/res/values-az/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> adlı istifadəçi üçün bu tətbiqi sistemdən silmək istəyirsiniz?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Tətbiq zavod versiyası ilə əvəz olunsun? Bütün data silinəcək."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Tətbiq zavod versiyası ilə əvəz olunsun? Bütün data silinəcək. Bu, iş profilləri daxil olmaqla bu cihazın bütün istifadəçilərinə təsir edir."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Həmçinin, <xliff:g id="SIZE">%1$s</xliff:g> həcmində əlaqədar media fayllarını silin."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Tətbiq datasının <xliff:g id="SIZE">%1$s</xliff:g> hissəsini saxlayın."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"İşləyən sistemlər silinmələr"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Uğursuz olan sistemlər silinmələr"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Sistemdən silinir..."</string>
diff --git a/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml b/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
index cab6be5..689692d 100644
--- a/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
+++ b/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Želite li da deinstalirate ovu aplikaciju za korisnika <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Želite li da zamenite ovu aplikaciju fabričkom verzijom? Svi podaci će biti uklonjeni."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Želite li da zamenite ovu aplikaciju fabričkom verzijom? Svi podaci će biti uklonjeni. Ovo utiče na sve korisnike ovog uređaja, uključujući i one sa profilima za Work."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Ukloni i <xliff:g id="SIZE">%1$s</xliff:g> povezanih medijskih datoteka."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Zadrži <xliff:g id="SIZE">%1$s</xliff:g> podataka aplikacije."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Aktivna deinstaliranja"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Neuspela deinstaliranja"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Deinstalira se…"</string>
diff --git a/packages/PackageInstaller/res/values-be/strings.xml b/packages/PackageInstaller/res/values-be/strings.xml
index 4433b73..a6e87e9 100644
--- a/packages/PackageInstaller/res/values-be/strings.xml
+++ b/packages/PackageInstaller/res/values-be/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Хочаце выдаліць гэту праграму для карыстальніка <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Замяніць гэту праграму заводскай версіяй? Усе даныя будуць выдалены."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Замяніць гэту праграму заводскай версіяй? Усе даныя будуць выдалены. Гэта паўплывае на ўсіх карыстальнікаў гэтай прылады, уключаючы карыстальнікаў з працоўнымі профілямі."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Таксама выдаліць <xliff:g id="SIZE">%1$s</xliff:g> звязаных файлаў мультымедыя."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Захаваць даныя праграмы (<xliff:g id="SIZE">%1$s</xliff:g>)."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Актыўныя выдаленні"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Нявыкананыя выдаленні"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Ідзе выдаленне…"</string>
diff --git a/packages/PackageInstaller/res/values-bg/strings.xml b/packages/PackageInstaller/res/values-bg/strings.xml
index 2b76bdd..51a0e88 100644
--- a/packages/PackageInstaller/res/values-bg/strings.xml
+++ b/packages/PackageInstaller/res/values-bg/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Искате ли да деинсталирате това приложение за потребителя <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Това приложение да се замени ли с фабричната версия? Всички данни ще бъдат премахнати."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Това приложение да се замени ли с фабричната версия? Всички данни ще бъдат премахнати. Промяната ще засегне всеки потребител на устройството, включително тези със служебни потребителски профили."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Премахване и на <xliff:g id="SIZE">%1$s</xliff:g> свързани мултимедийни файлове."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Запазване на <xliff:g id="SIZE">%1$s</xliff:g> данни от приложението."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Активни деинсталирания"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Неуспешни деинсталирания"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Деинсталира се..."</string>
diff --git a/packages/PackageInstaller/res/values-bn/strings.xml b/packages/PackageInstaller/res/values-bn/strings.xml
index ee264d9..2e610bb 100644
--- a/packages/PackageInstaller/res/values-bn/strings.xml
+++ b/packages/PackageInstaller/res/values-bn/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"আপনি কি <xliff:g id="USERNAME">%1$s</xliff:g>-এর জন্য এই অ্যাপটি আনইনস্টল করতে চান?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"ফ্যাক্টরি ভার্সন দিয়ে এই অ্যাপটিকে বদলাতে চান? সব ডেটা মুছে যাবে।"</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"ফ্যাক্টরি ভার্সন দিয়ে এই অ্যাপটিকে বদলাতে চান? সব ডেটা মুছে যাবে। এই ডিভাইসে কাজের প্রোফাইল আছে এমন ব্যবহারকারী সহ সবাই এর দ্বারা প্রভাবিত হবেন।"</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"অ্যাপের সাথে সম্পর্কিত <xliff:g id="SIZE">%1$s</xliff:g> সাইজের মিডিয়া ফাইলগুলিও সরিয়ে দিন।"</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"অ্যাপ ডেটার মধ্যে <xliff:g id="SIZE">%1$s</xliff:g> রেখে দিন।"</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"আনইনস্টল করা হচ্ছে"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"আনইনস্টল করা যায়নি"</string>
     <string name="uninstalling" msgid="8709566347688966845">"আনইনস্টল করা হচ্ছে…"</string>
diff --git a/packages/PackageInstaller/res/values-bs/strings.xml b/packages/PackageInstaller/res/values-bs/strings.xml
index 97ba693..84a5a16 100644
--- a/packages/PackageInstaller/res/values-bs/strings.xml
+++ b/packages/PackageInstaller/res/values-bs/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Želite li deinstalirati ovu aplikaciju za korisnika <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Želite li ovu aplikaciju zamijeniti s fabričkom verzijom? Svi podaci će biti uklonjeni."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Želite li ovu aplikaciju zamijeniti s fabričkom verzijom? Svi podaci će biti uklonjeni. To će uticati na sve korisnike uređaja, uključujući i one s radnim profilima."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Također ukloni <xliff:g id="SIZE">%1$s</xliff:g> povezanih medijskih fajlova."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Zadržati <xliff:g id="SIZE">%1$s</xliff:g> podataka aplikacije."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Tekuća deinstaliranja"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Neuspjela deinstaliranja"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Deinstaliranje..."</string>
diff --git a/packages/PackageInstaller/res/values-ca/strings.xml b/packages/PackageInstaller/res/values-ca/strings.xml
index a090808..6c08161 100644
--- a/packages/PackageInstaller/res/values-ca/strings.xml
+++ b/packages/PackageInstaller/res/values-ca/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Vols desinstal·lar aquesta aplicació per a l\'usuari <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Vols substituir aquesta aplicació per la versió de fàbrica? Se suprimiran totes les dades."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Vols substituir aquesta aplicació per la versió de fàbrica? Se suprimiran totes les dades. Això afectarà tots els usuaris d\'aquest dispositiu, inclosos els que tinguin un perfil professional."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Suprimeix també <xliff:g id="SIZE">%1$s</xliff:g> de fitxers multimèdia associats."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Conserva <xliff:g id="SIZE">%1$s</xliff:g> de dades de l\'aplicació."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Desinstal·lacions en curs"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Desinstal·lacions fallides"</string>
     <string name="uninstalling" msgid="8709566347688966845">"S\'està desinstal·lant…"</string>
diff --git a/packages/PackageInstaller/res/values-cs/strings.xml b/packages/PackageInstaller/res/values-cs/strings.xml
index 1e46214..8e4ce59 100644
--- a/packages/PackageInstaller/res/values-cs/strings.xml
+++ b/packages/PackageInstaller/res/values-cs/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Chcete tuto aplikaci pro uživatele <xliff:g id="USERNAME">%1$s</xliff:g> odinstalovat?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Chcete tuto aplikaci nahradit tovární verzí? Všechna data budou odstraněna."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Chcete tuto aplikaci nahradit tovární verzí? Všechna data budou odstraněna. Tato akce ovlivní všechny uživatele zařízení, včetně uživatelů s pracovním profilem."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Odstranit také <xliff:g id="SIZE">%1$s</xliff:g> přidružených mediálních souborů."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Ponechat data aplikace o velikosti <xliff:g id="SIZE">%1$s</xliff:g>."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Probíhající odinstalace"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Neúspěšné odinstalace"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Odinstalace…"</string>
diff --git a/packages/PackageInstaller/res/values-da/strings.xml b/packages/PackageInstaller/res/values-da/strings.xml
index d35fb9c..933ff3f 100644
--- a/packages/PackageInstaller/res/values-da/strings.xml
+++ b/packages/PackageInstaller/res/values-da/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Vil du afinstallere denne app for brugeren <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Vil du erstatte denne app med fabriksversionen? Alle data fjernes."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Vil du erstatte denne app med fabriksversionen? Alle data fjernes. Dette påvirker alle brugere af denne enhed, bl.a. brugere med arbejdsprofiler."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Fjern også <xliff:g id="SIZE">%1$s</xliff:g> tilknyttede mediefiler."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Behold <xliff:g id="SIZE">%1$s</xliff:g> appdata."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Igangværende afinstallationer"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Mislykkede afinstallationer"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Afinstallerer…"</string>
diff --git a/packages/PackageInstaller/res/values-de/strings.xml b/packages/PackageInstaller/res/values-de/strings.xml
index 367eb24..4ad9b73 100644
--- a/packages/PackageInstaller/res/values-de/strings.xml
+++ b/packages/PackageInstaller/res/values-de/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Möchtest du diese App für den Nutzer <xliff:g id="USERNAME">%1$s</xliff:g> deinstallieren?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Diese App durch die Werksversion ersetzen? Alle Daten werden entfernt."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Diese App durch die Werksversion ersetzen? Alle Daten werden entfernt. Dies betrifft alle Nutzer des Geräts, einschließlich Arbeitsprofilen."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Auch <xliff:g id="SIZE">%1$s</xliff:g> der verknüpften Mediendateien entfernen."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> an App-Daten behalten."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Laufende Deinstallationen"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Fehlgeschlagene Deinstallationen"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Wird deinstalliert..."</string>
diff --git a/packages/PackageInstaller/res/values-el/strings.xml b/packages/PackageInstaller/res/values-el/strings.xml
index d179acb..36f9f2d 100644
--- a/packages/PackageInstaller/res/values-el/strings.xml
+++ b/packages/PackageInstaller/res/values-el/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Θέλετε να απεγκαταστήσετε αυτήν την εφαρμογή για τον χρήστη <xliff:g id="USERNAME">%1$s</xliff:g>;"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Να αντικατασταθεί αυτή η εφαρμογή με την εργοστασιακή έκδοση; Όλα τα δεδομένα θα καταργηθούν."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Να αντικατασταθεί αυτή η εφαρμογή με την εργοστασιακή έκδοση; Όλα τα δεδομένα θα καταργηθούν. Αυτό επηρεάζει όλους τους χρήστες της συσκευής, συμπεριλαμβανομένων και των κατόχων προφίλ εργασίας."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Να καταργηθούν επίσης <xliff:g id="SIZE">%1$s</xliff:g> συσχετισμένων αρχείων μέσων."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Διατήρηση <xliff:g id="SIZE">%1$s</xliff:g> δεδομένων εφαρμογών."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Απεγκαταστάσεις σε εξέλιξη"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Αποτυχημένες απεγκαταστάσεις"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Απεγκατάσταση…"</string>
diff --git a/packages/PackageInstaller/res/values-en-rAU/strings.xml b/packages/PackageInstaller/res/values-en-rAU/strings.xml
index 4016288..51e61d02 100644
--- a/packages/PackageInstaller/res/values-en-rAU/strings.xml
+++ b/packages/PackageInstaller/res/values-en-rAU/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Do you want to uninstall this app for the user <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Replace this app with the factory version? All data will be removed."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Replace this app with the factory version? All data will be removed. This affects all users of this device, including those with work profiles."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Also remove <xliff:g id="SIZE">%1$s</xliff:g> of associated media files."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Keep <xliff:g id="SIZE">%1$s</xliff:g> of app data."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Running uninstallations"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Failed uninstallations"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Uninstalling…"</string>
diff --git a/packages/PackageInstaller/res/values-en-rCA/strings.xml b/packages/PackageInstaller/res/values-en-rCA/strings.xml
index 4016288..51e61d02 100644
--- a/packages/PackageInstaller/res/values-en-rCA/strings.xml
+++ b/packages/PackageInstaller/res/values-en-rCA/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Do you want to uninstall this app for the user <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Replace this app with the factory version? All data will be removed."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Replace this app with the factory version? All data will be removed. This affects all users of this device, including those with work profiles."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Also remove <xliff:g id="SIZE">%1$s</xliff:g> of associated media files."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Keep <xliff:g id="SIZE">%1$s</xliff:g> of app data."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Running uninstallations"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Failed uninstallations"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Uninstalling…"</string>
diff --git a/packages/PackageInstaller/res/values-en-rGB/strings.xml b/packages/PackageInstaller/res/values-en-rGB/strings.xml
index 4016288..51e61d02 100644
--- a/packages/PackageInstaller/res/values-en-rGB/strings.xml
+++ b/packages/PackageInstaller/res/values-en-rGB/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Do you want to uninstall this app for the user <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Replace this app with the factory version? All data will be removed."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Replace this app with the factory version? All data will be removed. This affects all users of this device, including those with work profiles."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Also remove <xliff:g id="SIZE">%1$s</xliff:g> of associated media files."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Keep <xliff:g id="SIZE">%1$s</xliff:g> of app data."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Running uninstallations"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Failed uninstallations"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Uninstalling…"</string>
diff --git a/packages/PackageInstaller/res/values-en-rIN/strings.xml b/packages/PackageInstaller/res/values-en-rIN/strings.xml
index 4016288..51e61d02 100644
--- a/packages/PackageInstaller/res/values-en-rIN/strings.xml
+++ b/packages/PackageInstaller/res/values-en-rIN/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Do you want to uninstall this app for the user <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Replace this app with the factory version? All data will be removed."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Replace this app with the factory version? All data will be removed. This affects all users of this device, including those with work profiles."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Also remove <xliff:g id="SIZE">%1$s</xliff:g> of associated media files."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Keep <xliff:g id="SIZE">%1$s</xliff:g> of app data."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Running uninstallations"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Failed uninstallations"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Uninstalling…"</string>
diff --git a/packages/PackageInstaller/res/values-en-rXC/strings.xml b/packages/PackageInstaller/res/values-en-rXC/strings.xml
index 6833263..de4b31e 100644
--- a/packages/PackageInstaller/res/values-en-rXC/strings.xml
+++ b/packages/PackageInstaller/res/values-en-rXC/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‏‎‎‎‎‎‏‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‎‏‎‎‏‏‎‏‎‏‏‎‏‎‏‎‎‎‎‏‏‏‎‎Do you want to uninstall this app for the user ‎‏‎‎‏‏‎<xliff:g id="USERNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‎‏‏‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‎‏‎‎‎‏‎‎‎‎‎‏‎‏‎‏‎‏‏‎‎‎‎‎‏‎Replace this app with the factory version? All data will be removed.‎‏‎‎‏‎"</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‎‏‎‎‏‎‎‎‏‏‏‎‎‏‎‏‎‎‎‏‏‎‏‏‎‏‏‏‎‎‏‏‎‏‎‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‎Replace this app with the factory version? All data will be removed. This affects all users of this device, including those with work profiles.‎‏‎‎‏‎"</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‎‏‏‏‎‎‎‎‏‎‎‎‏‏‏‎‎‎‏‎‏‏‎‎‏‏‎‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎Also remove ‎‏‎‎‏‏‎<xliff:g id="SIZE">%1$s</xliff:g>‎‏‎‎‏‏‏‎ of associated media files.‎‏‎‎‏‎"</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‎‏‏‎‏‎‏‏‏‎‎‏‏‎‎‏‎‎‎‏‎‎‏‏‏‎‏‎‏‎‏‎‏‎‎‏‏‏‎‏‎‎‏‎‎‏‎‏‏‏‏‏‎‎Keep ‎‏‎‎‏‏‎<xliff:g id="SIZE">%1$s</xliff:g>‎‏‎‎‏‏‏‎ of app data.‎‏‎‎‏‎"</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‎‏‎‏‎‎‎‏‏‎‏‎‎‏‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‏‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‏‏‏‎‏‎Running uninstalls‎‏‎‎‏‎"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‏‎‎‏‏‎‎‎‏‏‏‎‏‎‏‏‎‏‎‎‏‎‏‎‏‏‎‎‏‏‏‏‎‏‏‏‎‎‎‎‎‏‏‎‎‎Failed uninstalls‎‏‎‎‏‎"</string>
     <string name="uninstalling" msgid="8709566347688966845">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‏‏‎‎‏‎‏‎‏‎‏‏‎‏‏‎‏‏‎‎‏‎‎‏‏‏‏‎‏‎‏‎‏‏‏‏‎‏‎Uninstalling…‎‏‎‎‏‎"</string>
diff --git a/packages/PackageInstaller/res/values-es-rUS/strings.xml b/packages/PackageInstaller/res/values-es-rUS/strings.xml
index dd45a79..ce8edfe 100644
--- a/packages/PackageInstaller/res/values-es-rUS/strings.xml
+++ b/packages/PackageInstaller/res/values-es-rUS/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"¿Quieres desinstalar esta app para el usuario <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"¿Deseas reemplazar esta app con la versión de fábrica? Se quitarán todos los datos."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"¿Deseas reemplazar esta app con la versión de fábrica? Se quitarán todos los datos. Esta acción afectará a todos los usuarios de este dispositivo, incluidos los que tengan perfiles de trabajo."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"También quitar <xliff:g id="SIZE">%1$s</xliff:g> de archivos multimedia asociados."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Guardar <xliff:g id="SIZE">%1$s</xliff:g> en datos de apps"</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Desinstalaciones activas"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Desinstalaciones con errores"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Desinstalando…"</string>
diff --git a/packages/PackageInstaller/res/values-es/strings.xml b/packages/PackageInstaller/res/values-es/strings.xml
index 7aef333..72351d6 100644
--- a/packages/PackageInstaller/res/values-es/strings.xml
+++ b/packages/PackageInstaller/res/values-es/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"¿Quieres desinstalar esta aplicación para el usuario <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"¿Quieres sustituir esta aplicación con la versión de fábrica? Ten en cuenta que se borrarán todos los datos."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"¿Quieres sustituir esta aplicación con la versión de fábrica? Ten en cuenta que se borrarán todos los datos. Esto afecta a todos los usuarios del dispositivo, incluidos los que tienen perfiles de trabajo."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Eliminar también <xliff:g id="SIZE">%1$s</xliff:g> de archivos multimedia asociados."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Mantener <xliff:g id="SIZE">%1$s</xliff:g> de datos de aplicaciones."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Desinstalaciones en curso"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Desinstalaciones fallidas"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Desinstalando…"</string>
diff --git a/packages/PackageInstaller/res/values-et/strings.xml b/packages/PackageInstaller/res/values-et/strings.xml
index 873ac48..41e428a 100644
--- a/packages/PackageInstaller/res/values-et/strings.xml
+++ b/packages/PackageInstaller/res/values-et/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Kas soovite selle rakenduse kasutaja <xliff:g id="USERNAME">%1$s</xliff:g> kontolt desinstallida?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Kas asendada see rakendus tehaseversiooniga? Kõik andmed eemaldatakse."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Kas asendada see rakendus tehaseversiooniga? Kõik andmed eemaldatakse. See mõjutab kõiki seadme kasutajaid, sh neid, kellel on tööprofiilid."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Eemalda ka <xliff:g id="SIZE">%1$s</xliff:g> seotud meediafailidest."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Säilita rakenduse andmete hulk <xliff:g id="SIZE">%1$s</xliff:g>."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Käimasolevad desinstallimised"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Ebaõnnestunud desinstallimised"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Desinstallimine …"</string>
diff --git a/packages/PackageInstaller/res/values-eu/strings.xml b/packages/PackageInstaller/res/values-eu/strings.xml
index 6e79ac8..30a3665 100644
--- a/packages/PackageInstaller/res/values-eu/strings.xml
+++ b/packages/PackageInstaller/res/values-eu/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> erabiltzaileari desinstalatu nahi diozu aplikazioa?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Aplikazio hau jatorrizko bertsioarekin ordeztu nahi duzu? Datu guztiak ezabatuko dira."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Aplikazio hau jatorrizko bertsioarekin ordeztu nahi duzu? Datu guztiak ezabatuko dira. Gailuaren erabiltzaile guztiengan izango du eragina, laneko profilak dituztenak barne."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Halaber, kendu erlazionatutako multimedia-fitxategiak (<xliff:g id="SIZE">%1$s</xliff:g>)."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Mantendu aplikazio-datuen <xliff:g id="SIZE">%1$s</xliff:g>."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Abian diren desinstalatze-eragiketak"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Desinstalatu ezin izan direnak"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Desinstalatzen…"</string>
diff --git a/packages/PackageInstaller/res/values-fa/strings.xml b/packages/PackageInstaller/res/values-fa/strings.xml
index 7a36387..2353d4a 100644
--- a/packages/PackageInstaller/res/values-fa/strings.xml
+++ b/packages/PackageInstaller/res/values-fa/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"آیا می‌خواهید این برنامه را برای این کاربر <xliff:g id="USERNAME">%1$s</xliff:g> حذف نصب کنید؟"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"این برنامه با نسخه کارخانه جایگزین شود؟ همه داده‌ها پاک می‌شود."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"این برنامه با نسخه کارخانه جایگزین شود؟ همه داده‌ها پاک می‌شود. این کار همه کاربران این دستگاه (ازجمله کاربرانی که نمایه کاری دارند) را تحت تأثیر قرار خواهد داد."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"همچنین <xliff:g id="SIZE">%1$s</xliff:g> از فایل‌های رسانه مرتبط برداشته شود."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> از داده‌های برنامه را نگه‌دارید."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"حذف‌نصب‌های درحال انجام"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"حذف‌نصب‌های ناموفق"</string>
     <string name="uninstalling" msgid="8709566347688966845">"درحال حذف نصب..."</string>
diff --git a/packages/PackageInstaller/res/values-fi/strings.xml b/packages/PackageInstaller/res/values-fi/strings.xml
index b73c50b..f4f1c08 100644
--- a/packages/PackageInstaller/res/values-fi/strings.xml
+++ b/packages/PackageInstaller/res/values-fi/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Haluatko poistaa tämän sovelluksen käyttäjältä <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Haluatko korvata tämän sovelluksen tehdasversiolla? Kaikki data poistetaan."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Haluatko korvata tämän sovelluksen tehdasversiolla? Kaikki data poistetaan. Tämä vaikuttaa kaikkiin laitteen käyttäjiin, myös työprofiileihin."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Poista myös <xliff:g id="SIZE">%1$s</xliff:g> tähän liittyviä mediatiedostoja"</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Säilytä <xliff:g id="SIZE">%1$s</xliff:g> sovellusdataa"</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Käynnissä olevat poistot"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Epäonnistuneet poistot"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Poistetaan…"</string>
diff --git a/packages/PackageInstaller/res/values-fr-rCA/strings.xml b/packages/PackageInstaller/res/values-fr-rCA/strings.xml
index a6cd289..78c04b3 100644
--- a/packages/PackageInstaller/res/values-fr-rCA/strings.xml
+++ b/packages/PackageInstaller/res/values-fr-rCA/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Voulez-vous désinstaller cette application pour l\'utilisateur <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Remplacer cette application par la version d\'usine? Toutes les données seront supprimées."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Remplacer cette application par la version d\'usine? Toutes les données seront supprimées. Cela touchera tous les utilisateurs de cet appareil, y compris ceux qui utilisent un profil professionnel."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Aussi supprimer <xliff:g id="SIZE">%1$s</xliff:g> de fichiers médias connexes."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Garder <xliff:g id="SIZE">%1$s</xliff:g> de données d\'application."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Désinstallations en cours…"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Désinstallations échouées"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Désinstallation en cours…"</string>
diff --git a/packages/PackageInstaller/res/values-fr/strings.xml b/packages/PackageInstaller/res/values-fr/strings.xml
index d36322a..7d69edf 100644
--- a/packages/PackageInstaller/res/values-fr/strings.xml
+++ b/packages/PackageInstaller/res/values-fr/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Voulez-vous désinstaller cette application pour l\'utilisateur <xliff:g id="USERNAME">%1$s</xliff:g> ?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Remplacer cette application par la version d\'usine ? Toutes les données seront supprimées."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Remplacer cette application par la version d\'usine ? Toutes les données seront supprimées. Tous les utilisateurs de cet appareil seront affectés, y compris ceux qui ont un profil professionnel."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Supprimer également <xliff:g id="SIZE">%1$s</xliff:g> de fichiers multimédias associés."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Conserver <xliff:g id="SIZE">%1$s</xliff:g> de données d\'application."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Désinstallations en cours"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Échec des désinstallations"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Désinstallation…"</string>
diff --git a/packages/PackageInstaller/res/values-gl/strings.xml b/packages/PackageInstaller/res/values-gl/strings.xml
index a7b4278..fa010c2 100644
--- a/packages/PackageInstaller/res/values-gl/strings.xml
+++ b/packages/PackageInstaller/res/values-gl/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Queres desinstalar esta aplicación para o usuario que se chama <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Queres substituír esta aplicación pola versión que viña de fábrica? Eliminaranse todos os datos."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Queres substituír esta aplicación pola versión que viña de fábrica? Eliminaranse todos os datos. Isto afectará a todos os usuarios do dispositivo, incluídos os que teñan perfís de traballo."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Quitar tamén <xliff:g id="SIZE">%1$s</xliff:g> de ficheiros multimedia asociados."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Conservar os datos da aplicación, que ocupan <xliff:g id="SIZE">%1$s</xliff:g>."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Desinstalacións en curso"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Erros nas desinstalacións"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Desinstalando…"</string>
diff --git a/packages/PackageInstaller/res/values-gu/strings.xml b/packages/PackageInstaller/res/values-gu/strings.xml
index 66e6fd1..1b7e936 100644
--- a/packages/PackageInstaller/res/values-gu/strings.xml
+++ b/packages/PackageInstaller/res/values-gu/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"શું તમે <xliff:g id="USERNAME">%1$s</xliff:g> વપરાશકર્તા માટે આ ઍપ્લિકેશનને અનઇન્સ્ટૉલ કરવા માગો છો?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"આ ઍપ્લિકેશનને ફેક્ટરી વર્ઝનથી બદલીએ? બધો ડેટા કાઢી નાખવામાં આવશે."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"આ ઍપ્લિકેશનને ફેક્ટરી વર્ઝનથી બદલીએ? બધો ડેટા કાઢી નાખવામાં આવશે. આનાથી કાર્યાલયની પ્રોફાઇલ ધરાવનારા વપરાશકર્તાઓ સહિત આ ડિવાઇસના બધા વપરાશકર્તાઓ પ્રભાવિત થશે."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"<xliff:g id="SIZE">%1$s</xliff:g>ની સંકળાયેલી મીડિયા ફાઇલોને પણ કાઢી નાખો."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g>નો ઍપ ડેટા રાખો."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"ચાલી રહેલા અનઇન્સ્ટૉલ"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"નિષ્ફળ થયેલા અનઇન્સ્ટૉલ"</string>
     <string name="uninstalling" msgid="8709566347688966845">"અનઇન્સ્ટૉલ કરી રહ્યાં છીએ…"</string>
diff --git a/packages/PackageInstaller/res/values-hi/strings.xml b/packages/PackageInstaller/res/values-hi/strings.xml
index 3c6031c..3ffa7e3 100644
--- a/packages/PackageInstaller/res/values-hi/strings.xml
+++ b/packages/PackageInstaller/res/values-hi/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"क्या आप उपयोगकर्ता <xliff:g id="USERNAME">%1$s</xliff:g> के लिए इस ऐप्लिकेशन को अनइंस्टॉल करना चाहते हैं?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"इस ऐप्लिकेशन को फ़ैक्ट्री वर्शन से बदलें? सभी डेटा हटा दिया जाएगा."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"इस ऐप्लिकेशन को फ़ैक्ट्री वर्शन से बदलें? पूरा डेटा हटा दिया जाएगा. इसका असर इस डिवाइस के सभी उपयोगकर्ताओं पर पड़ेगा, जिनमें काम की प्रोफ़ाइलों वाले उपयोगकर्ता शामिल हैं."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"<xliff:g id="SIZE">%1$s</xliff:g> जुड़ी हुई मीडिया फ़ाइलें भी हटाएं."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> ऐप्लिकेशन डेटा रखें."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"वे अनइंस्टॉल जो चल रहे हैं"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"वे अनइंस्टॉल जो सफल नहीं रहे"</string>
     <string name="uninstalling" msgid="8709566347688966845">"अनइंस्‍टॉल हो रहा है…"</string>
diff --git a/packages/PackageInstaller/res/values-hr/strings.xml b/packages/PackageInstaller/res/values-hr/strings.xml
index 380df4a..769001a 100644
--- a/packages/PackageInstaller/res/values-hr/strings.xml
+++ b/packages/PackageInstaller/res/values-hr/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Želite li deinstalirati tu aplikaciju za korisnika <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Želite li tu aplikaciju zamijeniti tvorničkom verzijom? Izgubit ćete sve podatke."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Želite li tu aplikaciju zamijeniti tvorničkom verzijom? Izgubit ćete sve podatke. To se odnosi na sve korisnike uređaja, uključujući one s radnim profilima."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Ukloni i <xliff:g id="SIZE">%1$s</xliff:g> povezanih medijskih datoteka."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Zadrži <xliff:g id="SIZE">%1$s</xliff:g> podataka aplikacije."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Deinstaliranja u tijeku"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Neuspjela deinstaliranja"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Deinstaliranje…"</string>
diff --git a/packages/PackageInstaller/res/values-hu/strings.xml b/packages/PackageInstaller/res/values-hu/strings.xml
index 9cbff8b..9aceb8d 100644
--- a/packages/PackageInstaller/res/values-hu/strings.xml
+++ b/packages/PackageInstaller/res/values-hu/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Eltávolítja ezt az alkalmazást <xliff:g id="USERNAME">%1$s</xliff:g> felhasználó esetében?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Lecseréli az alkalmazást a gyári verzióra? Minden adat törlődik."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Lecseréli az alkalmazást a gyári verzióra? Minden adat törlődik. Ez az eszköz összes felhasználóját érinti, így a munkaprofilokkal rendelkezőket is."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Távolítsa el a társított médiafájlokat is (összesen: <xliff:g id="SIZE">%1$s</xliff:g>)."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> alkalmazásadat megtartása."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Futó eltávolítások"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Sikertelen telepítések"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Eltávolítás…"</string>
diff --git a/packages/PackageInstaller/res/values-hy/strings.xml b/packages/PackageInstaller/res/values-hy/strings.xml
index 2e2c855..da1bf3c 100644
--- a/packages/PackageInstaller/res/values-hy/strings.xml
+++ b/packages/PackageInstaller/res/values-hy/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Ապատեղադրե՞լ այս հավելվածը <xliff:g id="USERNAME">%1$s</xliff:g> օգտատիրոջ համար:"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Փոխարինե՞լ այս հավելվածը գործարանային տարբերակով: Բոլոր տվյալները կհեռացվեն:"</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Փոխարինե՞լ այս հավելվածը գործարանային տարբերակով: Բոլոր տվյալները կհեռացվեն: Դա վերաբերում է այս սարքի բոլոր օգտատերերին, այդ թվում նաև աշխատանքային պրոֆիլներ ունեցողներին:"</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Նաև հեռացնել առնչվող մեդիա ֆայլերը (<xliff:g id="SIZE">%1$s</xliff:g>)"</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Չհեռացնել հավելվածների տվյալները (<xliff:g id="SIZE">%1$s</xliff:g>):"</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Ընթացիկ ապատեղադրումներ"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Ձախողված ապատեղադրումներ"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Ապատեղադրվում է…"</string>
diff --git a/packages/PackageInstaller/res/values-in/strings.xml b/packages/PackageInstaller/res/values-in/strings.xml
index fadff61..15f3b41 100644
--- a/packages/PackageInstaller/res/values-in/strings.xml
+++ b/packages/PackageInstaller/res/values-in/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Apakah Anda ingin meng-uninstal aplikasi ini untuk pengguna <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Ganti aplikasi ini dengan versi setelan pabrik? Semua data akan dihapus."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Ganti aplikasi ini dengan versi setelan pabrik? Semua data akan dihapus. Tindakan ini memengaruhi semua pengguna perangkat ini, termasuk yang memiliki profil kerja."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Hapus juga <xliff:g id="SIZE">%1$s</xliff:g> dari file media terkait."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Pertahankan data aplikasi sebesar <xliff:g id="SIZE">%1$s</xliff:g>."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Menjalankan proses uninstal"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Proses uninstal yang gagal"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Meng-uninstal..."</string>
diff --git a/packages/PackageInstaller/res/values-is/strings.xml b/packages/PackageInstaller/res/values-is/strings.xml
index a50714f..e27be00 100644
--- a/packages/PackageInstaller/res/values-is/strings.xml
+++ b/packages/PackageInstaller/res/values-is/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Viltu fjarlægja þetta forrit fyrir notandann <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Viltu skipta þessu forriti út fyrir verksmiðjuútgáfuna? Öll gögn verða fjarlægð."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Viltu skipta þessu forriti út fyrir verksmiðjuútgáfuna? Öll gögn verða fjarlægð. Þetta hefur áhrif á alla notendur tækisins, þar á meðal þá sem eru með vinnusnið."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Fjarlægja einnig <xliff:g id="SIZE">%1$s</xliff:g> af tengdum efnisskrám."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Halda <xliff:g id="SIZE">%1$s</xliff:g> af forritagögnum."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Fjarlægingar í gangi"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Fjarlægingar sem mistókust"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Fjarlægir…"</string>
diff --git a/packages/PackageInstaller/res/values-it/strings.xml b/packages/PackageInstaller/res/values-it/strings.xml
index 5ca7eda..9f67a58 100644
--- a/packages/PackageInstaller/res/values-it/strings.xml
+++ b/packages/PackageInstaller/res/values-it/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Disinstallare l\'app per l\'utente <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Sostituire questa app con la versione di fabbrica? Tutti i dati verranno rimossi."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Sostituire questa app con la versione di fabbrica? Tutti i dati verranno rimossi. Saranno interessati tutti gli utenti del dispositivo, inclusi quelli che hanno profili di lavoro."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Rimuovi anche <xliff:g id="SIZE">%1$s</xliff:g> di file multimediali associati."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Mantieni <xliff:g id="SIZE">%1$s</xliff:g> di dati delle app."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Disinstallazioni in esecuzione"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Disinstallazioni non riuscite"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Disinstallazione…"</string>
diff --git a/packages/PackageInstaller/res/values-iw/strings.xml b/packages/PackageInstaller/res/values-iw/strings.xml
index cd94cda..628b335 100644
--- a/packages/PackageInstaller/res/values-iw/strings.xml
+++ b/packages/PackageInstaller/res/values-iw/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"האם ברצונך להסיר את התקנתה של אפליקציה זו עבור המשתמש <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"האם להחליף את האפליקציה הזאת בגרסת היצרן? כל הנתונים יוסרו."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"האם להחליף את האפליקציה הזאת בגרסת היצרן? כל הנתונים יוסרו. הפעולה תשפיע על כל משתמשי המכשיר, כולל משתמשים בעלי פרופיל עבודה."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"להסיר גם <xliff:g id="SIZE">%1$s</xliff:g> של קובצי מדיה משויכים."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"שמירת <xliff:g id="SIZE">%1$s</xliff:g> מנתוני האפליקציה."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"התקנות בתהליכי הסרה"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"הסרות התקנה שנכשלו"</string>
     <string name="uninstalling" msgid="8709566347688966845">"מסיר התקנה..."</string>
diff --git a/packages/PackageInstaller/res/values-ja/strings.xml b/packages/PackageInstaller/res/values-ja/strings.xml
index 7496016..4e748bc 100644
--- a/packages/PackageInstaller/res/values-ja/strings.xml
+++ b/packages/PackageInstaller/res/values-ja/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> さんのアプリをアンインストールしますか?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"このアプリを出荷時の状態に戻しますか?データがすべて削除されます。"</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"このアプリを出荷時の状態に戻しますか?データがすべて削除されます。これは、仕事用プロファイルを設定しているユーザーも含めて、この端末を使用するすべてのユーザーが対象となります。"</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"関連付けられたメディア ファイル(<xliff:g id="SIZE">%1$s</xliff:g>)も削除する。"</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"アプリのデータ(<xliff:g id="SIZE">%1$s</xliff:g>)を保持"</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"アンインストールを実行しています"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"エラーになったアンインストール"</string>
     <string name="uninstalling" msgid="8709566347688966845">"アンインストールしています…"</string>
diff --git a/packages/PackageInstaller/res/values-ka/strings.xml b/packages/PackageInstaller/res/values-ka/strings.xml
index aac14dc..fc4ba29 100644
--- a/packages/PackageInstaller/res/values-ka/strings.xml
+++ b/packages/PackageInstaller/res/values-ka/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"გსურთ ამ აპის დეინსტალაცია <xliff:g id="USERNAME">%1$s</xliff:g>-ისთვის?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"გსურთ ამ აპის ჩანაცვლება ქარხნული ვერსიით? მონაცემები მთლიანად ამოიშლება."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"გსურთ ამ აპის ჩანაცვლება ქარხნული ვერსიით? მონაცემები მთლიანად ამოიშლება. ეს ქმედება აისახება ამ მოწყობილობის ყველა მომხმარებელზე, მათ შორის, სამსახურის პროფილებით მოსარგებლეებზეც."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"ასევე ამოიშალოს დაკავშირებული მედიაფაილების <xliff:g id="SIZE">%1$s</xliff:g>."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"შენარჩუნდეს აპების მონაცემების <xliff:g id="SIZE">%1$s</xliff:g>."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"გაშვებული დეინსტალაციები"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"შეუსრულებელი დეინსტალაციები"</string>
     <string name="uninstalling" msgid="8709566347688966845">"მიმდინარეობს დეინსტალაცია…"</string>
diff --git a/packages/PackageInstaller/res/values-kk/strings.xml b/packages/PackageInstaller/res/values-kk/strings.xml
index a105a00..cecc482 100644
--- a/packages/PackageInstaller/res/values-kk/strings.xml
+++ b/packages/PackageInstaller/res/values-kk/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> үшін осы қолданба жойылсын ба?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Осы қолданбаны зауыттық нұсқамен ауыстыру керек пе? Барлық деректер жойылады."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Осы қолданбаны зауыттық нұсқамен ауыстыру керек пе? Барлық деректер жойылады. Бұл осы құрылғының барлық пайдаланушыларына, соның ішінде жұмыс профильдері бар пайдаланушыларға әсер етеді."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Байланыстырылған <xliff:g id="SIZE">%1$s</xliff:g> медиа файлдар да өшірілсін."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Қолданба деректерін (<xliff:g id="SIZE">%1$s</xliff:g>) сақтау."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Орындалып жатқан жою процестері"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Сәтсіз жою әрекеттері"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Жойылуда…"</string>
diff --git a/packages/PackageInstaller/res/values-km/strings.xml b/packages/PackageInstaller/res/values-km/strings.xml
index ce0db91..6bdc582 100644
--- a/packages/PackageInstaller/res/values-km/strings.xml
+++ b/packages/PackageInstaller/res/values-km/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"តើ​អ្នក​ចង់​លុប​កម្មវិធី​នេះ​សម្រាប់​អ្នកប្រើប្រាស់ <xliff:g id="USERNAME">%1$s</xliff:g> ដែរទេ?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"ជំនួសកម្មវិធីនេះដោយប្រើកំណែរោងចក្រ? ទិន្នន័យទាំងអស់នឹងត្រូវបានលុប។"</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"ជំនួសកម្មវិធីនេះដោយប្រើកំណែរោងចក្រ? ទិន្នន័យទាំងអស់នឹងត្រូវបានលុប។ សកម្មភាព​នេះប៉ះពាល់ដល់អ្នកប្រើប្រាស់ទាំងអស់​របស់ឧបករណ៍នេះ រួម​ទាំងអ្នកប្រើប្រាស់ដែលមានកម្រង​ព័ត៌មាន​ការងារ​ផងដែរ។"</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"ក៏លុប​ឯកសារ​មេឌៀ​ដែល​ពាក់ព័ន្ធ​ទំហំ <xliff:g id="SIZE">%1$s</xliff:g> ផងដែរ។"</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"រក្សាទុក​ទិន្នន័យ​កម្មវិធីទំហំ <xliff:g id="SIZE">%1$s</xliff:g>។"</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"កំពុង​ដំណើរការ​ការលុប"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"ការលុប​ដែល​បរាជ័យ"</string>
     <string name="uninstalling" msgid="8709566347688966845">"កំពុង​លុប…"</string>
diff --git a/packages/PackageInstaller/res/values-kn/strings.xml b/packages/PackageInstaller/res/values-kn/strings.xml
index 4c5373e..d78d368 100644
--- a/packages/PackageInstaller/res/values-kn/strings.xml
+++ b/packages/PackageInstaller/res/values-kn/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> ಬಳಕೆದಾರರಿಗೆ ಈ ಆ್ಯಪ್‌ ಅನ್ನು ಅನ್‌ಇನ್‌ಸ್ಟಾಲ್‌ ಮಾಡಲು ನೀವು ಬಯಸುವಿರಾ?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"ಈ ಆ್ಯಪ್‌ ಬದಲಿಗೆ ಫ್ಯಾಕ್ಟರಿ ಆವೃತ್ತಿಯನ್ನು ಬದಲಾಯಿಸುವುದೇ? ಎಲ್ಲಾ ಡೇಟಾ ತೆಗೆದುಹಾಕಲಾಗುತ್ತದೆ."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"ಈ ಆ್ಯಪ್‌ ಬದಲಿಗೆ ಫ್ಯಾಕ್ಟರಿ ಆವೃತ್ತಿಯನ್ನು ಬದಲಾಯಿಸುವುದೇ? ಎಲ್ಲಾ ಡೇಟಾ ತೆಗೆದುಹಾಕಲಾಗುತ್ತದೆ. ಕೆಲಸದ ಪ್ರೊಫೈಲ್‌ಗಳನ್ನು ಹೊಂದಿರುವವುಗಳನ್ನು ಒಳಗೊಂಡಂತೆ ಈ ಸಾಧನದ ಎಲ್ಲಾ ಬಳಕೆದಾರರಿಗೆ ಇದು ಪರಿಣಾಮ ಬೀರುತ್ತದೆ."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"ಸಂಯೋಜಿತ ಮಾಧ್ಯಮ ಫೈಲ್‌ಗಳ <xliff:g id="SIZE">%1$s</xliff:g> ಗಾತ್ರವನ್ನು ಕೂಡ ತೆಗೆದುಹಾಕಿ"</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"ಆ್ಯಪ್ ಡೇಟಾದಲ್ಲಿ <xliff:g id="SIZE">%1$s</xliff:g> ಇರಿಸಿಕೊಳ್ಳಿ."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"ಚಾಲನೆಯಲ್ಲಿರುವ ಅನ್‌ಇನ್‌ಸ್ಟಾಲ್‌ಗಳು"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"ವಿಫಲಗೊಂಡ ಅನ್‌ಇನ್‌ಸ್ಟಾಲ್‌ಗಳು"</string>
     <string name="uninstalling" msgid="8709566347688966845">"ಅನ್‌ಇನ್‌ಸ್ಟಾಲ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ..."</string>
diff --git a/packages/PackageInstaller/res/values-ko/strings.xml b/packages/PackageInstaller/res/values-ko/strings.xml
index 1741b6e..f5b4dc8 100644
--- a/packages/PackageInstaller/res/values-ko/strings.xml
+++ b/packages/PackageInstaller/res/values-ko/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g>님의 기기에 설치된 앱을 제거하시겠습니까?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"이 앱을 초기 버전으로 바꾸시겠습니까? 모든 데이터가 삭제됩니다."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"이 앱을 초기 버전으로 바꾸시겠습니까? 모든 데이터가 삭제되며 직장 프로필 사용자를 포함해 이 기기의 모든 사용자에게 영향을 미칩니다."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"<xliff:g id="SIZE">%1$s</xliff:g>의 관련 미디어 파일도 삭제합니다."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"앱 데이터 크기를 <xliff:g id="SIZE">%1$s</xliff:g>로 유지합니다."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"실행 중인 제거 작업"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"실패한 제거 작업"</string>
     <string name="uninstalling" msgid="8709566347688966845">"제거 중..."</string>
diff --git a/packages/PackageInstaller/res/values-ky/strings.xml b/packages/PackageInstaller/res/values-ky/strings.xml
index 6b4abb9..77c05b0 100644
--- a/packages/PackageInstaller/res/values-ky/strings.xml
+++ b/packages/PackageInstaller/res/values-ky/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Бул колдонмону <xliff:g id="USERNAME">%1$s</xliff:g> үчүн чыгарып саласызбы?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Бул колдонмонун баштапкы версиясы орнотулсунбу? Бардык дайындар өчүп калат."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Бул колдонмонун баштапкы версиясы орнотулсунбу? Түзмөктөгү бардык профилдердин, ошондой эле жумушчу профилдердин дайындары өчүп калат."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Ошондой эле, тиешелүү медиа файлдардын <xliff:g id="SIZE">%1$s</xliff:g> өчүрүү."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Колдонмонун <xliff:g id="SIZE">%1$s</xliff:g> дайындарын сактоо."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Чыгарылып салынууда"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Чыгарылып салынбай калгандар"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Чыгарылып салынууда…"</string>
diff --git a/packages/PackageInstaller/res/values-lo/strings.xml b/packages/PackageInstaller/res/values-lo/strings.xml
index aa55e3c..df51634 100644
--- a/packages/PackageInstaller/res/values-lo/strings.xml
+++ b/packages/PackageInstaller/res/values-lo/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"ທ່ານ​ຕ້ອງ​ການ​ຖອນ​ການ​ຕິດ​ຕັ້ງ​ແອັບ​ນີ້​ສຳ​ລັບ​ຜູ້​ໃຊ້ <xliff:g id="USERNAME">%1$s</xliff:g> ບໍ່?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"ແທນທີ່ແອັບນີ້ດ້ວຍເວີຊັນທີ່ມາຈາກໂຮງງານບໍ? ຂໍ້ມູນທັງໝົດຈະຖືກລຶບອອກ."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"ແທນທີ່ແອັບນີ້ດ້ວຍເວີຊັນທີ່ມາຈາກໂຮງງານບໍ? ຂໍ້ມູນທັງໝົດຈະຖືກລຶບອອກ ເຊິ່ງມີຜົນກັບຜູ້ໃຊ້ອຸປະກອນນີ້ທຸກຄົນ ຮວມທັງຄົນທີ່ມີໂປຣໄຟລ໌ບ່ອນເຮັດວຽກນຳ."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"ລຶບໄຟລ໌ມີເດຍ <xliff:g id="SIZE">%1$s</xliff:g> ທີ່ກ່ຽວຂ້ອງກັນອອກນຳບໍ."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"ຮັກສາຂະໜາດຂໍ້ມູນແອັບ <xliff:g id="SIZE">%1$s</xliff:g> ໄວ້."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"ກຳລັງຖອນການຕິດຕັ້ງ"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"ຖອນການຕິດຕັ້ງບໍ່ສຳເລັດ"</string>
     <string name="uninstalling" msgid="8709566347688966845">"ກຳລັງຖອນການຕິດຕັ້ງ..."</string>
diff --git a/packages/PackageInstaller/res/values-lt/strings.xml b/packages/PackageInstaller/res/values-lt/strings.xml
index aec94c4..bec4145 100644
--- a/packages/PackageInstaller/res/values-lt/strings.xml
+++ b/packages/PackageInstaller/res/values-lt/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Ar norite pašalinti šią naudotojo <xliff:g id="USERNAME">%1$s</xliff:g> programą?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Pakeisti šios programos versiją į gamyklinę? Visi duomenys bus pašalinti."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Pakeisti šios programos versiją į gamyklinę? Visi duomenys bus pašalinti. Tai paveiks visus šio įrenginio naudotojus, įskaitant turinčius darbo profilius."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Taip pat pašalinti <xliff:g id="SIZE">%1$s</xliff:g> susijusių medijos failų."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Palikti <xliff:g id="SIZE">%1$s</xliff:g> programų duomenų."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Vykdomi pašalinimai"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Nepavykę pašalinimai"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Pašalinama…"</string>
diff --git a/packages/PackageInstaller/res/values-lv/strings.xml b/packages/PackageInstaller/res/values-lv/strings.xml
index 76840cb..835af00 100644
--- a/packages/PackageInstaller/res/values-lv/strings.xml
+++ b/packages/PackageInstaller/res/values-lv/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Vai vēlaties atinstalēt šo lietotni lietotājam <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Vai aizstāt šo lietotni ar rūpnīcas versiju? Visi dati tiks noņemti."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Vai aizstāt šo lietotni ar rūpnīcas versiju? Visi dati tiks noņemti. Tas ietekmēs visu šīs ierīces lietotāju datus, pat ja viņiem ir darba profili."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Noņemt arī saistītos multivides failus (<xliff:g id="SIZE">%1$s</xliff:g>)."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Iegūstiet <xliff:g id="SIZE">%1$s</xliff:g> (lietotnes dati)."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Pašreizējās atinstalēšanas operācijas"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Nesekmīgi atinstalēšanas mēģinājumi"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Notiek atinstalēšana…"</string>
diff --git a/packages/PackageInstaller/res/values-mk/strings.xml b/packages/PackageInstaller/res/values-mk/strings.xml
index 998850e..aa94284 100644
--- a/packages/PackageInstaller/res/values-mk/strings.xml
+++ b/packages/PackageInstaller/res/values-mk/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Дали сакате да ја деинсталирате апликацијава за корисникот <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Сакате да ја замените оваа апликација со фабричката верзија? Сите податоци ќе се отстранат."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Сакате да ја замените оваа апликација со фабричката верзија? Сите податоци ќе се отстранат. Тоа важи за сите корисници на овој уред, вклучувајќи ги и тие со работни профили."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Отстрани и <xliff:g id="SIZE">%1$s</xliff:g> поврзани аудиовизуелни датотеки."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Задржи <xliff:g id="SIZE">%1$s</xliff:g> податоци на апликацијата."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Деинсталации во тек"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Неуспешни деинсталации"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Се деинсталира…"</string>
diff --git a/packages/PackageInstaller/res/values-ml/strings.xml b/packages/PackageInstaller/res/values-ml/strings.xml
index d2dbdcf..1ef8d14 100644
--- a/packages/PackageInstaller/res/values-ml/strings.xml
+++ b/packages/PackageInstaller/res/values-ml/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> എന്ന ഉപയോക്താവിനായി ഈ ആപ്പ് അൺ ഇൻസ്‌റ്റാൾ ചെയ്യണോ?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"ഫാക്‌ടറി പതിപ്പ് ഉപയോഗിച്ച് ഈ ആപ്പിന് പകരം വയ്ക്കണോ? എല്ലാ ഡാറ്റയും നീക്കം ചെയ്യപ്പെടും."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"ഫാക്‌ടറി പതിപ്പ് ഉപയോഗിച്ച് ഈ ആപ്പിന് പകരം വയ്ക്കണോ? എല്ലാ ഡാറ്റയും നീക്കം ചെയ്യപ്പെടും. ഔദ്യോഗിക പ്രൊഫൈലുകൾ ഉള്ളവർ ഉൾപ്പെടെ, ഈ ഉപകരണത്തിന്റെ എല്ലാ ഉപയോക്താക്കളെയും ഇത് ബാധിക്കും."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"ബന്ധപ്പെട്ട മീഡിയ ഫയലുകളുടെ <xliff:g id="SIZE">%1$s</xliff:g>-യും നീക്കം ചെയ്യുക."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> ആപ്പ് ഡാറ്റ വയ്ക്കുക."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"നിലവിൽ അൺഇൻസ്‌റ്റാൾ ചെയ്യുന്നവ"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"അൺ ഇൻസ്‌റ്റാൾ ചെയ്യാൻ കഴിയാഞ്ഞവ"</string>
     <string name="uninstalling" msgid="8709566347688966845">"അണ്‍‌ ഇൻസ്‌റ്റാൾ ചെയ്യുന്നു..."</string>
diff --git a/packages/PackageInstaller/res/values-mn/strings.xml b/packages/PackageInstaller/res/values-mn/strings.xml
index 56a2128..ca15978 100644
--- a/packages/PackageInstaller/res/values-mn/strings.xml
+++ b/packages/PackageInstaller/res/values-mn/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Та энэ аппыг <xliff:g id="USERNAME">%1$s</xliff:g> хэрэглэгчийн өмнөөс устгахыг хүсэж байна уу?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Энэ аппыг үйлдвэрээс ирсэн хувилбараар солих уу? Бүх өгөгдөл устах болно."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Энэ аппыг үйлдвэрээс ирсэн хувилбараар солих уу? Бүх өгөгдөл устах болно. Энэ нь эдгээр ажлын профайлтай бүхий энэ төхөөрөмжийн бүх хэрэглэгчид нөлөөлнө."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Холбоотой медиа файлуудын <xliff:g id="SIZE">%1$s</xliff:g>-г мөн адил устгана уу."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Аппын өгөгдлийн <xliff:g id="SIZE">%1$s</xliff:g>-г үлдээнэ үү."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Устгаж байна"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Устгаж чадсангүй"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Устгаж байна…"</string>
diff --git a/packages/PackageInstaller/res/values-mr/strings.xml b/packages/PackageInstaller/res/values-mr/strings.xml
index 01f3e5b..2182631 100644
--- a/packages/PackageInstaller/res/values-mr/strings.xml
+++ b/packages/PackageInstaller/res/values-mr/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"तुम्हाला <xliff:g id="USERNAME">%1$s</xliff:g> वापरकर्त्यासाठी हे अ‍ॅप अनइंस्टॉल करायचे आहे का?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"फॅक्टरी आवृत्तीसह हे अ‍ॅप बदलायचे का? सर्व डेटा काढला जाईल."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"फॅक्टरी आवृत्तीसह हे अ‍ॅप बदलायचे? सर्व डेटा काढला जाईल. हे कार्य प्रोफाइल असलेल्यांसह या डिव्हाइसच्या सर्व वापरकर्त्यांना प्रभावित करते."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"<xliff:g id="SIZE">%1$s</xliff:g> आकाराच्या संबंधित मीडिया फायलीदेखील काढा."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"अॅप डेटा पैकी <xliff:g id="SIZE">%1$s</xliff:g> ठेवा."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"अनइंस्टॉल रन होत आहेत"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"अनइंस्टॉल करता आले नाही"</string>
     <string name="uninstalling" msgid="8709566347688966845">"अनइंस्टॉल करत आहे…"</string>
diff --git a/packages/PackageInstaller/res/values-ms/strings.xml b/packages/PackageInstaller/res/values-ms/strings.xml
index 674f55b..50e00d6 100644
--- a/packages/PackageInstaller/res/values-ms/strings.xml
+++ b/packages/PackageInstaller/res/values-ms/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Adakah anda ingin menyahpasang apl ini untuk pengguna <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Gantikan apl ini dengan versi kilang? Semua data akan dialih keluar."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Gantikan apl ini dengan versi kilang? Semua data akan dialih keluar. Tindakan ini melibatkan semua pengguna peranti ini, termasuk mereka yang mempunyai profil kerja."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Alih keluar juga <xliff:g id="SIZE">%1$s</xliff:g> fail media yang berkaitan."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Simpan <xliff:g id="SIZE">%1$s</xliff:g> data apl."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Penyahpasangan yang sedang berjalan"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Penyahpasangan yang gagal"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Menyahpasang…"</string>
diff --git a/packages/PackageInstaller/res/values-my/strings.xml b/packages/PackageInstaller/res/values-my/strings.xml
index 9c92957..31c3f93 100644
--- a/packages/PackageInstaller/res/values-my/strings.xml
+++ b/packages/PackageInstaller/res/values-my/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"သင်သည် အသုံးပြုသူ <xliff:g id="USERNAME">%1$s</xliff:g> အတွက် ဤအကောင့်ကို ဖယ်ရှားလိုပါသလား။"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"ဤအက်ပ်ကို စက်ရုံထုတ်ဗားရှင်းဖြင့် အစားထိုးလိုပါသလား။ ဒေတာများအားလုံးကို ဖယ်ရှားလိုက်ပါမည်။"</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"ဤအက်ပ်ကို စက်ရုံထုတ်ဗားရှင်းဖြင့် အစားထိုးလိုသလား။ ဒေတာများအားလုံးကို ဖယ်ရှားလိုက်ပါမည်။ ၎င်းသည် အလုပ်ပရိုဖိုင်ဖြင့်သုံးသူများအပါအဝင် အသုံးပြုသူများအားလုံးကို အကျိုးသက်ရောက်စေပါမည်။"</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"သက်ဆိုင်ရာ မီဒီယာဖိုင် <xliff:g id="SIZE">%1$s</xliff:g> ကိုလည်း ဖယ်ရှားပါ။"</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"အက်ပ်ဒေတာများ၏ <xliff:g id="SIZE">%1$s</xliff:g> ကို ထားရှိရန်။"</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"ပရိုဂရမ်ကို ဖယ်ရှားနေပါသည်"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"ပရိုဂရမ်ကို ဖယ်ရှား၍မရပါ"</string>
     <string name="uninstalling" msgid="8709566347688966845">"ပရိုဂရမ်ကို ဖယ်ရှားနေသည်..."</string>
diff --git a/packages/PackageInstaller/res/values-nb/strings.xml b/packages/PackageInstaller/res/values-nb/strings.xml
index 0d3384e..34c20a1 100644
--- a/packages/PackageInstaller/res/values-nb/strings.xml
+++ b/packages/PackageInstaller/res/values-nb/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Ønsker du å avinstallere denne appen for brukeren <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Vil du erstatte denne appen med den opprinnelige versjonen? Alle dataene fjernes."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Vil du erstatte denne appen med den opprinnelige versjonen? Alle dataene fjernes. Dette påvirker alle som bruker denne enheten – også personer med jobbprofiler."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Fjern også <xliff:g id="SIZE">%1$s</xliff:g> med tilknyttede mediefiler."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Behold <xliff:g id="SIZE">%1$s</xliff:g> med appdata."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Avinstalleringer som er i gang"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Mislykkede avinstalleringer"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Avinstallerer …"</string>
diff --git a/packages/PackageInstaller/res/values-ne/strings.xml b/packages/PackageInstaller/res/values-ne/strings.xml
index a7abc17..e55afd5 100644
--- a/packages/PackageInstaller/res/values-ne/strings.xml
+++ b/packages/PackageInstaller/res/values-ne/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"तपाईं प्रयोगकर्ता <xliff:g id="USERNAME">%1$s</xliff:g> का लागि यो अनुप्रयोगको स्थापना रद्द गर्न चाहनुहुन्छ?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"यस अनुप्रयोगलाई फ्याक्ट्रीको संस्करणले बदल्ने हो? सबै डेटा हटाइने छ।"</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"यस अनुप्रयोगलाई फ्याक्ट्रीको संस्करणले बदल्ने हो? सबै डेटा हटाइने छ। यसले यस यन्त्रका कार्य प्रोफाइल भएका लगायत सबै प्रयोगकर्ताहरूमा असर पार्छ।"</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"यसका साथै <xliff:g id="SIZE">%1$s</xliff:g> सम्बद्ध मिडिया फाइलहरू पनि हटाउनुहोस्।"</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> अनुप्रयोगको डेटा राख्नुहोस्।"</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"चलिरहेका स्थापना रद्द गर्ने कार्यहरू"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"असफल भएका स्थापना रद्द गर्ने कार्यहरू"</string>
     <string name="uninstalling" msgid="8709566347688966845">"स्थापना रद्द गर्दै…"</string>
diff --git a/packages/PackageInstaller/res/values-nl/strings.xml b/packages/PackageInstaller/res/values-nl/strings.xml
index a7a5a34..8423fda 100644
--- a/packages/PackageInstaller/res/values-nl/strings.xml
+++ b/packages/PackageInstaller/res/values-nl/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Wil je deze app verwijderen voor de gebruiker <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Deze app vervangen door de fabrieksversie? Alle gegevens worden verwijderd."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Deze app vervangen door de fabrieksversie? Alle gegevens worden verwijderd. Dit geldt voor alle gebruikers van het apparaat, dus ook voor gebruikers met een werkprofiel."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Ook <xliff:g id="SIZE">%1$s</xliff:g> aan gekoppelde mediabestanden verwijderen."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> aan app-gegevens behouden."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Actieve verwijderingen"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Mislukte verwijderingen"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Verwijderen…"</string>
diff --git a/packages/PackageInstaller/res/values-or/strings.xml b/packages/PackageInstaller/res/values-or/strings.xml
index 348cc61..2fcf70e 100644
--- a/packages/PackageInstaller/res/values-or/strings.xml
+++ b/packages/PackageInstaller/res/values-or/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"ୟୁଜର୍‌ <xliff:g id="USERNAME">%1$s</xliff:g>ଙ୍କ ପାଇଁ ଆପଣ ଏହି ଆପ୍‍ ଇନଷ୍ଟଲ୍‍ କରିବେ କି?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"ଏହି ଆପ୍‍ ଫ୍ୟାକ୍ଟୋରୀ ଭର୍ସନ୍‍‍ ସହ ବଦଳାଇବେ? ସମସ୍ତ ଡାଟା କାଢ଼ିଦିଆଯିବ।"</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"ଏହି ଆପ୍‍ ଫ୍ୟାକ୍ଟୋରୀ ଭର୍ସନ୍‍‍ ସହ ବଦଳାଇବେ? ସମସ୍ତ ଡାଟା ବାହାର କରିଦିଆଯିବ। ୱର୍କ ପ୍ରୋଫାଇଲ୍‍ ଥିବା ସମେତ, ଏହାଦ୍ୱାରା ଡିଭାଇସରେ ଥିବା ସମସ୍ତ ୟୁଜର୍‌ ପ୍ରଭାବିତ ହେବେ।"</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"<xliff:g id="SIZE">%1$s</xliff:g> ର ସମ୍ବନ୍ଧିତ ମିଡିଆ ଫାଇଲ୍‌ଗୁଡ଼ିକ ମଧ୍ୟ କାଢନ୍ତୁ।"</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> ଆକାରର ଆପ୍‍ ଡାଟା ରଖନ୍ତୁୁ।"</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"ଅନଇନଷ୍ଟଲ୍‌ ଚାଲୁଛି"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"ବିଫଳ ହୋଇଥିବା ଅନଇନଷ୍ଟଲ୍‌"</string>
     <string name="uninstalling" msgid="8709566347688966845">"ଅନ୍‌ଇନଷ୍ଟଲ୍‌ କରାଯାଉଛି…"</string>
diff --git a/packages/PackageInstaller/res/values-pa/strings.xml b/packages/PackageInstaller/res/values-pa/strings.xml
index 7c519a0..ca8f73b 100644
--- a/packages/PackageInstaller/res/values-pa/strings.xml
+++ b/packages/PackageInstaller/res/values-pa/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"ਕੀ ਤੁਸੀਂ ਵਰਤੋਂਕਾਰ <xliff:g id="USERNAME">%1$s</xliff:g> ਲਈ ਇਸ ਐਪ ਨੂੰ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"ਕੀ ਇਸ ਐਪ ਨੂੰ ਫੈਕਟਰੀ ਵਰਜਨ ਨਾਲ ਬਦਲਣਾ ਹੈ? ਸਾਰਾ ਡਾਟਾ ਹਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"ਕੀ ਇਸ ਐਪ ਨੂੰ ਫੈਕਟਰੀ ਵਰਜਨ ਨਾਲ ਬਦਲਣਾ ਹੈ? ਸਾਰਾ ਡਾਟਾ ਹਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ। ਇਹ ਇਸ ਡੀਵਾਈਸ ਦੇ ਸਾਰੇ ਵਰਤੋਂਕਾਰਾਂ ਨੂੰ ਪ੍ਰਭਾਵਿਤ ਕਰੇਗਾ, ਜਿਸ ਵਿੱਚ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਵਾਲੇ ਵਰਤੋਂਕਾਰ ਵੀ ਸ਼ਾਮਲ ਹਨ।"</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"<xliff:g id="SIZE">%1$s</xliff:g> ਦੀਆਂ ਸੰਬੰਧਿਤ ਮੀਡੀਆ ਫ਼ਾਈਲਾਂ ਵੀ ਹਟਾਓ।"</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> ਐਪ ਡਾਟਾ ਰੱਖੋ।"</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"ਚੱਲ ਰਹੀਆਂ ਅਣਸਥਾਪਨਾਵਾਂ"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"ਅਸਫਲ ਅਣਸਥਾਪਨਾਵਾਂ"</string>
     <string name="uninstalling" msgid="8709566347688966845">"ਅਣਸਥਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
diff --git a/packages/PackageInstaller/res/values-pl/strings.xml b/packages/PackageInstaller/res/values-pl/strings.xml
index 19b05a9..6bcf20a 100644
--- a/packages/PackageInstaller/res/values-pl/strings.xml
+++ b/packages/PackageInstaller/res/values-pl/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Chcesz odinstalować tę aplikację dla użytkownika <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Przywrócić fabryczną wersję tej aplikacji? Wszystkie dane zostaną usunięte."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Przywrócić fabryczną wersję tej aplikacji? Wszystkie dane zostaną usunięte. Dotyczy to wszystkich użytkowników tego urządzenia, również tych korzystających z profilu do pracy."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Usuń też <xliff:g id="SIZE">%1$s</xliff:g> powiązanych plików multimedialnych."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Zachowaj <xliff:g id="SIZE">%1$s</xliff:g> danych aplikacji."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Aktywne odinstalowania"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Nieudane odinstalowania"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Odinstalowuję…"</string>
diff --git a/packages/PackageInstaller/res/values-pt-rBR/strings.xml b/packages/PackageInstaller/res/values-pt-rBR/strings.xml
index 1a31837..7a0c7fb 100644
--- a/packages/PackageInstaller/res/values-pt-rBR/strings.xml
+++ b/packages/PackageInstaller/res/values-pt-rBR/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Quer desinstalar este app para o usuário <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Substituir este app pela versão de fábrica? Todos os dados serão removidos."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Substituir este app pela versão de fábrica? Todos os dados serão removidos. Isso afeta todos os usuários deste dispositivo, incluindo aqueles com perfis de trabalho."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Remover também <xliff:g id="SIZE">%1$s</xliff:g> de arquivos de mídia associados."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Manter <xliff:g id="SIZE">%1$s</xliff:g> de dados do app."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Executando desinstalações"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Falha nas desinstalações"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Desinstalando…"</string>
diff --git a/packages/PackageInstaller/res/values-pt-rPT/strings.xml b/packages/PackageInstaller/res/values-pt-rPT/strings.xml
index e27fed5..903db3e 100644
--- a/packages/PackageInstaller/res/values-pt-rPT/strings.xml
+++ b/packages/PackageInstaller/res/values-pt-rPT/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Pretende desinstalar esta aplicação para o utilizador <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Pretende substituir esta aplicação pela versão de fábrica? Todos os dados são removidos."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Pretende substituir esta aplicação pela versão de fábrica? Todos os dados são removidos. Esta ação afeta todos os utilizadores deste dispositivo, incluindo os que têm perfis de trabalho."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Remover também <xliff:g id="SIZE">%1$s</xliff:g> de ficheiros multimédia associados."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Manter <xliff:g id="SIZE">%1$s</xliff:g> de dados da aplicação."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Desinstalações em execução"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Desinstalações com falha"</string>
     <string name="uninstalling" msgid="8709566347688966845">"A desinstalar…"</string>
diff --git a/packages/PackageInstaller/res/values-pt/strings.xml b/packages/PackageInstaller/res/values-pt/strings.xml
index 1a31837..7a0c7fb 100644
--- a/packages/PackageInstaller/res/values-pt/strings.xml
+++ b/packages/PackageInstaller/res/values-pt/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Quer desinstalar este app para o usuário <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Substituir este app pela versão de fábrica? Todos os dados serão removidos."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Substituir este app pela versão de fábrica? Todos os dados serão removidos. Isso afeta todos os usuários deste dispositivo, incluindo aqueles com perfis de trabalho."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Remover também <xliff:g id="SIZE">%1$s</xliff:g> de arquivos de mídia associados."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Manter <xliff:g id="SIZE">%1$s</xliff:g> de dados do app."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Executando desinstalações"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Falha nas desinstalações"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Desinstalando…"</string>
diff --git a/packages/PackageInstaller/res/values-ro/strings.xml b/packages/PackageInstaller/res/values-ro/strings.xml
index 8bcc969..620a382 100644
--- a/packages/PackageInstaller/res/values-ro/strings.xml
+++ b/packages/PackageInstaller/res/values-ro/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Dezinstalați această aplicație pentru utilizatorul <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Înlocuiți această aplicație cu versiunea din fabrică? Toate datele vor fi eliminate."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Înlocuiți această aplicație cu versiunea din fabrică? Toate datele vor fi eliminate. Această acțiune va afecta toți utilizatorii dispozitivului, inclusiv pe cei cu profiluri de serviciu."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Eliminați și <xliff:g id="SIZE">%1$s</xliff:g> de fișiere media asociate."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Păstrează <xliff:g id="SIZE">%1$s</xliff:g> din datele aplicației."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Dezinstalări în curs"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Dezinstalări nereușite"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Se dezinstalează…"</string>
diff --git a/packages/PackageInstaller/res/values-ru/strings.xml b/packages/PackageInstaller/res/values-ru/strings.xml
index 4e5d1ac..5aba2fa 100644
--- a/packages/PackageInstaller/res/values-ru/strings.xml
+++ b/packages/PackageInstaller/res/values-ru/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Удалить это приложение из профиля <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Установить исходную версию приложения? Все его данные будут удалены."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Установить исходную версию приложения? Его данные будут удалены из всех профилей устройства, в том числе рабочих."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Также удалить связанные медиафайлы (<xliff:g id="SIZE">%1$s</xliff:g>)"</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Сохранить данные приложения (<xliff:g id="SIZE">%1$s</xliff:g>)"</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Активные процессы удаления"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Ошибки удаления"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Удаление…"</string>
diff --git a/packages/PackageInstaller/res/values-si/strings.xml b/packages/PackageInstaller/res/values-si/strings.xml
index 7d854dc..7f67a64 100644
--- a/packages/PackageInstaller/res/values-si/strings.xml
+++ b/packages/PackageInstaller/res/values-si/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> පරිශීලකයා සඳහා මෙම යෙදුම අස්ථාපනය කිරීමට ඔබට අවශ්‍යයද?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"මෙම යෙදුම කර්මාන්ත ශාලා අනුවාදයක් සමගින් ප්‍රතිස්ථාපනය කරන්නද? සියලු දත්ත ඉවත් කරනු ඇත."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"මෙම යෙදුම කර්මාන්ත ශාලා අනුවාදයක් සමගින් ප්‍රතිස්ථාපනය කරන්නද? සියලු දත්ත ඉවත් කරනු ඇත. මෙය කාර්යාල පැතිකඩවල් සහිත අය ඇතුළුව, මෙම උපාංගයෙහි සියලු පරිශීලකයන් වෙත බලපානු ඇත."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"ආශ්‍රිත මාධ්‍ය ගොනු <xliff:g id="SIZE">%1$s</xliff:g> ද ඉවත් කරන්න."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"යෙදුම් දත්තවලින් <xliff:g id="SIZE">%1$s</xliff:g> තබා ගන්න."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"අස්ථාපන ධාවනය කරමින්"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"අසාර්ථක වූ අස්ථාපන"</string>
     <string name="uninstalling" msgid="8709566347688966845">"අස්ථාපනය කරමින්…"</string>
diff --git a/packages/PackageInstaller/res/values-sk/strings.xml b/packages/PackageInstaller/res/values-sk/strings.xml
index 36614bc..509dac5 100644
--- a/packages/PackageInstaller/res/values-sk/strings.xml
+++ b/packages/PackageInstaller/res/values-sk/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Chcete túto aplikáciu odinštalovať pre používateľa <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Nahradiť túto aplikáciu výrobnou verziou? Všetky údaje sa odstránia."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Nahradiť túto aplikáciu výrobnou verziou? Všetky údaje sa odstránia. Ovplyvní to všetkých používateľov tohto zariadenia vrátane tých s pracovnými profilmi."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Odstrániť tiež <xliff:g id="SIZE">%1$s</xliff:g> prepojených súborov médií."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Zachovať nasledujúcu veľkosť dát aplikácie: <xliff:g id="SIZE">%1$s</xliff:g>."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Prebiehajúce odinštalovania"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Neúspešné odinštalácie"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Prebieha odinštalovanie..."</string>
diff --git a/packages/PackageInstaller/res/values-sl/strings.xml b/packages/PackageInstaller/res/values-sl/strings.xml
index a679e08..6cd7d1b 100644
--- a/packages/PackageInstaller/res/values-sl/strings.xml
+++ b/packages/PackageInstaller/res/values-sl/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Ali želite to aplikacijo odstraniti za uporabnika <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Želite to aplikacijo nadomestiti s tovarniško različico? Odstranjeni bodo vsi podatki."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Želite to aplikacijo nadomestiti s tovarniško različico? Odstranjeni bodo vsi podatki. To vpliva na vse uporabnike te naprave, vključno s tistimi z delovnimi profili."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Odstranite tudi <xliff:g id="SIZE">%1$s</xliff:g> povezanih predstavnostnih datotek."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Obdrži <xliff:g id="SIZE">%1$s</xliff:g> podatkov aplikacije."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Odstranitve v teku"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Neuspele odstranitve"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Odstranjevanje …"</string>
diff --git a/packages/PackageInstaller/res/values-sq/strings.xml b/packages/PackageInstaller/res/values-sq/strings.xml
index dd35024..2a59288 100644
--- a/packages/PackageInstaller/res/values-sq/strings.xml
+++ b/packages/PackageInstaller/res/values-sq/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Dëshiron ta çinstalosh këtë aplikacion për përdoruesin <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Të zëvendësohet ky aplikacion me versionin e fabrikës? Të gjitha të dhënat do të hiqen."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Të zëvendësohet ky aplikacion me versionin e fabrikës? Të gjitha të dhënat do të hiqen. Kjo ndikon te të gjithë përdoruesit e kësaj pajisjeje, duke përfshirë ata me profile të punës."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Hiq gjithashtu <xliff:g id="SIZE">%1$s</xliff:g> skedarë media të shoqëruar."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Mbaj <xliff:g id="SIZE">%1$s</xliff:g> nga të dhënat e aplikacionit."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Çinstalimet në ekzekutim"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Çinstalimet e dështuara"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Po çinstalohet…"</string>
diff --git a/packages/PackageInstaller/res/values-sr/strings.xml b/packages/PackageInstaller/res/values-sr/strings.xml
index c25b8e0f..7d889c4 100644
--- a/packages/PackageInstaller/res/values-sr/strings.xml
+++ b/packages/PackageInstaller/res/values-sr/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Желите ли да деинсталирате ову апликацију за корисника <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Желите ли да замените ову апликацију фабричком верзијом? Сви подаци ће бити уклоњени."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Желите ли да замените ову апликацију фабричком верзијом? Сви подаци ће бити уклоњени. Ово утиче на све кориснике овог уређаја, укључујући и оне са профилима за Work."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Уклони и <xliff:g id="SIZE">%1$s</xliff:g> повезаних медијских датотека."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Задржи <xliff:g id="SIZE">%1$s</xliff:g> података апликације."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Активна деинсталирања"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Неуспела деинсталирања"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Деинсталира се…"</string>
diff --git a/packages/PackageInstaller/res/values-sv/strings.xml b/packages/PackageInstaller/res/values-sv/strings.xml
index 0c66b53c..9902bda 100644
--- a/packages/PackageInstaller/res/values-sv/strings.xml
+++ b/packages/PackageInstaller/res/values-sv/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Vill du avinstallera appen för användaren <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Vill du ersätta den här appen med den version som var installerad när enheten var ny? All information tas bort."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Vill du ersätta den här appen med den version som var installerad när enheten var ny? All information tas bort. Detta påverkar alla som använder enheten, även dem med jobbprofiler."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Ta även bort <xliff:g id="SIZE">%1$s</xliff:g> tillhörande mediefiler."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Behåll <xliff:g id="SIZE">%1$s</xliff:g> appdata."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Avinstallationer som pågår"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Avinstallationer som misslyckats"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Avinstallerar …"</string>
diff --git a/packages/PackageInstaller/res/values-sw/strings.xml b/packages/PackageInstaller/res/values-sw/strings.xml
index 4557f66..b4c2d83 100644
--- a/packages/PackageInstaller/res/values-sw/strings.xml
+++ b/packages/PackageInstaller/res/values-sw/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Je, ungependa kuondoa programu hii kwa mtumiaji <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Ungependa kubadilisha programu hii na toleo la kiwandani? Data yote itaondolewa."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Ungependa kubadilisha programu hii na toleo la kiwandani? Data yote itaondolewa. Hatua hii itaathiri watumiaji wote wa kifaa hiki, ikiwa ni pamoja na wale walio na wasifu za kazini."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Ondoa pia faili <xliff:g id="SIZE">%1$s</xliff:g> zinazohusiana za maudhui."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Dumisha <xliff:g id="SIZE">%1$s</xliff:g> ya data ya programu."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Programu zinazoondolewa"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Mara ambazo programu haikuondolewa"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Inaondoa…"</string>
diff --git a/packages/PackageInstaller/res/values-ta/strings.xml b/packages/PackageInstaller/res/values-ta/strings.xml
index dab459a..b69ce16 100644
--- a/packages/PackageInstaller/res/values-ta/strings.xml
+++ b/packages/PackageInstaller/res/values-ta/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> என்ற பயனருக்கு இந்த ஆப்ஸை நிறுவல் நீக்க விரும்புகிறீர்களா?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"ஆரம்பநிலைப் பதிப்புக்கு இந்த ஆப்ஸை மாற்றியமைக்கவா? அனைத்துத் தரவும் அகற்றப்படும்."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"ஆரம்பநிலைப் பதிப்புக்கு இந்த ஆப்ஸை மாற்றியமைக்கவா? அனைத்துத் தரவும் அகற்றப்படும். பணிக் கணக்குகளுடன் உள்ளவர்கள் உட்பட இந்தச் சாதனத்தின் அனைத்துப் பயனர்களையும் இது பாதிக்கும்."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"இதனுடன் தொடர்புடைய <xliff:g id="SIZE">%1$s</xliff:g> மீடியா ஃபைல்களையும் அகற்று."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> ஆப்ஸ் தரவை வைத்திரு."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"இயக்கத்திலுள்ள நிறுவல் நீக்கங்கள்"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"தோல்வியுற்ற நிறுவல் நீக்கங்கள்"</string>
     <string name="uninstalling" msgid="8709566347688966845">"நிறுவல் நீக்குகிறது…"</string>
diff --git a/packages/PackageInstaller/res/values-te/strings.xml b/packages/PackageInstaller/res/values-te/strings.xml
index 64c5da7..e13b893 100644
--- a/packages/PackageInstaller/res/values-te/strings.xml
+++ b/packages/PackageInstaller/res/values-te/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"మీరు వినియోగదారు <xliff:g id="USERNAME">%1$s</xliff:g> కోసం ఈ యాప్‌ను అన్‌ఇన్‌స్టాల్ చేయాలనుకుంటున్నారా?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"ఈ యాప్‌ను ఫ్యాక్టరీ వెర్షన్‌తో భర్తీ చేయాలా? మొత్తం డేటా తీసివేయబడుతుంది."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"ఈ యాప్‌ను ఫ్యాక్టరీ వెర్షన్‌తో భర్తీ చేయాలా? మొత్తం డేటా తీసివేయబడుతుంది. దీని ప్రభావం కార్యాలయ ప్రొఫైల్‌లు కలిగి ఉన్నవారితో సహా ఈ పరికర వినియోగదారులందరిపై ఉంటుంది."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"అలాగే దానికి సంబంధించిన మీడియా ఫైల్‌ల డేటా <xliff:g id="SIZE">%1$s</xliff:g>ని కూడా తీసివేయి"</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> యాప్ డేటాని ఉంచండి."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"అన్ఇన్‌స్టాల్ చేయబడుతున్నవి"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"విఫలమైన అన్‌ఇన్‌స్టాల్‌లు"</string>
     <string name="uninstalling" msgid="8709566347688966845">"అన్ఇన్‌స్టాల్ చేస్తోంది…"</string>
diff --git a/packages/PackageInstaller/res/values-th/strings.xml b/packages/PackageInstaller/res/values-th/strings.xml
index b6595d0..272571c 100644
--- a/packages/PackageInstaller/res/values-th/strings.xml
+++ b/packages/PackageInstaller/res/values-th/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"ต้องการถอนการติดตั้งแอปนี้สำหรับผู้ใช้ <xliff:g id="USERNAME">%1$s</xliff:g> ไหม"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"แทนที่แอปนี้ด้วยเวอร์ชันเริ่มต้นไหม ระบบจะนำข้อมูลทั้งหมดออก"</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"แทนที่แอปนี้ด้วยเวอร์ชันเริ่มต้นไหม ระบบจะนำข้อมูลทั้งหมดออก วิธีนี้จะส่งผลต่อผู้ใช้ทุกคนที่ใช้อุปกรณ์เครื่องนี้ รวมทั้งผู้ที่มีโปรไฟล์งาน"</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"และนำไฟล์สื่อต่างๆ ที่เกี่ยวข้องซึ่งรวมแล้วมีขนาด <xliff:g id="SIZE">%1$s</xliff:g> ออกด้วย"</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"เก็บข้อมูลแอปไว้ <xliff:g id="SIZE">%1$s</xliff:g>"</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"กำลังเรียกใช้การถอนการติดตั้ง"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"ถอนการติดตั้งไม่สำเร็จ"</string>
     <string name="uninstalling" msgid="8709566347688966845">"กำลังถอนการติดตั้ง…"</string>
diff --git a/packages/PackageInstaller/res/values-tl/strings.xml b/packages/PackageInstaller/res/values-tl/strings.xml
index e7b15fa..903b908 100644
--- a/packages/PackageInstaller/res/values-tl/strings.xml
+++ b/packages/PackageInstaller/res/values-tl/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Gusto mo bang i-uninstall ang app na ito para sa user na si <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Gusto mo bang palitan ang app na ito ng factory na bersyon? Maaalis ang lahat ng data."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Gusto mo bang palitan ang app na ito ng factory na bersyon? Maaalis ang lahat ng data. Nakakaapekto ito sa lahat ng user ng device na ito, kabilang ang mga may profile sa trabaho."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Alisin din ang <xliff:g id="SIZE">%1$s</xliff:g> ng mga nauugnay na media file."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Panatilihin ang <xliff:g id="SIZE">%1$s</xliff:g> ng data ng app."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Mga kasalukuyang pag-uninstall"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Mga hindi na-uninstall"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Ina-uninstall…"</string>
diff --git a/packages/PackageInstaller/res/values-tr/strings.xml b/packages/PackageInstaller/res/values-tr/strings.xml
index 33dbc3a9..e372e11 100644
--- a/packages/PackageInstaller/res/values-tr/strings.xml
+++ b/packages/PackageInstaller/res/values-tr/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> adlı kullanıcı için bu uygulamanın yüklemesini kaldırmak istiyor musunuz?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Bu uygulamayı fabrika sürümüyle değiştirmek istiyor musunuz? Tüm veriler silinecektir."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Bu uygulamayı fabrika sürümüyle değiştirmek istiyor musunuz? Tüm veriler silinecektir. Bu, çalışma profilleri olan kullanıcılar da dahil olmak üzere cihazı kullanan tüm kullanıcıları etkiler."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"<xliff:g id="SIZE">%1$s</xliff:g> boyutundaki ilişkili medya dosyalarını da kaldır."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Uygulama verilerinin <xliff:g id="SIZE">%1$s</xliff:g> kadarını sakla."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Devam eden yükleme kaldırma işlemleri"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Başarısız yükleme kaldırma işlemleri"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Yükleme kaldırılıyor…"</string>
diff --git a/packages/PackageInstaller/res/values-uk/strings.xml b/packages/PackageInstaller/res/values-uk/strings.xml
index d9cb954..f04ead6 100644
--- a/packages/PackageInstaller/res/values-uk/strings.xml
+++ b/packages/PackageInstaller/res/values-uk/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Видалити цей додаток для користувача <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Відновити заводську версію цього додатка? Усі дані буде видалено."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Відновити заводську версію цього додатка? Усі дані буде видалено. Це вплине на всіх користувачів цього пристрою, зокрема на користувачів із робочими профілями."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Видалити пов’язані файли розміром у <xliff:g id="SIZE">%1$s</xliff:g>"</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Залишити <xliff:g id="SIZE">%1$s</xliff:g> даних додатка."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Активні видалення"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Невиконані видалення"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Видалення..."</string>
diff --git a/packages/PackageInstaller/res/values-ur/strings.xml b/packages/PackageInstaller/res/values-ur/strings.xml
index 61b1579..c989a7c 100644
--- a/packages/PackageInstaller/res/values-ur/strings.xml
+++ b/packages/PackageInstaller/res/values-ur/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"کیا آپ اس ایپ کو صارف <xliff:g id="USERNAME">%1$s</xliff:g> کیلئے اَن انسٹال کرنا چاہتے ہیں؟"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"اس ایپ کو فیکٹری ورژن سے تبدیل کریں؟ تمام ڈیٹا ہٹا دیا جائے گا۔"</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"اس ایپ کو فیکٹری ورژن سے تبدیل کریں؟ تمام ڈیٹا ہٹا دیا جائے گا۔ اس سے دفتری پروفائلز کے حاملین سمیت اس آلہ کے تمام صارفین متاثر ہوں گے۔"</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"<xliff:g id="SIZE">%1$s</xliff:g> منسلکہ میڈیا فائلز بھی حذف کریں۔"</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"ایپ ڈیٹا کا <xliff:g id="SIZE">%1$s</xliff:g> رکھیں۔"</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"چل رہے اَن انسٹالز"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"ناکام اَن انسٹالز"</string>
     <string name="uninstalling" msgid="8709566347688966845">"اَن انسٹال ہو رہا ہے…"</string>
diff --git a/packages/PackageInstaller/res/values-uz/strings.xml b/packages/PackageInstaller/res/values-uz/strings.xml
index dedf00c..3aad35d 100644
--- a/packages/PackageInstaller/res/values-uz/strings.xml
+++ b/packages/PackageInstaller/res/values-uz/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Haqiqatdan ham <xliff:g id="USERNAME">%1$s</xliff:g> foydalanuvchi uchun ushbu ilovani olib tashlamoqchimisiz?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Bu ilova boshlang‘ich versiyasi bilan almashtirilsinmi? Barcha axborotlar o‘chirib tashlanadi."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Bu ilova boshlang‘ich versiyasi bilan almashtirilsinmi? Barcha axborotlar o‘chirib tashlanadi. Bu qurilmaning barcha foydalanuvchilariga, jumladan, ularning ishchi profillariga ham ta’sir qiladi."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Shuningdek, biriktirilgan media fayllarni ham olib tashlash (<xliff:g id="SIZE">%1$s</xliff:g>)."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> hajmdagi ilova axborotlari saqlab qolinsin"</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Davom etayotganlar"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Amalga oshmaganlar"</string>
     <string name="uninstalling" msgid="8709566347688966845">"O‘chirilmoqda…"</string>
diff --git a/packages/PackageInstaller/res/values-vi/strings.xml b/packages/PackageInstaller/res/values-vi/strings.xml
index 7043093..84c02e2 100644
--- a/packages/PackageInstaller/res/values-vi/strings.xml
+++ b/packages/PackageInstaller/res/values-vi/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Bạn có muốn gỡ cài đặt ứng dụng này cho người dùng <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Thay thế ứng dụng này bằng phiên bản gốc? Tất cả dữ liệu sẽ bị xóa."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Thay thế ứng dụng này bằng phiên bản gốc? Tất cả dữ liệu sẽ bị xóa. Điều này ảnh hưởng đến tất cả người dùng thiết bị này, bao gồm cả những người có hồ sơ công việc."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Đồng thời xóa <xliff:g id="SIZE">%1$s</xliff:g> tệp đa phương tiện liên kết."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Giữ lại <xliff:g id="SIZE">%1$s</xliff:g> dữ liệu ứng dụng."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Các quá trình gỡ cài đặt đang chạy"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Gỡ cài đặt không thành công"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Đang gỡ cài đặt..."</string>
diff --git a/packages/PackageInstaller/res/values-zh-rCN/strings.xml b/packages/PackageInstaller/res/values-zh-rCN/strings.xml
index a91f00f..04d6c37 100644
--- a/packages/PackageInstaller/res/values-zh-rCN/strings.xml
+++ b/packages/PackageInstaller/res/values-zh-rCN/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"要为用户<xliff:g id="USERNAME">%1$s</xliff:g>卸载此应用吗?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"要将此应用替换为出厂版本吗?这样会移除所有数据。"</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"要将此应用替换为出厂版本吗?这样会移除所有数据,并会影响此设备的所有用户(包括已设置工作资料的用户)。"</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"同时移除 <xliff:g id="SIZE">%1$s</xliff:g> 的关联媒体文件。"</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"保留 <xliff:g id="SIZE">%1$s</xliff:g> 的应用数据。"</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"正在进行卸载操作"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"卸载操作失败"</string>
     <string name="uninstalling" msgid="8709566347688966845">"正在卸载…"</string>
diff --git a/packages/PackageInstaller/res/values-zh-rHK/strings.xml b/packages/PackageInstaller/res/values-zh-rHK/strings.xml
index 286e360..01440ae 100644
--- a/packages/PackageInstaller/res/values-zh-rHK/strings.xml
+++ b/packages/PackageInstaller/res/values-zh-rHK/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"您要為使用者<xliff:g id="USERNAME">%1$s</xliff:g>解除安裝此應用程式嗎?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"要將此應用程式回復至原廠版本嗎?系統會移除所有資料。"</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"要將此應用程式回復至原廠版本嗎?系統會移除所有資料。此裝置的所有使用者 (包括使用工作設定檔的使用者) 亦會受影響。"</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"同時移除相關的媒體檔案 (<xliff:g id="SIZE">%1$s</xliff:g>)。"</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"保留應用程式資料 (<xliff:g id="SIZE">%1$s</xliff:g>)。"</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"正在執行的解除安裝操作"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"失敗的解除安裝操作"</string>
     <string name="uninstalling" msgid="8709566347688966845">"正在解除安裝…"</string>
diff --git a/packages/PackageInstaller/res/values-zh-rTW/strings.xml b/packages/PackageInstaller/res/values-zh-rTW/strings.xml
index b0de2c25..048aa9c 100644
--- a/packages/PackageInstaller/res/values-zh-rTW/strings.xml
+++ b/packages/PackageInstaller/res/values-zh-rTW/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"要為使用者 <xliff:g id="USERNAME">%1$s</xliff:g> 解除安裝這個應用程式嗎?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"要將應用程式換成原廠版本嗎?這麼做會移除所有資料。"</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"要將應用程式換成原廠版本嗎?這麼做會移除所有資料。凡是這個裝置的使用者 (包括設置工作資料夾的使用者),皆會受到影響。"</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"一併移除 <xliff:g id="SIZE">%1$s</xliff:g> 的相關媒體檔案。"</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"保留 <xliff:g id="SIZE">%1$s</xliff:g> 的應用程式資料。"</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"執行中的解除安裝作業"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"失敗的解除安裝作業"</string>
     <string name="uninstalling" msgid="8709566347688966845">"解除安裝中…"</string>
diff --git a/packages/PackageInstaller/res/values-zu/strings.xml b/packages/PackageInstaller/res/values-zu/strings.xml
index 5d28479..bb0405c 100644
--- a/packages/PackageInstaller/res/values-zu/strings.xml
+++ b/packages/PackageInstaller/res/values-zu/strings.xml
@@ -58,6 +58,8 @@
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Ingabe ufuna ukukhiphela lolu hlelo lokusebenza kumsebenzisi ongu-<xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Shintshanisa lolu hlelo lokusebenza ngenguqulo yasekuqaleni? Yonke idatha izosuswa."</string>
     <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Shintshanisa lolu hlelo lokusebenza ngenguqulo yasekuqaleni? Yonke idatha izosuswa. Lokhu kuthinta bonke abasebenzisi bale divayisi, abafaka labo abanamaphrofayela wokusebenza."</string>
+    <string name="uninstall_remove_contributed_files" msgid="2048594420923203453">"Futhi susa u-<xliff:g id="SIZE">%1$s</xliff:g> wamafayela emidiya ahlobene."</string>
+    <string name="uninstall_keep_data" msgid="7002379587465487550">"Gcina u-<xliff:g id="SIZE">%1$s</xliff:g> wedatha yohlelo lokusebenza."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Ukukhishwa okuqhubekayo"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Ukukhishwa okuhlulekile"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Iyakhipha..."</string>
diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml
index 1e0ff50..a05a219 100644
--- a/packages/PackageInstaller/res/values/strings.xml
+++ b/packages/PackageInstaller/res/values/strings.xml
@@ -121,7 +121,7 @@
     <string name="uninstall_update_text_multiuser">Replace this app with the factory version? All data will be removed. This affects all users of this device, including those with work profiles.</string>
     <!-- Label of a checkbox that allows to remove the files contributed by app during uninstall [CHAR LIMIT=none] -->
     <string name="uninstall_remove_contributed_files">Also remove <xliff:g id="size" example="1.5MB">%1$s</xliff:g> of associated media files.</string>
-    <!-- Label of a checkbox that allows to remove the files contributed by app during uninstall [CHAR LIMIT=none] -->
+    <!-- Label of a checkbox that allows to keep the data (e.g. files, settings) of the app on uninstall [CHAR LIMIT=none] -->
     <string name="uninstall_keep_data">Keep <xliff:g id="size" example="1.5MB">%1$s</xliff:g> of app data.</string>
 
     <!-- Label for the notification channel containing notifications for current uninstall operations [CHAR LIMIT=40] -->
diff --git a/packages/PrintSpooler/res/values-as/strings.xml b/packages/PrintSpooler/res/values-as/strings.xml
index ba4fd99..a93fceb 100644
--- a/packages/PrintSpooler/res/values-as/strings.xml
+++ b/packages/PrintSpooler/res/values-as/strings.xml
@@ -45,12 +45,12 @@
     <string name="collapse_handle" msgid="6886637989442507451">"হেণ্ডেল সংকুচিত কৰক"</string>
     <string name="print_button" msgid="645164566271246268">"প্ৰিণ্ট কৰক"</string>
     <string name="savetopdf_button" msgid="2976186791686924743">"PDFৰ জৰিয়তে ছেভ কৰক"</string>
-    <string name="print_options_expanded" msgid="6944679157471691859">"প্ৰিণ্ট বিকল্পসমূহ বিস্তাৰ কৰা হ\'ল"</string>
-    <string name="print_options_collapsed" msgid="7455930445670414332">"প্ৰিণ্ট বিকল্পসমূহ সংকুচিত কৰা হ\'ল"</string>
+    <string name="print_options_expanded" msgid="6944679157471691859">"প্ৰিণ্ট বিকল্পসমূহ বিস্তাৰ কৰা হ’ল"</string>
+    <string name="print_options_collapsed" msgid="7455930445670414332">"প্ৰিণ্ট বিকল্পসমূহ সংকুচিত কৰা হ’ল"</string>
     <string name="search" msgid="5421724265322228497">"সন্ধান কৰক"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"সকলো প্ৰিণ্টাৰ"</string>
     <string name="add_print_service_label" msgid="5356702546188981940">"সেৱা যোগ কৰক"</string>
-    <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"সন্ধান বাকচটো দেখুওৱা হ\'ল"</string>
+    <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"সন্ধান বাকচটো দেখুওৱা হ’ল"</string>
     <string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"সন্ধান বাকচটো ঢাক খাই আছে"</string>
     <string name="print_add_printer" msgid="1088656468360653455">"প্ৰিণ্টাৰ যোগ কৰক"</string>
     <string name="print_select_printer" msgid="7388760939873368698">"প্ৰিণ্টাৰ বাছনি কৰক"</string>
diff --git a/packages/PrintSpooler/tests/outofprocess/Android.bp b/packages/PrintSpooler/tests/outofprocess/Android.bp
index e88074e..c6dc263 100644
--- a/packages/PrintSpooler/tests/outofprocess/Android.bp
+++ b/packages/PrintSpooler/tests/outofprocess/Android.bp
@@ -19,7 +19,7 @@
 
     libs: ["android.test.runner.stubs"],
     static_libs: [
-        "android-support-test",
+        "androidx.test.rules",
         "ub-uiautomator",
         "mockito-target-minus-junit4",
         "print-test-util-lib",
diff --git a/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml b/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml
index 307cc93..fdcaa52 100644
--- a/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml
+++ b/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml
@@ -54,7 +54,7 @@
     </application>
 
     <!-- This runs in its own process, hence it instruments itself -->
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.printspooler.outofprocess.tests"
             android:label="PrintSpooler Out of Process Test Cases">
     </instrumentation>
diff --git a/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml b/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml
index d21a2e4..b649e82 100644
--- a/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml
+++ b/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml
@@ -24,7 +24,7 @@
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.printspooler.outofprocess.tests" />
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
         <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
index 7ebf93d..61c2f54 100644
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
+++ b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
@@ -35,7 +35,6 @@
 import android.print.test.services.FirstPrintService;
 import android.print.test.services.PrinterDiscoverySessionCallbacks;
 import android.print.test.services.StubbablePrinterDiscoverySession;
-import android.support.test.filters.LargeTest;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiObject;
 import android.support.test.uiautomator.UiObjectNotFoundException;
@@ -43,6 +42,8 @@
 import android.support.test.uiautomator.Until;
 import android.util.Log;
 
+import androidx.test.filters.LargeTest;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
diff --git a/packages/SettingsLib/ActionButtonsPreference/Android.bp b/packages/SettingsLib/ActionButtonsPreference/Android.bp
index e518e0b..cd3fb0c 100644
--- a/packages/SettingsLib/ActionButtonsPreference/Android.bp
+++ b/packages/SettingsLib/ActionButtonsPreference/Android.bp
@@ -1,5 +1,5 @@
 android_library {
-    name: "ActionButtonsPreference",
+    name: "SettingsLibActionButtonsPreference",
 
     srcs: ["src/**/*.java"],
     resource_dirs: ["res"],
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 0126e7e5..2321790 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -16,9 +16,10 @@
         "SettingsLibAppPreference",
         "SettingsLibSearchWidget",
         "SettingsLibSettingsSpinner",
-        "SettingsLayoutPreference",
-        "ActionButtonsPreference",
+        "SettingsLibLayoutPreference",
+        "SettingsLibActionButtonsPreference",
         "SettingsLibEntityHeaderWidgets",
+        "SettingsLibBarChartPreference"
     ],
 
     // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/SettingsLayoutPreference/Android.bp b/packages/SettingsLib/BarChartPreference/Android.bp
similarity index 82%
copy from packages/SettingsLib/SettingsLayoutPreference/Android.bp
copy to packages/SettingsLib/BarChartPreference/Android.bp
index 489d360..477e897 100644
--- a/packages/SettingsLib/SettingsLayoutPreference/Android.bp
+++ b/packages/SettingsLib/BarChartPreference/Android.bp
@@ -1,5 +1,5 @@
 android_library {
-    name: "SettingsLayoutPreference",
+    name: "SettingsLibBarChartPreference",
 
     srcs: ["src/**/*.java"],
     resource_dirs: ["res"],
diff --git a/packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml b/packages/SettingsLib/BarChartPreference/AndroidManifest.xml
similarity index 100%
copy from packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml
copy to packages/SettingsLib/BarChartPreference/AndroidManifest.xml
diff --git a/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_chart.xml b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_chart.xml
new file mode 100644
index 0000000..b063e13
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_chart.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginStart="16dp"
+    android:layout_marginEnd="16dp"
+    android:gravity="center"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/bar_chart_title"
+        android:layout_width="wrap_content"
+        android:layout_height="48dp"
+        android:gravity="center"
+        android:textAppearance="@style/BarChart.Text.HeaderTitle"/>
+
+    <LinearLayout
+        android:id="@+id/bar_views_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:orientation="vertical">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center|bottom">
+
+            <com.android.settingslib.widget.BarView
+                android:id="@+id/bar_view1"
+                style="@style/BarViewStyle"
+                settings:barColor="#FA7B17"/>
+            <com.android.settingslib.widget.BarView
+                android:id="@+id/bar_view2"
+                style="@style/BarViewStyle"
+                settings:barColor="#F439A0"/>
+            <com.android.settingslib.widget.BarView
+                android:id="@+id/bar_view3"
+                style="@style/BarViewStyle"
+                settings:barColor="#A142F4"/>
+            <com.android.settingslib.widget.BarView
+                android:id="@+id/bar_view4"
+                style="@style/BarViewStyle"
+                settings:barColor="#24C1E0"/>
+        </LinearLayout>
+
+        <Button
+            android:id="@+id/bar_chart_details"
+            style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+            android:layout_width="wrap_content"
+            android:layout_height="48dp"
+            android:gravity="center"/>
+    </LinearLayout>
+
+    <TextView
+        android:id="@+id/empty_view"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/settings_bar_view_max_height"
+        android:gravity="center"
+        android:visibility="gone"
+        android:textAppearance="@style/BarChart.Text.Summary"/>
+
+</LinearLayout>
diff --git a/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_view.xml b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_view.xml
new file mode 100644
index 0000000..b053317
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_view.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:gravity="center"
+    android:orientation="vertical">
+
+    <View
+        android:id="@+id/bar_view"
+        android:layout_width="8dp"
+        android:layout_height="wrap_content"
+        android:background="?android:attr/colorAccent"/>
+
+    <ImageView
+        android:id="@+id/icon_view"
+        android:layout_width="@dimen/settings_bar_view_icon_size"
+        android:layout_height="@dimen/settings_bar_view_icon_size"
+        android:scaleType="fitCenter"
+        android:layout_marginTop="12dp"
+        android:layout_marginBottom="12dp"/>
+
+    <TextView
+        android:id="@+id/bar_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="10dp"
+        android:layout_marginBottom="2dp"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:textAppearance="@style/BarChart.Text.Title"/>
+
+    <TextView
+        android:id="@+id/bar_summary"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="12dp"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:textAppearance="@style/BarChart.Text.Summary"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/values-port/dimens_permission_controller.xml b/packages/SettingsLib/BarChartPreference/res/values/attrs.xml
similarity index 75%
copy from core/res/res/values-port/dimens_permission_controller.xml
copy to packages/SettingsLib/BarChartPreference/res/values/attrs.xml
index af28713..df3eb0a 100644
--- a/core/res/res/values-port/dimens_permission_controller.xml
+++ b/packages/SettingsLib/BarChartPreference/res/values/attrs.xml
@@ -15,9 +15,11 @@
   limitations under the License.
   -->
 
-<!-- portrait dimensions for the permission grant dialog. -->
 <resources>
-    <!-- This yields 95% width -->
-    <dimen name="permissionGrantDialogWeight">380</dimen>
-    <dimen name="permissionGrantDialogWidth">0dp</dimen>
+
+    <declare-styleable name="SettingsBarView">
+        <!-- The color of bar view -->
+        <attr name="barColor" format="color" />
+    </declare-styleable>
+
 </resources>
diff --git a/core/res/res/values-port/dimens_permission_controller.xml b/packages/SettingsLib/BarChartPreference/res/values/dimens.xml
similarity index 75%
rename from core/res/res/values-port/dimens_permission_controller.xml
rename to packages/SettingsLib/BarChartPreference/res/values/dimens.xml
index af28713..7148afa 100644
--- a/core/res/res/values-port/dimens_permission_controller.xml
+++ b/packages/SettingsLib/BarChartPreference/res/values/dimens.xml
@@ -15,9 +15,7 @@
   limitations under the License.
   -->
 
-<!-- portrait dimensions for the permission grant dialog. -->
 <resources>
-    <!-- This yields 95% width -->
-    <dimen name="permissionGrantDialogWeight">380</dimen>
-    <dimen name="permissionGrantDialogWidth">0dp</dimen>
-</resources>
+    <dimen name="settings_bar_view_max_height">106dp</dimen>
+    <dimen name="settings_bar_view_icon_size">24dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/BarChartPreference/res/values/styles.xml b/packages/SettingsLib/BarChartPreference/res/values/styles.xml
new file mode 100644
index 0000000..647d080
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/res/values/styles.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<resources>
+    <style name="BarViewStyle">
+        <item name="android:layout_width">0dp</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_weight">1</item>
+        <item name="android:layout_marginStart">8dp</item>
+        <item name="android:layout_marginEnd">8dp</item>
+    </style>
+
+    <style name="BarChart.Text"
+           parent="@android:style/TextAppearance.Material.Subhead">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="BarChart.Text.HeaderTitle">
+        <item name="android:textSize">14sp</item>
+    </style>
+
+    <style name="BarChart.Text.Title">
+        <item name="android:textSize">22sp</item>
+    </style>
+
+    <style name="BarChart.Text.Summary"
+           parent="@android:style/TextAppearance.Material.Body1">
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
+        <item name="android:textSize">14sp</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartInfo.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartInfo.java
new file mode 100644
index 0000000..eeaf273
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartInfo.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * BarChartInfo is responsible for storing information about {@link BarChartPreference}.
+ */
+public class BarChartInfo {
+    @StringRes
+    private final int mTitle;
+    @StringRes
+    private final int mDetails;
+    @StringRes
+    private final int mEmptyText;
+    private final View.OnClickListener mDetailsOnClickListener;
+
+    private BarViewInfo[] mBarViewInfos;
+
+    /**
+     * Gets the resource id for the title shown in {@link BarChartPreference}.
+     *
+     * @return the string resource id for title.
+     */
+    public int getTitle() {
+        return mTitle;
+    }
+
+    /**
+     * Gets the resource id for the details shown in {@link BarChartPreference}.
+     *
+     * @return the string resource id for details.
+     */
+    public int getDetails() {
+        return mDetails;
+    }
+
+    /**
+     * Gets the resource id for the empty text shown in {@link BarChartPreference} when there is no
+     * any bar view in {@link BarChartPreference}.
+     *
+     * @return the string resource id for empty text.
+     */
+    public int getEmptyText() {
+        return mEmptyText;
+    }
+
+    /**
+     * Gets the click listener for the details view.
+     *
+     * @return click listener for details view.
+     */
+    public View.OnClickListener getDetailsOnClickListener() {
+        return mDetailsOnClickListener;
+    }
+
+    /**
+     * Gets an array which contains up to four {@link BarViewInfo}
+     *
+     * @return an array holding the current all {@link BarViewInfo} state of the bar chart.
+     */
+    public BarViewInfo[] getBarViewInfos() {
+        return mBarViewInfos;
+    }
+
+    void setBarViewInfos(BarViewInfo[] barViewInfos) {
+        mBarViewInfos = barViewInfos;
+    }
+
+    private BarChartInfo(Builder builder) {
+        mTitle = builder.mTitle;
+        mDetails = builder.mDetails;
+        mEmptyText = builder.mEmptyText;
+        mDetailsOnClickListener = builder.mDetailsOnClickListener;
+
+        if (builder.mBarViewInfos != null) {
+            mBarViewInfos = builder.mBarViewInfos.stream().toArray(BarViewInfo[]::new);
+        }
+    }
+
+    /**
+     * Builder class for {@link BarChartInfo}
+     */
+    public static class Builder {
+        @StringRes
+        private int mTitle;
+        @StringRes
+        private int mDetails;
+        @StringRes
+        private int mEmptyText;
+        private View.OnClickListener mDetailsOnClickListener;
+        private List<BarViewInfo> mBarViewInfos;
+
+        /**
+         * Creates an instance of a {@link BarChartInfo} based on the current builder settings.
+         *
+         * @return The {@link BarChartInfo}.
+         */
+        public BarChartInfo build() {
+            if (mTitle == 0) {
+                throw new IllegalStateException("You must call Builder#setTitle() once.");
+            }
+            return new BarChartInfo(this);
+        }
+
+        /**
+         * Sets the string resource id for the title.
+         */
+        public Builder setTitle(@StringRes int title) {
+            mTitle = title;
+            return this;
+        }
+
+        /**
+         * Sets the string resource id for the details.
+         */
+        public Builder setDetails(@StringRes int details) {
+            mDetails = details;
+            return this;
+        }
+
+        /**
+         * Sets the string resource id for the empty text.
+         */
+        public Builder setEmptyText(@StringRes int emptyText) {
+            mEmptyText = emptyText;
+            return this;
+        }
+
+        /**
+         * Sets the click listener for details view.
+         */
+        public Builder setDetailsOnClickListener(
+                @Nullable View.OnClickListener clickListener) {
+            mDetailsOnClickListener = clickListener;
+            return this;
+        }
+
+        /**
+         * Adds a {@link BarViewInfo} for {@link BarChartPreference}.
+         * Maximum of 4 {@link BarViewInfo} can be added.
+         */
+        public Builder addBarViewInfo(@NonNull BarViewInfo barViewInfo) {
+            if (mBarViewInfos == null) {
+                mBarViewInfos = new ArrayList<>();
+            }
+            if (mBarViewInfos.size() >= BarChartPreference.MAXIMUM_BAR_VIEWS) {
+                throw new IllegalStateException("We only support up to four bar views");
+            }
+            mBarViewInfos.add(barViewInfo);
+            return this;
+        }
+    }
+}
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
new file mode 100644
index 0000000..eed66e9
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+import java.util.Arrays;
+
+/**
+ * This BarChartPreference shows up to four bar views in this preference at most.
+ *
+ * <p>The following code sample shows a typical use, with an XML layout and code to initialize the
+ * contents of the BarChartPreference:
+ *
+ * <pre>
+ * &lt;com.android.settingslib.widget.BarChartPreference
+ *        android:key="bar_chart"/&gt;
+ * </pre>
+ *
+ * <p>This code sample demonstrates how to initialize the contents of the BarChartPreference
+ * defined in the previous XML layout:
+ *
+ * <pre>
+ * BarChartPreference preference = ((BarChartPreference) findPreference("bar_chart"));
+ *
+ * BarChartInfo info = new BarChartInfo.Builder()
+ *     .setTitle(R.string.permission_bar_chart_title)
+ *     .setDetails(R.string.permission_bar_chart_details)
+ *     .setEmptyText(R.string.permission_bar_chart_empty_text)
+ *     .addBarViewInfo(new barViewInfo(...))
+ *     .addBarViewInfo(new barViewInfo(...))
+ *     .addBarViewInfo(new barViewInfo(...))
+ *     .addBarViewInfo(new barViewInfo(...))
+ *     .setDetailsOnClickListener(v -> doSomething())
+ *     .build();
+ *
+ * preference.initializeBarChart(info);
+ * </pre>
+ *
+ *
+ * <p>You also can update new information for bar views by
+ * {@link BarChartPreference#setBarViewInfos(BarViewInfo[])}
+ *
+ * <pre>
+ * BarViewInfo[] barViewsInfo = new BarViewInfo [] {
+ *     new BarViewInfo(...),
+ *     new BarViewInfo(...),
+ *     new BarViewInfo(...),
+ *     new BarViewInfo(...),
+ * };
+ *
+ * preference.setBarViewInfos(barViewsInfo);
+ * </pre>
+ */
+public class BarChartPreference extends Preference {
+
+    static final int MAXIMUM_BAR_VIEWS = 4;
+    private static final String TAG = "BarChartPreference";
+    private static final int[] BAR_VIEWS = {
+            R.id.bar_view1,
+            R.id.bar_view2,
+            R.id.bar_view3,
+            R.id.bar_view4
+    };
+
+    private int mMaxBarHeight;
+    private BarChartInfo mBarChartInfo;
+
+    public BarChartPreference(Context context) {
+        super(context);
+        init();
+    }
+
+    public BarChartPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public BarChartPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init();
+    }
+
+    public BarChartPreference(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        init();
+    }
+
+    /**
+     * According to the information in {@link BarChartInfo} to initialize bar chart.
+     *
+     * @param barChartInfo The barChartInfo contains title, details, empty text, click listener
+     *                     attached on details view and four bar views.
+     */
+    public void initializeBarChart(@NonNull BarChartInfo barChartInfo) {
+        mBarChartInfo = barChartInfo;
+        notifyChanged();
+    }
+
+    /**
+     * Sets all bar view information which you'd like to show in preference.
+     *
+     * @param barViewInfos the barViewInfos contain at least one {@link BarViewInfo}.
+     */
+    public void setBarViewInfos(@Nullable BarViewInfo[] barViewInfos) {
+        if (barViewInfos != null && barViewInfos.length > MAXIMUM_BAR_VIEWS) {
+            throw new IllegalStateException("We only support up to four bar views");
+        }
+        mBarChartInfo.setBarViewInfos(barViewInfos);
+        notifyChanged();
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        holder.setDividerAllowedAbove(true);
+        holder.setDividerAllowedBelow(true);
+
+        // We must show title of bar chart.
+        bindChartTitleView(holder);
+
+        final BarViewInfo[] barViewInfos = mBarChartInfo.getBarViewInfos();
+        // If there is no any bar view, we just show an empty text.
+        if (barViewInfos == null || barViewInfos.length == 0) {
+            setEmptyViewVisible(holder, true /* visible */);
+            return;
+        }
+        setEmptyViewVisible(holder, false /* visible */);
+
+        bindChartDetailsView(holder);
+        updateBarChart(holder);
+    }
+
+    private void init() {
+        setSelectable(false);
+        setLayoutResource(R.layout.settings_bar_chart);
+        mMaxBarHeight = getContext().getResources().getDimensionPixelSize(
+                R.dimen.settings_bar_view_max_height);
+    }
+
+    private void bindChartTitleView(PreferenceViewHolder holder) {
+        final TextView titleView = (TextView) holder.findViewById(R.id.bar_chart_title);
+        titleView.setText(mBarChartInfo.getTitle());
+    }
+
+    private void bindChartDetailsView(PreferenceViewHolder holder) {
+        final Button detailsView = (Button) holder.findViewById(R.id.bar_chart_details);
+        final int details = mBarChartInfo.getDetails();
+        if (details == 0) {
+            detailsView.setVisibility(View.GONE);
+        } else {
+            detailsView.setVisibility(View.VISIBLE);
+            detailsView.setText(details);
+            detailsView.setOnClickListener(mBarChartInfo.getDetailsOnClickListener());
+        }
+    }
+
+    private void updateBarChart(PreferenceViewHolder holder) {
+        normalizeBarViewHeights();
+
+        final BarViewInfo[] barViewInfos = mBarChartInfo.getBarViewInfos();
+
+        for (int index = 0; index < MAXIMUM_BAR_VIEWS; index++) {
+            final BarView barView = (BarView) holder.findViewById(BAR_VIEWS[index]);
+
+            // If there is no bar view info can be shown.
+            if (barViewInfos == null || index >= barViewInfos.length) {
+                barView.setVisibility(View.GONE);
+                continue;
+            }
+            barView.setVisibility(View.VISIBLE);
+            barView.updateView(barViewInfos[index]);
+        }
+    }
+
+    private void normalizeBarViewHeights() {
+        final BarViewInfo[] barViewInfos = mBarChartInfo.getBarViewInfos();
+        // If there is no any bar view info, we don't need to calculate the height of all bar views.
+        if (barViewInfos == null || barViewInfos.length == 0) {
+            return;
+        }
+        // Do a sort in descending order, the first element would have max {@link
+        // BarViewInfo#mHeight}
+        Arrays.sort(barViewInfos);
+        // Since we sorted this array in advance, the first element must have the max {@link
+        // BarViewInfo#mHeight}.
+        final int maxBarHeight = barViewInfos[0].getHeight();
+        // If the max number of bar view is zero, then we don't calculate the unit for bar height.
+        final int unit = maxBarHeight == 0 ? 0 : mMaxBarHeight / maxBarHeight;
+
+        for (BarViewInfo barView : barViewInfos) {
+            barView.setNormalizedHeight(barView.getHeight() * unit);
+        }
+    }
+
+    private void setEmptyViewVisible(PreferenceViewHolder holder, boolean visible) {
+        final View barViewsContainer = holder.findViewById(R.id.bar_views_container);
+        final TextView emptyView = (TextView) holder.findViewById(R.id.empty_view);
+        final int emptyTextRes = mBarChartInfo.getEmptyText();
+
+        if (emptyTextRes != 0) {
+            emptyView.setText(emptyTextRes);
+        }
+        emptyView.setVisibility(visible ? View.VISIBLE : View.GONE);
+        barViewsContainer.setVisibility(visible ? View.GONE : View.VISIBLE);
+    }
+}
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
new file mode 100644
index 0000000..6bf61ae
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * {@link View} for a single vertical bar with icon and summary.
+ */
+public class BarView extends LinearLayout {
+
+    private static final String TAG = "BarView";
+
+    private View mBarView;
+    private ImageView mIcon;
+    private TextView mBarTitle;
+    private TextView mBarSummary;
+
+    public BarView(Context context) {
+        super(context);
+        init();
+    }
+
+    public BarView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+
+        // Get accent color
+        TypedArray a = context.obtainStyledAttributes(new int[]{android.R.attr.colorAccent});
+        @ColorInt final int colorAccent = a.getColor(0, 0);
+
+        // Get bar color from layout XML
+        a = context.obtainStyledAttributes(attrs, R.styleable.SettingsBarView);
+        @ColorInt final int barColor = a.getColor(R.styleable.SettingsBarView_barColor,
+                colorAccent);
+        a.recycle();
+
+        mBarView.setBackgroundColor(barColor);
+    }
+
+    /**
+     * Updates the view with a {@link BarViewInfo}.
+     */
+    void updateView(BarViewInfo barViewInfo) {
+        setOnClickListener(barViewInfo.getClickListener());
+        //Set height of bar view
+        mBarView.getLayoutParams().height = barViewInfo.getNormalizedHeight();
+        mIcon.setImageDrawable(barViewInfo.getIcon());
+        // For now, we use the bar number as title.
+        mBarTitle.setText(Integer.toString(barViewInfo.getHeight()));
+        mBarSummary.setText(barViewInfo.getSummary());
+    }
+
+    @VisibleForTesting
+    CharSequence getTitle() {
+        return mBarTitle.getText();
+    }
+
+    @VisibleForTesting
+    CharSequence getSummary() {
+        return mBarSummary.getText();
+    }
+
+    private void init() {
+        LayoutInflater.from(getContext()).inflate(R.layout.settings_bar_view, this);
+        setOrientation(LinearLayout.VERTICAL);
+        setGravity(Gravity.CENTER);
+
+        mBarView = findViewById(R.id.bar_view);
+        mIcon = findViewById(R.id.icon_view);
+        mBarTitle = findViewById(R.id.bar_title);
+        mBarSummary = findViewById(R.id.bar_summary);
+    }
+
+    private void setOnClickListner(View.OnClickListener listener) {
+        mBarView.setOnClickListener(listener);
+    }
+}
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java
new file mode 100644
index 0000000..409f9ea
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+import androidx.annotation.IntRange;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+
+import java.util.Comparator;
+
+/**
+ * A class responsible for saving bar view information.
+ */
+public class BarViewInfo implements Comparable<BarViewInfo> {
+
+    private final Drawable mIcon;
+    private View.OnClickListener mClickListener;
+    @StringRes
+    private int mSummary;
+    // A number indicates this bar's height. The larger number shows a higher bar view.
+    private int mHeight;
+    // A real height of bar view.
+    private int mNormalizedHeight;
+
+    /**
+     * Construct a BarViewInfo instance.
+     *
+     * @param icon      The icon of bar view.
+     * @param barHeight The height of bar view. Larger number shows a higher bar view.
+     * @param summary   The string resource id for summary.
+     */
+    public BarViewInfo(Drawable icon, @IntRange(from = 0) int barHeight, @StringRes int summary) {
+        mIcon = icon;
+        mHeight = barHeight;
+        mSummary = summary;
+    }
+
+    /**
+     * Set a click listener for bar view.
+     */
+    public void setClickListener(@Nullable View.OnClickListener listener) {
+        mClickListener = listener;
+    }
+
+    @Override
+    public int compareTo(BarViewInfo other) {
+        // Descending order
+        return Comparator.comparingInt((BarViewInfo barViewInfo) -> barViewInfo.mHeight)
+                .compare(other, this);
+    }
+
+    void setHeight(@IntRange(from = 0) int height) {
+        mHeight = height;
+    }
+
+    void setSummary(@StringRes int resId) {
+        mSummary = resId;
+    }
+
+    Drawable getIcon() {
+        return mIcon;
+    }
+
+    int getHeight() {
+        return mHeight;
+    }
+
+    View.OnClickListener getClickListener() {
+        return mClickListener;
+    }
+
+    @StringRes
+    int getSummary() {
+        return mSummary;
+    }
+
+    void setNormalizedHeight(@IntRange(from = 0) int barHeight) {
+        mNormalizedHeight = barHeight;
+    }
+
+    int getNormalizedHeight() {
+        return mNormalizedHeight;
+    }
+}
diff --git a/packages/SettingsLib/SettingsLayoutPreference/Android.bp b/packages/SettingsLib/LayoutPreference/Android.bp
similarity index 83%
rename from packages/SettingsLib/SettingsLayoutPreference/Android.bp
rename to packages/SettingsLib/LayoutPreference/Android.bp
index 489d360..a1f9a76 100644
--- a/packages/SettingsLib/SettingsLayoutPreference/Android.bp
+++ b/packages/SettingsLib/LayoutPreference/Android.bp
@@ -1,5 +1,5 @@
 android_library {
-    name: "SettingsLayoutPreference",
+    name: "SettingsLibLayoutPreference",
 
     srcs: ["src/**/*.java"],
     resource_dirs: ["res"],
diff --git a/packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml b/packages/SettingsLib/LayoutPreference/AndroidManifest.xml
similarity index 100%
rename from packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml
rename to packages/SettingsLib/LayoutPreference/AndroidManifest.xml
diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/layout/layout_preference_frame.xml b/packages/SettingsLib/LayoutPreference/res/layout/layout_preference_frame.xml
similarity index 100%
rename from packages/SettingsLib/SettingsLayoutPreference/res/layout/layout_preference_frame.xml
rename to packages/SettingsLib/LayoutPreference/res/layout/layout_preference_frame.xml
diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml b/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml
similarity index 100%
rename from packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
rename to packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml
diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml b/packages/SettingsLib/LayoutPreference/res/values/styles.xml
similarity index 100%
rename from packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml
rename to packages/SettingsLib/LayoutPreference/res/values/styles.xml
diff --git a/packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java b/packages/SettingsLib/LayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
similarity index 100%
rename from packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
rename to packages/SettingsLib/LayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
diff --git a/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml b/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
index a046332..7e65848 100644
--- a/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
+++ b/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
@@ -20,7 +20,7 @@
         android:height="24dp"
         android:viewportWidth="24"
         android:viewportHeight="24"
-        android:tint="?android:attr/colorAccent">
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#FF000000"
         android:pathData="M20.49,19l-5.73,-5.73C15.53,12.2 16,10.91 16,9.5C16,5.91 13.09,3 9.5,3S3,5.91 3,9.5C3,13.09 5.91,16 9.5,16c1.41,0 2.7,-0.47 3.77,-1.24L19,20.49L20.49,19zM5,9.5C5,7.01 7.01,5 9.5,5S14,7.01 14,9.5S11.99,14 9.5,14S5,11.99 5,9.5z"/>
diff --git a/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml b/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml
index cbebbb3..d6dc211 100644
--- a/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml
@@ -18,12 +18,11 @@
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android"
             android:paddingMode="stack">
     <item>
-        <shape
-            android:tint="?android:attr/colorForeground">
+        <shape>
             <corners
                 android:radius="20dp"/>
             <solid
-                android:color="@android:color/transparent"/>
+                android:color="?android:attr/colorPrimary"/>
             <stroke
                 android:color="#1f000000"
                 android:width="1dp"/>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 842779d..7dcc3ac 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1138,7 +1138,7 @@
     <string name="notice_header" translatable="false"></string>
 
     <!-- UI debug setting: opt in to use updated graphics driver? [CHAR LIMIT=100] -->
-    <string name="updated_gfx_driver_dev_opt_in_app_summary">Opt in app to use updated graphcis driver in developement</string>
+    <string name="gup_dev_opt_in_app_summary">Opt in app to use Game Update Package in developement</string>
 
     <!-- Name of the phone device [CHAR LIMIT=NONE] -->
     <string name="media_transfer_phone_device_name">Phone speaker</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java b/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java
index 2387b01..5e5c22a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java
@@ -16,8 +16,8 @@
 package com.android.settingslib.applications;
 
 import android.content.Context;
+import android.permission.PermissionControllerManager;
 import android.permission.RuntimePermissionPresentationInfo;
-import android.permission.RuntimePermissionPresenter;
 
 import java.text.Collator;
 import java.util.ArrayList;
@@ -28,9 +28,9 @@
 
     public static void getPermissionSummary(Context context, String pkg,
             final PermissionsResultCallback callback) {
-        final RuntimePermissionPresenter presenter =
-                RuntimePermissionPresenter.getInstance(context);
-        presenter.getAppPermissions(pkg, permissions -> {
+        final PermissionControllerManager permController =
+                context.getSystemService(PermissionControllerManager.class);
+        permController.getAppPermissions(pkg, permissions -> {
             final int permissionCount = permissions.size();
 
             int grantedStandardCount = 0;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 58feef5..24d7011 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -35,7 +35,6 @@
 
 public class A2dpProfile implements LocalBluetoothProfile {
     private static final String TAG = "A2dpProfile";
-    private static boolean V = false;
 
     private Context mContext;
 
@@ -60,7 +59,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (V) Log.d(TAG,"Bluetooth service connected");
             mService = (BluetoothA2dp) proxy;
             // We just bound to the service, so refresh the UI for any connected A2DP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -79,7 +77,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            if (V) Log.d(TAG,"Bluetooth service disconnected");
             mIsProfileReady=false;
         }
     }
@@ -302,7 +299,7 @@
     }
 
     protected void finalize() {
-        if (V) Log.d(TAG, "finalize()");
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.A2DP,
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
index 988062d..873dd1a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -55,7 +55,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG, "Bluetooth service connected");
             mService = (BluetoothA2dpSink) proxy;
             // We just bound to the service, so refresh the UI for any connected A2DP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -74,7 +73,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG, "Bluetooth service disconnected");
             mIsProfileReady=false;
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 2b7babd..4f18d45 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -321,6 +321,11 @@
                 // Dispatch device add callback to show bonded but
                 // not connected devices in discovery mode
                 dispatchDeviceAdded(cachedDevice);
+                Log.d(TAG, "DeviceFoundHandler found bonded and not connected device:"
+                        + cachedDevice);
+            } else {
+                Log.d(TAG, "DeviceFoundHandler found existing CachedBluetoothDevice:"
+                        + cachedDevice);
             }
             cachedDevice.setRssi(rssi);
             cachedDevice.setJustDiscovered(true);
@@ -463,12 +468,26 @@
     private class AclStateChangedHandler implements Handler {
         @Override
         public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+            if (device == null) {
+                Log.w(TAG, "AclStateChangedHandler: device is null");
+                return;
+            }
+
+            // Avoid to notify Settings UI for Hearing Aid sub device.
+            if (mDeviceManager.isSubDevice(device)) {
+                return;
+            }
+
             final String action = intent.getAction();
             if (action == null) {
                 Log.w(TAG, "AclStateChangedHandler: action is null");
                 return;
             }
             final CachedBluetoothDevice activeDevice = mDeviceManager.findDevice(device);
+            if (activeDevice == null) {
+                Log.w(TAG, "AclStateChangedHandler: activeDevice is null");
+                return;
+            }
             final int state;
             switch (action) {
                 case BluetoothDevice.ACTION_ACL_CONNECTED:
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 1bffff7..e28c894 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -116,8 +116,8 @@
 
     void onProfileStateChanged(LocalBluetoothProfile profile, int newProfileState) {
         if (BluetoothUtils.D) {
-            Log.d(TAG, "onProfileStateChanged: profile " + profile +
-                    " newProfileState " + newProfileState);
+            Log.d(TAG, "onProfileStateChanged: profile " + profile + ", device=" + mDevice
+                    + ", newProfileState " + newProfileState);
         }
         if (mLocalAdapter.getState() == BluetoothAdapter.STATE_TURNING_OFF)
         {
@@ -570,7 +570,7 @@
         }
 
         if (BluetoothUtils.D) {
-            Log.e(TAG, "updating profiles for " + mDevice.getAliasName());
+            Log.e(TAG, "updating profiles for " + mDevice.getAliasName() + ", " + mDevice);
             BluetoothClass bluetoothClass = mDevice.getBluetoothClass();
 
             if (bluetoothClass != null) Log.v(TAG, "Class: " + bluetoothClass.toString());
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 3a62838..5b4a8b4f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -127,6 +127,25 @@
     }
 
     /**
+     * Search for existing sub device {@link CachedBluetoothDevice}.
+     *
+     * @param device the address of the Bluetooth device
+     * @return true for found sub device or false.
+     */
+    public synchronized boolean isSubDevice(BluetoothDevice device) {
+        for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
+            if (!cachedDevice.getDevice().equals(device)) {
+                // Check sub devices if it exists
+                CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
+                if (subDevice != null && subDevice.getDevice().equals(device)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
      * Updates the Hearing Aid devices; specifically the HiSyncId's. This routine is called when the
      * Hearing Aid Service is connected and the HiSyncId's are now available.
      * @param LocalBluetoothProfileManager profileManager
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index 62507f5..6b6df9b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -58,7 +58,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG,"Bluetooth service connected");
             mService = (BluetoothHeadset) proxy;
             // We just bound to the service, so refresh the UI for any connected HFP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -80,7 +79,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG,"Bluetooth service disconnected");
             mProfileManager.callServiceDisconnectedListeners();
             mIsProfileReady=false;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index adb5ab3..577d98d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -51,7 +51,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (V) Log.d(TAG,"Bluetooth service connected");
             mService = (BluetoothHearingAid) proxy;
             // We just bound to the service, so refresh the UI for any connected HearingAid devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -77,7 +76,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            if (V) Log.d(TAG,"Bluetooth service disconnected");
             mIsProfileReady=false;
         }
     }
@@ -234,7 +232,7 @@
     }
 
     protected void finalize() {
-        if (V) Log.d(TAG, "finalize()");
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HEARING_AID,
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
index 4879144..c6bb2b3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
@@ -59,7 +59,6 @@
 
         @Override
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG, "Bluetooth service connected");
             mService = (BluetoothHeadsetClient) proxy;
             // We just bound to the service, so refresh the UI for any connected HFP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -80,7 +79,6 @@
 
         @Override
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG, "Bluetooth service disconnected");
             mIsProfileReady=false;
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
index 61e5b6b..4dc050c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
@@ -58,7 +58,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG, "Bluetooth service connected :-), profile:" + profile);
             mService = (BluetoothHidDevice) proxy;
             // We just bound to the service, so refresh the UI for any connected HID devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -78,7 +77,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
             mIsProfileReady = false;
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index 75d16db..ca840d9a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -33,7 +33,6 @@
  */
 public class HidProfile implements LocalBluetoothProfile {
     private static final String TAG = "HidProfile";
-    private static boolean V = true;
 
     private BluetoothHidHost mService;
     private boolean mIsProfileReady;
@@ -51,7 +50,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (V) Log.d(TAG,"Bluetooth service connected");
             mService = (BluetoothHidHost) proxy;
             // We just bound to the service, so refresh the UI for any connected HID devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -70,7 +68,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            if (V) Log.d(TAG,"Bluetooth service disconnected");
             mIsProfileReady=false;
         }
     }
@@ -186,7 +183,7 @@
     }
 
     protected void finalize() {
-        if (V) Log.d(TAG, "finalize()");
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HID_HOST,
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index 1e22f44..6acdcac 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -59,7 +59,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG, "Bluetooth service connected, profile:" + profile);
             mService = (BluetoothMapClient) proxy;
             // We just bound to the service, so refresh the UI for any connected MAP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -81,7 +80,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
             mProfileManager.callServiceDisconnectedListeners();
             mIsProfileReady=false;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
index 7582024..28975d4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
@@ -58,7 +58,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG, "Bluetooth service connected");
             mService = (BluetoothMap) proxy;
             // We just bound to the service, so refresh the UI for any connected MAP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -80,7 +79,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG, "Bluetooth service disconnected");
             mProfileManager.callServiceDisconnectedListeners();
             mIsProfileReady=false;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
index 7b81162..2d0a090 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
@@ -34,7 +34,6 @@
  */
 public class PanProfile implements LocalBluetoothProfile {
     private static final String TAG = "PanProfile";
-    private static boolean V = true;
 
     private BluetoothPan mService;
     private boolean mIsProfileReady;
@@ -53,13 +52,11 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (V) Log.d(TAG,"Bluetooth service connected");
             mService = (BluetoothPan) proxy;
             mIsProfileReady=true;
         }
 
         public void onServiceDisconnected(int profile) {
-            if (V) Log.d(TAG,"Bluetooth service disconnected");
             mIsProfileReady=false;
         }
     }
@@ -173,7 +170,7 @@
     }
 
     protected void finalize() {
-        if (V) Log.d(TAG, "finalize()");
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.PAN, mService);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index 1f15601..4672393 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -55,7 +55,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG, "Bluetooth service connected, profile:" + profile);
             mService = (BluetoothPbapClient) proxy;
             // We just bound to the service, so refresh the UI for any connected PBAP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -74,7 +73,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
             mIsProfileReady = false;
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
index adef0841..1b3c453 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
@@ -33,7 +33,6 @@
  */
 public class PbapServerProfile implements LocalBluetoothProfile {
     private static final String TAG = "PbapServerProfile";
-    private static boolean V = true;
 
     private BluetoothPbap mService;
     private boolean mIsProfileReady;
@@ -56,13 +55,11 @@
             implements BluetoothPbap.ServiceListener {
 
         public void onServiceConnected(BluetoothPbap proxy) {
-            if (V) Log.d(TAG,"Bluetooth service connected");
             mService = (BluetoothPbap) proxy;
             mIsProfileReady=true;
         }
 
         public void onServiceDisconnected() {
-            if (V) Log.d(TAG,"Bluetooth service disconnected");
             mIsProfileReady=false;
         }
     }
@@ -142,7 +139,7 @@
     }
 
     protected void finalize() {
-        if (V) Log.d(TAG, "finalize()");
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 mService.close();
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
index b4acc48..ea2ebde 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
@@ -57,7 +57,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG, "Bluetooth service connected, profile:" + profile);
             mService = (BluetoothSap) proxy;
             // We just bound to the service, so refresh the UI for any connected SAP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -79,7 +78,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
             mProfileManager.callServiceDisconnectedListeners();
             mIsProfileReady=false;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
index 9572fb3..20fe495 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
@@ -1,6 +1,8 @@
 package com.android.settingslib.core;
 
 import android.content.Context;
+import android.text.TextUtils;
+import android.util.Log;
 
 import androidx.preference.Preference;
 import androidx.preference.PreferenceGroup;
@@ -11,6 +13,8 @@
  */
 public abstract class AbstractPreferenceController {
 
+    private static final String TAG = "AbstractPrefController";
+
     protected final Context mContext;
 
     public AbstractPreferenceController(Context context) {
@@ -22,6 +26,10 @@
      */
     public void displayPreference(PreferenceScreen screen) {
         final String prefKey = getPreferenceKey();
+        if (TextUtils.isEmpty(prefKey)) {
+            Log.w(TAG, "Skipping displayPreference because key is empty:" + getClass().getName());
+            return;
+        }
         if (isAvailable()) {
             setVisible(screen, prefKey, true /* visible */);
             if (this instanceof Preference.OnPreferenceChangeListener) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
index 9699294..7177821 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
@@ -19,9 +19,7 @@
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.net.ConnectivityManager;
-import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
-import android.provider.Settings;
 import android.text.TextUtils;
 
 import androidx.annotation.VisibleForTesting;
@@ -83,15 +81,13 @@
     @SuppressLint("HardwareIds")
     @Override
     protected void updateConnectivity() {
-        WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
-        final int macRandomizationMode = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, OFF);
-        final String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress();
+        final String[] macAddresses = mWifiManager.getFactoryMacAddresses();
+        String macAddress = null;
+        if (macAddresses != null && macAddresses.length > 0) {
+            macAddress = macAddresses[0];
+        }
 
-        if (macRandomizationMode == ON && WifiInfo.DEFAULT_MAC_ADDRESS.equals(macAddress)) {
-            mWifiMacAddress.setSummary(R.string.wifi_status_mac_randomized);
-        } else if (TextUtils.isEmpty(macAddress)
-                || WifiInfo.DEFAULT_MAC_ADDRESS.equals(macAddress)) {
+        if (TextUtils.isEmpty(macAddress)) {
             mWifiMacAddress.setSummary(R.string.status_unavailable);
         } else {
             mWifiMacAddress.setSummary(macAddress);
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
index f7b16f8..c8c05a0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
@@ -22,6 +22,7 @@
 import android.os.PowerManager;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
+import android.text.TextUtils;
 import android.util.KeyValueListParser;
 import android.util.Log;
 import android.util.Slog;
@@ -176,4 +177,22 @@
             setAutoBatterySaverTriggerLevel(context, level);
         }
     }
+
+    /**
+     * Reverts battery saver schedule mode to none if we are in a bad state where routine mode
+     * is selected but no app is configured to actually provide the signal.
+     * @param context a valid context
+     */
+    public static void revertScheduleToNoneIfNeeded(Context context) {
+        ContentResolver resolver = context.getContentResolver();
+        final int currentMode = Global.getInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+                PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+        boolean providerConfigured = !TextUtils.isEmpty(context.getString(
+                com.android.internal.R.string.config_batterySaverScheduleProvider));
+        if (currentMode == PowerManager.POWER_SAVER_MODE_DYNAMIC && !providerConfigured) {
+            Global.putInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
+            Global.putInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+                    PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+        }
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
index 5c126b1..120acd3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
@@ -263,6 +263,10 @@
             // The user canceled to enable a 3rd party IME.
             setCheckedInternal(false);
         });
+        builder.setOnCancelListener((dialog) -> {
+            // The user canceled to enable a 3rd party IME.
+            setCheckedInternal(false);
+        });
         mDialog = builder.create();
         mDialog.show();
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
new file mode 100644
index 0000000..4ac3ce4
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.location;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.drawable.Drawable;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.format.DateUtils;
+import android.util.IconDrawableFactory;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.time.Clock;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Retrieves the information of applications which accessed location recently.
+ */
+public class RecentLocationAccesses {
+    private static final String TAG = RecentLocationAccesses.class.getSimpleName();
+    @VisibleForTesting
+    static final String ANDROID_SYSTEM_PACKAGE_NAME = "android";
+
+    // Keep last 24 hours of location app information.
+    private static final long RECENT_TIME_INTERVAL_MILLIS = DateUtils.DAY_IN_MILLIS;
+
+    @VisibleForTesting
+    static final int[] LOCATION_OPS = new int[]{
+            AppOpsManager.OP_FINE_LOCATION,
+            AppOpsManager.OP_COARSE_LOCATION,
+    };
+
+    private final PackageManager mPackageManager;
+    private final Context mContext;
+    private final IconDrawableFactory mDrawableFactory;
+    private final Clock mClock;
+
+    public RecentLocationAccesses(Context context) {
+        this(context, Clock.systemDefaultZone());
+    }
+
+    @VisibleForTesting
+    RecentLocationAccesses(Context context, Clock clock) {
+        mContext = context;
+        mPackageManager = context.getPackageManager();
+        mDrawableFactory = IconDrawableFactory.newInstance(context);
+        mClock = clock;
+    }
+
+    /**
+     * Fills a list of applications which queried location recently within specified time.
+     * Apps are sorted by recency. Apps with more recent location accesses are in the front.
+     */
+    public List<Access> getAppList() {
+        // Retrieve a location usage list from AppOps
+        AppOpsManager aoManager =
+                (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+        List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(LOCATION_OPS);
+
+        final int appOpsCount = appOps != null ? appOps.size() : 0;
+
+        // Process the AppOps list and generate a preference list.
+        ArrayList<Access> accesses = new ArrayList<>(appOpsCount);
+        final long now = mClock.millis();
+        final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        final List<UserHandle> profiles = um.getUserProfiles();
+
+        for (int i = 0; i < appOpsCount; ++i) {
+            AppOpsManager.PackageOps ops = appOps.get(i);
+            // Don't show the Android System in the list - it's not actionable for the user.
+            // Also don't show apps belonging to background users except managed users.
+            String packageName = ops.getPackageName();
+            int uid = ops.getUid();
+            int userId = UserHandle.getUserId(uid);
+            boolean isAndroidOs =
+                    (uid == Process.SYSTEM_UID) && ANDROID_SYSTEM_PACKAGE_NAME.equals(packageName);
+            if (isAndroidOs || !profiles.contains(new UserHandle(userId))) {
+                continue;
+            }
+            Access access = getAccessFromOps(now, ops);
+            if (access != null) {
+                accesses.add(access);
+            }
+        }
+        return accesses;
+    }
+
+    public List<Access> getAppListSorted() {
+        List<Access> accesses = getAppList();
+        // Sort the list of Access by recency. Most recent accesses first.
+        Collections.sort(accesses, Collections.reverseOrder(new Comparator<Access>() {
+            @Override
+            public int compare(Access access1, Access access2) {
+                return Long.compare(access1.accessFinishTime, access2.accessFinishTime);
+            }
+        }));
+        return accesses;
+    }
+
+    /**
+     * Creates a Access entry for the given PackageOps.
+     *
+     * This method examines the time interval of the PackageOps first. If the PackageOps is older
+     * than the designated interval, this method ignores the PackageOps object and returns null.
+     * When the PackageOps is fresh enough, this method returns a Access object for the package
+     */
+    private Access getAccessFromOps(long now,
+            AppOpsManager.PackageOps ops) {
+        String packageName = ops.getPackageName();
+        List<AppOpsManager.OpEntry> entries = ops.getOps();
+        long locationAccessFinishTime = 0L;
+        // Earliest time for a location access to end and still be shown in list.
+        long recentLocationCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS;
+        for (AppOpsManager.OpEntry entry : entries) {
+            locationAccessFinishTime = Math.max(entry.getLastAccessBackgroundTime(),
+                    entry.getLastAccessForegroundTime());
+        }
+        // Bail out if the entry is out of date.
+        if (locationAccessFinishTime < recentLocationCutoffTime) {
+            return null;
+        }
+
+        // The package is fresh enough, continue.
+        int uid = ops.getUid();
+        int userId = UserHandle.getUserId(uid);
+
+        Access access = null;
+        try {
+            ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser(
+                    packageName, PackageManager.GET_META_DATA, userId);
+            if (appInfo == null) {
+                Log.w(TAG, "Null application info retrieved for package " + packageName
+                        + ", userId " + userId);
+                return null;
+            }
+
+            final UserHandle userHandle = new UserHandle(userId);
+            Drawable icon = mDrawableFactory.getBadgedIcon(appInfo, userId);
+            CharSequence appLabel = mPackageManager.getApplicationLabel(appInfo);
+            CharSequence badgedAppLabel = mPackageManager.getUserBadgedLabel(appLabel, userHandle);
+            if (appLabel.toString().contentEquals(badgedAppLabel)) {
+                // If badged label is not different from original then no need for it as
+                // a separate content description.
+                badgedAppLabel = null;
+            }
+            access = new Access(packageName, userHandle, icon, appLabel, badgedAppLabel,
+                    locationAccessFinishTime);
+        } catch (NameNotFoundException e) {
+            Log.w(TAG, "package name not found for " + packageName + ", userId " + userId);
+        }
+        return access;
+    }
+
+    public static class Access {
+        public final String packageName;
+        public final UserHandle userHandle;
+        public final Drawable icon;
+        public final CharSequence label;
+        public final CharSequence contentDescription;
+        public final long accessFinishTime;
+
+        public Access(String packageName, UserHandle userHandle, Drawable icon,
+                CharSequence label, CharSequence contentDescription,
+                long accessFinishTime) {
+            this.packageName = packageName;
+            this.userHandle = userHandle;
+            this.icon = icon;
+            this.label = label;
+            this.contentDescription = contentDescription;
+            this.accessFinishTime = accessFinishTime;
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index e535348..959f9b2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -15,6 +15,7 @@
  */
 package com.android.settingslib.media;
 
+import android.bluetooth.BluetoothClass;
 import android.content.Context;
 import android.util.Log;
 
@@ -33,6 +34,7 @@
     BluetoothMediaDevice(Context context, CachedBluetoothDevice device) {
         super(context, MediaDeviceType.TYPE_BLUETOOTH_DEVICE);
         mCachedDevice = device;
+        initDeviceRecord();
     }
 
     @Override
@@ -55,6 +57,7 @@
     public void connect() {
         //TODO(b/117129183): add callback to notify LocalMediaManager connection state.
         mIsConnected = mCachedDevice.setActive();
+        super.connect();
         Log.d(TAG, "connect() device : " + getName() + ", is selected : " + mIsConnected);
     }
 
@@ -70,4 +73,18 @@
     public CachedBluetoothDevice getCachedDevice() {
         return mCachedDevice;
     }
+
+    @Override
+    protected boolean isCarKitDevice() {
+        final BluetoothClass bluetoothClass = mCachedDevice.getDevice().getBluetoothClass();
+        if (bluetoothClass != null) {
+            switch (bluetoothClass.getDeviceClass()) {
+                // Both are common CarKit class
+                case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
+                case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
+                    return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
index 04188e9..ab1cca0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
@@ -75,6 +75,7 @@
             Log.w(TAG, "addConnectedA2dpDevices() a2dp profile is null!");
             return;
         }
+
         final List<BluetoothDevice> devices = a2dpProfile.getConnectedDevices();
         final CachedBluetoothDeviceManager cachedBluetoothDeviceManager =
                 mLocalBluetoothManager.getCachedDeviceManager();
@@ -103,6 +104,7 @@
             Log.w(TAG, "addConnectedA2dpDevices() hap profile is null!");
             return;
         }
+
         final List<Long> devicesHiSyncIds = new ArrayList<>();
         final List<BluetoothDevice> devices = hapProfile.getConnectedDevices();
         final CachedBluetoothDeviceManager cachedBluetoothDeviceManager =
@@ -185,7 +187,7 @@
     private boolean isCachedDeviceConnected(CachedBluetoothDevice cachedDevice) {
         final boolean isConnectedHearingAidDevice = cachedDevice.isConnectedHearingAidDevice();
         final boolean isConnectedA2dpDevice = cachedDevice.isConnectedA2dpDevice();
-        Log.d(TAG, "isCachedDeviceConnected() cachedDevice : " + cachedDevice.getName()
+        Log.d(TAG, "isCachedDeviceConnected() cachedDevice : " + cachedDevice
                 + ", is hearing aid connected : " + isConnectedHearingAidDevice
                 + ", is a2dp connected : " + isConnectedA2dpDevice);
 
@@ -226,7 +228,7 @@
     @Override
     public void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state,
             int bluetoothProfile) {
-        Log.d(TAG, "onProfileConnectionStateChanged() device: " + cachedDevice.getName()
+        Log.d(TAG, "onProfileConnectionStateChanged() device: " + cachedDevice
                 + ", state: " + state + ", bluetoothProfile: " + bluetoothProfile);
 
         if (isCachedDeviceConnected(cachedDevice)) {
@@ -240,8 +242,7 @@
 
     @Override
     public void onAclConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
-        Log.d(TAG, "onAclConnectionStateChanged() device: " + cachedDevice.getName()
-                + ", state: " + state);
+        Log.d(TAG, "onAclConnectionStateChanged() device: " + cachedDevice + ", state: " + state);
 
         if (isCachedDeviceConnected(cachedDevice)) {
             addMediaDevice(cachedDevice);
@@ -251,7 +252,6 @@
             dispatchDeviceRemoved(cachedDevice);
         }
     }
-
     class DeviceAttributeChangeCallback implements CachedBluetoothDevice.Callback {
         @Override
         public void onDeviceAttributesChanged() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ConnectionRecordManager.java b/packages/SettingsLib/src/com/android/settingslib/media/ConnectionRecordManager.java
new file mode 100644
index 0000000..af343c4
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/media/ConnectionRecordManager.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.media;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+/**
+ * ConnectionRecordManager represents the sharedPreferences operation on device usage record
+ */
+public class ConnectionRecordManager {
+    private static final Object sInstanceSync = new Object();
+    private static final String KEY_LAST_SELECTED_DEVICE = "last_selected_device";
+    private static final String SHARED_PREFERENCES_NAME = "seamless_transfer_record";
+    private static final String TAG = "ConnectionRecordManager";
+    private static ConnectionRecordManager sInstance;
+
+    private String mLastSelectedDevice;
+
+    /**
+     * Get an {@code ConnectionRecordManager} instance (create one if necessary).
+     */
+    public static ConnectionRecordManager getInstance() {
+        synchronized (sInstanceSync) {
+            if (sInstance == null) {
+                sInstance = new ConnectionRecordManager();
+            }
+        }
+        return sInstance;
+    }
+
+    private SharedPreferences getSharedPreferences(Context context) {
+        return context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
+    }
+
+    /**
+     * Get connection record from sharedPreferences
+     *
+     * @param id a unique device Id
+     * @return the the usage result
+     */
+    public synchronized int fetchConnectionRecord(Context context, String id) {
+        return getSharedPreferences(context).getInt(id, 0);
+    }
+
+    /**
+     * Get the last selected device from sharedPreferences
+     */
+    public synchronized void fetchLastSelectedDevice(Context context) {
+        mLastSelectedDevice = getSharedPreferences(context).getString(KEY_LAST_SELECTED_DEVICE,
+                null);
+    }
+
+    /**
+     * Set device usage time and last selected device in sharedPreference
+     *
+     * @param id a unique device Id
+     * @param record usage times
+     */
+    public synchronized void setConnectionRecord(Context context, String id, int record) {
+        final SharedPreferences.Editor editor = getSharedPreferences(context).edit();
+        // Update used times
+        mLastSelectedDevice = id;
+        editor.putInt(mLastSelectedDevice, record);
+        // Update last used device
+        editor.putString(KEY_LAST_SELECTED_DEVICE, mLastSelectedDevice);
+        editor.apply();
+    }
+
+    /**
+     * @return the last selected device
+     */
+    public synchronized String getLastSelectedDevice() {
+        return mLastSelectedDevice;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index 498a0fc..21a81e0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -33,6 +33,7 @@
     InfoMediaDevice(Context context, MediaRouter.RouteInfo info) {
         super(context, MediaDeviceType.TYPE_CAST_DEVICE);
         mRouteInfo = info;
+        initDeviceRecord();
     }
 
     @Override
@@ -55,6 +56,7 @@
     public void connect() {
         //TODO(b/117129183): use MediaController2 to transfer media
         mIsConnected = true;
+        super.connect();
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index e375ea0..c9479d4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -28,13 +28,15 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 
 /**
  * LocalMediaManager provide interface to get MediaDevice list and transfer media to MediaDevice.
  */
 public class LocalMediaManager implements BluetoothCallback {
-
+    private static final Comparator<MediaDevice> COMPARATOR = Comparator.naturalOrder();
     private static final String TAG = "LocalMediaManager";
 
     @Retention(RetentionPolicy.SOURCE)
@@ -153,6 +155,7 @@
 
     void dispatchDeviceListUpdate() {
         synchronized (mCallbacks) {
+            Collections.sort(mMediaDevices, COMPARATOR);
             for (DeviceCallback callback : mCallbacks) {
                 callback.onDeviceListUpdate(new ArrayList<>(mMediaDevices));
             }
@@ -169,6 +172,23 @@
         mInfoMediaManager.stopScan();
     }
 
+    /**
+     * Find the MediaDevice through id.
+     *
+     * @param devices the list of MediaDevice
+     * @param id the unique id of MediaDevice
+     * @return MediaDevice
+     */
+    public MediaDevice getMediaDeviceById(List<MediaDevice> devices, String id) {
+        for (MediaDevice mediaDevice : devices) {
+            if (mediaDevice.getId().equals(id)) {
+                return mediaDevice;
+            }
+        }
+        Log.i(TAG, "getMediaDeviceById() can't found device");
+        return null;
+    }
+
     class MediaDeviceCallback implements MediaManager.MediaDeviceCallback {
         @Override
         public void onDeviceAdded(MediaDevice device) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 6c536f0..33b621c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -16,6 +16,7 @@
 package com.android.settingslib.media;
 
 import android.content.Context;
+import android.text.TextUtils;
 
 import androidx.annotation.IntDef;
 
@@ -25,20 +26,21 @@
 /**
  * MediaDevice represents a media device(such like Bluetooth device, cast device and phone device).
  */
-public abstract class MediaDevice {
-
+public abstract class MediaDevice implements Comparable<MediaDevice> {
     private static final String TAG = "MediaDevice";
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({MediaDeviceType.TYPE_BLUETOOTH_DEVICE,
-            MediaDeviceType.TYPE_CAST_DEVICE,
+    @IntDef({MediaDeviceType.TYPE_CAST_DEVICE,
+            MediaDeviceType.TYPE_BLUETOOTH_DEVICE,
             MediaDeviceType.TYPE_PHONE_DEVICE})
     public @interface MediaDeviceType {
-        int TYPE_BLUETOOTH_DEVICE = 1;
-        int TYPE_CAST_DEVICE = 2;
+        int TYPE_CAST_DEVICE = 1;
+        int TYPE_BLUETOOTH_DEVICE = 2;
         int TYPE_PHONE_DEVICE = 3;
     }
 
+    private int mConnectedRecord;
+
     protected boolean mIsConnected = false;
     protected Context mContext;
     protected int mType;
@@ -48,12 +50,18 @@
         mContext = context;
     }
 
+    void initDeviceRecord() {
+        ConnectionRecordManager.getInstance().fetchLastSelectedDevice(mContext);
+        mConnectedRecord = ConnectionRecordManager.getInstance().fetchConnectionRecord(mContext,
+                getId());
+    }
+
     /**
      * Check the MediaDevice is be connected to transfer.
      *
      * @return true if the MediaDevice is be connected to transfer, false otherwise.
      */
-    protected boolean isConnected() {
+    public boolean isConnected() {
         return mIsConnected;
     }
 
@@ -80,10 +88,71 @@
     /**
      * Transfer MediaDevice for media
      */
-    public abstract void connect();
+    public void connect() {
+        mConnectedRecord++;
+        ConnectionRecordManager.getInstance().setConnectionRecord(mContext, getId(),
+                mConnectedRecord);
+    }
 
     /**
      * Stop transfer MediaDevice
      */
     public abstract void disconnect();
+
+    /**
+     * Rules:
+     * 1. If there is one of the connected devices identified as a carkit, this carkit will
+     * be always on the top of the device list. Rule 2 and Rule 3 can’t overrule this rule.
+     * 2. For devices without any usage data yet
+     * WiFi device group sorted by alphabetical order + BT device group sorted by alphabetical
+     * order + phone speaker
+     * 3. For devices with usage record.
+     * The most recent used one + device group with usage info sorted by how many times the
+     * device has been used.
+     *
+     * So the device list will look like 4 slots ranked as below.
+     * Rule 1 + the most recently used device + Rule 3 + Rule 2
+     * Any slot could be empty. And available device will belong to one of the slots.
+     *
+     * @return a negative integer, zero, or a positive integer
+     * as this object is less than, equal to, or greater than the specified object.
+     */
+    @Override
+    public int compareTo(MediaDevice another) {
+        // Check carkit
+        if (isCarKitDevice()) {
+            return -1;
+        } else if (another.isCarKitDevice()) {
+            return 1;
+        }
+        // Set last used device at the first item
+        String lastSelectedDevice = ConnectionRecordManager.getInstance().getLastSelectedDevice();
+        if (TextUtils.equals(lastSelectedDevice, getId())) {
+            return -1;
+        } else if (TextUtils.equals(lastSelectedDevice, another.getId())) {
+            return 1;
+        }
+        // Sort by how many times the device has been used if there is usage record
+        if ((mConnectedRecord != another.mConnectedRecord)
+                && (another.mConnectedRecord > 0 || mConnectedRecord > 0)) {
+            return (another.mConnectedRecord - mConnectedRecord);
+        }
+        // Both devices have never been used
+        // To devices with the same type, sort by alphabetical order
+        if (mType == another.mType) {
+            final String s1 = getName();
+            final String s2 = another.getName();
+            return s1.compareToIgnoreCase(s2);
+        }
+        // Both devices have never been used, the priority is Cast > Bluetooth > Phone
+        return mType - another.mType;
+    }
+
+    /**
+     * Check if it is CarKit device
+     * @return true if it is CarKit device
+     */
+    protected boolean isCarKitDevice() {
+        return false;
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
index 060e9ad..bdddaf3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
@@ -23,7 +23,6 @@
  * MediaDeviceUtils provides utility function for MediaDevice
  */
 public class MediaDeviceUtils {
-
     /**
      * Use CachedBluetoothDevice address to represent unique id
      *
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 5e49d6b..e0f3c2f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -41,6 +41,7 @@
 
         mLocalBluetoothManager = localBluetoothManager;
         mProfileManager = mLocalBluetoothManager.getProfileManager();
+        initDeviceRecord();
     }
 
     @Override
@@ -66,8 +67,8 @@
         final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
 
         if (hapProfile != null && a2dpProfile != null) {
-            mIsConnected =
-                    hapProfile.setActiveDevice(null) && a2dpProfile.setActiveDevice(null);
+            mIsConnected = hapProfile.setActiveDevice(null) && a2dpProfile.setActiveDevice(null);
+            super.connect();
         }
         Log.d(TAG, "connect() device : " + getName() + ", is selected : " + mIsConnected);
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 089f773..871e248 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -101,6 +101,9 @@
     }
 
     public void handleBroadcast(Intent intent) {
+        if (mWifiManager == null) {
+            return;
+        }
         String action = intent.getAction();
         if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
             updateWifiState();
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 2f082b9..e47ca32 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -212,7 +212,7 @@
         mConnectivityManager = connectivityManager;
 
         // check if verbose logging developer option has been turned on or off
-        sVerboseLogging = (mWifiManager.getVerboseLoggingLevel() > 0);
+        sVerboseLogging = mWifiManager != null && (mWifiManager.getVerboseLoggingLevel() > 0);
 
         mFilter = filter;
 
@@ -283,7 +283,7 @@
             mScanner = new Scanner();
         }
 
-        if (mWifiManager.isWifiEnabled()) {
+        if (isWifiEnabled()) {
             mScanner.resume();
         }
     }
@@ -413,7 +413,7 @@
     }
 
     public boolean isWifiEnabled() {
-        return mWifiManager.isWifiEnabled();
+        return mWifiManager != null && mWifiManager.isWifiEnabled();
     }
 
     /**
@@ -638,7 +638,7 @@
     private void updateNetworkInfo(NetworkInfo networkInfo) {
 
         /* Sticky broadcasts can call this when wifi is disabled */
-        if (!mWifiManager.isWifiEnabled()) {
+        if (!isWifiEnabled()) {
             clearAccessPointsAndConditionallyUpdate();
             return;
         }
diff --git a/packages/SettingsLib/tests/integ/Android.mk b/packages/SettingsLib/tests/integ/Android.mk
index c893b6d..4a814df 100644
--- a/packages/SettingsLib/tests/integ/Android.mk
+++ b/packages/SettingsLib/tests/integ/Android.mk
@@ -31,8 +31,8 @@
 LOCAL_USE_AAPT2 := true
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
-    espresso-core \
+    androidx.test.rules \
+    androidx.test.espresso.core \
     mockito-target-minus-junit4 \
     truth-prebuilt
 
diff --git a/packages/SettingsLib/tests/integ/AndroidManifest.xml b/packages/SettingsLib/tests/integ/AndroidManifest.xml
index e8e0b41..da808dd 100644
--- a/packages/SettingsLib/tests/integ/AndroidManifest.xml
+++ b/packages/SettingsLib/tests/integ/AndroidManifest.xml
@@ -31,7 +31,7 @@
         <activity android:name=".drawer.SettingsDrawerActivityTest$TestActivity"/>
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.settingslib"
         android:label="Tests for SettingsLib">
     </instrumentation>
diff --git a/packages/SettingsLib/tests/integ/AndroidTest.xml b/packages/SettingsLib/tests/integ/AndroidTest.xml
index d7ee618..b5d0947 100644
--- a/packages/SettingsLib/tests/integ/AndroidTest.xml
+++ b/packages/SettingsLib/tests/integ/AndroidTest.xml
@@ -22,7 +22,7 @@
     <option name="test-tag" value="SettingsLibTests" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.settingslib" />
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
         <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java
index d0ab46a..50f5b9d 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java
@@ -16,15 +16,10 @@
 
 package com.android.settingslib.bluetooth;
 
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.mock;
 
-import static java.util.concurrent.TimeUnit.SECONDS;
-
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
@@ -32,15 +27,18 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import static java.util.concurrent.TimeUnit.SECONDS;
+
 import java.util.concurrent.CountDownLatch;
 
 /**
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawable/UserIconDrawableTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawable/UserIconDrawableTest.java
index 3fa2ce5..a436cb5 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawable/UserIconDrawableTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawable/UserIconDrawableTest.java
@@ -27,8 +27,9 @@
 import android.graphics.Color;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuff.Mode;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.settingslib.R;
 
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
index dddfa7a..08484bc 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
@@ -1,19 +1,7 @@
 package com.android.settingslib.graph;
 
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import com.android.settingslib.R;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
 import static com.google.common.truth.Truth.assertThat;
-import static junit.framework.Assert.assertTrue;
+
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyFloat;
 import static org.mockito.Matchers.anyString;
@@ -21,6 +9,21 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.settingslib.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class BatteryMeterDrawableBaseTest {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java
index 93b038e..9962e1c 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java
@@ -20,12 +20,13 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodSubtypePreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodSubtypePreferenceTest.java
index e591d8c..f1c0bea 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodSubtypePreferenceTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodSubtypePreferenceTest.java
@@ -16,11 +16,12 @@
 
 package com.android.settingslib.inputmethod;
 
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.TextUtils;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java
index 54510b2..46557d3 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java
@@ -18,7 +18,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -33,8 +32,9 @@
 import android.os.Handler;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java
index ee03d50..37f2600 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java
@@ -16,19 +16,22 @@
 
 package com.android.settingslib.utils;
 
+import static junit.framework.Assert.assertEquals;
+
 import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkTemplate;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.settingslib.NetworkPolicyEditor;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static junit.framework.Assert.assertEquals;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class NetworkPolicyEditorTest {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/ZoneGetterTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/ZoneGetterTest.java
index 0ec75ec..ff8dbda 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/ZoneGetterTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/ZoneGetterTest.java
@@ -15,21 +15,23 @@
  */
 package com.android.settingslib.utils;
 
+import static junit.framework.Assert.assertTrue;
+
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.filters.SmallTest;
 import android.text.Spanned;
 import android.text.style.TtsSpan;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.settingslib.datetime.ZoneGetter;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.*;
-import com.android.settingslib.datetime.ZoneGetter;
-
-import static junit.framework.Assert.assertTrue;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index 0324799..fc3034e 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -22,7 +22,6 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -44,12 +43,12 @@
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.SpannableString;
 import android.text.format.DateUtils;
-import android.text.style.TtsSpan;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.settingslib.R;
 import com.android.settingslib.utils.ThreadUtils;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index 1860b31..42eb0b9 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -53,9 +53,10 @@
 import android.os.HandlerThread;
 import android.os.SystemClock;
 import android.provider.Settings;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.settingslib.utils.ThreadUtils;
 
@@ -79,7 +80,6 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 
 // TODO(sghuman): Change these to robolectric tests b/35766684.
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class WifiTrackerTest {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index 27b8dfc..2c70cbb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -18,6 +18,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -154,4 +155,43 @@
         verify(mBluetoothCallback).onAclConnectionStateChanged(mCachedBluetoothDevice,
                 BluetoothAdapter.STATE_CONNECTED);
     }
+
+    @Test
+    public void dispatchAclConnectionStateChanged_aclDisconnected_shouldNotCallbackSubDevice() {
+        when(mCachedDeviceManager.isSubDevice(mBluetoothDevice)).thenReturn(true);
+        mBluetoothEventManager.registerCallback(mBluetoothCallback);
+        mIntent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+        mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
+        mContext.sendBroadcast(mIntent);
+
+        verify(mBluetoothCallback, never()).onAclConnectionStateChanged(mCachedBluetoothDevice,
+                BluetoothAdapter.STATE_DISCONNECTED);
+    }
+
+    @Test
+    public void dispatchAclConnectionStateChanged_aclConnected_shouldNotCallbackSubDevice() {
+        when(mCachedDeviceManager.isSubDevice(mBluetoothDevice)).thenReturn(true);
+        mBluetoothEventManager.registerCallback(mBluetoothCallback);
+        mIntent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
+        mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
+        mContext.sendBroadcast(mIntent);
+
+        verify(mBluetoothCallback, never()).onAclConnectionStateChanged(mCachedBluetoothDevice,
+                BluetoothAdapter.STATE_CONNECTED);
+    }
+
+    @Test
+    public void dispatchAclConnectionStateChanged_findDeviceReturnNull_shouldNotDispatchCallback() {
+        when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(null);
+        mBluetoothEventManager.registerCallback(mBluetoothCallback);
+        mIntent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
+        mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
+        mContext.sendBroadcast(mIntent);
+
+        verify(mBluetoothCallback, never()).onAclConnectionStateChanged(mCachedBluetoothDevice,
+                BluetoothAdapter.STATE_CONNECTED);
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 47b1210..43b2894 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -18,6 +18,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -334,6 +335,27 @@
     }
 
     /**
+     * Test to verify isSubDevice_validSubDevice().
+     */
+    @Test
+    public void isSubDevice_validSubDevice() {
+        doReturn(HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
+        mCachedDeviceManager.addDevice(mDevice1);
+
+        // Both device are not sub device in default value.
+        assertThat(mCachedDeviceManager.isSubDevice(mDevice1)).isFalse();
+        assertThat(mCachedDeviceManager.isSubDevice(mDevice2)).isFalse();
+
+        // Add Device-2 as sub device of Device-1 with same HiSyncId.
+        doReturn(HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice2);
+        mCachedDeviceManager.addDevice(mDevice2);
+
+        // Verify Device-2 is sub device, but Device-1 is not.
+        assertThat(mCachedDeviceManager.isSubDevice(mDevice2)).isTrue();
+        assertThat(mCachedDeviceManager.isSubDevice(mDevice1)).isFalse();
+    }
+
+    /**
      * Test to verify updateHearingAidsDevices().
      */
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
index 28de191..f695e0c3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
@@ -35,6 +35,8 @@
 @RunWith(RobolectricTestRunner.class)
 public class AbstractPreferenceControllerTest {
 
+    private static final String KEY_PREF = "test_pref";
+
     @Mock
     private PreferenceScreen mScreen;
 
@@ -47,9 +49,9 @@
         MockitoAnnotations.initMocks(this);
         mContext = RuntimeEnvironment.application;
         mPreference = new Preference(mContext);
-        mPreference.setKey(TestPrefController.KEY_PREF);
-        when(mScreen.findPreference(TestPrefController.KEY_PREF)).thenReturn(mPreference);
-        mTestPrefController = new TestPrefController(mContext);
+        mPreference.setKey(KEY_PREF);
+        when(mScreen.findPreference(KEY_PREF)).thenReturn(mPreference);
+        mTestPrefController = new TestPrefController(mContext, KEY_PREF);
     }
 
     @Test
@@ -62,15 +64,24 @@
     }
 
     @Test
+    public void displayPref_noKey_shouldDoNothing() {
+        mTestPrefController.isAvailable = true;
+
+        mTestPrefController.displayPreference(mScreen);
+
+        assertThat(mPreference.isVisible()).isTrue();
+    }
+
+    @Test
     public void setVisible_prefIsVisible_shouldSetToVisible() {
-        mTestPrefController.setVisible(mScreen, TestPrefController.KEY_PREF, true /* visible */);
+        mTestPrefController.setVisible(mScreen, KEY_PREF, true /* visible */);
 
         assertThat(mPreference.isVisible()).isTrue();
     }
 
     @Test
     public void setVisible_prefNotVisible_shouldSetToInvisible() {
-        mTestPrefController.setVisible(mScreen, TestPrefController.KEY_PREF, false /* visible */);
+        mTestPrefController.setVisible(mScreen, KEY_PREF, false /* visible */);
 
         assertThat(mPreference.isVisible()).isFalse();
     }
@@ -92,13 +103,14 @@
     }
 
     private static class TestPrefController extends AbstractPreferenceController {
-        private static final String KEY_PREF = "test_pref";
         private static final CharSequence TEST_SUMMARY = "Test";
 
         public boolean isAvailable;
+        private final String mPrefKey;
 
-        public TestPrefController(Context context) {
+        TestPrefController(Context context, String key) {
             super(context);
+            mPrefKey = key;
         }
 
         @Override
@@ -113,7 +125,7 @@
 
         @Override
         public String getPreferenceKey() {
-            return KEY_PREF;
+            return mPrefKey;
         }
 
         @Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
index 74e5bf5..1f7f4bc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
@@ -27,7 +27,6 @@
 import android.net.ConnectivityManager;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
-import android.provider.Settings;
 
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
@@ -93,105 +92,23 @@
     }
 
     @Test
-    public void updateConnectivity_nullWifiInfoWithMacRandomizationOff_setMacUnavailable() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
-                AbstractWifiMacAddressPreferenceController.OFF);
-        doReturn(null).when(mWifiManager).getConnectionInfo();
-
+    public void updateConnectivity_null_setMacUnavailable() {
+        doReturn(null).when(mWifiManager).getFactoryMacAddresses();
         mController.displayPreference(mScreen);
-
         assertThat(mPreference.getSummary())
                 .isEqualTo(mContext.getString(R.string.status_unavailable));
     }
 
     @Test
-    public void updateConnectivity_nullMacWithMacRandomizationOff_setMacUnavailable() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
-                AbstractWifiMacAddressPreferenceController.OFF);
-        doReturn(null).when(mWifiInfo).getMacAddress();
-
+    public void updateConnectivity_validMac_setValidMac() {
+        final String[] macAddresses = new String[]{TEST_MAC_ADDRESS};
+        doReturn(macAddresses).when(mWifiManager).getFactoryMacAddresses();
         mController.displayPreference(mScreen);
-
-        assertThat(mPreference.getSummary())
-                .isEqualTo(mContext.getString(R.string.status_unavailable));
-    }
-
-    @Test
-    public void updateConnectivity_defaultMacWithMacRandomizationOff_setMacUnavailable() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
-                AbstractWifiMacAddressPreferenceController.OFF);
-        doReturn(WifiInfo.DEFAULT_MAC_ADDRESS).when(mWifiInfo).getMacAddress();
-
-        mController.displayPreference(mScreen);
-
-        assertThat(mPreference.getSummary())
-                .isEqualTo(mContext.getString(R.string.status_unavailable));
-    }
-
-    @Test
-    public void updateConnectivity_validMacWithMacRandomizationOff_setValidMac() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
-                AbstractWifiMacAddressPreferenceController.OFF);
-        doReturn(TEST_MAC_ADDRESS).when(mWifiInfo).getMacAddress();
-
-        mController.displayPreference(mScreen);
-
         assertThat(mPreference.getSummary()).isEqualTo(TEST_MAC_ADDRESS);
-    }
 
-    @Test
-    public void updateConnectivity_nullWifiInfoWithMacRandomizationOn_setMacUnavailable() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
-                AbstractWifiMacAddressPreferenceController.ON);
-        doReturn(null).when(mWifiManager).getConnectionInfo();
 
-        mController.displayPreference(mScreen);
 
-        assertThat(mPreference.getSummary())
-                .isEqualTo(mContext.getString(R.string.status_unavailable));
-    }
 
-    @Test
-    public void updateConnectivity_nullMacWithMacRandomizationOn_setMacUnavailable() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
-                AbstractWifiMacAddressPreferenceController.ON);
-        doReturn(null).when(mWifiInfo).getMacAddress();
-
-        mController.displayPreference(mScreen);
-
-        assertThat(mPreference.getSummary())
-                .isEqualTo(mContext.getString(R.string.status_unavailable));
-    }
-
-    @Test
-    public void updateConnectivity_defaultMacWithMacRandomizationOn_setMacRandomized() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
-                AbstractWifiMacAddressPreferenceController.ON);
-        doReturn(WifiInfo.DEFAULT_MAC_ADDRESS).when(mWifiInfo).getMacAddress();
-
-        mController.displayPreference(mScreen);
-
-        assertThat(mPreference.getSummary())
-                .isEqualTo(mContext.getString(R.string.wifi_status_mac_randomized));
-    }
-
-    @Test
-    public void updateConnectivity_validMacWithMacRandomizationOn_setValidMac() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
-                AbstractWifiMacAddressPreferenceController.ON);
-        doReturn(TEST_MAC_ADDRESS).when(mWifiInfo).getMacAddress();
-
-        mController.displayPreference(mScreen);
-
-        assertThat(mPreference.getSummary()).isEqualTo(TEST_MAC_ADDRESS);
     }
 
     private static class ConcreteWifiMacAddressPreferenceController
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java
new file mode 100644
index 0000000..d5b89ca
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java
@@ -0,0 +1,162 @@
+package com.android.settingslib.location;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.OpEntry;
+import android.app.AppOpsManager.PackageOps;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.time.Clock;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(RobolectricTestRunner.class)
+public class RecentLocationAccessesTest {
+
+    private static final int TEST_UID = 1234;
+    private static final long NOW = 1_000_000_000;  // Approximately 9/8/2001
+    private static final long ONE_MIN_AGO = NOW - TimeUnit.MINUTES.toMillis(1);
+    private static final long TWENTY_THREE_HOURS_AGO = NOW - TimeUnit.HOURS.toMillis(23);
+    private static final long TWO_DAYS_AGO = NOW - TimeUnit.DAYS.toMillis(2);
+    private static final String[] TEST_PACKAGE_NAMES =
+            {"package_1MinAgo", "package_14MinAgo", "package_20MinAgo"};
+
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private AppOpsManager mAppOpsManager;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private Clock mClock;
+    private Context mContext;
+    private int mTestUserId;
+    private RecentLocationAccesses mRecentLocationAccesses;
+
+    @Before
+    public void setUp() throws NameNotFoundException {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
+        when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
+        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+        when(mPackageManager.getApplicationLabel(isA(ApplicationInfo.class)))
+                .thenReturn("testApplicationLabel");
+        when(mPackageManager.getUserBadgedLabel(isA(CharSequence.class), isA(UserHandle.class)))
+                .thenReturn("testUserBadgedLabel");
+        mTestUserId = UserHandle.getUserId(TEST_UID);
+        when(mUserManager.getUserProfiles())
+                .thenReturn(Collections.singletonList(new UserHandle(mTestUserId)));
+
+        long[] testRequestTime = {ONE_MIN_AGO, TWENTY_THREE_HOURS_AGO, TWO_DAYS_AGO};
+        List<PackageOps> appOps = createTestPackageOpsList(TEST_PACKAGE_NAMES, testRequestTime);
+        when(mAppOpsManager.getPackagesForOps(RecentLocationAccesses.LOCATION_OPS)).thenReturn(
+                appOps);
+        mockTestApplicationInfos(mTestUserId, TEST_PACKAGE_NAMES);
+
+        when(mClock.millis()).thenReturn(NOW);
+        mRecentLocationAccesses = new RecentLocationAccesses(mContext, mClock);
+    }
+
+    @Test
+    public void testGetAppList_shouldFilterRecentAccesses() {
+        List<RecentLocationAccesses.Access> requests = mRecentLocationAccesses.getAppList();
+        // Only two of the apps have requested location within 15 min.
+        assertThat(requests).hasSize(2);
+        // Make sure apps are ordered by recency
+        assertThat(requests.get(0).packageName).isEqualTo(TEST_PACKAGE_NAMES[0]);
+        assertThat(requests.get(0).accessFinishTime).isEqualTo(ONE_MIN_AGO);
+        assertThat(requests.get(1).packageName).isEqualTo(TEST_PACKAGE_NAMES[1]);
+        assertThat(requests.get(1).accessFinishTime).isEqualTo(TWENTY_THREE_HOURS_AGO);
+    }
+
+    @Test
+    public void testGetAppList_shouldNotShowAndroidOS() throws NameNotFoundException {
+        // Add android OS to the list of apps.
+        PackageOps androidSystemPackageOps =
+                createPackageOps(
+                        RecentLocationAccesses.ANDROID_SYSTEM_PACKAGE_NAME,
+                        Process.SYSTEM_UID,
+                        AppOpsManager.OP_FINE_LOCATION,
+                        ONE_MIN_AGO);
+        long[] testRequestTime =
+                {ONE_MIN_AGO, TWENTY_THREE_HOURS_AGO, TWO_DAYS_AGO, ONE_MIN_AGO};
+        List<PackageOps> appOps = createTestPackageOpsList(TEST_PACKAGE_NAMES, testRequestTime);
+        appOps.add(androidSystemPackageOps);
+        when(mAppOpsManager.getPackagesForOps(RecentLocationAccesses.LOCATION_OPS)).thenReturn(
+                appOps);
+        mockTestApplicationInfos(
+                Process.SYSTEM_UID, RecentLocationAccesses.ANDROID_SYSTEM_PACKAGE_NAME);
+
+        List<RecentLocationAccesses.Access> requests = mRecentLocationAccesses.getAppList();
+        // Android OS shouldn't show up in the list of apps.
+        assertThat(requests).hasSize(2);
+        // Make sure apps are ordered by recency
+        assertThat(requests.get(0).packageName).isEqualTo(TEST_PACKAGE_NAMES[0]);
+        assertThat(requests.get(0).accessFinishTime).isEqualTo(ONE_MIN_AGO);
+        assertThat(requests.get(1).packageName).isEqualTo(TEST_PACKAGE_NAMES[1]);
+        assertThat(requests.get(1).accessFinishTime).isEqualTo(TWENTY_THREE_HOURS_AGO);
+    }
+
+    private void mockTestApplicationInfos(int userId, String... packageNameList)
+            throws NameNotFoundException {
+        for (String packageName : packageNameList) {
+            ApplicationInfo appInfo = new ApplicationInfo();
+            appInfo.packageName = packageName;
+            when(mPackageManager.getApplicationInfoAsUser(
+                    packageName, PackageManager.GET_META_DATA, userId)).thenReturn(appInfo);
+        }
+    }
+
+    private List<PackageOps> createTestPackageOpsList(String[] packageNameList, long[] time) {
+        List<PackageOps> packageOpsList = new ArrayList<>();
+        for (int i = 0; i < packageNameList.length; i++) {
+            PackageOps packageOps = createPackageOps(
+                    packageNameList[i],
+                    TEST_UID,
+                    AppOpsManager.OP_FINE_LOCATION,
+                    time[i]);
+            packageOpsList.add(packageOps);
+        }
+        return packageOpsList;
+    }
+
+    private PackageOps createPackageOps(String packageName, int uid, int op, long time) {
+        return new PackageOps(
+                packageName,
+                uid,
+                Collections.singletonList(createOpEntryWithTime(op, time)));
+    }
+
+    private OpEntry createOpEntryWithTime(int op, long time) {
+        final long[] times = new long[AppOpsManager._NUM_UID_STATE];
+        // Slot for background access timestamp.
+        times[AppOpsManager.UID_STATE_LAST_NON_RESTRICTED + 1] = time;
+        final long[] rejectTimes = new long[AppOpsManager._NUM_UID_STATE];
+        return new OpEntry(op, AppOpsManager.MODE_ALLOWED, times, rejectTimes, 0 /* duration */,
+                0 /* proxyUid */, "" /* proxyPackage */);
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
new file mode 100644
index 0000000..9bbdd01
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.media;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+
+import androidx.mediarouter.media.MediaRouter;
+
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class MediaDeviceTest {
+    private static final Comparator<MediaDevice> COMPARATOR = Comparator.naturalOrder();
+    private static final String DEVICE_ADDRESS_1 = "AA:BB:CC:DD:EE:11";
+    private static final String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22";
+    private static final String DEVICE_ADDRESS_3 = "AA:BB:CC:DD:EE:33";
+    private static final String DEVICE_NAME_1 = "TestName_1";
+    private static final String DEVICE_NAME_2 = "TestName_2";
+    private static final String DEVICE_NAME_3 = "TestName_3";
+    private static final String ROUTER_ID_1 = "RouterId_1";
+    private static final String ROUTER_ID_2 = "RouterId_2";
+    private static final String ROUTER_ID_3 = "RouterId_3";
+    private final BluetoothClass mHeadreeClass =
+            new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
+    private final BluetoothClass mCarkitClass =
+            new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO);
+
+    @Mock
+    private BluetoothDevice mDevice1;
+    @Mock
+    private BluetoothDevice mDevice2;
+    @Mock
+    private BluetoothDevice mDevice3;
+    @Mock
+    private CachedBluetoothDevice mCachedDevice1;
+    @Mock
+    private CachedBluetoothDevice mCachedDevice2;
+    @Mock
+    private CachedBluetoothDevice mCachedDevice3;
+    @Mock
+    private LocalBluetoothManager mLocalBluetoothManager;
+    @Mock
+    private MediaRouter.RouteInfo mRouteInfo1;
+    @Mock
+    private MediaRouter.RouteInfo mRouteInfo2;
+    @Mock
+    private MediaRouter.RouteInfo mRouteInfo3;
+
+    private BluetoothMediaDevice mBluetoothMediaDevice1;
+    private BluetoothMediaDevice mBluetoothMediaDevice2;
+    private BluetoothMediaDevice mBluetoothMediaDevice3;
+    private Context mContext;
+    private InfoMediaDevice mInfoMediaDevice1;
+    private InfoMediaDevice mInfoMediaDevice2;
+    private InfoMediaDevice mInfoMediaDevice3;
+    private List<MediaDevice> mMediaDevices = new ArrayList<>();
+    private PhoneMediaDevice mPhoneMediaDevice;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+
+        when(mCachedDevice1.getAddress()).thenReturn(DEVICE_ADDRESS_1);
+        when(mCachedDevice2.getAddress()).thenReturn(DEVICE_ADDRESS_2);
+        when(mCachedDevice3.getAddress()).thenReturn(DEVICE_ADDRESS_3);
+        when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME_1);
+        when(mCachedDevice2.getName()).thenReturn(DEVICE_NAME_2);
+        when(mCachedDevice3.getName()).thenReturn(DEVICE_NAME_3);
+        when(mCachedDevice1.getDevice()).thenReturn(mDevice1);
+        when(mCachedDevice2.getDevice()).thenReturn(mDevice2);
+        when(mCachedDevice3.getDevice()).thenReturn(mDevice3);
+        when(mRouteInfo1.getId()).thenReturn(ROUTER_ID_1);
+        when(mRouteInfo2.getId()).thenReturn(ROUTER_ID_2);
+        when(mRouteInfo3.getId()).thenReturn(ROUTER_ID_3);
+        when(mRouteInfo1.getName()).thenReturn(DEVICE_NAME_1);
+        when(mRouteInfo2.getName()).thenReturn(DEVICE_NAME_2);
+        when(mRouteInfo3.getName()).thenReturn(DEVICE_NAME_3);
+
+        mBluetoothMediaDevice1 = new BluetoothMediaDevice(mContext, mCachedDevice1);
+        mBluetoothMediaDevice2 = new BluetoothMediaDevice(mContext, mCachedDevice2);
+        mBluetoothMediaDevice3 = new BluetoothMediaDevice(mContext, mCachedDevice3);
+        mInfoMediaDevice1 = new InfoMediaDevice(mContext, mRouteInfo1);
+        mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2);
+        mInfoMediaDevice3 = new InfoMediaDevice(mContext, mRouteInfo3);
+        mPhoneMediaDevice = new PhoneMediaDevice(mContext, mLocalBluetoothManager);
+    }
+
+    @Test
+    public void compareTo_carKit_nonCarKitBluetooth_carKitFirst() {
+        when(mDevice1.getBluetoothClass()).thenReturn(mHeadreeClass);
+        when(mDevice2.getBluetoothClass()).thenReturn(mCarkitClass);
+        mMediaDevices.add(mBluetoothMediaDevice1);
+        mMediaDevices.add(mBluetoothMediaDevice2);
+
+        assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
+        Collections.sort(mMediaDevices, COMPARATOR);
+        assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice2);
+    }
+
+    @Test
+    public void compareTo_carKit_info_carKitFirst() {
+        when(mDevice1.getBluetoothClass()).thenReturn(mCarkitClass);
+        mMediaDevices.add(mInfoMediaDevice1);
+        mMediaDevices.add(mBluetoothMediaDevice1);
+
+        assertThat(mMediaDevices.get(0)).isEqualTo(mInfoMediaDevice1);
+        Collections.sort(mMediaDevices, COMPARATOR);
+        assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
+    }
+
+    @Test
+    public void compareTo_carKit_phone_carKitFirst() {
+        when(mDevice1.getBluetoothClass()).thenReturn(mCarkitClass);
+        mMediaDevices.add(mPhoneMediaDevice);
+        mMediaDevices.add(mBluetoothMediaDevice1);
+
+        assertThat(mMediaDevices.get(0)).isEqualTo(mPhoneMediaDevice);
+        Collections.sort(mMediaDevices, COMPARATOR);
+        assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
+    }
+
+    @Test
+    public void compareTo_lastSelected_others_lastSelectedFirst() {
+        mMediaDevices.add(mBluetoothMediaDevice1);
+        mMediaDevices.add(mBluetoothMediaDevice2);
+        mBluetoothMediaDevice2.connect();
+
+        assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
+        Collections.sort(mMediaDevices, COMPARATOR);
+        assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice2);
+    }
+    @Test
+    public void compareTo_connectionRecord_sortByRecord() {
+        mMediaDevices.add(mBluetoothMediaDevice1);
+        mMediaDevices.add(mBluetoothMediaDevice2);
+        mBluetoothMediaDevice1.connect();
+        mBluetoothMediaDevice2.connect();
+        mBluetoothMediaDevice2.connect();
+        // Reset last selected record
+        ConnectionRecordManager.getInstance().setConnectionRecord(mContext, null, 0);
+
+        assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
+        assertThat(mMediaDevices.get(1)).isEqualTo(mBluetoothMediaDevice2);
+        Collections.sort(mMediaDevices, COMPARATOR);
+        assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice2);
+        assertThat(mMediaDevices.get(1)).isEqualTo(mBluetoothMediaDevice1);
+    }
+
+    @Test
+    public void compareTo_info_bluetooth_infoFirst() {
+        mMediaDevices.add(mBluetoothMediaDevice1);
+        mMediaDevices.add(mInfoMediaDevice1);
+
+        assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
+        Collections.sort(mMediaDevices, COMPARATOR);
+        assertThat(mMediaDevices.get(0)).isEqualTo(mInfoMediaDevice1);
+    }
+
+    @Test
+    public void compareTo_bluetooth_phone_bluetoothFirst() {
+        mMediaDevices.add(mPhoneMediaDevice);
+        mMediaDevices.add(mBluetoothMediaDevice1);
+
+        assertThat(mMediaDevices.get(0)).isEqualTo(mPhoneMediaDevice);
+        Collections.sort(mMediaDevices, COMPARATOR);
+        assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
+    }
+
+    @Test
+    public void compareTo_twoInfo_sortByAlphabet() {
+        mMediaDevices.add(mInfoMediaDevice2);
+        mMediaDevices.add(mInfoMediaDevice1);
+
+        assertThat(mMediaDevices.get(0)).isEqualTo(mInfoMediaDevice2);
+        Collections.sort(mMediaDevices, COMPARATOR);
+        assertThat(mMediaDevices.get(0)).isEqualTo(mInfoMediaDevice1);
+    }
+
+    @Test
+    public void compareTo_twoBluetooth_sortByAlphabet() {
+        mMediaDevices.add(mBluetoothMediaDevice2);
+        mMediaDevices.add(mBluetoothMediaDevice1);
+
+        assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice2);
+        Collections.sort(mMediaDevices, COMPARATOR);
+        assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
+    }
+
+    // 1.mInfoMediaDevice1:      Last Selected device
+    // 2.mBluetoothMediaDevice1: CarKit device
+    // 3.mInfoMediaDevice2:      * 2 times usage
+    // 4.mInfoMediaDevice3:      * 1 time usage
+    // 5.mBluetoothMediaDevice2: * 2 times usage
+    // 6.mBluetoothMediaDevice3: * 1 time usage
+    // 7.mPhoneMediaDevice:      * 0 time usage
+    // Order: 2 -> 1 -> 3 -> 5 -> 4 -> 6 -> 7
+    @Test
+    public void compareTo_mixedDevices_carKitFirst() {
+        when(mDevice1.getBluetoothClass()).thenReturn(mCarkitClass);
+        when(mDevice2.getBluetoothClass()).thenReturn(mHeadreeClass);
+        when(mDevice3.getBluetoothClass()).thenReturn(mHeadreeClass);
+        mMediaDevices.add(mBluetoothMediaDevice1);
+        mMediaDevices.add(mBluetoothMediaDevice2);
+        mMediaDevices.add(mBluetoothMediaDevice3);
+        mMediaDevices.add(mInfoMediaDevice1);
+        mMediaDevices.add(mInfoMediaDevice2);
+        mMediaDevices.add(mInfoMediaDevice3);
+        mMediaDevices.add(mPhoneMediaDevice);
+        mBluetoothMediaDevice3.connect();
+        mBluetoothMediaDevice2.connect();
+        mBluetoothMediaDevice2.connect();
+        mInfoMediaDevice3.connect();
+        mInfoMediaDevice2.connect();
+        mInfoMediaDevice2.connect();
+        mInfoMediaDevice1.connect();
+
+        Collections.sort(mMediaDevices, COMPARATOR);
+        assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
+        assertThat(mMediaDevices.get(1)).isEqualTo(mInfoMediaDevice1);
+        assertThat(mMediaDevices.get(2)).isEqualTo(mInfoMediaDevice2);
+        assertThat(mMediaDevices.get(3)).isEqualTo(mBluetoothMediaDevice2);
+        assertThat(mMediaDevices.get(4)).isEqualTo(mInfoMediaDevice3);
+        assertThat(mMediaDevices.get(5)).isEqualTo(mBluetoothMediaDevice3);
+        assertThat(mMediaDevices.get(6)).isEqualTo(mPhoneMediaDevice);
+    }
+
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartInfoTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartInfoTest.java
new file mode 100644
index 0000000..29d57b7
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartInfoTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.view.View;
+
+import androidx.annotation.StringRes;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class BarChartInfoTest {
+
+    @Rule
+    public final ExpectedException thrown = ExpectedException.none();
+    @StringRes
+    private final int mTitle = 0x11111111;
+    @StringRes
+    private final int mDetails = 0x22222222;
+    @StringRes
+    private final int mEmptyText = 0x33333333;
+    private final View.OnClickListener mClickListener = v -> {
+    };
+
+    @Test
+    public void builder_shouldSetFieldsInTheInfo() {
+        final BarChartInfo barChartInfo = new BarChartInfo.Builder()
+                .setTitle(mTitle)
+                .setDetails(mDetails)
+                .setEmptyText(mEmptyText)
+                .setDetailsOnClickListener(mClickListener)
+                .build();
+        assertThat(barChartInfo.getTitle()).isEqualTo(mTitle);
+        assertThat(barChartInfo.getDetails()).isEqualTo(mDetails);
+        assertThat(barChartInfo.getEmptyText()).isEqualTo(mEmptyText);
+        assertThat(barChartInfo.getDetailsOnClickListener()).isEqualTo(mClickListener);
+    }
+
+    @Test
+    public void builder_noTitle_shouldThrowIllegalStateException() {
+        thrown.expect(IllegalStateException.class);
+
+        new BarChartInfo.Builder()
+                .setDetails(mDetails)
+                .setEmptyText(mEmptyText)
+                .setDetailsOnClickListener(mClickListener)
+                .build();
+    }
+
+    @Test
+    public void addBarViewInfo_oneBarViewInfo_shouldSetOneBarViewInfo() {
+        final BarViewInfo barViewInfo = new BarViewInfo(
+                null /* icon */,
+                50,
+                mTitle);
+
+        final BarChartInfo mBarChartInfo = new BarChartInfo.Builder()
+                .setTitle(mTitle)
+                .setDetails(mDetails)
+                .setEmptyText(mEmptyText)
+                .setDetailsOnClickListener(mClickListener)
+                .addBarViewInfo(barViewInfo)
+                .build();
+
+        assertThat(mBarChartInfo.getBarViewInfos().length).isEqualTo(1);
+        assertThat(mBarChartInfo.getBarViewInfos()[0]).isEqualTo(barViewInfo);
+    }
+
+    @Test
+    public void addBarViewInfo_maxNumberOfInfoAllowed_shouldSetMaxBarViewInfos() {
+        final BarViewInfo barViewInfo = new BarViewInfo(
+                null /* icon */,
+                50,
+                mTitle);
+        final BarChartInfo mBarChartInfo = new BarChartInfo.Builder()
+                .setTitle(mTitle)
+                .setDetails(mDetails)
+                .setEmptyText(mEmptyText)
+                .setDetailsOnClickListener(mClickListener)
+                .addBarViewInfo(barViewInfo)
+                .addBarViewInfo(barViewInfo)
+                .addBarViewInfo(barViewInfo)
+                .addBarViewInfo(barViewInfo)
+                .build();
+
+        assertThat(mBarChartInfo.getBarViewInfos().length).isEqualTo(4);
+    }
+
+    @Test
+    public void addBarViewInfo_moreInfosThanMaxAllowed_shouldThrowIllegalStateException() {
+        thrown.expect(IllegalStateException.class);
+
+        final BarViewInfo barViewInfo = new BarViewInfo(
+                null /* icon */,
+                50,
+                mTitle);
+        new BarChartInfo.Builder()
+                .setTitle(mTitle)
+                .setDetails(mDetails)
+                .setEmptyText(mEmptyText)
+                .setDetailsOnClickListener(mClickListener)
+                .addBarViewInfo(barViewInfo)
+                .addBarViewInfo(barViewInfo)
+                .addBarViewInfo(barViewInfo)
+                .addBarViewInfo(barViewInfo)
+                .addBarViewInfo(barViewInfo)
+                .build();
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
new file mode 100644
index 0000000..cf6137d
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.preference.PreferenceViewHolder;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class BarChartPreferenceTest {
+
+    @Rule
+    public final ExpectedException thrown = ExpectedException.none();
+
+    private Context mContext;
+    private View mBarChartView;
+    private Drawable mIcon;
+    private BarView mBarView1;
+    private BarView mBarView2;
+    private BarView mBarView3;
+    private BarView mBarView4;
+    private TextView mDetailsView;
+    private PreferenceViewHolder mHolder;
+    private BarChartPreference mPreference;
+    private BarChartInfo mBarChartInfo;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mBarChartView = View.inflate(mContext, R.layout.settings_bar_chart, null /* parent */);
+        mHolder = PreferenceViewHolder.createInstanceForTests(mBarChartView);
+        mPreference = new BarChartPreference(mContext, null /* attrs */);
+
+        mIcon = mContext.getDrawable(R.drawable.ic_menu);
+        mBarView1 = mBarChartView.findViewById(R.id.bar_view1);
+        mBarView2 = mBarChartView.findViewById(R.id.bar_view2);
+        mBarView3 = mBarChartView.findViewById(R.id.bar_view3);
+        mBarView4 = mBarChartView.findViewById(R.id.bar_view4);
+        mDetailsView = mBarChartView.findViewById(R.id.bar_chart_details);
+
+        mBarChartInfo = new BarChartInfo.Builder()
+                .setTitle(R.string.debug_app)
+                .setDetails(R.string.debug_app)
+                .setEmptyText(R.string.debug_app)
+                .setDetailsOnClickListener(v -> {
+                })
+                .build();
+    }
+
+    @Test
+    public void initializeBarChart_titleSet_shouldSetTitleInChartView() {
+        final TextView titleView = mBarChartView.findViewById(R.id.bar_chart_title);
+        final BarChartInfo barChartInfo = new BarChartInfo.Builder()
+                .setTitle(R.string.debug_app)
+                .build();
+
+        mPreference.initializeBarChart(barChartInfo);
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(titleView.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(titleView.getText()).isEqualTo(mContext.getText(R.string.debug_app));
+    }
+
+    @Test
+    public void initializeBarChart_noBarViewSet_shouldShowTitleAndEmptyView() {
+        final BarChartInfo barChartInfo = new BarChartInfo.Builder()
+                .setTitle(R.string.debug_app)
+                .setEmptyText(R.string.debug_app)
+                .build();
+
+        mPreference.initializeBarChart(barChartInfo);
+        // We don't add any bar view yet.
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(mBarChartView.findViewById(R.id.bar_chart_title).getVisibility())
+                .isEqualTo(View.VISIBLE);
+        assertThat(mBarChartView.findViewById(R.id.empty_view).getVisibility())
+                .isEqualTo(View.VISIBLE);
+        assertThat(mBarChartView.findViewById(R.id.bar_views_container).getVisibility())
+                .isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void initializeBarChart_detailsSet_shouldShowBarChartDetailsView() {
+        final BarChartInfo barChartInfo = new BarChartInfo.Builder()
+                .setTitle(R.string.debug_app)
+                .setDetails(R.string.debug_app)
+                .addBarViewInfo(new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app))
+                .build();
+
+        mPreference.initializeBarChart(barChartInfo);
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(mDetailsView.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mDetailsView.getText()).isEqualTo(mContext.getText(R.string.debug_app));
+    }
+
+    @Test
+    public void initializeBarChart_detailsNotSet_shouldHideBarChartDetailsView() {
+        // We don't call BarChartInfo.Builder#setDetails yet.
+        final BarChartInfo barChartInfo = new BarChartInfo.Builder()
+                .setTitle(R.string.debug_app)
+                .addBarViewInfo(new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app))
+                .build();
+
+        mPreference.initializeBarChart(barChartInfo);
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(mDetailsView.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void initializeBarChart_clickListenerSet_shouldSetClickListenerOnDetailsView() {
+        final BarChartInfo barChartInfo = new BarChartInfo.Builder()
+                .setTitle(R.string.debug_app)
+                .setDetails(R.string.debug_app)
+                .setDetailsOnClickListener(v -> {
+                })
+                .addBarViewInfo(new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app))
+                .build();
+
+        mPreference.initializeBarChart(barChartInfo);
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(mDetailsView.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mDetailsView.hasOnClickListeners()).isTrue();
+    }
+
+    @Test
+    public void setBarViewInfos_oneBarViewInfoSet_shouldShowOneBarView() {
+        final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+                new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app)
+        };
+
+        mPreference.initializeBarChart(mBarChartInfo);
+        mPreference.setBarViewInfos(barViewsInfo);
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView1.getTitle()).isEqualTo("10");
+
+        assertThat(mBarView2.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mBarView3.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mBarView4.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void setBarViewInfos_twoBarViewInfosSet_shouldShowTwoBarViews() {
+        final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+                new BarViewInfo(mIcon, 20 /* barNumber */, R.string.debug_app),
+                new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app)
+        };
+
+        mPreference.initializeBarChart(mBarChartInfo);
+        mPreference.setBarViewInfos(barViewsInfo);
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView1.getTitle()).isEqualTo("20");
+        assertThat(mBarView2.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView2.getTitle()).isEqualTo("10");
+
+        assertThat(mBarView3.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mBarView4.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void setBarViewInfos_threeBarViewInfosSet_shouldShowThreeBarViews() {
+        final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+                new BarViewInfo(mIcon, 20 /* barNumber */, R.string.debug_app),
+                new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app),
+                new BarViewInfo(mIcon, 5 /* barNumber */, R.string.debug_app)
+        };
+
+        mPreference.initializeBarChart(mBarChartInfo);
+        mPreference.setBarViewInfos(barViewsInfo);
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView1.getTitle()).isEqualTo("20");
+        assertThat(mBarView2.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView2.getTitle()).isEqualTo("10");
+        assertThat(mBarView3.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView3.getTitle()).isEqualTo("5");
+
+        assertThat(mBarView4.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void setBarViewInfos_fourBarViewInfosSet_shouldShowFourBarViews() {
+        final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+                new BarViewInfo(mIcon, 20 /* barNumber */, R.string.debug_app),
+                new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app),
+                new BarViewInfo(mIcon, 5 /* barNumber */, R.string.debug_app),
+                new BarViewInfo(mIcon, 2 /* barNumber */, R.string.debug_app),
+        };
+
+        mPreference.initializeBarChart(mBarChartInfo);
+        mPreference.setBarViewInfos(barViewsInfo);
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView1.getTitle()).isEqualTo("20");
+        assertThat(mBarView2.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView2.getTitle()).isEqualTo("10");
+        assertThat(mBarView3.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView3.getTitle()).isEqualTo("5");
+        assertThat(mBarView4.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView4.getTitle()).isEqualTo("2");
+    }
+
+    @Test
+    public void setBarViewInfos_moreInfosThanMaxAllowed_shouldThrowIllegalStateException() {
+        thrown.expect(IllegalStateException.class);
+
+        final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+                new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app),
+                new BarViewInfo(mIcon, 50 /* barNumber */, R.string.debug_app),
+                new BarViewInfo(mIcon, 5 /* barNumber */, R.string.debug_app),
+                new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app),
+                new BarViewInfo(mIcon, 70 /* barNumber */, R.string.debug_app),
+        };
+
+        mPreference.setBarViewInfos(barViewsInfo);
+    }
+
+    @Test
+    public void setBarViewInfos_barViewInfosSet_shouldBeSortedInDescending() {
+        final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+                new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app),
+                new BarViewInfo(mIcon, 50 /* barNumber */, R.string.debug_app),
+                new BarViewInfo(mIcon, 5 /* barNumber */, R.string.debug_app),
+                new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app),
+        };
+
+        mPreference.initializeBarChart(mBarChartInfo);
+        mPreference.setBarViewInfos(barViewsInfo);
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView1.getTitle()).isEqualTo("50");
+        assertThat(mBarView2.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView2.getTitle()).isEqualTo("30");
+        assertThat(mBarView3.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView3.getTitle()).isEqualTo("10");
+        assertThat(mBarView4.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView4.getTitle()).isEqualTo("5");
+    }
+
+    @Test
+    public void setBarViewInfos_validBarViewSummarySet_barViewShouldShowSummary() {
+        final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+                new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app),
+        };
+
+        mPreference.initializeBarChart(mBarChartInfo);
+        mPreference.setBarViewInfos(barViewsInfo);
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView1.getSummary()).isEqualTo(mContext.getText(R.string.debug_app));
+    }
+
+    @Test
+    public void setBarViewInfos_clickListenerForBarViewSet_barViewShouldHaveClickListener() {
+        final BarViewInfo viewInfo = new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app);
+        viewInfo.setClickListener(v -> {
+        });
+        final BarViewInfo[] barViewsInfo = new BarViewInfo[]{viewInfo};
+
+        mPreference.initializeBarChart(mBarChartInfo);
+        mPreference.setBarViewInfos(barViewsInfo);
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView1.hasOnClickListeners()).isTrue();
+    }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index 3520918..d0ffe7a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -20,7 +20,6 @@
 import android.annotation.SystemApi;
 import android.app.ActivityManager;
 import android.content.IContentProvider;
-import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Process;
@@ -28,6 +27,7 @@
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
 import android.os.ShellCommand;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 
 import java.io.FileDescriptor;
@@ -46,13 +46,6 @@
  */
 @SystemApi
 public final class DeviceConfigService extends Binder {
-    /**
-     * TODO(b/113100523): Move this to DeviceConfig.java when it is added, and expose it as a System
-     *     API.
-     */
-    private static final Uri CONFIG_CONTENT_URI =
-            Uri.parse("content://" + Settings.AUTHORITY + "/config");
-
     final SettingsProvider mProvider;
 
     public DeviceConfigService(SettingsProvider provider) {
@@ -191,10 +184,10 @@
             final PrintWriter pout = getOutPrintWriter();
             switch (verb) {
                 case GET:
-                    pout.println(get(iprovider, namespace, key));
+                    pout.println(DeviceConfig.getProperty(namespace, key));
                     break;
                 case PUT:
-                    put(iprovider, namespace, key, value, makeDefault);
+                    DeviceConfig.setProperty(namespace, key, value, makeDefault);
                     break;
                 case DELETE:
                     pout.println(delete(iprovider, namespace, key)
@@ -207,7 +200,7 @@
                     }
                     break;
                 case RESET:
-                    reset(iprovider, resetMode, namespace);
+                    DeviceConfig.resetToDefaults(resetMode, namespace);
                     break;
                 default:
                     perr.println("Unspecified command");
@@ -241,43 +234,6 @@
                     + "flags are reset");
         }
 
-        private String get(IContentProvider provider, String namespace, String key) {
-            String compositeKey = namespace + "/" + key;
-            String result = null;
-            try {
-                Bundle args = new Bundle();
-                args.putInt(Settings.CALL_METHOD_USER_KEY,
-                        ActivityManager.getService().getCurrentUser().id);
-                Bundle b = provider.call(resolveCallingPackage(), Settings.CALL_METHOD_GET_CONFIG,
-                        compositeKey, args);
-                if (b != null) {
-                    result = b.getPairValue();
-                }
-            } catch (RemoteException e) {
-                throw new RuntimeException("Failed in IPC", e);
-            }
-            return result;
-        }
-
-        private void put(IContentProvider provider, String namespace, String key, String value,
-                boolean makeDefault) {
-            String compositeKey = namespace + "/" + key;
-
-            try {
-                Bundle args = new Bundle();
-                args.putString(Settings.NameValueTable.VALUE, value);
-                args.putInt(Settings.CALL_METHOD_USER_KEY,
-                        ActivityManager.getService().getCurrentUser().id);
-                if (makeDefault) {
-                    args.putBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY, true);
-                }
-                provider.call(resolveCallingPackage(), Settings.CALL_METHOD_PUT_CONFIG,
-                        compositeKey, args);
-            } catch (RemoteException e) {
-                throw new RuntimeException("Failed in IPC", e);
-            }
-        }
-
         private boolean delete(IContentProvider provider, String namespace, String key) {
             String compositeKey = namespace + "/" + key;
             boolean success;
@@ -286,7 +242,7 @@
                 Bundle args = new Bundle();
                 args.putInt(Settings.CALL_METHOD_USER_KEY,
                         ActivityManager.getService().getCurrentUser().id);
-                Bundle b = provider.call(resolveCallingPackage(),
+                Bundle b = provider.call(resolveCallingPackage(), Settings.AUTHORITY,
                         Settings.CALL_METHOD_DELETE_CONFIG, compositeKey, args);
                 success = (b != null && b.getInt(SettingsProvider.RESULT_ROWS_DELETED) == 1);
             } catch (RemoteException e) {
@@ -305,7 +261,7 @@
                 if (namespace != null) {
                     args.putString(Settings.CALL_METHOD_PREFIX_KEY, namespace);
                 }
-                Bundle b = provider.call(resolveCallingPackage(),
+                Bundle b = provider.call(resolveCallingPackage(), Settings.AUTHORITY,
                         Settings.CALL_METHOD_LIST_CONFIG, null, args);
                 if (b != null) {
                     Map<String, String> flagsToValues =
@@ -322,20 +278,6 @@
             return lines;
         }
 
-        private void reset(IContentProvider provider, int resetMode, @Nullable String namespace) {
-            try {
-                Bundle args = new Bundle();
-                args.putInt(Settings.CALL_METHOD_USER_KEY,
-                        ActivityManager.getService().getCurrentUser().id);
-                args.putInt(Settings.CALL_METHOD_RESET_MODE_KEY, resetMode);
-                args.putString(Settings.CALL_METHOD_PREFIX_KEY, namespace);
-                provider.call(
-                        resolveCallingPackage(), Settings.CALL_METHOD_RESET_CONFIG, null, args);
-            } catch (RemoteException e) {
-                throw new RuntimeException("Failed in IPC", e);
-            }
-        }
-
         private static String resolveCallingPackage() {
             switch (Binder.getCallingUid()) {
                 case Process.ROOT_UID: {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index e3d3d81..526efcb 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -705,8 +705,11 @@
                 Settings.Global.GPU_DEBUG_LAYERS_GLES,
                 GlobalSettingsProto.Gpu.DEBUG_LAYERS_GLES);
         dumpSetting(s, p,
-                Settings.Global.UPDATED_GFX_DRIVER_DEV_OPT_IN_APP,
-                GlobalSettingsProto.Gpu.UPDATED_GFX_DRIVER_DEV_OPT_IN_APP);
+                Settings.Global.GUP_DEV_OPT_IN_APPS,
+                GlobalSettingsProto.Gpu.GUP_DEV_OPT_IN_APPS);
+        dumpSetting(s, p,
+                Settings.Global.GUP_BLACK_LIST,
+                GlobalSettingsProto.Gpu.GUP_BLACK_LIST);
         p.end(gpuToken);
 
         final long hdmiToken = p.start(GlobalSettingsProto.HDMI);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index b071355..bce5593 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -63,6 +63,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.UserManagerInternal;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
@@ -195,13 +196,6 @@
     private static final Set<String> OVERLAY_ALLOWED_SYSTEM_INSTANT_APP_SETTINGS = new ArraySet<>();
     private static final Set<String> OVERLAY_ALLOWED_SECURE_INSTANT_APP_SETTINGS = new ArraySet<>();
 
-    /**
-     * TODO(b/113100523): Move this to DeviceConfig.java when it is added, and expose it as a System
-     *     API.
-     */
-    private static final Uri CONFIG_CONTENT_URI =
-            Uri.parse("content://" + Settings.AUTHORITY + "/config");
-
     static {
         for (String name : Resources.getSystem().getStringArray(
                 com.android.internal.R.array.config_allowedGlobalInstantAppSettings)) {
@@ -1103,7 +1097,7 @@
                 case MUTATION_OPERATION_INSERT: {
                     return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_CONFIG,
                             UserHandle.USER_SYSTEM, name, value, null, makeDefault, true,
-                            getCallingPackage(), false, null);
+                            resolveCallingPackage(), false, null);
                 }
 
                 case MUTATION_OPERATION_DELETE: {
@@ -1113,7 +1107,7 @@
 
                 case MUTATION_OPERATION_RESET: {
                     mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_CONFIG,
-                            UserHandle.USER_SYSTEM, getCallingPackage(), mode, null, prefix);
+                            UserHandle.USER_SYSTEM, resolveCallingPackage(), mode, null, prefix);
                 } return true;
             }
         }
@@ -2253,6 +2247,22 @@
         return !(TextUtils.isEmpty(key) || SettingsState.isBinary(key));
     }
 
+    private String resolveCallingPackage() {
+        switch (Binder.getCallingUid()) {
+            case Process.ROOT_UID: {
+                return "root";
+            }
+
+            case Process.SHELL_UID: {
+                return "com.android.shell";
+            }
+
+            default: {
+                return getCallingPackage();
+            }
+        }
+    }
+
     private static final class Arguments {
         private static final Pattern WHERE_PATTERN_WITH_PARAM_NO_BRACKETS =
                 Pattern.compile("[\\s]*name[\\s]*=[\\s]*\\?[\\s]*");
@@ -3148,8 +3158,8 @@
 
         private Uri getNotificationUriFor(int key, String name) {
             if (isConfigSettingsKey(key)) {
-                return (name != null) ? Uri.withAppendedPath(CONFIG_CONTENT_URI, name)
-                        : CONFIG_CONTENT_URI;
+                return (name != null) ? Uri.withAppendedPath(DeviceConfig.CONTENT_URI, name)
+                        : DeviceConfig.CONTENT_URI;
             } else if (isGlobalSettingsKey(key)) {
                 return (name != null) ? Uri.withAppendedPath(Settings.Global.CONTENT_URI, name)
                         : Settings.Global.CONTENT_URI;
@@ -4131,10 +4141,12 @@
                             Secure.CHARGING_SOUNDS_ENABLED);
 
                     if (!globalChargingSoundEnabled.isNull()) {
-                        secureSettings.insertSettingLocked(
-                                Secure.CHARGING_SOUNDS_ENABLED,
-                                globalChargingSoundEnabled.getValue(), null, false,
-                                SettingsState.SYSTEM_PACKAGE_NAME);
+                        if (secureChargingSoundsEnabled.isNull()) {
+                            secureSettings.insertSettingLocked(
+                                    Secure.CHARGING_SOUNDS_ENABLED,
+                                    globalChargingSoundEnabled.getValue(), null, false,
+                                    SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
 
                         // set global charging_sounds_enabled setting to null since it's deprecated
                         globalSettings.insertSettingLocked(
diff --git a/packages/SettingsProvider/test/Android.mk b/packages/SettingsProvider/test/Android.mk
index 0d681ed..ac97adb 100644
--- a/packages/SettingsProvider/test/Android.mk
+++ b/packages/SettingsProvider/test/Android.mk
@@ -10,7 +10,7 @@
     ../src/com/android/providers/settings/SettingsState.java \
     ../src/com/android/providers/settings/SettingsHelper.java
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
 
 LOCAL_JAVA_LIBRARIES := android.test.base
 
diff --git a/packages/SettingsProvider/test/AndroidManifest.xml b/packages/SettingsProvider/test/AndroidManifest.xml
index 71e0b15..87a4f60 100644
--- a/packages/SettingsProvider/test/AndroidManifest.xml
+++ b/packages/SettingsProvider/test/AndroidManifest.xml
@@ -29,7 +29,7 @@
     </application>
 
     <instrumentation
-        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.providers.setting.test"
         android:label="Settings Provider Tests" />
 
diff --git a/packages/SettingsProvider/test/AndroidTest.xml b/packages/SettingsProvider/test/AndroidTest.xml
index 46b8f94..9d23526 100644
--- a/packages/SettingsProvider/test/AndroidTest.xml
+++ b/packages/SettingsProvider/test/AndroidTest.xml
@@ -22,7 +22,7 @@
     <option name="test-tag" value="SettingsProviderTest" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.providers.setting.test" />
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
         <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/BaseSettingsProviderTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/BaseSettingsProviderTest.java
index ab23af3..68efa67 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/BaseSettingsProviderTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/BaseSettingsProviderTest.java
@@ -25,9 +25,12 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
 import libcore.io.Streams;
+
 import org.junit.runner.RunWith;
 
 import java.io.FileInputStream;
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/DeviceConfigServiceTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/DeviceConfigServiceTest.java
index 9d0462e..df4656a 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/DeviceConfigServiceTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/DeviceConfigServiceTest.java
@@ -21,11 +21,12 @@
 import static junit.framework.Assert.assertNull;
 
 import android.content.ContentResolver;
-import android.net.Uri;
 import android.os.Bundle;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
 
 import libcore.io.Streams;
 
@@ -43,12 +44,6 @@
  */
 @RunWith(AndroidJUnit4.class)
 public class DeviceConfigServiceTest {
-    /**
-     * TODO(b/113100523): Move this to DeviceConfig.java when it is added, and expose it as a System
-     *     API.
-     */
-    private static final Uri CONFIG_CONTENT_URI =
-            Uri.parse("content://" + Settings.AUTHORITY + "/config");
     private static final String sNamespace = "namespace1";
     private static final String sKey = "key1";
     private static final String sValue = "value1";
@@ -152,7 +147,7 @@
         // make sValue the default value
         executeShellCommand(
                 "device_config put " + sNamespace + " " + sKey + " " + sValue + " default");
-        // make newValue the current value
+        // make newValue the current value (as set by a trusted package)
         executeShellCommand(
                 "device_config put " + sNamespace + " " + sKey + " " + newValue);
         String result = getFromContentProvider(mContentResolver, sNamespace, sKey);
@@ -161,14 +156,14 @@
         // reset values that were set by untrusted packages
         executeShellCommand("device_config reset untrusted_defaults " + sNamespace);
         result = getFromContentProvider(mContentResolver, sNamespace, sKey);
-        // the default value has been restored
-        assertEquals(sValue, result);
+        // the current value was set by a trusted package, so it's not reset
+        assertEquals(newValue, result);
 
-        // clear values that were set by untrusted packages
+        // reset values that were set by untrusted or trusted packages
         executeShellCommand("device_config reset trusted_defaults " + sNamespace);
         result = getFromContentProvider(mContentResolver, sNamespace, sKey);
-        // even the default value is gone now
-        assertNull(result);
+        // the default value has been restored
+        assertEquals(sValue, result);
     }
 
     private static void executeShellCommand(String command) throws IOException {
@@ -190,14 +185,15 @@
         if (makeDefault) {
             args.putBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY, true);
         }
-        resolver.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, compositeName, args);
+        resolver.call(
+                DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, compositeName, args);
     }
 
     private static String getFromContentProvider(ContentResolver resolver, String namespace,
             String key) {
         String compositeName = namespace + "/" + key;
         Bundle result = resolver.call(
-                CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, compositeName, null);
+                DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, compositeName, null);
         assertNotNull(result);
         return result.getString(Settings.NameValueTable.VALUE);
     }
@@ -206,7 +202,7 @@
             String key) {
         String compositeName = namespace + "/" + key;
         Bundle result = resolver.call(
-                CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, compositeName, null);
+                DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, compositeName, null);
         assertNotNull(result);
         return compositeName.equals(result.getString(Settings.NameValueTable.VALUE));
     }
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/InstallNonMarketAppsDeprecationTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/InstallNonMarketAppsDeprecationTest.java
index d8ee9b6..863b035 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/InstallNonMarketAppsDeprecationTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/InstallNonMarketAppsDeprecationTest.java
@@ -26,10 +26,11 @@
 import android.os.SystemClock;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java
index 1074957..54f8688 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java
@@ -24,8 +24,10 @@
 import android.net.Uri;
 import android.os.Build;
 import android.provider.Settings;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
index 6fa014d..d112fac 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
@@ -17,19 +17,10 @@
 package com.android.providers.settings;
 
 import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertSame;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.fail;
-
-import com.android.internal.app.LocalePicker;
-import com.android.providers.settings.SettingsHelper;
 
 import android.os.LocaleList;
-import android.support.test.runner.AndroidJUnit4;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 5fe08aa..bff2c84 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -27,6 +27,7 @@
     <uses-permission android:name="android.permission.READ_SMS" />
     <uses-permission android:name="android.permission.CALL_PHONE" />
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.READ_PRECISE_PHONE_STATE" />
     <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <uses-permission android:name="android.permission.WRITE_CONTACTS" />
@@ -81,6 +82,7 @@
     <uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
     <uses-permission android:name="android.permission.DELETE_CACHE_FILES" />
     <uses-permission android:name="android.permission.DELETE_PACKAGES" />
+    <uses-permission android:name="android.permission.MANAGE_ROLLBACKS" />
     <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
     <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
     <uses-permission android:name="android.permission.DEVICE_POWER" />
diff --git a/packages/Shell/res/values-as/strings.xml b/packages/Shell/res/values-as/strings.xml
index 0e59223..6e80931 100644
--- a/packages/Shell/res/values-as/strings.xml
+++ b/packages/Shell/res/values-as/strings.xml
@@ -19,7 +19,7 @@
     <string name="app_label" msgid="3701846017049540910">"শ্বেল"</string>
     <string name="bugreport_notification_channel" msgid="2574150205913861141">"বাগ সম্পৰ্কীয় প্ৰতিবেদন"</string>
     <string name="bugreport_in_progress_title" msgid="4311705936714972757">"বাগ সম্পর্কীয় অভিযোগ <xliff:g id="ID">#%d</xliff:g> সৃষ্টি কৰি থকা হৈছে"</string>
-    <string name="bugreport_finished_title" msgid="4429132808670114081">"বাগ সম্পর্কীয় অভিযোগ <xliff:g id="ID">#%d</xliff:g> ৰেকৰ্ড কৰা হ\'ল"</string>
+    <string name="bugreport_finished_title" msgid="4429132808670114081">"বাগ সম্পর্কীয় অভিযোগ <xliff:g id="ID">#%d</xliff:g> ৰেকৰ্ড কৰা হ’ল"</string>
     <string name="bugreport_updating_title" msgid="4423539949559634214">"বাগ সম্পর্কীয় অভিযোগটোত বিশদ তথ্য যোগ কৰি থকা হৈছে"</string>
     <string name="bugreport_updating_wait" msgid="3322151947853929470">"অনুগ্রহ কৰি অপেক্ষা কৰক…"</string>
     <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"ফ\'নত বাগ সম্পর্কীয় অভিযোগ অতি সোনকালে উপলব্ধ হ\'ব"</string>
diff --git a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
index 8e8d9f7..abe82a8 100644
--- a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
+++ b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
@@ -18,7 +18,7 @@
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
-import android.os.SystemProperties;
+import android.sysprop.SetupWizardProperties;
 import android.text.TextUtils;
 import android.view.View;
 import android.widget.Button;
@@ -51,7 +51,7 @@
         // Setup theme for aosp/pixel
         setTheme(
                 WizardManagerHelper.getThemeRes(
-                        SystemProperties.get("setupwizard.theme"),
+                        SetupWizardProperties.theme().orElse(""),
                         R.style.SuwThemeGlif_Light
                 )
         );
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 75492f3..8be67d9 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -44,7 +44,6 @@
         "SystemUIPluginLib",
         "SystemUISharedLib",
         "SettingsLib",
-        "androidx.car_car",
         "androidx.legacy_legacy-support-v4",
         "androidx.recyclerview_recyclerview",
         "androidx.preference_preference",
@@ -67,8 +66,6 @@
 
     libs: [
         "telephony-common",
-        "android.car",
-        "android.car.userlib",
     ],
 
     aaptflags: [
@@ -98,7 +95,6 @@
         "SystemUIPluginLib",
         "SystemUISharedLib",
         "SettingsLib",
-        "androidx.car_car",
         "androidx.legacy_legacy-support-v4",
         "androidx.recyclerview_recyclerview",
         "androidx.preference_preference",
@@ -119,18 +115,19 @@
         "mockito-target-inline-minus-junit4",
         "testables",
         "truth-prebuilt",
+        "dagger2-2.19",
+        "jsr330"
     ],
     libs: [
         "android.test.runner",
         "telephony-common",
-        "android.car",
-        "android.car.userlib",
         "android.test.base",
     ],
     aaptflags: [
         "--extra-packages",
         "com.android.keyguard:com.android.systemui",
     ],
+    annotation_processors: ["dagger2-compiler-2.19"],
 }
 
 android_app {
@@ -140,6 +137,7 @@
     ],
 
     platform_apis: true,
+    product_specific: true,
     certificate: "platform",
     privileged: true,
 
@@ -149,8 +147,6 @@
 
     libs: [
         "telephony-common",
-        "android.car",
-        "android.car.userlib",
     ],
 
     dxflags: ["--multi-dex"],
@@ -158,6 +154,7 @@
         "--extra-packages",
         "com.android.keyguard",
     ],
+    required: ["privapp_whitelist_com.android.systemui"],
 
 }
 
@@ -186,8 +183,6 @@
     ],
     libs: [
         "telephony-common",
-        "android.car",
-        "android.car.userlib",
     ],
 
     srcs: [
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 7d53c2f..1c1a140 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -77,6 +77,7 @@
     <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
     <uses-permission android:name="android.permission.MASTER_CLEAR" />
     <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="android.permission.MANAGE_SENSOR_PRIVACY" />
 
     <!-- ActivityManager -->
     <uses-permission android:name="android.permission.REAL_GET_TASKS" />
diff --git a/packages/SystemUI/docs/dagger.md b/packages/SystemUI/docs/dagger.md
new file mode 100644
index 0000000..cca2d13
--- /dev/null
+++ b/packages/SystemUI/docs/dagger.md
@@ -0,0 +1,202 @@
+# Dagger 2 in SystemUI
+*Dagger 2 is a dependency injection framework that compiles annotations to code
+to create dependencies without reflection*
+
+## Recommended reading
+
+Go read about Dagger 2.
+
+ - [User's guide](https://google.github.io/dagger/users-guide)
+
+TODO: Add some links.
+
+## State of the world
+
+Dagger 2 has been turned on for SystemUI and a early first pass has been taken
+for converting everything in Dependency.java to use Dagger. Since a lot of
+SystemUI depends on Dependency, stubs have been added to Dependency to proxy
+any gets through to the instances provided by dagger, this will allow migration
+of SystemUI through a number of CLs.
+
+### How it works in SystemUI
+
+For the classes that we're using in Dependency and are switching to dagger, the
+equivalent dagger version is using @Singleton and only having one instance.
+To have the single instance span all of SystemUI and be easily accessible for
+other components, there is a single root Component that exists that generates
+these. The component lives in SystemUIFactory and is called SystemUIRootComponent.
+
+```java
+@Singleton
+@Component(modules = {SystemUIFactory.class, DependencyProvider.class, ContextHolder.class})
+public interface SystemUIRootComponent {
+    @Singleton
+    Dependency.DependencyInjector createDependency();
+}
+```
+
+The root modules are what provides the global singleton dependencies across
+SystemUI. ContextHolder is just a wrapper that provides a context.
+SystemUIFactory @Provide dependencies that need to be overridden by SystemUI
+variants (like other form factors). DependencyBinder creates the mapping from
+interfaces to implementation classes. DependencyProvider provides or binds any
+remaining depedencies required.
+
+### 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 that 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.
+
+```java
+public static class DependencyCreator implements Injector {
+    @Override
+    public SystemUI apply(Context context) {
+        return SystemUIFactory.getInstance().getRootComponent()
+                .createDependency()
+                .createSystemUI();
+    }
+}
+```
+
+### Adding a new injectable object
+
+First tag the constructor with @Inject. Also tag it with @Singleton if only one
+instance should be created.
+
+```java
+@Singleton
+public class SomethingController {
+  @Inject
+  public SomethingController(Context context,
+    @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+      // context and mainHandler will be automatically populated.
+  }
+}
+```
+
+If you have an interface class and an implementation class, dagger needs to know
+how to map it. The simplest way to do this is to add a provides method to
+DependencyProvider.
+
+```java
+public class DependencyProvider {
+  ...
+  @Singleton
+  @Provide
+  public SomethingController provideSomethingController(Context context,
+      @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+    return new SomethingControllerImpl(context, mainHandler);
+  }
+}
+```
+
+If you need to access this from Dependency#get, then add an adapter to Dependency
+that maps to the instance provided by Dagger. The changes should be similar
+to the following diff.
+
+```java
+public class Dependency {
+  ...
+  @Inject Lazy<SomethingController> mSomethingController;
+  ...
+  public void start() {
+    ...
+    mProviders.put(SomethingController.class, mSomethingController::get);
+  }
+}
+```
+
+### Using injection with Fragments
+
+Fragments are created as part of the FragmentManager, so they need to be
+setup so the manager knows how to create them. To do that, add a method
+to com.android.systemui.fragments.FragmentService$FragmentCreator that
+returns your fragment class. Thats all thats required, once the method
+exists, FragmentService will automatically pick it up and use injection
+whenever your fragment needs to be created.
+
+```java
+public interface FragmentCreator {
++   NavigationBarFragment createNavigationBar();
+}
+```
+
+If you need to create your fragment (i.e. for the add or replace transaction),
+then the FragmentHostManager can do this for you.
+
+```java
+FragmentHostManager.get(view).create(NavigationBarFragment.class);
+```
+
+### 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.
+
+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.
+
+```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) {
+    ...
+}
+```
+
+## TODO List
+
+ - Eliminate usages of Dependency#get
+ - Add links in above TODO
diff --git a/packages/SystemUI/legacy/recents/res/drawable/ic_lock_to_app_24dp.xml b/packages/SystemUI/legacy/recents/res/drawable/ic_lock_to_app_24dp.xml
deleted file mode 100644
index 2d77949..0000000
--- a/packages/SystemUI/legacy/recents/res/drawable/ic_lock_to_app_24dp.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24.0dp"
-        android:height="24.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-
-    <path
-        android:fillColor="@color/recents_task_view_lock_to_app_button_color"
-        android:pathData="M18.0,8.0l-1.0,0.0L17.0,6.0c0.0,-2.8 -2.2,-5.0 -5.0,-5.0C9.2,1.0 7.0,3.2 7.0,6.0l0.0,2.0L6.0,8.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,10.0c0.0,1.1 0.9,2.0 2.0,2.0l12.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L20.0,10.0C20.0,8.9 19.1,8.0 18.0,8.0zM12.0,17.0c-1.1,0.0 -2.0,-0.9 -2.0,-2.0s0.9,-2.0 2.0,-2.0c1.1,0.0 2.0,0.9 2.0,2.0S13.1,17.0 12.0,17.0zM15.1,8.0L8.9,8.0L8.9,6.0c0.0,-1.7 1.4,-3.1 3.1,-3.1c1.7,0.0 3.1,1.4 3.1,3.1L15.1,8.0z"/>
-</vector>
diff --git a/packages/SystemUI/legacy/recents/res/drawable/recents_freeform_workspace_bg.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_freeform_workspace_bg.xml
deleted file mode 100644
index 5f9341c..0000000
--- a/packages/SystemUI/legacy/recents/res/drawable/recents_freeform_workspace_bg.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.
--->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:shape="rectangle">
-    <corners android:topLeftRadius="@dimen/recents_task_view_rounded_corners_radius"
-             android:topRightRadius="@dimen/recents_task_view_rounded_corners_radius"/>
-    <solid android:color="#00000000" />
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/drawable/recents_move_task_freeform_dark.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_move_task_freeform_dark.xml
deleted file mode 100644
index 9a060b4..0000000
--- a/packages/SystemUI/legacy/recents/res/drawable/recents_move_task_freeform_dark.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
-Copyright (C) 2015 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-
-    <group
-            android:translateX="-286.000000"
-            android:translateY="-602.000000">
-        <group
-                android:translateX="109.000000"
-                android:translateY="514.000000">
-            <group
-                    android:translateX="178.000000"
-                    android:translateY="89.000000">
-                <path
-                    android:strokeColor="@color/recents_task_bar_dark_icon_color"
-                    android:strokeWidth="2"
-                    android:pathData="M10,12 L10,3 L19,3 L19,5 L19,11 L19,12 L10,12 Z" />
-                <path
-                    android:strokeColor="@color/recents_task_bar_dark_icon_color"
-                    android:strokeWidth="2"
-                    android:pathData="M15,17 L5,17 L5,7 L5,17 Z" />
-            </group>
-        </group>
-    </group>
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/drawable/recents_move_task_freeform_light.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_move_task_freeform_light.xml
deleted file mode 100644
index b8acedb..0000000
--- a/packages/SystemUI/legacy/recents/res/drawable/recents_move_task_freeform_light.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
-Copyright (C) 2015 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-
-    <group
-            android:translateX="-286.000000"
-            android:translateY="-602.000000">
-        <group
-                android:translateX="109.000000"
-                android:translateY="514.000000">
-            <group
-                    android:translateX="178.000000"
-                    android:translateY="89.000000">
-                <path
-                    android:strokeColor="@color/recents_task_bar_light_icon_color"
-                    android:strokeWidth="2"
-                    android:pathData="M10,12 L10,3 L19,3 L19,5 L19,11 L19,12 L10,12 Z" />
-                <path
-                    android:strokeColor="@color/recents_task_bar_light_icon_color"
-                    android:strokeWidth="2"
-                    android:pathData="M15,17 L5,17 L5,7 L5,17 Z" />
-            </group>
-        </group>
-    </group>
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index 4891e50..5317a6d 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -32,10 +32,11 @@
     void startPendingIntentDismissingKeyguard(PendingIntent intent);
 
     /**
-     * Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent, Runnable)}, but
-     * allow you to specify the callback that is executed after the intent is sent.
+     * Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent, Runnable)}, but allows
+     * you to specify the callback that is executed on the UI thread after the intent is sent.
      */
-    void startPendingIntentDismissingKeyguard(PendingIntent intent, Runnable intentSentCallback);
+    void startPendingIntentDismissingKeyguard(PendingIntent intent,
+            Runnable intentSentUiThreadCallback);
     void startActivity(Intent intent, boolean dismissShade);
     void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade);
     void startActivity(Intent intent, boolean dismissShade, Callback callback);
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
index 1a18f60..ac69043 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
@@ -36,6 +36,13 @@
     View getView();
 
     /**
+     * Get clock view for a large clock that appears behind NSSL.
+     */
+    default View getBigClockView() {
+        return null;
+    }
+
+    /**
      * Set clock paint style.
      * @param style The new style to set in the paint.
      */
@@ -62,4 +69,12 @@
      * Notifies that the time zone has changed.
      */
     default void onTimeZoneChanged(TimeZone timeZone) {}
+
+    /**
+     * Indicates whether the keyguard status area (date) should be shown below
+     * the clock.
+     */
+    default boolean shouldShowStatusArea() {
+        return true;
+    }
 }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java
new file mode 100644
index 0000000..c7bc858
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.plugins;
+
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.plugins.annotations.DependsOn;
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/**
+ * Dispatches events to {@link DarkReceiver}s about changes in darkness, tint area and dark
+ * intensity. Accessible through {@link PluginDependency}
+ */
+@ProvidesInterface(version = DarkIconDispatcher.VERSION)
+@DependsOn(target = DarkReceiver.class)
+public interface DarkIconDispatcher {
+    int VERSION = 1;
+
+    /**
+     * Sets the dark area so {@link #applyDark} only affects the icons in the specified area.
+     *
+     * @param r the area in which icons should change its tint, in logical screen
+     *                 coordinates
+     */
+    void setIconsDarkArea(Rect r);
+
+    /**
+     * Adds a receiver to receive callbacks onDarkChanged
+     */
+    void addDarkReceiver(DarkReceiver receiver);
+
+    /**
+     * Adds a receiver to receive callbacks onDarkChanged
+     */
+    void addDarkReceiver(ImageView imageView);
+
+    /**
+     * Must have been previously been added through one of the addDarkReceive methods above.
+     */
+    void removeDarkReceiver(DarkReceiver object);
+
+    /**
+     * Must have been previously been added through one of the addDarkReceive methods above.
+     */
+    void removeDarkReceiver(ImageView object);
+
+    /**
+     * Used to reapply darkness on an object, must have previously been added through
+     * addDarkReceiver.
+      */
+    void applyDark(DarkReceiver object);
+
+    int DEFAULT_ICON_TINT = Color.WHITE;
+    Rect sTmpRect = new Rect();
+    int[] sTmpInt2 = new int[2];
+
+    /**
+     * @return the tint to apply to view depending on the desired tint color and
+     *         the screen tintArea in which to apply that tint
+     */
+    static int getTint(Rect tintArea, View view, int color) {
+        if (isInArea(tintArea, view)) {
+            return color;
+        } else {
+            return DEFAULT_ICON_TINT;
+        }
+    }
+
+    /**
+     * @return the dark intensity to apply to view depending on the desired dark
+     *         intensity and the screen tintArea in which to apply that intensity
+     */
+    static float getDarkIntensity(Rect tintArea, View view, float intensity) {
+        if (isInArea(tintArea, view)) {
+            return intensity;
+        } else {
+            return 0f;
+        }
+    }
+
+    /**
+     * @return true if more than half of the view area are in area, false
+     *         otherwise
+     */
+    static boolean isInArea(Rect area, View view) {
+        if (area.isEmpty()) {
+            return true;
+        }
+        sTmpRect.set(area);
+        view.getLocationOnScreen(sTmpInt2);
+        int left = sTmpInt2[0];
+
+        int intersectStart = Math.max(left, area.left);
+        int intersectEnd = Math.min(left + view.getWidth(), area.right);
+        int intersectAmount = Math.max(0, intersectEnd - intersectStart);
+
+        boolean coversFullStatusBar = area.top <= 0;
+        boolean majorityOfWidth = 2 * intersectAmount > view.getWidth();
+        return majorityOfWidth && coversFullStatusBar;
+    }
+
+    /**
+     * Receives a callback on darkness changes
+     */
+    @ProvidesInterface(version = DarkReceiver.VERSION)
+    interface DarkReceiver {
+        int VERSION = 1;
+        void onDarkChanged(Rect area, float darkIntensity, int tint);
+    }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
index 88b8dd8..fbd863d 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
@@ -11,13 +11,12 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.systemui.plugins;
 
-import android.hardware.Sensor;
-import android.hardware.TriggerEventListener;
+import android.hardware.SensorListener;
 
 import com.android.systemui.plugins.annotations.ProvidesInterface;
 
@@ -31,26 +30,30 @@
     int VERSION = 1;
 
     /**
-     * Registers for trigger events from the sensor. Trigger events are one-shot and need to
-     * re-registered in order for them to be fired again.
+     * Registers for sensor events. Events will be sent until the listener is unregistered.
      * @param sensor
      * @param listener
-     * @see android.hardware.SensorManager#requestTriggerSensor(
-     *     android.hardware.TriggerEventListener, android.hardware.Sensor)
+     * @see android.hardware.SensorManager#registerListener(SensorListener, int)
      */
-    void registerTriggerEvent(Sensor sensor, TriggerEventListener listener);
+    void registerListener(Sensor sensor, SensorEventListener listener);
 
     /**
-     * Unregisters trigger events from the sensor.
+     * Unregisters events from the sensor.
      * @param sensor
      * @param listener
      */
-    void unregisterTriggerEvent(Sensor sensor, TriggerEventListener listener);
+    void unregisterListener(Sensor sensor, SensorEventListener listener);
 
-    interface TriggerEventListener {
-        void onTrigger(TriggerEvent event);
+    /**
+     * Listener triggered whenever the Sensor has new data.
+     */
+    interface SensorEventListener {
+        void onSensorChanged(SensorEvent event);
     }
 
+    /**
+     * Sensor that can be defined in a plugin.
+     */
     class Sensor {
         public static final int TYPE_WAKE_LOCK_SCREEN = 1;
         public static final int TYPE_WAKE_DISPLAY = 2;
@@ -67,29 +70,32 @@
         }
     }
 
-    class TriggerEvent {
+    /**
+     * Event sent by a {@link Sensor}.
+     */
+    class SensorEvent {
         Sensor mSensor;
         int mVendorType;
         float[] mValues;
 
         /**
-         * Creates a trigger event
+         * Creates a sensor event.
          * @param sensor The type of sensor, e.g. TYPE_WAKE_LOCK_SCREEN
          * @param vendorType The vendor type, which should be unique for each type of sensor,
          *                   e.g. SINGLE_TAP = 1, DOUBLE_TAP = 2, etc.
          */
-        public TriggerEvent(Sensor sensor, int vendorType) {
+        public SensorEvent(Sensor sensor, int vendorType) {
             this(sensor, vendorType, null);
         }
 
         /**
-         * Creates a trigger event
+         * Creates a sensor event.
          * @param sensor The type of sensor, e.g. TYPE_WAKE_LOCK_SCREEN
          * @param vendorType The vendor type, which should be unique for each type of sensor,
          *                   e.g. SINGLE_TAP = 1, DOUBLE_TAP = 2, etc.
          * @param values Values captured by the sensor.
          */
-        public TriggerEvent(Sensor sensor, int vendorType, float[] values) {
+        public SensorEvent(Sensor sensor, int vendorType, float[] values) {
             mSensor = sensor;
             mVendorType = vendorType;
             mValues = values;
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index ee94aed..22b0ab7 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -17,6 +17,7 @@
 -keep class com.android.systemui.car.CarSystemUIFactory
 -keep class com.android.systemui.SystemUIFactory
 -keep class * extends com.android.systemui.SystemUI
+-keep class * implements com.android.systemui.SystemUI$Injector
 
 -keepclasseswithmembers class * {
     public <init>(android.content.Context, android.util.AttributeSet);
@@ -27,4 +28,10 @@
 -keep class com.android.systemui.plugins.** {
     *;
 }
+-keep class com.android.systemui.fragments.FragmentService$FragmentCreator {
+    *;
+}
+-keep class com.android.systemui.util.InjectionInflationController$ViewInstanceCreator {
+    *;
+}
 -keep class androidx.core.app.CoreComponentFactory
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 89b873e..d52866f 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -20,19 +20,31 @@
 <!-- This is a view that shows clock information in Keyguard. -->
 <com.android.keyguard.KeyguardClockSwitch
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/keyguard_clock_container"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:layout_gravity="center_horizontal"
-    android:layout_alignParentTop="true">
-    <TextClock
-        android:id="@+id/default_clock_view"
-        android:layout_width="wrap_content"
+    android:layout_gravity="center_horizontal|top">
+    <FrameLayout
+         android:id="@+id/clock_view"
+         android:layout_width="match_parent"
+         android:layout_height="wrap_content"
+         android:layout_gravity="center_horizontal"
+         android:layout_alignParentTop="true">
+        <TextClock
+             android:id="@+id/default_clock_view"
+             android:layout_width="wrap_content"
+             android:layout_height="wrap_content"
+             android:layout_gravity="center_horizontal"
+             android:letterSpacing="0.03"
+             android:textColor="?attr/wallpaperTextColor"
+             android:singleLine="true"
+             style="@style/widget_big"
+             android:format12Hour="@string/keyguard_widget_12_hours_format"
+             android:format24Hour="@string/keyguard_widget_24_hours_format" />
+    </FrameLayout>
+    <include layout="@layout/keyguard_status_area"
+        android:id="@+id/keyguard_status_area"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal"
-        android:letterSpacing="0.03"
-        android:textColor="?attr/wallpaperTextColor"
-        android:singleLine="true"
-        style="@style/widget_big_thin"
-        android:format12Hour="@string/keyguard_widget_12_hours_format"
-        android:format24Hour="@string/keyguard_widget_24_hours_format" />
+        android:layout_below="@id/clock_view" />
 </com.android.keyguard.KeyguardClockSwitch>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
index 7d8a1f5b..a9ba19d 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
@@ -33,21 +33,11 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="vertical">
-            <RelativeLayout
+            <include
+                layout="@layout/keyguard_clock_switch"
                 android:id="@+id/keyguard_clock_container"
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center_horizontal|top">
-                <include layout="@layout/keyguard_clock_switch"
-                         android:id="@+id/clock_view"
-                         android:layout_width="match_parent"
-                         android:layout_height="wrap_content" />
-                <include layout="@layout/keyguard_status_area"
-                         android:id="@+id/keyguard_status_area"
-                         android:layout_width="match_parent"
-                         android:layout_height="wrap_content"
-                         android:layout_below="@id/clock_view" />
-            </RelativeLayout>
+                android:layout_height="wrap_content" />
             <ImageView
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index 32a7147..10fea9d 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -44,27 +44,17 @@
             android:paddingLeft="@dimen/logout_button_padding_horizontal"
             android:paddingRight="@dimen/logout_button_padding_horizontal"
             android:background="@drawable/logout_button_background"
-            android:fontFamily="roboto-medium"
+            android:fontFamily="@*android:string/config_bodyFontFamilyMedium"
             android:textAllCaps="true"
             android:textColor="?android:attr/textColorPrimary"
             android:textSize="13sp"
             android:text="@*android:string/global_action_logout" />
 
-        <RelativeLayout
+        <include
+            layout="@layout/keyguard_clock_switch"
             android:id="@+id/keyguard_clock_container"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_horizontal|top">
-            <include layout="@layout/keyguard_clock_switch"
-                 android:id="@+id/clock_view"
-                 android:layout_width="match_parent"
-                 android:layout_height="wrap_content" />
-            <include layout="@layout/keyguard_status_area"
-                android:id="@+id/keyguard_status_area"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_below="@id/clock_view" />
-        </RelativeLayout>
+            android:layout_height="wrap_content" />
 
         <TextView
             android:id="@+id/owner_info"
diff --git a/packages/SystemUI/res-keyguard/values-as/strings.xml b/packages/SystemUI/res-keyguard/values-as/strings.xml
index 89c468d..258219a 100644
--- a/packages/SystemUI/res-keyguard/values-as/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-as/strings.xml
@@ -71,7 +71,7 @@
     <string name="kg_pattern_instructions" msgid="5547646893001491340">"আপোনাৰ আৰ্হি আঁকক"</string>
     <string name="kg_sim_pin_instructions" msgid="6389000973113699187">"ছিমৰ পিন দিয়ক।"</string>
     <string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\"ৰ ছিমৰ পিন দিয়ক।"</string>
-    <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> ম\'বাইল সেৱা অবিহনে ডিভাইচ ব্যৱহাৰ কৰিবলৈ ই-ছিম অক্ষম কৰক।"</string>
+    <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> ম’বাইল সেৱা অবিহনে ডিভাইচ ব্যৱহাৰ কৰিবলৈ ই-ছিম অক্ষম কৰক।"</string>
     <string name="kg_pin_instructions" msgid="4069609316644030034">"পিন দিয়ক"</string>
     <string name="kg_password_instructions" msgid="136952397352976538">"পাছৱৰ্ড দিয়ক"</string>
     <string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"ছিমখন বর্তমান অক্ষম অৱস্থাত আছে। অব্যাহত ৰাখিবলৈ PUK ক\'ড লিখক। সবিশেষ জানিবলৈ বাহকৰ সৈতে যোগাযোগ কৰক।"</string>
@@ -113,7 +113,7 @@
     </plurals>
     <string name="kg_password_pin_failed" msgid="8769990811451236223">"ছিম পিনৰ জৰিয়তে আনলক কৰিব পৰা নগ\'ল!"</string>
     <string name="kg_password_puk_failed" msgid="1331621440873439974">"ছিম PUKৰ জৰিয়তে আনলক কৰিব পৰা নগ\'ল!"</string>
-    <string name="kg_pin_accepted" msgid="7637293533973802143">"ক\'ড গ্ৰহণ কৰা হ\'ল!"</string>
+    <string name="kg_pin_accepted" msgid="7637293533973802143">"ক\'ড গ্ৰহণ কৰা হ’ল!"</string>
     <string name="keyguard_carrier_default" msgid="4274828292998453695">"কোনো সেৱা নাই।"</string>
     <string name="accessibility_ime_switch_button" msgid="2695096475319405612">"ইনপুট পদ্ধতি সলনি কৰক"</string>
     <string name="airplane_mode" msgid="3807209033737676010">"এয়াৰপ্লেন ম\'ড"</string>
@@ -147,7 +147,7 @@
       <item quantity="other">ছিমৰ পিন দিয়ক। আপুনি আৰু <xliff:g id="NUMBER_1">%d</xliff:g>বাৰ প্ৰয়াস কৰিব পাৰে।</item>
     </plurals>
     <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
-      <item quantity="one">ছিমখন অক্ষম হ\'ল। অব্যাহত ৰাখিবলৈ PUK দিয়ক। ছিমখন স্থায়ীভাৱে ব্যৱহাৰৰ অনুপযোগী হোৱাৰ পূৰ্বে আপোনাৰ হাতত <xliff:g id="_NUMBER_1">%d</xliff:g>টা প্ৰয়াস বাকী আছে। সবিশেষ জানিবলৈ বাহকৰ সৈতে যোগাযোগ কৰক।</item>
-      <item quantity="other">ছিমখন অক্ষম হ\'ল। অব্যাহত ৰাখিবলৈ PUK দিয়ক। ছিমখন স্থায়ীভাৱে ব্যৱহাৰৰ অনুপযোগী হোৱাৰ পূৰ্বে আপোনাৰ হাতত <xliff:g id="_NUMBER_1">%d</xliff:g>টা প্ৰয়াস বাকী আছে। সবিশেষ জানিবলৈ বাহকৰ সৈতে যোগাযোগ কৰক।</item>
+      <item quantity="one">ছিমখন অক্ষম হ’ল। অব্যাহত ৰাখিবলৈ PUK দিয়ক। ছিমখন স্থায়ীভাৱে ব্যৱহাৰৰ অনুপযোগী হোৱাৰ পূৰ্বে আপোনাৰ হাতত <xliff:g id="_NUMBER_1">%d</xliff:g>টা প্ৰয়াস বাকী আছে। সবিশেষ জানিবলৈ বাহকৰ সৈতে যোগাযোগ কৰক।</item>
+      <item quantity="other">ছিমখন অক্ষম হ’ল। অব্যাহত ৰাখিবলৈ PUK দিয়ক। ছিমখন স্থায়ীভাৱে ব্যৱহাৰৰ অনুপযোগী হোৱাৰ পূৰ্বে আপোনাৰ হাতত <xliff:g id="_NUMBER_1">%d</xliff:g>টা প্ৰয়াস বাকী আছে। সবিশেষ জানিবলৈ বাহকৰ সৈতে যোগাযোগ কৰক।</item>
     </plurals>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml
index 041e990..c21faea 100644
--- a/packages/SystemUI/res-keyguard/values-ne/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml
@@ -28,8 +28,8 @@
     <string name="keyguard_password_entry_touch_hint" msgid="5790410752696806482"><font size="17">"पासवर्ड टाइप गर्न छुनुहोस्"</font></string>
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"अनलक गर्न पासवर्ड टाइप गर्नुहोस्"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"अनलक गर्न PIN कोड टाइप गर्नुहोस्"</string>
-    <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"आफ्नो PIN प्रविष्ट गर्नुहोस्"</string>
-    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"आफ्नो ढाँचा प्रविष्ट गर्नुहोस्"</string>
+    <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"आफ्नो PIN प्रविष्टि गर्नुहोस्"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"आफ्नो ढाँचा प्रविष्टि गर्नुहोस्"</string>
     <string name="keyguard_enter_your_password" msgid="5397328359341314506">"आफ्नो पासवर्ड प्रविष्ट गर्नु…"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"PIN कोड गलत छ।"</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"अमान्य कार्ड।"</string>
@@ -70,18 +70,18 @@
     </plurals>
     <string name="kg_pattern_instructions" msgid="5547646893001491340">"आफ्नो ढाँचा कोर्नुहोस्"</string>
     <string name="kg_sim_pin_instructions" msgid="6389000973113699187">"SIM को PIN प्रविष्टि गर्नुहोस्।"</string>
-    <string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" को SIM को PIN प्रविष्ट गर्नुहोस्।"</string>
+    <string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" को SIM को PIN प्रविष्टि गर्नुहोस्।"</string>
     <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> मोबाइल सेवा बिना यन्त्रको प्रयोग गर्न eSIM लाई असक्षम पार्नुहोस्।"</string>
     <string name="kg_pin_instructions" msgid="4069609316644030034">"PIN प्रविष्टि गर्नुहोस्"</string>
-    <string name="kg_password_instructions" msgid="136952397352976538">"पासवर्ड प्रविष्ट गर्नुहोस्"</string>
-    <string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM कार्ड अहिले असक्षम छ। सुचारु गर्नको लागि PUK कोड प्रविष्ट गर्नुहोस्।  विवरणको लागि सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।"</string>
-    <string name="kg_puk_enter_puk_hint_multi" msgid="1373131883510840794">"SIM <xliff:g id="CARRIER">%1$s</xliff:g> अहिले असक्षम छ। सुचारु गर्नको लागि PUK कोड प्रविष्ट गर्नुहोस्। विवरणका लागि सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।"</string>
+    <string name="kg_password_instructions" msgid="136952397352976538">"पासवर्ड प्रविष्टि गर्नुहोस्"</string>
+    <string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM कार्ड अहिले असक्षम छ। सुचारु गर्नको लागि PUK कोड प्रविष्टि गर्नुहोस्।  विवरणको लागि सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।"</string>
+    <string name="kg_puk_enter_puk_hint_multi" msgid="1373131883510840794">"SIM <xliff:g id="CARRIER">%1$s</xliff:g> अहिले असक्षम छ। सुचारु गर्नको लागि PUK कोड प्रविष्टि गर्नुहोस्। विवरणका लागि सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।"</string>
     <string name="kg_puk_enter_pin_hint" msgid="3137789674920391087">"रूचाइएको PIN कोड प्रविष्टि गर्नुहोस्"</string>
     <string name="kg_enter_confirm_pin_hint" msgid="3089485999116759671">"रूचाइएको PIN कोड पुष्टि गर्नुहोस्"</string>
     <string name="kg_sim_unlock_progress_dialog_message" msgid="4471738151810900114">"SIM कार्ड अनलक गरिँदै..."</string>
     <string name="kg_invalid_sim_pin_hint" msgid="3057533256729513335">"४ देखि ८ वटा नम्बर भएको एउटा PIN टाइप गर्नुहोस्।"</string>
     <string name="kg_invalid_sim_puk_hint" msgid="6003602401368264144">"PUK कोड ८ वा सो भन्दा बढी नम्बरको हुनु पर्छ।"</string>
-    <string name="kg_invalid_puk" msgid="5399287873762592502">"PUK कोड पुन: प्रविष्ट गर्नुहोस्। पटक-पटकको प्रयासले SIM सदाका लागि असक्षम हुनेछ।"</string>
+    <string name="kg_invalid_puk" msgid="5399287873762592502">"PUK कोड पुन: प्रविष्टि गर्नुहोस्। पटक-पटकको प्रयासले SIM सदाका लागि असक्षम हुनेछ।"</string>
     <string name="kg_invalid_confirm_pin_hint" product="default" msgid="5672736555427444330">"PIN कोडहरू मिलेनन्"</string>
     <string name="kg_login_too_many_attempts" msgid="6604574268387867255">"अत्यन्त धेरै ढाँचा कोर्ने प्रयासहरू"</string>
     <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8637788033282252027">"तपाईंले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले आफ्नो PIN प्रविष्ट गर्नुभएको छ। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string>
@@ -143,11 +143,11 @@
     <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"पहिचान भएन"</string>
     <string name="kg_face_not_recognized" msgid="6382535088345875294">"पहिचान भएन"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
-      <item quantity="other">SIM को PIN प्रविष्ट गर्नुहोस्। तपाईंसँग <xliff:g id="NUMBER_1">%d</xliff:g>  प्रयासहरू बाँकी छन्।</item>
-      <item quantity="one">SIM को PIN प्रविष्ट गर्नुहोस्। तपाईंसँग <xliff:g id="NUMBER_0">%d</xliff:g> प्रयास बाँकी छ, त्यसपछि भने आफ्नो यन्त्र अनलक गर्नका लागि तपाईंले अनिवार्य रूपमा आफ्नो सेवा प्रदायकलाई सम्पर्क गर्नु पर्ने हुन्छ।</item>
+      <item quantity="other">SIM को PIN प्रविष्टि गर्नुहोस्। तपाईंसँग <xliff:g id="NUMBER_1">%d</xliff:g>  प्रयासहरू बाँकी छन्।</item>
+      <item quantity="one">SIM को PIN प्रविष्टि गर्नुहोस्। तपाईंसँग <xliff:g id="NUMBER_0">%d</xliff:g> प्रयास बाँकी छ, त्यसपछि भने आफ्नो यन्त्र अनलक गर्नका लागि तपाईंले अनिवार्य रूपमा आफ्नो सेवा प्रदायकलाई सम्पर्क गर्नु पर्ने हुन्छ।</item>
     </plurals>
     <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
-      <item quantity="other">SIM लाई असक्षम पारिएको छ। जारी राख्न PUK कोड प्रविष्ट गर्नुहोस्। तपाईंसँग <xliff:g id="_NUMBER_1">%d</xliff:g> प्रयासहरू बाँकी छन्, त्यसपछि SIM सदाका लागि प्रयोग गर्न नमिल्ने हुन्छ। विवरणहरूका लागि सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।</item>
-      <item quantity="one">SIM लाई असक्षम पारिएको छ। जारी राख्न PUK कोड प्रविष्ट गर्नुहोस्। तपाईंसँग <xliff:g id="_NUMBER_0">%d</xliff:g> प्रयास बाँकी छ, त्यसपछि SIM सदाका लागि प्रयोग गर्न नमिल्ने हुन्छ। विवरणहरूका लागि सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।</item>
+      <item quantity="other">SIM लाई असक्षम पारिएको छ। जारी राख्न PUK कोड प्रविष्टि गर्नुहोस्। तपाईंसँग <xliff:g id="_NUMBER_1">%d</xliff:g> प्रयासहरू बाँकी छन्, त्यसपछि SIM सदाका लागि प्रयोग गर्न नमिल्ने हुन्छ। विवरणहरूका लागि सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।</item>
+      <item quantity="one">SIM लाई असक्षम पारिएको छ। जारी राख्न PUK कोड प्रविष्टि गर्नुहोस्। तपाईंसँग <xliff:g id="_NUMBER_0">%d</xliff:g> प्रयास बाँकी छ, त्यसपछि SIM सदाका लागि प्रयोग गर्न नमिल्ने हुन्छ। विवरणहरूका लागि सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।</item>
     </plurals>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 9baeaaa..b9966cf 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -33,7 +33,7 @@
         <item name="android:gravity">center_horizontal|center_vertical</item>
         <item name="android:background">@null</item>
         <item name="android:textSize">32sp</item>
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyLight</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
         <item name="android:textColor">?attr/wallpaperTextColor</item>
         <item name="android:paddingBottom">-16dp</item>
     </style>
@@ -50,7 +50,7 @@
     </style>
     <style name="Widget.TextView.NumPadKey.Klondike" parent="Widget.TextView.NumPadKey">
         <item name="android:textSize">12sp</item>
-        <item name="android:fontFamily">sans-serif</item>
+        <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
         <item name="android:textColor">?attr/wallpaperTextColorSecondary</item>
         <item name="android:paddingBottom">0dp</item>
     </style>
@@ -59,10 +59,10 @@
     <style name="widget_label">
         <item name="android:textSize">@dimen/widget_label_font_size</item>
     </style>
-    <style name="widget_big_thin">
+    <style name="widget_big">
         <item name="android:textSize">@dimen/widget_big_font_size</item>
         <item name="android:paddingBottom">@dimen/bottom_text_spacing_digital</item>
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyLight</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
         <item name="android:fontFeatureSettings">@*android:string/config_headlineFontFeatureSettings</item>
         <item name="android:ellipsize">none</item>
     </style>
@@ -93,7 +93,7 @@
         <item name="android:gravity">center</item>
         <item name="android:ellipsize">end</item>
         <item name="android:maxLines">2</item>
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyLight</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
     </style>
 
     <style name="TextAppearance.Keyguard.Secondary">
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_cancel_white_24dp.png b/packages/SystemUI/res/drawable-hdpi/ic_cancel_white_24dp.png
deleted file mode 100644
index 73f5116..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_cancel_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_dismiss_outline.png b/packages/SystemUI/res/drawable-hdpi/ic_dismiss_outline.png
deleted file mode 100755
index 9afd8fa..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_dismiss_outline.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_lights_out_dot_large.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_lights_out_dot_large.png
deleted file mode 100644
index 552a3d1..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_lights_out_dot_large.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land/search_panel_scrim.xml b/packages/SystemUI/res/drawable-land/search_panel_scrim.xml
deleted file mode 100644
index 102cc9c..0000000
--- a/packages/SystemUI/res/drawable-land/search_panel_scrim.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-  ~ Copyright (C) 2014 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
-  -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <gradient
-            android:type="linear"
-            android:angle="180"
-            android:startColor="#55000000"
-            android:endColor="#00000000" />
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_cancel_white_24dp.png b/packages/SystemUI/res/drawable-mdpi/ic_cancel_white_24dp.png
deleted file mode 100644
index 787e259..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_cancel_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_dismiss_outline.png b/packages/SystemUI/res/drawable-mdpi/ic_dismiss_outline.png
deleted file mode 100755
index 35737aa..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_dismiss_outline.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_lights_out_dot_large.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_lights_out_dot_large.png
deleted file mode 100644
index 48b96d8..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_lights_out_dot_large.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/scorecard_gameover.xml b/packages/SystemUI/res/drawable-nodpi/scorecard_gameover.xml
deleted file mode 100644
index f663a66..0000000
--- a/packages/SystemUI/res/drawable-nodpi/scorecard_gameover.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle"
-    >
-    <corners
-        android:radius="8dp" />
-    <solid
-        android:color="#ffff0000" />
-</shape>
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_lights_out_dot_large.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_lights_out_dot_large.png
deleted file mode 100644
index 23ec6db..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_lights_out_dot_large.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_lights_out_dot_large.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_lights_out_dot_large.png
deleted file mode 100644
index e450058..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_lights_out_dot_large.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_lights_out_dot_large.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_lights_out_dot_large.png
deleted file mode 100644
index d18e419..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_lights_out_dot_large.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_lights_out_dot_large.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_lights_out_dot_large.png
deleted file mode 100644
index 00a751c..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_lights_out_dot_large.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp/search_panel_scrim.xml b/packages/SystemUI/res/drawable-sw600dp/search_panel_scrim.xml
deleted file mode 100644
index bbb2617..0000000
--- a/packages/SystemUI/res/drawable-sw600dp/search_panel_scrim.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-  ~ Copyright (C) 2014 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
-  -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <gradient
-            android:type="linear"
-            android:angle="90"
-            android:startColor="#55000000"
-            android:endColor="#00000000" />
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_back_light_old.png b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_back_light_old.png
deleted file mode 100644
index b336ccd..0000000
--- a/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_back_light_old.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_cancel_white_24dp.png b/packages/SystemUI/res/drawable-xhdpi/ic_cancel_white_24dp.png
deleted file mode 100644
index 6ebbc83..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_cancel_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_dismiss_outline.png b/packages/SystemUI/res/drawable-xhdpi/ic_dismiss_outline.png
deleted file mode 100755
index f1bfa89..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_dismiss_outline.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_lights_out_dot_large.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_lights_out_dot_large.png
deleted file mode 100644
index e49db34..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_lights_out_dot_large.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_lights_out_dot_large.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_lights_out_dot_large.png
deleted file mode 100644
index b91704a..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_lights_out_dot_large.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/dismiss_all_shape.xml b/packages/SystemUI/res/drawable/dismiss_all_shape.xml
deleted file mode 100644
index fb371c6..0000000
--- a/packages/SystemUI/res/drawable/dismiss_all_shape.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:height="17dp"
-    android:width="85dp"
-    android:viewportHeight="48"
-    android:viewportWidth="260" >
-    <group
-        android:name="dismiss_all"
-        android:translateX="48"
-        android:translateY="6" >
-        <group
-            android:name="3"
-            android:translateX="-24"
-            android:translateY="36" >
-            <path
-                android:name="rectangle_path_1_2"
-                android:pathData="M -24.0,-6.0 l 48.0,0 l 0,12.0 l -48.0,0 Z"
-                android:fillColor="#FFFFFFFF"
-                android:fillAlpha="1" />
-        </group>
-        <group
-            android:name="2"
-            android:translateX="-12"
-            android:translateY="18" >
-            <path
-                android:name="rectangle_path_1_1"
-                android:pathData="M -24.0,-6.0 l 48.0,0 l 0,12.0 l -48.0,0 Z"
-                android:fillColor="#FFFFFFFF"
-                android:fillAlpha="1" />
-        </group>
-        <group
-            android:name="1" >
-            <path
-                android:name="rectangle_path_1"
-                android:pathData="M -24.0,-6.0 l 48.0,0 l 0,12.0 l -48.0,0 Z"
-                android:fillColor="#FFFFFFFF"
-                android:fillAlpha="1" />
-        </group>
-    </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/face_dialog_error_to_face.xml b/packages/SystemUI/res/drawable/face_dialog_error_to_face.xml
new file mode 100644
index 0000000..75311f4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/face_dialog_error_to_face.xml
@@ -0,0 +1,517 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="60dp"
+            android:height="60dp"
+            android:viewportWidth="60"
+            android:viewportHeight="60">
+            <group android:name="_R_G">
+                <group
+                    android:name="_R_G_L_0_G_N_1_T_0"
+                    android:translateX="30"
+                    android:translateY="30">
+                    <group
+                        android:name="_R_G_L_0_G"
+                        android:translateX="-30"
+                        android:translateY="-30">
+                        <path
+                            android:name="_R_G_L_0_G_D_0_P_0"
+                            android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
+                            android:strokeWidth="2.5"
+                            android:strokeAlpha="1"
+                            android:strokeColor="?android:attr/colorError"
+                            android:trimPathStart="0"
+                            android:trimPathEnd="1"
+                            android:trimPathOffset="0" />
+                        <path
+                            android:name="_R_G_L_0_G_D_1_P_0"
+                            android:pathData=" M34.78 38.76 C33.83,38.75 31.54,38.75 30.01,38.75 C26.97,38.75 26.14,38.75 24.3,38.76 "
+                            android:strokeWidth="2.5"
+                            android:strokeAlpha="1"
+                            android:strokeColor="?android:attr/colorError"
+                            android:trimPathStart="0.34"
+                            android:trimPathEnd="0.5700000000000001"
+                            android:trimPathOffset="0" />
+                        <group
+                            android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0"
+                            android:scaleX="0.3"
+                            android:scaleY="0.3"
+                            android:translateX="37.788"
+                            android:translateY="19.53">
+                            <path
+                                android:name="_R_G_L_0_G_D_2_P_0"
+                                android:fillAlpha="0"
+                                android:fillColor="@color/biometric_face_icon_gray"
+                                android:fillType="nonZero"
+                                android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.1,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
+                        </group>
+                        <group
+                            android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0"
+                            android:scaleX="0.3"
+                            android:scaleY="0.3"
+                            android:translateX="22.005"
+                            android:translateY="19.51">
+                            <path
+                                android:name="_R_G_L_0_G_D_3_P_0"
+                                android:fillAlpha="0"
+                                android:fillColor="@color/biometric_face_icon_gray"
+                                android:fillType="nonZero"
+                                android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.2,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
+                        </group>
+                        <group
+                            android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0"
+                            android:translateX="30.3"
+                            android:translateY="29.215">
+                            <path
+                                android:name="_R_G_L_0_G_D_4_P_0"
+                                android:fillAlpha="1"
+                                android:fillColor="?android:attr/colorError"
+                                android:fillType="nonZero"
+                                android:pathData=" M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-11.71 -1.5,-11.71 C-1.5,-11.71 0.9,-11.71 0.9,-11.71 C0.9,-11.71 0.9,3.25 0.9,3.25c " />
+                        </group>
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="strokeColor"
+                    android:startOffset="0"
+                    android:valueFrom="?android:attr/colorError"
+                    android:valueTo="?android:attr/colorError"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="17"
+                    android:propertyName="strokeColor"
+                    android:startOffset="83"
+                    android:valueFrom="?android:attr/colorError"
+                    android:valueTo="@color/biometric_face_icon_gray"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="strokeColor"
+                    android:startOffset="0"
+                    android:valueFrom="?android:attr/colorError"
+                    android:valueTo="?android:attr/colorError"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="17"
+                    android:propertyName="strokeColor"
+                    android:startOffset="83"
+                    android:valueFrom="?android:attr/colorError"
+                    android:valueTo="@color/biometric_face_icon_gray"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="217"
+                    android:propertyName="strokeWidth"
+                    android:startOffset="0"
+                    android:valueFrom="2.5"
+                    android:valueTo="2"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="217"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M34.78 38.76 C33.83,38.75 31.54,38.75 30.01,38.75 C26.97,38.75 26.14,38.75 24.3,38.76 "
+                    android:valueTo="M33.75 42.75 C32.75,43.76 31.37,44.39 29.83,44.39 C26.8,44.39 24.34,41.93 24.34,38.9 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="217"
+                    android:propertyName="trimPathStart"
+                    android:startOffset="0"
+                    android:valueFrom="0.34"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="217"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="0"
+                    android:valueFrom="0.5700000000000001"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_2_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="17"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="83"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="50"
+                    android:pathData="M 37.788,19.53C 38.3400184636116,20.241653709411622 37.235981536388394,18.81834629058838 37.788,19.53"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="0">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="167"
+                    android:pathData="M 37.788,19.53C 38.3400184636116,20.241653709411622 40.5479815363884,23.08834629058838 41.1,23.8"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="50">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="0.3"
+                    android:valueTo="0.3"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="0.3"
+                    android:valueTo="0.3"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="117"
+                    android:propertyName="scaleX"
+                    android:startOffset="50"
+                    android:valueFrom="0.3"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="117"
+                    android:propertyName="scaleY"
+                    android:startOffset="50"
+                    android:valueFrom="0.3"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_3_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="17"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="83"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="50"
+                    android:pathData="M 22.005,19.51C 21.43742198228836,20.224974105358122 22.57257801771164,18.79502589464188 22.005,19.51"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="0">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="167"
+                    android:pathData="M 22.005,19.51C 21.43742198228836,20.224974105358122 19.16757801771164,23.08502589464188 18.6,23.8"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="50">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="0.3"
+                    android:valueTo="0.3"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="0.3"
+                    android:valueTo="0.3"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="117"
+                    android:propertyName="scaleX"
+                    android:startOffset="50"
+                    android:valueFrom="0.3"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="117"
+                    android:propertyName="scaleY"
+                    android:startOffset="50"
+                    android:valueFrom="0.3"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_4_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="fillColor"
+                    android:startOffset="0"
+                    android:valueFrom="?android:attr/colorError"
+                    android:valueTo="?android:attr/colorError"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="17"
+                    android:propertyName="fillColor"
+                    android:startOffset="83"
+                    android:valueFrom="?android:attr/colorError"
+                    android:valueTo="@color/biometric_face_icon_gray"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="150"
+                    android:pathData="M 30.3,29.215C 30.3,29.58759101867676 30.3,31.077408981323238 30.3,31.45"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="0">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_4_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-11.71 -1.5,-11.71 C-1.5,-11.71 0.9,-11.71 0.9,-11.71 C0.9,-11.71 0.9,3.25 0.9,3.25c "
+                    android:valueTo="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-3.25 -1.5,-3.25 C-1.5,-3.25 0.9,-3.25 0.9,-3.25 C0.9,-3.25 0.9,3.25 0.9,3.25c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.321,0 0.67,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="133"
+                    android:propertyName="pathData"
+                    android:startOffset="83"
+                    android:valueFrom="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-3.25 -1.5,-3.25 C-1.5,-3.25 0.9,-3.25 0.9,-3.25 C0.9,-3.25 0.9,3.25 0.9,3.25c "
+                    android:valueTo="M2.6 3.25 C2.6,3.25 -2.6,3.25 -2.6,3.25 C-2.6,3.25 -2.6,1.25 -2.6,1.25 C-2.6,1.25 0.6,1.25 0.6,1.25 C0.6,1.25 0.6,-3.25 0.6,-3.25 C0.6,-3.25 2.6,-3.25 2.6,-3.25 C2.6,-3.25 2.6,3.25 2.6,3.25c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.568,0 0.456,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="233"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_face_blue_to_checkmark.xml b/packages/SystemUI/res/drawable/face_dialog_face_blue_to_checkmark.xml
new file mode 100644
index 0000000..e4ace67
--- /dev/null
+++ b/packages/SystemUI/res/drawable/face_dialog_face_blue_to_checkmark.xml
@@ -0,0 +1,637 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="60dp"
+            android:height="60dp"
+            android:viewportWidth="60"
+            android:viewportHeight="60">
+            <group android:name="_R_G">
+                <group
+                    android:name="_R_G_L_0_G_N_2_T_0"
+                    android:translateX="30"
+                    android:translateY="30">
+                    <group
+                        android:name="_R_G_L_0_G"
+                        android:translateX="-30"
+                        android:translateY="-30">
+                        <group
+                            android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
+                            android:scaleX="0.08"
+                            android:scaleY="0.08"
+                            android:translateX="30.1"
+                            android:translateY="30.083">
+                            <path
+                                android:name="_R_G_L_0_G_D_0_P_0"
+                                android:fillAlpha="0"
+                                android:fillColor="?android:attr/colorAccent"
+                                android:fillType="nonZero"
+                                android:pathData=" M-116 -16.5 C-116,-16.5 -31.25,68.5 -31.25,68.5 C-31.25,68.5 108.75,-71.5 108.75,-71.5 "
+                                android:trimPathStart="0"
+                                android:trimPathEnd="0"
+                                android:trimPathOffset="0" />
+                        </group>
+                        <group
+                            android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0"
+                            android:scaleX="0.08"
+                            android:scaleY="0.08"
+                            android:translateX="30.1"
+                            android:translateY="30.083">
+                            <path
+                                android:name="_R_G_L_0_G_D_1_P_0"
+                                android:pathData=" M-116 -16.5 C-116,-16.5 -31.25,68.5 -31.25,68.5 C-31.25,68.5 108.75,-71.5 108.75,-71.5 "
+                                android:strokeWidth="20"
+                                android:strokeAlpha="1"
+                                android:strokeColor="?android:attr/colorAccent"
+                                android:trimPathStart="0"
+                                android:trimPathEnd="0"
+                                android:trimPathOffset="0" />
+                        </group>
+                        <path
+                            android:name="_R_G_L_0_G_D_2_P_0"
+                            android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
+                            android:strokeWidth="2.5"
+                            android:strokeAlpha="1"
+                            android:strokeColor="?android:attr/colorAccent"
+                            android:trimPathStart="0"
+                            android:trimPathEnd="1"
+                            android:trimPathOffset="0" />
+                        <group
+                            android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0"
+                            android:pivotX="1.05"
+                            android:pivotY="-9.891"
+                            android:scaleX="1"
+                            android:scaleY="1"
+                            android:translateX="29.044"
+                            android:translateY="41.647">
+                            <path
+                                android:name="_R_G_L_0_G_D_3_P_0"
+                                android:pathData=" M4.71 1.1 C3.71,2.12 2.32,2.75 0.79,2.75 C-2.25,2.75 -4.7,0.29 -4.7,-2.75 "
+                                android:strokeWidth="2"
+                                android:strokeAlpha="1"
+                                android:strokeColor="?android:attr/colorAccent"
+                                android:trimPathStart="0"
+                                android:trimPathEnd="1"
+                                android:trimPathOffset="0" />
+                        </group>
+                        <group
+                            android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0"
+                            android:scaleX="1"
+                            android:scaleY="1"
+                            android:translateX="41.1"
+                            android:translateY="23.8">
+                            <path
+                                android:name="_R_G_L_0_G_D_4_P_0"
+                                android:fillAlpha="1"
+                                android:fillColor="?android:attr/colorAccent"
+                                android:fillType="nonZero"
+                                android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.1,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
+                        </group>
+                        <group
+                            android:name="_R_G_L_0_G_D_5_P_0_G_0_T_0"
+                            android:scaleX="1"
+                            android:scaleY="1"
+                            android:translateX="18.6"
+                            android:translateY="23.8">
+                            <path
+                                android:name="_R_G_L_0_G_D_5_P_0"
+                                android:fillAlpha="1"
+                                android:fillColor="?android:attr/colorAccent"
+                                android:fillType="nonZero"
+                                android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.2,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
+                        </group>
+                        <group
+                            android:name="_R_G_L_0_G_D_6_P_0_G_0_T_0"
+                            android:scaleX="1"
+                            android:scaleY="1"
+                            android:translateX="30.727"
+                            android:translateY="31.703">
+                            <path
+                                android:name="_R_G_L_0_G_D_6_P_0"
+                                android:fillAlpha="1"
+                                android:fillColor="?android:attr/colorAccent"
+                                android:fillType="nonZero"
+                                android:pathData=" M2.6 3.25 C2.6,3.25 -2.6,3.25 -2.6,3.25 C-2.6,3.25 -2.6,1.25 -2.6,1.25 C-2.6,1.25 0.6,1.25 0.6,1.25 C0.6,1.25 0.6,-3.25 0.6,-3.25 C0.6,-3.25 2.6,-3.25 2.6,-3.25 C2.6,-3.25 2.6,3.25 2.6,3.25c " />
+                        </group>
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="33"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="0.08"
+                    android:valueTo="0.08"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="33"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="0.08"
+                    android:valueTo="0.08"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="scaleX"
+                    android:startOffset="33"
+                    android:valueFrom="0.08"
+                    android:valueTo="0.12789"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="scaleY"
+                    android:startOffset="33"
+                    android:valueFrom="0.08"
+                    android:valueTo="0.12789"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="100"
+                    android:propertyName="scaleX"
+                    android:startOffset="200"
+                    android:valueFrom="0.12789"
+                    android:valueTo="0.12241"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="100"
+                    android:propertyName="scaleY"
+                    android:startOffset="200"
+                    android:valueFrom="0.12789"
+                    android:valueTo="0.12241"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="scaleX"
+                    android:startOffset="300"
+                    android:valueFrom="0.12241"
+                    android:valueTo="0.125"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="scaleY"
+                    android:startOffset="300"
+                    android:valueFrom="0.12241"
+                    android:valueTo="0.125"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="33"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="233"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="33"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="33"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="0.08"
+                    android:valueTo="0.08"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="33"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="0.08"
+                    android:valueTo="0.08"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="scaleX"
+                    android:startOffset="33"
+                    android:valueFrom="0.08"
+                    android:valueTo="0.12789"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="scaleY"
+                    android:startOffset="33"
+                    android:valueFrom="0.08"
+                    android:valueTo="0.12789"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="100"
+                    android:propertyName="scaleX"
+                    android:startOffset="200"
+                    android:valueFrom="0.12789"
+                    android:valueTo="0.12241"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="100"
+                    android:propertyName="scaleY"
+                    android:startOffset="200"
+                    android:valueFrom="0.12789"
+                    android:valueTo="0.12241"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="scaleX"
+                    android:startOffset="300"
+                    android:valueFrom="0.12241"
+                    android:valueTo="0.125"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="scaleY"
+                    android:startOffset="300"
+                    android:valueFrom="0.12241"
+                    android:valueTo="0.125"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="33"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="233"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="33"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_2_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="strokeColor"
+                    android:startOffset="0"
+                    android:valueFrom="?android:attr/colorAccent"
+                    android:valueTo="?android:attr/colorAccent"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="17"
+                    android:propertyName="strokeColor"
+                    android:startOffset="67"
+                    android:valueFrom="?android:attr/colorAccent"
+                    android:valueTo="?android:attr/colorAccent"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_3_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="strokeAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0.65"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0.65"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_3_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="trimPathStart"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="0.5"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_3_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0.5"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_4_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_5_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_5_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_6_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_6_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.287,0.12 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.287,0.12 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="383"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_face_gray_to_checkmark.xml b/packages/SystemUI/res/drawable/face_dialog_face_gray_to_checkmark.xml
new file mode 100644
index 0000000..b09f69b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/face_dialog_face_gray_to_checkmark.xml
@@ -0,0 +1,637 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="60dp"
+            android:height="60dp"
+            android:viewportWidth="60"
+            android:viewportHeight="60">
+            <group android:name="_R_G">
+                <group
+                    android:name="_R_G_L_0_G_N_2_T_0"
+                    android:translateX="30"
+                    android:translateY="30">
+                    <group
+                        android:name="_R_G_L_0_G"
+                        android:translateX="-30"
+                        android:translateY="-30">
+                        <group
+                            android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
+                            android:scaleX="0.08"
+                            android:scaleY="0.08"
+                            android:translateX="30.1"
+                            android:translateY="30.083">
+                            <path
+                                android:name="_R_G_L_0_G_D_0_P_0"
+                                android:fillAlpha="0"
+                                android:fillColor="@color/biometric_face_icon_gray"
+                                android:fillType="nonZero"
+                                android:pathData=" M-116 -16.5 C-116,-16.5 -31.25,68.5 -31.25,68.5 C-31.25,68.5 108.75,-71.5 108.75,-71.5 "
+                                android:trimPathStart="0"
+                                android:trimPathEnd="0"
+                                android:trimPathOffset="0" />
+                        </group>
+                        <group
+                            android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0"
+                            android:scaleX="0.08"
+                            android:scaleY="0.08"
+                            android:translateX="30.1"
+                            android:translateY="30.083">
+                            <path
+                                android:name="_R_G_L_0_G_D_1_P_0"
+                                android:pathData=" M-116 -16.5 C-116,-16.5 -31.25,68.5 -31.25,68.5 C-31.25,68.5 108.75,-71.5 108.75,-71.5 "
+                                android:strokeWidth="20"
+                                android:strokeAlpha="1"
+                                android:strokeColor="?android:attr/colorAccent"
+                                android:trimPathStart="0"
+                                android:trimPathEnd="0"
+                                android:trimPathOffset="0" />
+                        </group>
+                        <path
+                            android:name="_R_G_L_0_G_D_2_P_0"
+                            android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
+                            android:strokeWidth="2.5"
+                            android:strokeAlpha="1"
+                            android:strokeColor="@color/biometric_face_icon_gray"
+                            android:trimPathStart="0"
+                            android:trimPathEnd="1"
+                            android:trimPathOffset="0" />
+                        <group
+                            android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0"
+                            android:pivotX="1.05"
+                            android:pivotY="-9.891"
+                            android:scaleX="1"
+                            android:scaleY="1"
+                            android:translateX="29.044"
+                            android:translateY="41.647">
+                            <path
+                                android:name="_R_G_L_0_G_D_3_P_0"
+                                android:pathData=" M4.71 1.1 C3.71,2.12 2.32,2.75 0.79,2.75 C-2.25,2.75 -4.7,0.29 -4.7,-2.75 "
+                                android:strokeWidth="2"
+                                android:strokeAlpha="1"
+                                android:strokeColor="@color/biometric_face_icon_gray"
+                                android:trimPathStart="0"
+                                android:trimPathEnd="1"
+                                android:trimPathOffset="0" />
+                        </group>
+                        <group
+                            android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0"
+                            android:scaleX="1"
+                            android:scaleY="1"
+                            android:translateX="41.1"
+                            android:translateY="23.8">
+                            <path
+                                android:name="_R_G_L_0_G_D_4_P_0"
+                                android:fillAlpha="1"
+                                android:fillColor="@color/biometric_face_icon_gray"
+                                android:fillType="nonZero"
+                                android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.1,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
+                        </group>
+                        <group
+                            android:name="_R_G_L_0_G_D_5_P_0_G_0_T_0"
+                            android:scaleX="1"
+                            android:scaleY="1"
+                            android:translateX="18.6"
+                            android:translateY="23.8">
+                            <path
+                                android:name="_R_G_L_0_G_D_5_P_0"
+                                android:fillAlpha="1"
+                                android:fillColor="@color/biometric_face_icon_gray"
+                                android:fillType="nonZero"
+                                android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.2,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
+                        </group>
+                        <group
+                            android:name="_R_G_L_0_G_D_6_P_0_G_0_T_0"
+                            android:scaleX="1"
+                            android:scaleY="1"
+                            android:translateX="30.727"
+                            android:translateY="31.703">
+                            <path
+                                android:name="_R_G_L_0_G_D_6_P_0"
+                                android:fillAlpha="1"
+                                android:fillColor="@color/biometric_face_icon_gray"
+                                android:fillType="nonZero"
+                                android:pathData=" M2.6 3.25 C2.6,3.25 -2.6,3.25 -2.6,3.25 C-2.6,3.25 -2.6,1.25 -2.6,1.25 C-2.6,1.25 0.6,1.25 0.6,1.25 C0.6,1.25 0.6,-3.25 0.6,-3.25 C0.6,-3.25 2.6,-3.25 2.6,-3.25 C2.6,-3.25 2.6,3.25 2.6,3.25c " />
+                        </group>
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="33"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="0.08"
+                    android:valueTo="0.08"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="33"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="0.08"
+                    android:valueTo="0.08"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="scaleX"
+                    android:startOffset="33"
+                    android:valueFrom="0.08"
+                    android:valueTo="0.12789"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="scaleY"
+                    android:startOffset="33"
+                    android:valueFrom="0.08"
+                    android:valueTo="0.12789"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="100"
+                    android:propertyName="scaleX"
+                    android:startOffset="200"
+                    android:valueFrom="0.12789"
+                    android:valueTo="0.12241"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="100"
+                    android:propertyName="scaleY"
+                    android:startOffset="200"
+                    android:valueFrom="0.12789"
+                    android:valueTo="0.12241"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="scaleX"
+                    android:startOffset="300"
+                    android:valueFrom="0.12241"
+                    android:valueTo="0.125"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="scaleY"
+                    android:startOffset="300"
+                    android:valueFrom="0.12241"
+                    android:valueTo="0.125"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="33"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="233"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="33"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="33"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="0.08"
+                    android:valueTo="0.08"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="33"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="0.08"
+                    android:valueTo="0.08"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="scaleX"
+                    android:startOffset="33"
+                    android:valueFrom="0.08"
+                    android:valueTo="0.12789"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="scaleY"
+                    android:startOffset="33"
+                    android:valueFrom="0.08"
+                    android:valueTo="0.12789"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="100"
+                    android:propertyName="scaleX"
+                    android:startOffset="200"
+                    android:valueFrom="0.12789"
+                    android:valueTo="0.12241"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="100"
+                    android:propertyName="scaleY"
+                    android:startOffset="200"
+                    android:valueFrom="0.12789"
+                    android:valueTo="0.12241"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="scaleX"
+                    android:startOffset="300"
+                    android:valueFrom="0.12241"
+                    android:valueTo="0.125"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="scaleY"
+                    android:startOffset="300"
+                    android:valueFrom="0.12241"
+                    android:valueTo="0.125"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="33"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="233"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="33"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_2_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="strokeColor"
+                    android:startOffset="0"
+                    android:valueFrom="@color/biometric_face_icon_gray"
+                    android:valueTo="@color/biometric_face_icon_gray"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="17"
+                    android:propertyName="strokeColor"
+                    android:startOffset="67"
+                    android:valueFrom="@color/biometric_face_icon_gray"
+                    android:valueTo="?android:attr/colorAccent"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_3_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="strokeAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0.65"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0.65"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_3_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="trimPathStart"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="0.5"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_3_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0.5"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_4_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_5_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_5_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_6_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_6_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.287,0.12 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.287,0.12 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="383"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_face_gray_to_face_blue.xml b/packages/SystemUI/res/drawable/face_dialog_face_gray_to_face_blue.xml
new file mode 100644
index 0000000..9259dc7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/face_dialog_face_gray_to_face_blue.xml
@@ -0,0 +1,178 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="60dp"
+            android:height="60dp"
+            android:viewportHeight="60"
+            android:viewportWidth="60">
+            <group android:name="_R_G">
+                <group
+                    android:name="_R_G_L_0_G_N_1_T_0"
+                    android:translateX="30"
+                    android:translateY="30">
+                    <group
+                        android:name="_R_G_L_0_G"
+                        android:translateX="-30"
+                        android:translateY="-30">
+                        <path
+                            android:name="_R_G_L_0_G_D_0_P_0"
+                            android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
+                            android:strokeAlpha="1"
+                            android:strokeColor="@color/biometric_face_icon_gray"
+                            android:strokeLineCap="round"
+                            android:strokeLineJoin="round"
+                            android:strokeWidth="2.5"
+                            android:trimPathEnd="1"
+                            android:trimPathOffset="0"
+                            android:trimPathStart="0" />
+                        <path
+                            android:name="_R_G_L_0_G_D_1_P_0"
+                            android:pathData=" M33.75 42.75 C32.75,43.76 31.37,44.39 29.83,44.39 C26.8,44.39 24.34,41.93 24.34,38.9 "
+                            android:strokeAlpha="1"
+                            android:strokeColor="@color/biometric_face_icon_gray"
+                            android:strokeLineCap="round"
+                            android:strokeLineJoin="round"
+                            android:strokeWidth="2"
+                            android:trimPathEnd="1"
+                            android:trimPathOffset="0"
+                            android:trimPathStart="0" />
+                        <path
+                            android:name="_R_G_L_0_G_D_2_P_0"
+                            android:fillAlpha="1"
+                            android:fillColor="@color/biometric_face_icon_gray"
+                            android:fillType="nonZero"
+                            android:pathData=" M39 23.8 C39,25 39.9,25.9 41.1,25.9 C42.2,25.9 43.2,25 43.2,23.8 C43.2,22.6 42.3,21.7 41.1,21.7 C39.9,21.7 39,22.6 39,23.8c " />
+                        <path
+                            android:name="_R_G_L_0_G_D_3_P_0"
+                            android:fillAlpha="1"
+                            android:fillColor="@color/biometric_face_icon_gray"
+                            android:fillType="nonZero"
+                            android:pathData=" M16.5 23.8 C16.5,25 17.4,25.9 18.6,25.9 C19.8,25.9 20.7,25 20.7,23.8 C20.7,22.6 19.8,21.7 18.6,21.7 C17.4,21.7 16.5,22.6 16.5,23.8c " />
+                        <path
+                            android:name="_R_G_L_0_G_D_4_P_0"
+                            android:fillAlpha="1"
+                            android:fillColor="@color/biometric_face_icon_gray"
+                            android:fillType="nonZero"
+                            android:pathData=" M32.9 34.7 C32.9,34.7 27.7,34.7 27.7,34.7 C27.7,34.7 27.7,32.7 27.7,32.7 C27.7,32.7 30.9,32.7 30.9,32.7 C30.9,32.7 30.9,28.2 30.9,28.2 C30.9,28.2 32.9,28.2 32.9,28.2 C32.9,28.2 32.9,34.7 32.9,34.7c " />
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="33"
+                    android:propertyName="strokeColor"
+                    android:startOffset="0"
+                    android:valueFrom="@color/biometric_face_icon_gray"
+                    android:valueTo="?android:attr/colorAccent"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="33"
+                    android:propertyName="strokeColor"
+                    android:startOffset="0"
+                    android:valueFrom="@color/biometric_face_icon_gray"
+                    android:valueTo="?android:attr/colorAccent"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_2_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="33"
+                    android:propertyName="fillColor"
+                    android:startOffset="0"
+                    android:valueFrom="@color/biometric_face_icon_gray"
+                    android:valueTo="?android:attr/colorAccent"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_3_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="33"
+                    android:propertyName="fillColor"
+                    android:startOffset="0"
+                    android:valueFrom="@color/biometric_face_icon_gray"
+                    android:valueTo="?android:attr/colorAccent"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_4_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="33"
+                    android:propertyName="fillColor"
+                    android:startOffset="0"
+                    android:valueFrom="@color/biometric_face_icon_gray"
+                    android:valueTo="?android:attr/colorAccent"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_face_to_error.xml b/packages/SystemUI/res/drawable/face_dialog_face_to_error.xml
new file mode 100644
index 0000000..a96d21a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/face_dialog_face_to_error.xml
@@ -0,0 +1,473 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="60dp"
+            android:height="60dp"
+            android:viewportWidth="60"
+            android:viewportHeight="60">
+            <group android:name="_R_G">
+                <group
+                    android:name="_R_G_L_0_G_N_1_T_0"
+                    android:translateX="30"
+                    android:translateY="30">
+                    <group
+                        android:name="_R_G_L_0_G"
+                        android:translateX="-30"
+                        android:translateY="-30">
+                        <path
+                            android:name="_R_G_L_0_G_D_0_P_0"
+                            android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
+                            android:strokeWidth="2.5"
+                            android:strokeAlpha="1"
+                            android:strokeColor="@color/biometric_face_icon_gray"
+                            android:trimPathStart="0"
+                            android:trimPathEnd="1"
+                            android:trimPathOffset="0" />
+                        <path
+                            android:name="_R_G_L_0_G_D_1_P_0"
+                            android:pathData=" M33.75 42.75 C32.75,43.76 31.37,44.39 29.83,44.39 C26.8,44.39 24.34,41.93 24.34,38.9 "
+                            android:strokeWidth="2"
+                            android:strokeAlpha="1"
+                            android:strokeColor="@color/biometric_face_icon_gray"
+                            android:trimPathStart="0"
+                            android:trimPathEnd="1"
+                            android:trimPathOffset="0" />
+                        <group
+                            android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0"
+                            android:scaleX="1"
+                            android:scaleY="1"
+                            android:translateX="41.1"
+                            android:translateY="23.8">
+                            <path
+                                android:name="_R_G_L_0_G_D_2_P_0"
+                                android:fillAlpha="1"
+                                android:fillColor="@color/biometric_face_icon_gray"
+                                android:fillType="nonZero"
+                                android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.1,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
+                        </group>
+                        <group
+                            android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0"
+                            android:scaleX="1"
+                            android:scaleY="1"
+                            android:translateX="18.6"
+                            android:translateY="23.8">
+                            <path
+                                android:name="_R_G_L_0_G_D_3_P_0"
+                                android:fillAlpha="1"
+                                android:fillColor="@color/biometric_face_icon_gray"
+                                android:fillType="nonZero"
+                                android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.2,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
+                        </group>
+                        <group
+                            android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0"
+                            android:translateX="30.3"
+                            android:translateY="31.45">
+                            <path
+                                android:name="_R_G_L_0_G_D_4_P_0"
+                                android:fillAlpha="1"
+                                android:fillColor="@color/biometric_face_icon_gray"
+                                android:fillType="nonZero"
+                                android:pathData=" M2.6 3.25 C2.6,3.25 -2.6,3.25 -2.6,3.25 C-2.6,3.25 -2.6,1.25 -2.6,1.25 C-2.6,1.25 0.6,1.25 0.6,1.25 C0.6,1.25 0.6,-3.25 0.6,-3.25 C0.6,-3.25 2.6,-3.25 2.6,-3.25 C2.6,-3.25 2.6,3.25 2.6,3.25c " />
+                        </group>
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="strokeColor"
+                    android:startOffset="0"
+                    android:valueFrom="@color/biometric_face_icon_gray"
+                    android:valueTo="@color/biometric_face_icon_gray"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="17"
+                    android:propertyName="strokeColor"
+                    android:startOffset="50"
+                    android:valueFrom="@color/biometric_face_icon_gray"
+                    android:valueTo="?android:attr/colorError"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="strokeColor"
+                    android:startOffset="0"
+                    android:valueFrom="@color/biometric_face_icon_gray"
+                    android:valueTo="@color/biometric_face_icon_gray"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="17"
+                    android:propertyName="strokeColor"
+                    android:startOffset="50"
+                    android:valueFrom="@color/biometric_face_icon_gray"
+                    android:valueTo="?android:attr/colorError"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="217"
+                    android:propertyName="strokeWidth"
+                    android:startOffset="0"
+                    android:valueFrom="2"
+                    android:valueTo="2.5"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="217"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M33.75 42.75 C32.75,43.76 31.37,44.39 29.83,44.39 C26.8,44.39 24.34,41.93 24.34,38.9 "
+                    android:valueTo="M34.78 38.76 C33.83,38.75 31.54,38.75 30.01,38.75 C26.97,38.75 26.14,38.75 24.3,38.76 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="217"
+                    android:propertyName="trimPathStart"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="0.34"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="217"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0.5700000000000001"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_2_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="17"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="50"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="67"
+                    android:pathData="M 41.1,23.8C 40.547981774806985,23.08834635019302 38.34001822519301,20.24165364980698 37.788,19.53"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="0">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0.3"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.999,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0.3"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.999,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_3_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="17"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="50"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="67"
+                    android:pathData="M 18.6,23.8C 19.16757813692093,23.08502601385117 21.43742186307907,20.224973986148832 22.005,19.51"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="0">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0.3"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.999,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0.3"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.999,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_4_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="fillColor"
+                    android:startOffset="0"
+                    android:valueFrom="@color/biometric_face_icon_gray"
+                    android:valueTo="@color/biometric_face_icon_gray"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="17"
+                    android:propertyName="fillColor"
+                    android:startOffset="50"
+                    android:valueFrom="@color/biometric_face_icon_gray"
+                    android:valueTo="?android:attr/colorError"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="67"
+                    android:pathData="M 30.3,31.45C 30.3,31.07740886211395 30.3,31.82259113788605 30.3,31.45"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="0">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="150"
+                    android:pathData="M 30.3,31.45C 30.3,31.07740886211395 30.3,29.58759113788605 30.3,29.215"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="67">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_4_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M2.6 3.25 C2.6,3.25 -2.6,3.25 -2.6,3.25 C-2.6,3.25 -2.6,1.25 -2.6,1.25 C-2.6,1.25 0.6,1.25 0.6,1.25 C0.6,1.25 0.6,-3.25 0.6,-3.25 C0.6,-3.25 2.6,-3.25 2.6,-3.25 C2.6,-3.25 2.6,3.25 2.6,3.25c "
+                    android:valueTo="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-3.25 -1.5,-3.25 C-1.5,-3.25 0.9,-3.25 0.9,-3.25 C0.9,-3.25 0.9,3.25 0.9,3.25c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="150"
+                    android:propertyName="pathData"
+                    android:startOffset="67"
+                    android:valueFrom="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-3.25 -1.5,-3.25 C-1.5,-3.25 0.9,-3.25 0.9,-3.25 C0.9,-3.25 0.9,3.25 0.9,3.25c "
+                    android:valueTo="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-13.15 -1.5,-13.15 C-1.5,-13.15 0.9,-13.15 0.9,-13.15 C0.9,-13.15 0.9,3.25 0.9,3.25c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="100"
+                    android:propertyName="pathData"
+                    android:startOffset="217"
+                    android:valueFrom="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-13.15 -1.5,-13.15 C-1.5,-13.15 0.9,-13.15 0.9,-13.15 C0.9,-13.15 0.9,3.25 0.9,3.25c "
+                    android:valueTo="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-11.71 -1.5,-11.71 C-1.5,-11.71 0.9,-11.71 0.9,-11.71 C0.9,-11.71 0.9,3.25 0.9,3.25c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_icon.xml b/packages/SystemUI/res/drawable/face_dialog_icon.xml
deleted file mode 100644
index 6d28b5a..0000000
--- a/packages/SystemUI/res/drawable/face_dialog_icon.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:height="24dp"
-    android:width="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-    <path android:fillColor="#000" android:pathData="M9,11.75A1.25,1.25 0 0,0 7.75,13A1.25,1.25 0 0,0 9,14.25A1.25,1.25 0 0,0 10.25,13A1.25,1.25 0 0,0 9,11.75M15,11.75A1.25,1.25 0 0,0 13.75,13A1.25,1.25 0 0,0 15,14.25A1.25,1.25 0 0,0 16.25,13A1.25,1.25 0 0,0 15,11.75M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20C7.59,20 4,16.41 4,12C4,11.71 4,11.42 4.05,11.14C6.41,10.09 8.28,8.16 9.26,5.77C11.07,8.33 14.05,10 17.42,10C18.2,10 18.95,9.91 19.67,9.74C19.88,10.45 20,11.21 20,12C20,16.41 16.41,20 12,20Z" />
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
index af25e44..8f411f4 100644
--- a/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
+++ b/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
@@ -18,336 +18,268 @@
     xmlns:aapt="http://schemas.android.com/aapt">
     <aapt:attr name="android:drawable">
         <vector
-            android:width="24dp"
-            android:height="24dp"
-            android:viewportHeight="24"
-            android:viewportWidth="24">
+            android:width="60dp"
+            android:height="60dp"
+            android:viewportHeight="60"
+            android:viewportWidth="60">
             <group android:name="_R_G">
                 <group
-                    android:name="_R_G_L_2_G"
-                    android:pivotX="-33"
-                    android:pivotY="-34"
-                    android:rotation="180"
-                    android:scaleX="0.738"
-                    android:scaleY="0.738"
-                    android:translateX="45"
-                    android:translateY="46.4">
-                    <path
-                        android:name="_R_G_L_2_G_D_0_P_0"
-                        android:pathData=" M-25.36 -24.41 C-25.93,-24.31 -26.49,-24.27 -26.81,-24.27 C-28.11,-24.27 -29.35,-24.62 -30.43,-25.4 C-32.11,-26.6 -33.2,-28.57 -33.2,-30.79 "
-                        android:strokeAlpha="1"
-                        android:strokeColor="?android:attr/colorAccent"
-                        android:strokeLineCap="round"
-                        android:strokeLineJoin="round"
-                        android:strokeWidth="1.45"
-                        android:trimPathEnd="0"
-                        android:trimPathOffset="0"
-                        android:trimPathStart="0" />
-                    <path
-                        android:name="_R_G_L_2_G_D_1_P_0"
-                        android:pathData=" M-36.14 -21.78 C-37.15,-22.98 -37.72,-23.7 -38.51,-25.29 C-39.33,-26.94 -39.82,-28.78 -39.82,-30.77 C-39.82,-34.43 -36.85,-37.4 -33.19,-37.4 C-29.52,-37.4 -26.55,-34.43 -26.55,-30.77 "
-                        android:strokeAlpha="1"
-                        android:strokeColor="?android:attr/colorAccent"
-                        android:strokeLineCap="round"
-                        android:strokeLineJoin="round"
-                        android:strokeWidth="1.45"
-                        android:trimPathEnd="0"
-                        android:trimPathOffset="0"
-                        android:trimPathStart="0" />
-                    <path
-                        android:name="_R_G_L_2_G_D_2_P_0"
-                        android:pathData=" M-42.19 -25.68 C-42.95,-27.82 -43.09,-29.54 -43.09,-30.8 C-43.09,-32.27 -42.84,-33.65 -42.27,-34.9 C-40.71,-38.35 -37.24,-40.75 -33.2,-40.75 C-27.71,-40.75 -23.26,-36.3 -23.26,-30.8 C-23.26,-28.97 -24.74,-27.49 -26.57,-27.49 C-28.4,-27.49 -29.89,-28.97 -29.89,-30.8 C-29.89,-32.64 -31.37,-34.12 -33.2,-34.12 C-35.04,-34.12 -36.52,-32.64 -36.52,-30.8 C-36.52,-28.23 -35.53,-25.92 -33.92,-24.22 C-32.69,-22.93 -31.48,-22.12 -29.44,-21.53 "
-                        android:strokeAlpha="1"
-                        android:strokeColor="?android:attr/colorAccent"
-                        android:strokeLineCap="round"
-                        android:strokeLineJoin="round"
-                        android:strokeWidth="1.45"
-                        android:trimPathEnd="1"
-                        android:trimPathOffset="0"
-                        android:trimPathStart="1" />
-                    <path
-                        android:name="_R_G_L_2_G_D_3_P_0"
-                        android:pathData=" M-44.06 -38.17 C-42.87,-39.94 -41.39,-41.41 -39.51,-42.44 C-37.62,-43.47 -35.46,-44.05 -33.16,-44.05 C-30.88,-44.05 -28.72,-43.47 -26.85,-42.45 C-24.97,-41.43 -23.48,-39.97 -22.29,-38.21 "
-                        android:strokeAlpha="1"
-                        android:strokeColor="?android:attr/colorAccent"
-                        android:strokeLineCap="round"
-                        android:strokeLineJoin="round"
-                        android:strokeWidth="1.45"
-                        android:trimPathEnd="1"
-                        android:trimPathOffset="0"
-                        android:trimPathStart="1" />
-                    <path
-                        android:name="_R_G_L_2_G_D_4_P_0"
-                        android:pathData=" M-25.72 -45.45 C-27.99,-46.76 -30.43,-47.52 -33.28,-47.52 C-36.13,-47.52 -38.51,-46.74 -40.62,-45.45 "
-                        android:strokeAlpha="1"
-                        android:strokeColor="?android:attr/colorAccent"
-                        android:strokeLineCap="round"
-                        android:strokeLineJoin="round"
-                        android:strokeWidth="1.45"
-                        android:trimPathEnd="0"
-                        android:trimPathOffset="0"
-                        android:trimPathStart="0" />
-                </group>
-                <group
-                    android:name="_R_G_L_1_G"
-                    android:rotation="10"
-                    android:translateX="12"
-                    android:translateY="12">
-                    <path
-                        android:name="_R_G_L_1_G_D_0_P_0"
-                        android:pathData=" M0 -9 C4.97,-9 9,-4.97 9,0 C9,4.97 4.97,9 0,9 C-4.97,9 -9,4.97 -9,0 C-9,-4.97 -4.97,-9 0,-9c "
-                        android:strokeAlpha="1"
-                        android:strokeColor="?android:attr/colorError"
-                        android:strokeLineCap="round"
-                        android:strokeLineJoin="round"
-                        android:strokeWidth="2"
-                        android:trimPathEnd="1"
-                        android:trimPathOffset="0"
-                        android:trimPathStart="0" />
-                </group>
-                <group
-                    android:name="_R_G_L_0_G"
-                    android:translateX="12"
-                    android:translateY="12">
+                    android:name="_R_G_L_1_G_N_4_T_0"
+                    android:translateX="30"
+                    android:translateY="30">
                     <group
-                        android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
-                        android:pivotY="-0.012"
-                        android:rotation="0"
-                        android:scaleX="1"
-                        android:scaleY="1">
+                        android:name="_R_G_L_1_G"
+                        android:pivotX="114"
+                        android:pivotY="114"
+                        android:scaleX="0.42200000000000004"
+                        android:scaleY="0.42200000000000004"
+                        android:translateX="-114"
+                        android:translateY="-114">
                         <path
-                            android:name="_R_G_L_0_G_D_0_P_0"
-                            android:fillAlpha="1"
-                            android:fillColor="?android:attr/colorError"
-                            android:fillType="nonZero"
-                            android:pathData=" M1.1 3.94 C1.1,4.55 0.61,5.04 0,5.04 C-0.61,5.04 -1.1,4.55 -1.1,3.94 C-1.1,3.33 -0.61,2.84 0,2.84 C0.61,2.84 1.1,3.33 1.1,3.94c " />
+                            android:name="_R_G_L_1_G_D_0_P_0"
+                            android:pathData=" M79.63 67.24 C79.63,67.24 111.5,47.42 147.83,67.24 "
+                            android:strokeAlpha="1"
+                            android:strokeColor="?android:attr/colorAccent"
+                            android:strokeLineCap="round"
+                            android:strokeLineJoin="round"
+                            android:strokeWidth="5.5"
+                            android:trimPathEnd="0"
+                            android:trimPathOffset="0"
+                            android:trimPathStart="0" />
+                        <path
+                            android:name="_R_G_L_1_G_D_1_P_0"
+                            android:pathData=" M64.27 98.07 C64.27,98.07 80.13,73.02 113.98,73.02 C147.83,73.02 163.56,97.26 163.56,97.26 "
+                            android:strokeAlpha="1"
+                            android:strokeColor="?android:attr/colorAccent"
+                            android:strokeLineCap="round"
+                            android:strokeLineJoin="round"
+                            android:strokeWidth="5.5"
+                            android:trimPathEnd="0"
+                            android:trimPathOffset="0"
+                            android:trimPathStart="0" />
+                        <path
+                            android:name="_R_G_L_1_G_D_2_P_0"
+                            android:pathData=" M72.53 151.07 C72.53,151.07 62.46,122.89 76.16,105.55 C89.86,88.21 106.72,86.73 113.98,86.73 C121.08,86.73 153.51,90.62 158.7,125.87 C159.14,128.82 158.8,132.88 157.18,136.09 C154.88,140.63 150.62,143.63 145.85,143.97 C133.78,144.85 129.76,137.92 129.26,128.49 C128.88,121.19 122.49,115.35 113.15,115.35 C102.91,115.35 95.97,126.69 99.77,139.74 C103.57,152.78 111.33,163.85 130.32,169.13 "
+                            android:strokeAlpha="1"
+                            android:strokeColor="?android:attr/colorAccent"
+                            android:strokeLineCap="round"
+                            android:strokeLineJoin="round"
+                            android:strokeWidth="5.5"
+                            android:trimPathEnd="0"
+                            android:trimPathOffset="0"
+                            android:trimPathStart="0" />
+                        <path
+                            android:name="_R_G_L_1_G_D_3_P_0"
+                            android:pathData=" M100.6 167.84 C100.6,167.84 82.76,152.1 83.75,130.31 C84.75,108.53 102.58,100.7 113.73,100.7 C124.87,100.7 144.19,108.56 144.19,130.01 "
+                            android:strokeAlpha="1"
+                            android:strokeColor="?android:attr/colorAccent"
+                            android:strokeLineCap="round"
+                            android:strokeLineJoin="round"
+                            android:strokeWidth="5.5"
+                            android:trimPathEnd="0"
+                            android:trimPathOffset="0"
+                            android:trimPathStart="0" />
+                        <path
+                            android:name="_R_G_L_1_G_D_4_P_0"
+                            android:pathData=" M113.73 129.17 C113.73,129.17 113.15,161.33 149.15,156.58 "
+                            android:strokeAlpha="1"
+                            android:strokeColor="?android:attr/colorAccent"
+                            android:strokeLineCap="round"
+                            android:strokeLineJoin="round"
+                            android:strokeWidth="5.5"
+                            android:trimPathEnd="0"
+                            android:trimPathOffset="0"
+                            android:trimPathStart="0" />
                     </group>
+                </group>
+                <group
+                    android:name="_R_G_L_0_G_N_4_T_0"
+                    android:translateX="30"
+                    android:translateY="30">
                     <group
-                        android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0"
-                        android:pivotY="-0.012"
-                        android:rotation="0"
-                        android:scaleX="1"
-                        android:scaleY="1">
+                        android:name="_R_G_L_0_G"
+                        android:translateX="-30.05"
+                        android:translateY="-30">
+                        <group
+                            android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
+                            android:scaleX="1"
+                            android:scaleY="1"
+                            android:translateX="30"
+                            android:translateY="38.75">
+                            <path
+                                android:name="_R_G_L_0_G_D_0_P_0"
+                                android:fillAlpha="1"
+                                android:fillColor="?android:attr/colorError"
+                                android:fillType="nonZero"
+                                android:pathData=" M-1.2 -1.25 C-1.2,-1.25 1.2,-1.25 1.2,-1.25 C1.2,-1.25 1.2,1.25 1.2,1.25 C1.2,1.25 -1.2,1.25 -1.2,1.25 C-1.2,1.25 -1.2,-1.25 -1.2,-1.25c " />
+                        </group>
+                        <group
+                            android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0"
+                            android:pivotX="0.002"
+                            android:pivotY="7.488"
+                            android:scaleX="1"
+                            android:scaleY="1"
+                            android:translateX="30"
+                            android:translateY="25">
+                            <path
+                                android:name="_R_G_L_0_G_D_1_P_0"
+                                android:fillAlpha="1"
+                                android:fillColor="?android:attr/colorError"
+                                android:fillType="nonZero"
+                                android:pathData=" M-1.2 -7.5 C-1.2,-7.5 1.2,-7.5 1.2,-7.5 C1.2,-7.5 1.2,7.5 1.2,7.5 C1.2,7.5 -1.2,7.5 -1.2,7.5 C-1.2,7.5 -1.2,-7.5 -1.2,-7.5c " />
+                        </group>
                         <path
-                            android:name="_R_G_L_0_G_D_0_P_1"
-                            android:fillAlpha="1"
-                            android:fillColor="?android:attr/colorError"
-                            android:fillType="nonZero"
-                            android:pathData=" M1 -4.06 C1,-4.06 1,-0.06 1,-0.06 C1,0.49 0.55,0.94 0,0.94 C-0.55,0.94 -1,0.49 -1,-0.06 C-1,-0.06 -1,-4.06 -1,-4.06 C-1,-4.61 -0.55,-5.06 0,-5.06 C0.55,-5.06 1,-4.61 1,-4.06c " />
+                            android:name="_R_G_L_0_G_D_2_P_0"
+                            android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
+                            android:strokeAlpha="1"
+                            android:strokeColor="?android:attr/colorError"
+                            android:strokeLineCap="round"
+                            android:strokeLineJoin="round"
+                            android:strokeWidth="2.5"
+                            android:trimPathEnd="1"
+                            android:trimPathOffset="0"
+                            android:trimPathStart="0" />
                     </group>
                 </group>
             </group>
             <group android:name="time_group" />
         </vector>
     </aapt:attr>
-    <target android:name="_R_G_L_2_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="350"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="350"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="350"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_D_1_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="267"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="433"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="267"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_D_2_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="250"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="0"
-                    android:valueFrom="1"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="250"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="250"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_D_3_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="350"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="0"
-                    android:valueFrom="1"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="117"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="350"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_D_4_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="417"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="117"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="417"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="200"
-                    android:propertyName="rotation"
-                    android:startOffset="0"
-                    android:valueFrom="180"
-                    android:valueTo="180"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="500"
-                    android:propertyName="rotation"
-                    android:startOffset="200"
-                    android:valueFrom="180"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
     <target android:name="_R_G_L_1_G_D_0_P_0">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
                 <objectAnimator
-                    android:duration="383"
+                    android:duration="83"
                     android:propertyName="trimPathEnd"
                     android:startOffset="0"
-                    android:valueFrom="1"
+                    android:valueFrom="0"
                     android:valueTo="0"
                     android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
                         <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
+                <objectAnimator
+                    android:duration="250"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="83"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
             </set>
         </aapt:attr>
     </target>
-    <target android:name="_R_G_L_1_G">
+    <target android:name="_R_G_L_1_G_D_1_P_0">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
                 <objectAnimator
-                    android:duration="33"
-                    android:propertyName="rotation"
+                    android:duration="83"
+                    android:propertyName="trimPathEnd"
                     android:startOffset="0"
-                    android:valueFrom="10"
-                    android:valueTo="10"
+                    android:valueFrom="0"
+                    android:valueTo="0"
                     android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
                 <objectAnimator
-                    android:duration="367"
-                    android:propertyName="rotation"
-                    android:startOffset="33"
-                    android:valueFrom="10"
-                    android:valueTo="-180"
+                    android:duration="250"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="83"
+                    android:valueFrom="0"
+                    android:valueTo="1"
                     android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_2_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="250"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="83"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_3_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="250"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="83"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_4_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="250"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="83"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
             </set>
@@ -357,153 +289,125 @@
         <aapt:attr name="android:animation">
             <set android:ordering="together">
                 <objectAnimator
-                    android:duration="17"
-                    android:propertyName="rotation"
+                    android:duration="67"
+                    android:propertyName="scaleX"
                     android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
+                    android:valueFrom="1"
+                    android:valueTo="1.1"
                     android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.2,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.853,0 0.6,1 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
                 <objectAnimator
-                    android:duration="417"
-                    android:propertyName="rotation"
-                    android:startOffset="17"
-                    android:valueFrom="0"
-                    android:valueTo="-180"
+                    android:duration="67"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="1.1"
                     android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.2,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.853,0 0.6,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="100"
+                    android:propertyName="scaleX"
+                    android:startOffset="67"
+                    android:valueFrom="1.1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.06 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="100"
+                    android:propertyName="scaleY"
+                    android:startOffset="67"
+                    android:valueFrom="1.1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.06 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
             </set>
         </aapt:attr>
     </target>
-    <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
+    <target android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
                 <objectAnimator
-                    android:duration="117"
+                    android:duration="67"
                     android:propertyName="scaleX"
                     android:startOffset="0"
                     android:valueFrom="1"
                     android:valueTo="1"
                     android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.6,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.659,1 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
                 <objectAnimator
-                    android:duration="117"
+                    android:duration="67"
                     android:propertyName="scaleY"
                     android:startOffset="0"
                     android:valueFrom="1"
+                    android:valueTo="1.1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.6,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="100"
+                    android:propertyName="scaleX"
+                    android:startOffset="67"
+                    android:valueFrom="1"
                     android:valueTo="1"
                     android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.6,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.6,1 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
                 <objectAnimator
-                    android:duration="317"
-                    android:propertyName="scaleX"
-                    android:startOffset="117"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="317"
+                    android:duration="100"
                     android:propertyName="scaleY"
-                    android:startOffset="117"
-                    android:valueFrom="1"
+                    android:startOffset="67"
+                    android:valueFrom="1.1"
                     android:valueTo="0"
                     android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.6,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.096 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
             </set>
         </aapt:attr>
     </target>
-    <target android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0">
+    <target android:name="_R_G_L_0_G_D_2_P_0">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
                 <objectAnimator
-                    android:duration="17"
-                    android:propertyName="rotation"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="417"
-                    android:propertyName="rotation"
-                    android:startOffset="17"
-                    android:valueFrom="0"
-                    android:valueTo="-180"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="117"
-                    android:propertyName="scaleX"
+                    android:duration="67"
+                    android:propertyName="trimPathEnd"
                     android:startOffset="0"
                     android:valueFrom="1"
                     android:valueTo="1"
                     android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.6,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
                 <objectAnimator
-                    android:duration="117"
-                    android:propertyName="scaleY"
-                    android:startOffset="0"
-                    android:valueFrom="1"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="317"
-                    android:propertyName="scaleX"
-                    android:startOffset="117"
+                    android:duration="133"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="67"
                     android:valueFrom="1"
                     android:valueTo="0"
                     android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="317"
-                    android:propertyName="scaleY"
-                    android:startOffset="117"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.6,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
             </set>
@@ -513,7 +417,7 @@
         <aapt:attr name="android:animation">
             <set android:ordering="together">
                 <objectAnimator
-                    android:duration="717"
+                    android:duration="350"
                     android:propertyName="translateX"
                     android:startOffset="0"
                     android:valueFrom="0"
@@ -522,4 +426,4 @@
             </set>
         </aapt:attr>
     </target>
-</animated-vector>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
index 1a7a846..89b8228 100644
--- a/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
+++ b/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
@@ -18,614 +18,133 @@
     xmlns:aapt="http://schemas.android.com/aapt">
     <aapt:attr name="android:drawable">
         <vector
-            android:width="24dp"
-            android:height="24dp"
-            android:viewportHeight="24"
-            android:viewportWidth="24">
+            android:width="60dp"
+            android:height="60dp"
+            android:viewportHeight="60"
+            android:viewportWidth="60">
             <group android:name="_R_G">
                 <group
-                    android:name="_R_G_L_3_G"
-                    android:pivotX="-33"
-                    android:pivotY="-34"
-                    android:rotation="0"
-                    android:scaleX="0.738"
-                    android:scaleY="0.738"
-                    android:translateX="45"
-                    android:translateY="46.4">
-                    <path
-                        android:name="_R_G_L_3_G_D_0_P_0"
-                        android:pathData=" M-25.36 -24.41 C-25.93,-24.31 -26.49,-24.27 -26.81,-24.27 C-28.11,-24.27 -29.35,-24.62 -30.43,-25.4 C-32.11,-26.6 -33.2,-28.57 -33.2,-30.79 "
-                        android:strokeAlpha="1"
-                        android:strokeColor="?android:attr/colorAccent"
-                        android:strokeLineCap="round"
-                        android:strokeLineJoin="round"
-                        android:strokeWidth="1.45"
-                        android:trimPathEnd="1"
-                        android:trimPathOffset="0"
-                        android:trimPathStart="0" />
-                    <path
-                        android:name="_R_G_L_3_G_D_1_P_0"
-                        android:pathData=" M-36.14 -21.78 C-37.15,-22.98 -37.72,-23.7 -38.51,-25.29 C-39.33,-26.94 -39.82,-28.78 -39.82,-30.77 C-39.82,-34.43 -36.85,-37.4 -33.19,-37.4 C-29.52,-37.4 -26.55,-34.43 -26.55,-30.77 "
-                        android:strokeAlpha="1"
-                        android:strokeColor="?android:attr/colorAccent"
-                        android:strokeLineCap="round"
-                        android:strokeLineJoin="round"
-                        android:strokeWidth="1.45"
-                        android:trimPathEnd="1"
-                        android:trimPathOffset="0"
-                        android:trimPathStart="0" />
-                    <path
-                        android:name="_R_G_L_3_G_D_2_P_0"
-                        android:pathData=" M-42.19 -25.68 C-42.95,-27.82 -43.09,-29.54 -43.09,-30.8 C-43.09,-32.27 -42.84,-33.65 -42.27,-34.9 C-40.71,-38.35 -37.24,-40.75 -33.2,-40.75 C-27.71,-40.75 -23.26,-36.3 -23.26,-30.8 C-23.26,-28.97 -24.74,-27.49 -26.57,-27.49 C-28.4,-27.49 -29.89,-28.97 -29.89,-30.8 C-29.89,-32.64 -31.37,-34.12 -33.2,-34.12 C-35.04,-34.12 -36.52,-32.64 -36.52,-30.8 C-36.52,-28.23 -35.53,-25.92 -33.92,-24.22 C-32.69,-22.93 -31.48,-22.12 -29.44,-21.53 "
-                        android:strokeAlpha="1"
-                        android:strokeColor="?android:attr/colorAccent"
-                        android:strokeLineCap="round"
-                        android:strokeLineJoin="round"
-                        android:strokeWidth="1.45"
-                        android:trimPathEnd="1"
-                        android:trimPathOffset="0"
-                        android:trimPathStart="0" />
-                    <path
-                        android:name="_R_G_L_3_G_D_3_P_0"
-                        android:pathData=" M-44.06 -38.17 C-42.87,-39.94 -41.39,-41.41 -39.51,-42.44 C-37.62,-43.47 -35.46,-44.05 -33.16,-44.05 C-30.88,-44.05 -28.72,-43.47 -26.85,-42.45 C-24.97,-41.43 -23.48,-39.97 -22.29,-38.21 "
-                        android:strokeAlpha="1"
-                        android:strokeColor="?android:attr/colorAccent"
-                        android:strokeLineCap="round"
-                        android:strokeLineJoin="round"
-                        android:strokeWidth="1.45"
-                        android:trimPathEnd="1"
-                        android:trimPathOffset="0"
-                        android:trimPathStart="0" />
-                    <path
-                        android:name="_R_G_L_3_G_D_4_P_0"
-                        android:pathData=" M-25.72 -45.45 C-27.99,-46.76 -30.43,-47.52 -33.28,-47.52 C-36.13,-47.52 -38.51,-46.74 -40.62,-45.45 "
-                        android:strokeAlpha="1"
-                        android:strokeColor="?android:attr/colorAccent"
-                        android:strokeLineCap="round"
-                        android:strokeLineJoin="round"
-                        android:strokeWidth="1.45"
-                        android:trimPathEnd="1"
-                        android:trimPathOffset="0"
-                        android:trimPathStart="0" />
-                </group>
-                <group
-                    android:name="_R_G_L_2_G"
-                    android:pivotX="-33"
-                    android:pivotY="-34"
-                    android:rotation="0"
-                    android:scaleX="0.738"
-                    android:scaleY="0.738"
-                    android:translateX="45"
-                    android:translateY="46.4">
-                    <path
-                        android:name="_R_G_L_2_G_D_0_P_0"
-                        android:pathData=" M-25.36 -24.41 C-25.93,-24.31 -26.49,-24.27 -26.81,-24.27 C-28.11,-24.27 -29.35,-24.62 -30.43,-25.4 C-32.11,-26.6 -33.2,-28.57 -33.2,-30.79 "
-                        android:strokeAlpha="1"
-                        android:strokeColor="?android:attr/colorError"
-                        android:strokeLineCap="round"
-                        android:strokeLineJoin="round"
-                        android:strokeWidth="1.45"
-                        android:trimPathEnd="0"
-                        android:trimPathOffset="0"
-                        android:trimPathStart="0" />
-                    <path
-                        android:name="_R_G_L_2_G_D_1_P_0"
-                        android:pathData=" M-36.14 -21.78 C-37.15,-22.98 -37.72,-23.7 -38.51,-25.29 C-39.33,-26.94 -39.82,-28.78 -39.82,-30.77 C-39.82,-34.43 -36.85,-37.4 -33.19,-37.4 C-29.52,-37.4 -26.55,-34.43 -26.55,-30.77 "
-                        android:strokeAlpha="1"
-                        android:strokeColor="?android:attr/colorError"
-                        android:strokeLineCap="round"
-                        android:strokeLineJoin="round"
-                        android:strokeWidth="1.45"
-                        android:trimPathEnd="0"
-                        android:trimPathOffset="0"
-                        android:trimPathStart="0" />
-                    <path
-                        android:name="_R_G_L_2_G_D_2_P_0"
-                        android:pathData=" M-42.19 -25.68 C-42.95,-27.82 -43.09,-29.54 -43.09,-30.8 C-43.09,-32.27 -42.84,-33.65 -42.27,-34.9 C-40.71,-38.35 -37.24,-40.75 -33.2,-40.75 C-27.71,-40.75 -23.26,-36.3 -23.26,-30.8 C-23.26,-28.97 -24.74,-27.49 -26.57,-27.49 C-28.4,-27.49 -29.89,-28.97 -29.89,-30.8 C-29.89,-32.64 -31.37,-34.12 -33.2,-34.12 C-35.04,-34.12 -36.52,-32.64 -36.52,-30.8 C-36.52,-28.23 -35.53,-25.92 -33.92,-24.22 C-32.69,-22.93 -31.48,-22.12 -29.44,-21.53 "
-                        android:strokeAlpha="1"
-                        android:strokeColor="?android:attr/colorError"
-                        android:strokeLineCap="round"
-                        android:strokeLineJoin="round"
-                        android:strokeWidth="1.45"
-                        android:trimPathEnd="0"
-                        android:trimPathOffset="0"
-                        android:trimPathStart="0" />
-                    <path
-                        android:name="_R_G_L_2_G_D_3_P_0"
-                        android:pathData=" M-44.06 -38.17 C-42.87,-39.94 -41.39,-41.41 -39.51,-42.44 C-37.62,-43.47 -35.46,-44.05 -33.16,-44.05 C-30.88,-44.05 -28.72,-43.47 -26.85,-42.45 C-24.97,-41.43 -23.48,-39.97 -22.29,-38.21 "
-                        android:strokeAlpha="1"
-                        android:strokeColor="?android:attr/colorError"
-                        android:strokeLineCap="round"
-                        android:strokeLineJoin="round"
-                        android:strokeWidth="1.45"
-                        android:trimPathEnd="1"
-                        android:trimPathOffset="0"
-                        android:trimPathStart="1" />
-                    <path
-                        android:name="_R_G_L_2_G_D_4_P_0"
-                        android:pathData=" M-25.72 -45.45 C-27.99,-46.76 -30.43,-47.52 -33.28,-47.52 C-36.13,-47.52 -38.51,-46.74 -40.62,-45.45 "
-                        android:strokeAlpha="1"
-                        android:strokeColor="?android:attr/colorError"
-                        android:strokeLineCap="round"
-                        android:strokeLineJoin="round"
-                        android:strokeWidth="1.45"
-                        android:trimPathEnd="0"
-                        android:trimPathOffset="0"
-                        android:trimPathStart="0" />
-                </group>
-                <group
-                    android:name="_R_G_L_1_G"
-                    android:rotation="190"
-                    android:translateX="12"
-                    android:translateY="12">
-                    <path
-                        android:name="_R_G_L_1_G_D_0_P_0"
-                        android:pathData=" M0 -9 C4.97,-9 9,-4.97 9,0 C9,4.97 4.97,9 0,9 C-4.97,9 -9,4.97 -9,0 C-9,-4.97 -4.97,-9 0,-9c "
-                        android:strokeAlpha="1"
-                        android:strokeColor="?android:attr/colorError"
-                        android:strokeLineCap="round"
-                        android:strokeLineJoin="round"
-                        android:strokeWidth="2"
-                        android:trimPathEnd="1"
-                        android:trimPathOffset="0"
-                        android:trimPathStart="1" />
-                </group>
-                <group
-                    android:name="_R_G_L_0_G"
-                    android:translateX="12"
-                    android:translateY="12">
+                    android:name="_R_G_L_1_G_N_4_T_0"
+                    android:translateX="30"
+                    android:translateY="30">
                     <group
-                        android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
-                        android:pivotY="-0.012"
-                        android:rotation="184"
-                        android:scaleX="0"
-                        android:scaleY="0">
+                        android:name="_R_G_L_1_G"
+                        android:pivotX="114"
+                        android:pivotY="114"
+                        android:scaleX="0.42244"
+                        android:scaleY="0.42244"
+                        android:translateX="-114"
+                        android:translateY="-114">
                         <path
-                            android:name="_R_G_L_0_G_D_0_P_0"
-                            android:fillAlpha="1"
-                            android:fillColor="?android:attr/colorError"
-                            android:fillType="nonZero"
-                            android:pathData=" M1.1 3.94 C1.1,4.55 0.61,5.04 0,5.04 C-0.61,5.04 -1.1,4.55 -1.1,3.94 C-1.1,3.33 -0.61,2.84 0,2.84 C0.61,2.84 1.1,3.33 1.1,3.94c " />
+                            android:name="_R_G_L_1_G_D_0_P_0"
+                            android:pathData=" M79.63 67.24 C79.63,67.24 111.5,47.42 147.83,67.24 "
+                            android:strokeAlpha="1"
+                            android:strokeColor="?android:attr/colorAccent"
+                            android:strokeLineCap="round"
+                            android:strokeLineJoin="round"
+                            android:strokeWidth="5.5"
+                            android:trimPathEnd="1"
+                            android:trimPathOffset="0"
+                            android:trimPathStart="0" />
+                        <path
+                            android:name="_R_G_L_1_G_D_1_P_0"
+                            android:pathData=" M64.27 98.07 C64.27,98.07 80.13,73.02 113.98,73.02 C147.83,73.02 163.56,97.26 163.56,97.26 "
+                            android:strokeAlpha="1"
+                            android:strokeColor="?android:attr/colorAccent"
+                            android:strokeLineCap="round"
+                            android:strokeLineJoin="round"
+                            android:strokeWidth="5.5"
+                            android:trimPathEnd="1"
+                            android:trimPathOffset="0"
+                            android:trimPathStart="0" />
+                        <path
+                            android:name="_R_G_L_1_G_D_2_P_0"
+                            android:pathData=" M72.53 151.07 C72.53,151.07 62.46,122.89 76.16,105.55 C89.86,88.21 106.72,86.73 113.98,86.73 C121.08,86.73 153.51,90.62 158.7,125.87 C159.14,128.82 158.8,132.88 157.18,136.09 C154.88,140.63 150.62,143.63 145.85,143.97 C133.78,144.85 129.76,137.92 129.26,128.49 C128.88,121.19 122.49,115.35 113.15,115.35 C102.91,115.35 95.97,126.69 99.77,139.74 C103.57,152.78 111.33,163.85 130.32,169.13 "
+                            android:strokeAlpha="1"
+                            android:strokeColor="?android:attr/colorAccent"
+                            android:strokeLineCap="round"
+                            android:strokeLineJoin="round"
+                            android:strokeWidth="5.5"
+                            android:trimPathEnd="1"
+                            android:trimPathOffset="0"
+                            android:trimPathStart="0" />
+                        <path
+                            android:name="_R_G_L_1_G_D_3_P_0"
+                            android:pathData=" M100.6 167.84 C100.6,167.84 82.76,152.1 83.75,130.31 C84.75,108.53 102.58,100.7 113.73,100.7 C124.87,100.7 144.19,108.56 144.19,130.01 "
+                            android:strokeAlpha="1"
+                            android:strokeColor="?android:attr/colorAccent"
+                            android:strokeLineCap="round"
+                            android:strokeLineJoin="round"
+                            android:strokeWidth="5.5"
+                            android:trimPathEnd="1"
+                            android:trimPathOffset="0"
+                            android:trimPathStart="0" />
+                        <path
+                            android:name="_R_G_L_1_G_D_4_P_0"
+                            android:pathData=" M113.73 129.17 C113.73,129.17 113.15,161.33 149.15,156.58 "
+                            android:strokeAlpha="1"
+                            android:strokeColor="?android:attr/colorAccent"
+                            android:strokeLineCap="round"
+                            android:strokeLineJoin="round"
+                            android:strokeWidth="5.5"
+                            android:trimPathEnd="1"
+                            android:trimPathOffset="0"
+                            android:trimPathStart="0" />
                     </group>
+                </group>
+                <group
+                    android:name="_R_G_L_0_G_N_4_T_0"
+                    android:translateX="30"
+                    android:translateY="30">
                     <group
-                        android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0"
-                        android:pivotY="-0.012"
-                        android:rotation="184"
-                        android:scaleX="0"
-                        android:scaleY="0">
+                        android:name="_R_G_L_0_G"
+                        android:translateX="-30.05"
+                        android:translateY="-30">
+                        <group
+                            android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
+                            android:scaleX="0"
+                            android:scaleY="0"
+                            android:translateX="30"
+                            android:translateY="38.75">
+                            <path
+                                android:name="_R_G_L_0_G_D_0_P_0"
+                                android:fillAlpha="1"
+                                android:fillColor="?android:attr/colorError"
+                                android:fillType="nonZero"
+                                android:pathData=" M-1.2 -1.25 C-1.2,-1.25 1.2,-1.25 1.2,-1.25 C1.2,-1.25 1.2,1.25 1.2,1.25 C1.2,1.25 -1.2,1.25 -1.2,1.25 C-1.2,1.25 -1.2,-1.25 -1.2,-1.25c " />
+                        </group>
+                        <group
+                            android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0"
+                            android:pivotX="0.002"
+                            android:pivotY="7.488"
+                            android:scaleX="1"
+                            android:scaleY="0"
+                            android:translateX="30"
+                            android:translateY="25">
+                            <path
+                                android:name="_R_G_L_0_G_D_1_P_0"
+                                android:fillAlpha="1"
+                                android:fillColor="?android:attr/colorError"
+                                android:fillType="nonZero"
+                                android:pathData=" M-1.2 -7.5 C-1.2,-7.5 1.2,-7.5 1.2,-7.5 C1.2,-7.5 1.2,7.5 1.2,7.5 C1.2,7.5 -1.2,7.5 -1.2,7.5 C-1.2,7.5 -1.2,-7.5 -1.2,-7.5c " />
+                        </group>
                         <path
-                            android:name="_R_G_L_0_G_D_0_P_1"
-                            android:fillAlpha="1"
-                            android:fillColor="?android:attr/colorError"
-                            android:fillType="nonZero"
-                            android:pathData=" M1 -4.06 C1,-4.06 1,-0.06 1,-0.06 C1,0.49 0.55,0.94 0,0.94 C-0.55,0.94 -1,0.49 -1,-0.06 C-1,-0.06 -1,-4.06 -1,-4.06 C-1,-4.61 -0.55,-5.06 0,-5.06 C0.55,-5.06 1,-4.61 1,-4.06c " />
+                            android:name="_R_G_L_0_G_D_2_P_0"
+                            android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
+                            android:strokeAlpha="1"
+                            android:strokeColor="?android:attr/colorError"
+                            android:strokeLineCap="round"
+                            android:strokeLineJoin="round"
+                            android:strokeWidth="2.5"
+                            android:trimPathEnd="1"
+                            android:trimPathOffset="0"
+                            android:trimPathStart="1" />
                     </group>
                 </group>
             </group>
             <group android:name="time_group" />
         </vector>
     </aapt:attr>
-    <target android:name="_R_G_L_3_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="150"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_D_1_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="33"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="150"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="33"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_D_2_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="17"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="17"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_D_3_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="17"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="0"
-                    android:valueFrom="1"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="133"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="17"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G_D_4_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="17"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="67"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="17"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_3_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="rotation"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="567"
-                    android:propertyName="rotation"
-                    android:startOffset="100"
-                    android:valueFrom="0"
-                    android:valueTo="-305"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="150"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="150"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_D_1_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="133"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="200"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="133"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_D_1_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="17"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="217"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="17"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_D_2_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="133"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="200"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="133"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_D_2_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="250"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_D_3_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="0"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_D_3_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="117"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="0"
-                    android:valueFrom="1"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="117"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="117"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_D_4_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="100"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.5,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G_D_4_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="133"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_2_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="rotation"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="567"
-                    android:propertyName="rotation"
-                    android:startOffset="100"
-                    android:valueFrom="0"
-                    android:valueTo="-305"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
     <target android:name="_R_G_L_1_G_D_0_P_0">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
@@ -633,6 +152,163 @@
                     android:duration="167"
                     android:propertyName="trimPathStart"
                     android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="trimPathStart"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_2_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="trimPathStart"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_3_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="trimPathStart"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_4_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="trimPathStart"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="100"
+                    android:propertyName="scaleX"
+                    android:startOffset="167"
+                    android:valueFrom="0"
+                    android:valueTo="1.1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="100"
+                    android:propertyName="scaleY"
+                    android:startOffset="167"
+                    android:valueFrom="0"
+                    android:valueTo="1.1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="scaleX"
+                    android:startOffset="267"
+                    android:valueFrom="1.1"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.147,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="scaleY"
+                    android:startOffset="267"
+                    android:valueFrom="1.1"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.147,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
                     android:valueFrom="1"
                     android:valueTo="1"
                     android:valueType="floatType">
@@ -641,11 +317,55 @@
                     </aapt:attr>
                 </objectAnimator>
                 <objectAnimator
-                    android:duration="533"
-                    android:propertyName="trimPathStart"
+                    android:duration="167"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.096 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="100"
+                    android:propertyName="scaleX"
                     android:startOffset="167"
                     android:valueFrom="1"
-                    android:valueTo="0"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="100"
+                    android:propertyName="scaleY"
+                    android:startOffset="167"
+                    android:valueFrom="0"
+                    android:valueTo="1.1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.096 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="scaleX"
+                    android:startOffset="267"
+                    android:valueFrom="1"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.341,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="scaleY"
+                    android:startOffset="267"
+                    android:valueFrom="1.1"
+                    android:valueTo="1"
                     android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
                         <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
@@ -654,185 +374,18 @@
             </set>
         </aapt:attr>
     </target>
-    <target android:name="_R_G_L_1_G">
+    <target android:name="_R_G_L_0_G_D_2_P_0">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
                 <objectAnimator
-                    android:duration="150"
-                    android:propertyName="rotation"
+                    android:duration="267"
+                    android:propertyName="trimPathStart"
                     android:startOffset="0"
-                    android:valueFrom="190"
-                    android:valueTo="190"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="550"
-                    android:propertyName="rotation"
-                    android:startOffset="150"
-                    android:valueFrom="190"
-                    android:valueTo="-6"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="283"
-                    android:propertyName="rotation"
-                    android:startOffset="0"
-                    android:valueFrom="184"
-                    android:valueTo="184"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="317"
-                    android:propertyName="rotation"
-                    android:startOffset="283"
-                    android:valueFrom="184"
+                    android:valueFrom="1"
                     android:valueTo="0"
                     android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="283"
-                    android:propertyName="scaleX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="283"
-                    android:propertyName="scaleY"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="317"
-                    android:propertyName="scaleX"
-                    android:startOffset="283"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="317"
-                    android:propertyName="scaleY"
-                    android:startOffset="283"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="283"
-                    android:propertyName="rotation"
-                    android:startOffset="0"
-                    android:valueFrom="184"
-                    android:valueTo="184"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="317"
-                    android:propertyName="rotation"
-                    android:startOffset="283"
-                    android:valueFrom="184"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="283"
-                    android:propertyName="scaleX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="283"
-                    android:propertyName="scaleY"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="317"
-                    android:propertyName="scaleX"
-                    android:startOffset="283"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="317"
-                    android:propertyName="scaleY"
-                    android:startOffset="283"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.6,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
             </set>
@@ -842,7 +395,7 @@
         <aapt:attr name="android:animation">
             <set android:ordering="together">
                 <objectAnimator
-                    android:duration="717"
+                    android:duration="350"
                     android:propertyName="translateX"
                     android:startOffset="0"
                     android:valueFrom="0"
@@ -851,4 +404,4 @@
             </set>
         </aapt:attr>
     </target>
-</animated-vector>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/fingerprint_icon.xml b/packages/SystemUI/res/drawable/fingerprint_icon.xml
deleted file mode 100644
index 76a86ae..0000000
--- a/packages/SystemUI/res/drawable/fingerprint_icon.xml
+++ /dev/null
@@ -1,107 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="60dp"
-    android:height="60dp"
-    android:viewportWidth="60"
-    android:viewportHeight="60">
-
-    <path
-        android:fillColor="#1A73E8"
-        android:fillType="evenOdd"
-        android:strokeWidth="1"
-        android:pathData="M 30 0 C 46.5685424949 0 60 13.4314575051 60 30 C 60 46.5685424949 46.5685424949 60 30 60 C 13.4314575051 60 0 46.5685424949 0 30 C 0 13.4314575051 13.4314575051 0 30 0 Z" />
-    <group
-        android:translateX="17.727273"
-        android:translateY="16.363636">
-        <path
-            android:fillColor="#FFFFFF"
-            android:strokeWidth="1"
-            android:pathData="M20.3065726,3.44516129 C20.1974817,3.44516129 20.0883908,3.41788856
-19.9929362,3.36334311 C17.3747544,2.01334311 15.111118,1.44061584
-12.3974817,1.44061584 C9.69748166,1.44061584 7.1338453,2.08152493
-4.80202711,3.36334311 C4.47475439,3.54061584 4.06566348,3.41788856
-3.87475439,3.09061584 C3.69748166,2.76334311 3.82020893,2.34061584
-4.14748166,2.16334311 C6.6838453,0.786070381 9.46566348,0.0769794721
-12.3974817,0.0769794721 C15.3020271,0.0769794721 17.8383908,0.717888563
-20.6202089,2.14970674 C20.961118,2.32697947 21.0838453,2.73607038
-20.9065726,3.06334311 C20.7838453,3.30879765 20.5520271,3.44516129
-20.3065726,3.44516129 L20.3065726,3.44516129 Z M0.792936205,10.6042522
-C0.656572568,10.6042522 0.520208932,10.5633431 0.397481659,10.4815249
-C0.0838452956,10.2633431 0.0156634774,9.84061584 0.233845296,9.52697947
-C1.5838453,7.61788856 3.30202711,6.11788856 5.34748166,5.06788856
-C9.62929984,2.85879765 15.111118,2.84516129 19.4065726,5.0542522
-C21.4520271,6.1042522 23.1702089,7.59061584 24.5202089,9.48607038
-C24.7383908,9.78607038 24.6702089,10.222434 24.3565726,10.4406158
-C24.0429362,10.6587977 23.6202089,10.5906158 23.4020271,10.2769795
-C22.1747544,8.55879765 20.6202089,7.20879765 18.7792998,6.26788856
-C14.8656635,4.26334311 9.86111802,4.26334311 5.96111802,6.28152493
-C4.10657257,7.23607038 2.55202711,8.59970674 1.32475439,10.3178886
-C1.21566348,10.5087977 1.01111802,10.6042522 0.792936205,10.6042522
-L0.792936205,10.6042522 Z M9.31566348,27.0633431 C9.13839075,27.0633431
-8.96111802,26.9951613 8.83839075,26.8587977 C7.65202711,25.672434
-7.01111802,24.9087977 6.09748166,23.2587977 C5.15657257,21.5815249
-4.66566348,19.5360704 4.66566348,17.3406158 C4.66566348,13.2906158
-8.12929984,9.99061584 12.3838453,9.99061584 C16.6383908,9.99061584
-20.1020271,13.2906158 20.1020271,17.3406158 C20.1020271,17.722434
-19.8020271,18.022434 19.4202089,18.022434 C19.0383908,18.022434
-18.7383908,17.722434 18.7383908,17.3406158 C18.7383908,14.0406158
-15.8883908,11.3542522 12.3838453,11.3542522 C8.87929984,11.3542522
-6.02929984,14.0406158 6.02929984,17.3406158 C6.02929984,19.3042522
-6.46566348,21.1178886 7.29748166,22.5906158 C8.17020893,24.1587977
-8.77020893,24.8269795 9.82020893,25.8906158 C10.0792998,26.1633431
-10.0792998,26.5860704 9.82020893,26.8587977 C9.67020893,26.9951613
-9.4929362,27.0633431 9.31566348,27.0633431 Z M19.0929362,24.5406158
-C17.4702089,24.5406158 16.0383908,24.1315249 14.8656635,23.3269795
-C12.8338453,21.9497067 11.6202089,19.7133431 11.6202089,17.3406158
-C11.6202089,16.9587977 11.9202089,16.6587977 12.3020271,16.6587977
-C12.6838453,16.6587977 12.9838453,16.9587977 12.9838453,17.3406158
-C12.9838453,19.2633431 13.9656635,21.0769795 15.6292998,22.1951613
-C16.5974817,22.8497067 17.7292998,23.1633431 19.0929362,23.1633431
-C19.4202089,23.1633431 19.9656635,23.122434 20.511118,23.0269795
-C20.8792998,22.9587977 21.2338453,23.2042522 21.3020271,23.5860704
-C21.3702089,23.9542522 21.1247544,24.3087977 20.7429362,24.3769795
-C19.9656635,24.5269795 19.2838453,24.5406158 19.0929362,24.5406158
-L19.0929362,24.5406158 Z M16.3520271,27.3497067 C16.2974817,27.3497067
-16.2292998,27.3360704 16.1747544,27.322434 C14.0065726,26.722434
-12.5883908,25.9178886 11.1020271,24.4587977 C9.1929362,22.5633431
-8.1429362,20.0406158 8.1429362,17.3406158 C8.1429362,15.1315249
-10.0247544,13.3315249 12.3429362,13.3315249 C14.661118,13.3315249
-16.5429362,15.1315249 16.5429362,17.3406158 C16.5429362,18.7997067
-17.811118,19.9860704 19.3792998,19.9860704 C20.9474817,19.9860704
-22.2156635,18.7997067 22.2156635,17.3406158 C22.2156635,12.1997067
-17.7838453,8.02697947 12.3292998,8.02697947 C8.45657257,8.02697947
-4.91111802,10.1815249 3.31566348,13.522434 C2.7838453,14.6269795
-2.51111802,15.922434 2.51111802,17.3406158 C2.51111802,18.4042522
-2.60657257,20.0815249 3.42475439,22.2633431 C3.56111802,22.6178886
-3.3838453,23.0133431 3.02929984,23.1360704 C2.67475439,23.272434
-2.27929984,23.0815249 2.15657257,22.7406158 C1.48839075,20.9542522
-1.16111802,19.1815249 1.16111802,17.3406158 C1.16111802,15.7042522
-1.47475439,14.2178886 2.08839075,12.922434 C3.90202711,9.11788856
-7.92475439,6.64970674 12.3292998,6.64970674 C18.5338453,6.64970674
-23.5792998,11.4360704 23.5792998,17.3269795 C23.5792998,19.5360704
-21.6974817,21.3360704 19.3792998,21.3360704 C17.061118,21.3360704
-15.1792998,19.5360704 15.1792998,17.3269795 C15.1792998,15.8678886
-13.911118,14.6815249 12.3429362,14.6815249 C10.7747544,14.6815249
-9.50657257,15.8678886 9.50657257,17.3269795 C9.50657257,19.6587977
-10.4065726,21.8406158 12.0565726,23.4769795 C13.3520271,24.7587977
-14.5929362,25.4678886 16.5156635,25.9997067 C16.8838453,26.0951613
-17.0883908,26.4769795 16.9929362,26.8315249 C16.9247544,27.1451613
-16.6383908,27.3497067 16.3520271,27.3497067 L16.3520271,27.3497067 Z" />
-    </group>
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/header_dot.xml b/packages/SystemUI/res/drawable/header_dot.xml
deleted file mode 100644
index dbc6b37..0000000
--- a/packages/SystemUI/res/drawable/header_dot.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-** Copyright 2015, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
--->
-<shape
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval"
-    android:tint="?android:attr/colorControlNormal">
-
-    <solid
-        android:color="#FFFFFF"/>
-
-    <size
-        android:width="3dp"
-        android:height="3dp"/>
-</shape>
diff --git a/packages/SystemUI/res/drawable/ic_5g_mobiledata.xml b/packages/SystemUI/res/drawable/ic_5g_mobiledata.xml
new file mode 100644
index 0000000..2aa6e57f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_5g_mobiledata.xml
@@ -0,0 +1,27 @@
+<!--
+     Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="14dp"
+    android:height="17dp"
+    android:viewportWidth="14"
+    android:viewportHeight="17">
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M13.9,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07s-0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13s1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.23,0.79s0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45s-0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7S8.72,6.37 8.71,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M1.15,8.47l0.43,-4.96h4.33v1.17H2.6L2.37,7.39C2.78,7.1 3.22,6.96 3.69,6.96c0.77,0 1.38,0.3 1.83,0.9s0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43S4.32,13.6 3.48,13.6c-0.75,0 -1.36,-0.24 -1.83,-0.73s-0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59S3.88,8.09 3.4,8.09c-0.4,0 -0.72,0.1 -0.96,0.31L2.11,8.73L1.15,8.47z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_5g_plus_mobiledata.xml b/packages/SystemUI/res/drawable/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..10bbcc7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,33 @@
+<!--
+     Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M1.03 8.47l0.43-4.96h4.33v1.17H2.48L2.25 7.39C2.66 7.1 3.1 6.96 3.57 6.96c0.77 0 1.38 0.3 1.83 0.9 s0.66 1.41 0.66 2.43c0 1.03-0.24 1.84-0.72 2.43S4.2 13.6 3.36 13.6c-0.75 0-1.36-0.24-1.83-0.73s-0.74-1.16-0.81-2.02h1.13 c0.07 0.57 0.23 1 0.49 1.29s0.59 0.43 1.01 0.43c0.47 0 0.84-0.2 1.1-0.61c0.26-0.41 0.4-0.96 0.4-1.65 c0-0.65-0.14-1.18-0.43-1.59S3.76 8.09 3.28 8.09c-0.4 0-0.72 0.1-0.96 0.31L1.99 8.73L1.03 8.47z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M 18.93,5.74 L 18.93,3.39 L 17.63,3.39 L 17.63,5.74 L 15.28,5.74 L 15.28,7.04 L 17.63,7.04 L 17.63,9.39 L 18.93,9.39 L 18.93,7.04 L 21.28,7.04 L 21.28,5.74 z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.78 12.24l-0.22 0.27c-0.63 0.73-1.55 1.1-2.76 1.1c-1.08 0-1.92-0.36-2.53-1.07s-0.93-1.72-0.94-3.02V7.56 c0-1.39 0.28-2.44 0.84-3.13s1.39-1.04 2.51-1.04c0.95 0 1.69 0.26 2.23 0.79s0.83 1.28 0.89 2.26h-1.25 c-0.05-0.62-0.22-1.1-0.52-1.45s-0.74-0.52-1.34-0.52c-0.72 0-1.24 0.23-1.57 0.7S8.6 6.37 8.59 7.4v2.03c0 1 0.19 1.77 0.57 2.31 c0.38 0.54 0.93 0.8 1.65 0.8c0.67 0 1.19-0.16 1.54-0.49l0.18-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_aod_charging_24dp.xml b/packages/SystemUI/res/drawable/ic_aod_charging_24dp.xml
deleted file mode 100644
index 6134b8f..0000000
--- a/packages/SystemUI/res/drawable/ic_aod_charging_24dp.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24.0dp"
-        android:height="24.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:pathData="M11.0,22.98l0.0,-8.98 -4.0,0.0 6.0,-13.0 0.0,9.0 4.0,0.0z"
-        android:fillColor="#ffffff"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_cast.xml b/packages/SystemUI/res/drawable/ic_cast.xml
deleted file mode 100644
index b86dfea..0000000
--- a/packages/SystemUI/res/drawable/ic_cast.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
-  Copyright (C) 2017 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M1 18v2c0 .55 .45 1 1 1h2c0-1.66-1.34-3-3-3zm0-2.94c-.01 .51 .32 .93 .82 1.02
-2.08 .36 3.74 2 4.1 4.08 .09 .48 .5 .84 .99 .84 .61 0 1.09-.54 1-1.14a6.996
-6.996 0 0 0-5.8-5.78c-.59-.09-1.09 .38 -1.11 .98 zm0-4.03c-.01 .52 .34 .96 .85
-1.01 4.26 .43 7.68 3.82 8.1 8.08 .05 .5 .48 .88 .99 .88 .59 0 1.06-.51
-1-1.1-.52-5.21-4.66-9.34-9.87-9.85-.57-.05-1.05 .4 -1.07 .98 zM21 3H3c-1.1 0-2
-.9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"/>
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_chevron_left.xml b/packages/SystemUI/res/drawable/ic_chevron_left.xml
deleted file mode 100644
index 379382b..0000000
--- a/packages/SystemUI/res/drawable/ic_chevron_left.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-  ~ Copyright (C) 2014 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="36.0"
-        android:viewportHeight="36.0">
-
-    <path
-        android:fillColor="#ffffffff"
-        android:pathData="M23.1,11.1l-2.1000004,-2.1000004 -9.0,9.0 9.0,9.0 2.1000004,-2.1000004 -6.8999996,-6.8999996z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_data_off.xml b/packages/SystemUI/res/drawable/ic_data_off.xml
deleted file mode 100644
index b97ddae..0000000
--- a/packages/SystemUI/res/drawable/ic_data_off.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
-    Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24.0dp"
-        android:height="24.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:pathData="M21.6,21.6L10.8,10.9L2.1,2.1L0.8,3.4l3.3,3.3C3.1,8.2 2.5,10.0 2.5,12.0c0.0,5.2 4.3,9.5 9.5,9.5c2.0,0.0 3.8,-0.6 5.3,-1.6l3.0,3.0L21.6,21.6zM9.6,12.2l0.7,0.7L9.6,12.9L9.6,12.2zM13.9,18.6c-0.2,0.2 -0.5,0.2 -0.6,0.0l-2.4,-3.7l1.5,0.0l2.4,2.4L13.9,18.6z"
-        android:fillColor="#ffffff"/>
-    <path
-        android:pathData="M12.0,2.5c-2.0,0.0 -3.8,0.6 -5.3,1.6l2.5,2.5L10.0,5.4c0.2,-0.2 0.5,-0.2 0.6,0.0L13.0,9.1l-1.4,0.0l2.0,2.0l0.6,0.0l0.0,0.6l5.6,5.6c1.0,-1.5 1.6,-3.3 1.6,-5.3C21.5,6.8 17.2,2.5 12.0,2.5z"
-        android:fillColor="#ffffff"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_data_on.xml b/packages/SystemUI/res/drawable/ic_data_on.xml
deleted file mode 100644
index a65dc79..0000000
--- a/packages/SystemUI/res/drawable/ic_data_on.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-    Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24.0dp"
-        android:height="24.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:pathData="M12.0,12.0m-9.5,0.0a9.5,9.5 0.0,1.0 1.0,19.0 0.0a9.5,9.5 0.0,1.0 1.0,-19.0 0.0
-        M10.6,5.4c-0.2,-0.2 -0.5,-0.2 -0.6,0.0L7.6,9.1l2.0,0.0l0.0,3.8L11.0,12.900001L11.0,9.1l2.0,0.0L10.6,5.4z
-        M13.3,18.6c0.2,0.2 0.5,0.2 0.6,0.0l2.4,-3.7l-2.0,0.0l0.0,-3.8l-1.4,0.0l0.0,3.8l-2.0,0.0L13.3,18.6z"
-        android:fillColor="#ffffff"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_data_unavailable.xml b/packages/SystemUI/res/drawable/ic_data_unavailable.xml
deleted file mode 100644
index ffb2af7..0000000
--- a/packages/SystemUI/res/drawable/ic_data_unavailable.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
-    Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24.0dp"
-        android:height="24.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:pathData="M15.8,12.9l3.7,0.0c0.0,-0.3 0.0,-0.6 0.0,-0.9c0.0,-5.2 -4.3,-9.5 -9.5,-9.5S0.6,6.8 0.6,12.0s4.3,9.5 9.5,9.5c2.1,0.0 4.1,-0.7 5.7,-1.9L15.8,12.9zM5.7,9.1l2.4,-3.7c0.2,-0.2 0.5,-0.2 0.6,0.0l2.4,3.7l-2.0,0.0l0.0,3.8L7.8,12.900001L7.8,9.1L5.7,9.1zM11.4,18.6L9.0,14.9l2.0,0.0l0.0,-3.8l1.4,0.0l0.0,3.8l2.0,0.0L12.0,18.6C11.9,18.8 11.6,18.8 11.4,18.6z"
-        android:fillColor="#FFFFFF"/>
-    <path
-        android:pathData="M23.4,19.4l-2.1,-2.1 2.1,-2.199999 -1.1,-1.0 -2.099998,2.1 -2.1,-2.1 -1.1,1.099999 2.1,2.099999 -2.1,2.1 1.1,1.1 2.1,-2.200001 2.099998,2.200001z"
-        android:fillColor="#FFFFFF"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_expand_less.xml b/packages/SystemUI/res/drawable/ic_expand_less.xml
deleted file mode 100644
index e968013..0000000
--- a/packages/SystemUI/res/drawable/ic_expand_less.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
-Copyright (C) 2015 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="20.0dp"
-        android:height="20.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:pathData="M12.000000,8.000000l-6.000000,6.000000 1.400000,1.400000 4.600000,-4.599999 4.600000,4.599999 1.400000,-1.400000z"
-        android:fillColor="#FF000000"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_expand_more.xml b/packages/SystemUI/res/drawable/ic_expand_more.xml
deleted file mode 100644
index 72e98ec..0000000
--- a/packages/SystemUI/res/drawable/ic_expand_more.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
-Copyright (C) 2015 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="20.0dp"
-        android:height="20.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:pathData="M16.600000,8.600000l-4.600000,4.599999 -4.600000,-4.599999 -1.400000,1.400000 6.000000,6.000000 6.000000,-6.000000z"
-        android:fillColor="#FF000000"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_history.xml b/packages/SystemUI/res/drawable/ic_history.xml
deleted file mode 100644
index e936864..0000000
--- a/packages/SystemUI/res/drawable/ic_history.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-
-    <path
-        android:fillColor="#FFFFFF"
-        android:pathData="M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89 .07 .14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7
-7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13
-21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.28 2.54 .72 -1.21-3.5-2.08V8H12z" />
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_home_button_outline.xml b/packages/SystemUI/res/drawable/ic_home_button_outline.xml
deleted file mode 100644
index 5bf345d..0000000
--- a/packages/SystemUI/res/drawable/ic_home_button_outline.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="28dp"
-        android:height="10dp"
-        android:viewportWidth="28"
-        android:viewportHeight="10">
-    <path
-        android:pathData="M23,1H5C2.7909,1 1,2.7909 1,5C1,7.2091 2.7909,9 5,9H23C25.2091,9 27,7.2091 27,5C27,2.7909 25.2091,1 23,1ZM5,0C2.2386,0 0,2.2386 0,5C0,7.7614 2.2386,10 5,10H23C25.7614,10 28,7.7614 28,5C28,2.2386 25.7614,0 23,0H5Z"
-        android:fillColor="?attr/wallpaperTextColor"
-        android:fillType="evenOdd"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_notify_button_bg.xml b/packages/SystemUI/res/drawable/ic_notify_button_bg.xml
deleted file mode 100644
index 3a47261..0000000
--- a/packages/SystemUI/res/drawable/ic_notify_button_bg.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true"
-          android:drawable="@*android:drawable/list_selector_pressed_holo_dark" />
-</selector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_back.xml b/packages/SystemUI/res/drawable/ic_qs_back.xml
deleted file mode 100644
index f00ba03..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_back.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="64dp"
-        android:height="64dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M20.0,11.0L7.8,11.0l5.6,-5.6L12.0,4.0l-8.0,8.0l8.0,8.0l1.4,-1.4L7.8,13.0L20.0,13.0L20.0,11.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_battery_saver.xml b/packages/SystemUI/res/drawable/ic_qs_battery_saver.xml
deleted file mode 100644
index 7b29740..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_battery_saver.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-    Copyright (C) 2016 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:autoMirrored="true"
-        android:width="32.0dp"
-        android:height="32.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
-    <path
-        android:pathData="M5,3
-        l3.5,0 l0,-1.5 l7,0 l0,1.5 l3.5,0 l0,19.5 l-14,0z
-        M10.5,8.5 l0,3 l-3,0 l0,3 l3,0 l0,3 l3,0 l0,-3 l3,0 l0,-3 l-3,0 l0,-3 z"
-        android:fillColor="#FFFFFF"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_lock.xml b/packages/SystemUI/res/drawable/ic_qs_lock.xml
deleted file mode 100644
index 204af7e..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_lock.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24.0dp"
-        android:height="24.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-
-    <path
-        android:fillColor="@color/keyguard_affordance"
-        android:pathData="M18.0,8.0l-1.0,0.0L17.0,6.0c0.0,-2.8 -2.2,-5.0 -5.0,-5.0C9.2,1.0 7.0,3.2 7.0,6.0l0.0,2.0L6.0,8.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,10.0c0.0,1.1 0.9,2.0 2.0,2.0l12.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L20.0,10.0C20.0,8.9 19.1,8.0 18.0,8.0zM12.0,17.0c-1.1,0.0 -2.0,-0.9 -2.0,-2.0s0.9,-2.0 2.0,-2.0c1.1,0.0 2.0,0.9 2.0,2.0S13.1,17.0 12.0,17.0zM15.1,8.0L8.9,8.0L8.9,6.0c0.0,-1.7 1.4,-3.1 3.1,-3.1c1.7,0.0 3.1,1.4 3.1,3.1L15.1,8.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_lock_open.xml b/packages/SystemUI/res/drawable/ic_qs_lock_open.xml
deleted file mode 100644
index c877f06..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_lock_open.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24.0dp"
-        android:height="24.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-
-    <path
-        android:fillColor="@color/keyguard_affordance"
-        android:pathData="M12.0,17.0c1.1,0.0 2.0,-0.9 2.0,-2.0s-0.9,-2.0 -2.0,-2.0c-1.1,0.0 -2.0,0.9 -2.0,2.0S10.9,17.0 12.0,17.0zM18.0,8.0l-1.0,0.0L17.0,6.0c0.0,-2.8 -2.2,-5.0 -5.0,-5.0C9.2,1.0 7.0,3.2 7.0,6.0l1.9,0.0c0.0,-1.7 1.4,-3.1 3.1,-3.1c1.7,0.0 3.1,1.4 3.1,3.1l0.0,2.0L6.0,8.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,10.0c0.0,1.1 0.9,2.0 2.0,2.0l12.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L20.0,10.0C20.0,8.9 19.1,8.0 18.0,8.0zM18.0,20.0L6.0,20.0L6.0,10.0l12.0,0.0L18.0,20.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_network_logging.xml b/packages/SystemUI/res/drawable/ic_qs_network_logging.xml
deleted file mode 100644
index 7bdf50c..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_network_logging.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-
-<!-- Placeholder icon for network logging until the real icon is finalized-->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-  android:width="12.0dp"
-  android:height="12.0dp"
-  android:viewportWidth="24.0"
-  android:viewportHeight="24.0">
-    <path
-      android:fillColor="#FFFFFFFF"
-      android:pathData="M2,24v-4h12v4H2z M2,16v-4h20v4H2z M5,7 12,0 19,7 14,7 14,15 10,15 10,7z"/>
-
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_ringer_audible.xml b/packages/SystemUI/res/drawable/ic_qs_ringer_audible.xml
deleted file mode 100644
index 2974157..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_ringer_audible.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="64dp"
-        android:height="64dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M18,17v-6c0,-3.07 -1.63,-5.64 -4.5,-6.32V4c0,-0.83 -0.67,-1.5 -1.5,-1.5S10.5,3.17 10.5,4v0.68C7.64,5.36 6,7.92 6,11v6H4v2h10h0.38H20v-2H18zM16,17H8v-6c0,-2.48 1.51,-4.5 4,-4.5s4,2.02 4,4.5V17z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4C10,21.1 10.9,22 12,22z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_ringer_silent.xml b/packages/SystemUI/res/drawable/ic_qs_ringer_silent.xml
deleted file mode 100644
index 6db508c..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_ringer_silent.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="64dp"
-        android:height="64dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4C10,21.1 10.9,22 12,22z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M16,16L2.81,2.81L1.39,4.22l4.85,4.85C6.09,9.68 6,10.33 6,11v6H4v2h12.17l3.61,3.61l1.41,-1.41L16,16zM8,17c0,0 0.01,-6.11 0.01,-6.16L14.17,17H8z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M12,6.5c2.49,0 4,2.02 4,4.5v2.17l2,2V11c0,-3.07 -1.63,-5.64 -4.5,-6.32V4c0,-0.83 -0.67,-1.5 -1.5,-1.5S10.5,3.17 10.5,4v0.68C9.72,4.86 9.05,5.2 8.46,5.63L9.93,7.1C10.51,6.73 11.2,6.5 12,6.5z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_ringer_vibrate.xml b/packages/SystemUI/res/drawable/ic_qs_ringer_vibrate.xml
deleted file mode 100644
index c87b595..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_ringer_vibrate.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="64dp"
-        android:height="64dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M1,9h2v6H1V9zM4,17h2V7H4V17zM21,9v6h2V9H21zM18,17h2V7h-2V17zM17,5.5v13c0,0.83 -0.67,1.5 -1.5,1.5h-7C7.67,20 7,19.33 7,18.5v-13C7,4.67 7.67,4 8.5,4h7C16.33,4 17,4.67 17,5.5zM15,6H9v12h6V6z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_disabled.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_disabled.xml
deleted file mode 100644
index a72e9b8..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_disabled.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="32dp"
-    android:height="29.5dp"
-    android:viewportWidth="25.6"
-    android:viewportHeight="23.6">
-    <group
-        android:translateX="0.8"
-        android:translateY="-1.1">
-        <path
-            android:pathData="M23.66,8.11c0.39,-0.48 0.29,-1.19 -0.22,-1.54C21.67,5.36 17.55,3 12,3 6.44,3 2.33,5.36 0.56,6.57c-0.51,0.35 -0.61,1.06 -0.23,1.54L11.16,21.6c0.42,0.53 1.23,0.53 1.66,0L23.66,8.11z"
-            android:fillColor="#FFFFFFFF"/>
-    </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_full_0.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_full_0.xml
deleted file mode 100644
index 53e4efc..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_full_0.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="32.0dp"
-    android:height="29.5dp"
-    android:viewportWidth="25.6"
-    android:viewportHeight="23.6">
-    <group
-        android:translateX="0.8"
-        android:translateY="-0.9">
-        <path
-            android:pathData="M23.66,8.11c0.39,-0.48 0.29,-1.19 -0.22,-1.54C21.67,5.36 17.55,3 12,3 6.44,3 2.33,5.36 0.56,6.57c-0.51,0.35 -0.61,1.06 -0.23,1.54L11.16,21.6c0.42,0.53 1.23,0.53 1.66,0L23.66,8.11z"
-            android:fillColor="#4DFFFFFF"/>
-    </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_full_1.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_full_1.xml
deleted file mode 100644
index 8294183..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_full_1.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="32dp"
-    android:height="29.5dp"
-    android:viewportWidth="25.6"
-    android:viewportHeight="23.6">
-    <group
-        android:translateX="0.8"
-        android:translateY="-0.9">
-        <path
-            android:pathData="M23.66,8.11c0.39,-0.48 0.29,-1.19 -0.22,-1.54C21.67,5.36 17.55,3 12,3 6.44,3 2.33,5.36 0.56,6.57c-0.51,0.35 -0.61,1.06 -0.23,1.54L11.16,21.6c0.42,0.53 1.23,0.53 1.66,0L23.66,8.11z"
-            android:fillColor="#4DFFFFFF"/>
-        <path
-            android:pathData="M12.82,21.6l5.11,-6.36A8.942,8.942 0,0 0,12 13c-2.28,0 -4.35,0.85 -5.94,2.25l5.1,6.35c0.43,0.53 1.23,0.53 1.66,0z"
-            android:fillColor="#FFFFFFFF"/>
-    </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_full_2.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_full_2.xml
deleted file mode 100644
index 3d59cf2..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_full_2.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="32dp"
-    android:height="29.5dp"
-    android:viewportWidth="25.6"
-    android:viewportHeight="23.6">
-    <group
-        android:translateX="0.8"
-        android:translateY="-0.9">
-        <path
-            android:pathData="M23.66,8.11c0.39,-0.48 0.29,-1.19 -0.22,-1.54C21.67,5.36 17.55,3 12,3 6.44,3 2.33,5.36 0.56,6.57c-0.51,0.35 -0.61,1.06 -0.23,1.54L11.16,21.6c0.42,0.53 1.23,0.53 1.66,0L23.66,8.11z"
-            android:fillColor="#4DFFFFFF"/>
-        <path
-            android:pathData="M12.82,21.6l6.99,-8.7C17.71,11.1 14.99,10 12,10c-2.99,0 -5.72,1.1 -7.82,2.91l6.98,8.7c0.43,0.52 1.23,0.52 1.66,-0.01z"
-            android:fillColor="#FFFFFFFF"/>
-    </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_full_3.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_full_3.xml
deleted file mode 100644
index 21313b8..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_full_3.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="32dp"
-    android:height="29.5dp"
-    android:viewportWidth="25.6"
-    android:viewportHeight="23.6">
-    <group
-        android:translateX="0.8"
-        android:translateY="-0.9">
-        <path
-            android:pathData="M23.66,8.11c0.39,-0.48 0.29,-1.19 -0.22,-1.54C21.67,5.36 17.55,3 12,3 6.44,3 2.33,5.36 0.56,6.57c-0.51,0.35 -0.61,1.06 -0.23,1.54L11.16,21.6c0.42,0.53 1.23,0.53 1.66,0L23.66,8.11z"
-            android:fillColor="#4DFFFFFF"/>
-        <path
-            android:pathData="M12.82,21.6l8.25,-10.26A13.961,13.961 0,0 0,12 8c-3.46,0 -6.63,1.26 -9.07,3.35l8.23,10.26c0.43,0.52 1.23,0.52 1.66,-0.01z"
-            android:fillColor="#FFFFFFFF"/>
-    </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_full_4.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_full_4.xml
deleted file mode 100644
index fd763ff..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_full_4.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="32dp"
-    android:height="29.5dp"
-    android:viewportWidth="25.6"
-    android:viewportHeight="23.6">
-    <group
-        android:translateX="0.8"
-        android:translateY="-0.9">
-        <path
-            android:pathData="M23.66,8.11c0.39,-0.48 0.29,-1.19 -0.22,-1.54C21.67,5.36 17.55,3 12,3 6.44,3 2.33,5.36 0.56,6.57c-0.51,0.35 -0.61,1.06 -0.23,1.54L11.16,21.6c0.42,0.53 1.23,0.53 1.66,0L23.66,8.11z"
-            android:fillColor="#FFFFFFFF"/>
-    </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_sensors.xml b/packages/SystemUI/res/drawable/ic_signal_sensors.xml
new file mode 100644
index 0000000..faaddf6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_signal_sensors.xml
@@ -0,0 +1,28 @@
+<!--
+     Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<vector android:height="48dp" android:viewportHeight="5"
+    android:viewportWidth="5" android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#00000000"
+        android:pathData="m4.762,0.661 l-4.233,4.233"
+        android:strokeAlpha="1" android:strokeColor="#000000"
+        android:strokeLineCap="round" android:strokeLineJoin="miter" android:strokeWidth=".5"/>
+    <path android:fillColor="#00000000"
+        android:pathData="M0.265,2.778L1.058,2.778l0.529,-1.323 0.529,2.646 0.529,-3.175 0.529,2.646 0.529,-1.587 0.265,0.794h1.058"
+        android:strokeAlpha="1" android:strokeColor="#000000"
+        android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth=".33"/>
+</vector>
+
diff --git a/packages/SystemUI/res/drawable/ic_signal_workmode_disable_animation.xml b/packages/SystemUI/res/drawable/ic_signal_workmode_disable_animation.xml
deleted file mode 100644
index 4a2bd54..0000000
--- a/packages/SystemUI/res/drawable/ic_signal_workmode_disable_animation.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<animated-vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:drawable="@drawable/ic_signal_workmode_disable" >
-    <target
-        android:name="mask_1"
-        android:animation="@anim/ic_signal_workmode_disable_mask_1_animation" />
-    <target
-        android:name="whole"
-        android:animation="@anim/ic_signal_workmode_disable_whole_animation" />
-    <target
-        android:name="rectangle_path_3_position"
-        android:animation="@anim/ic_signal_workmode_disable_rectangle_path_3_position_animation" />
-    <target
-        android:name="rectangle_path_3"
-        android:animation="@anim/ic_signal_workmode_disable_rectangle_path_3_animation" />
-    <target
-        android:name="rectangle_path_4_position"
-        android:animation="@anim/ic_signal_workmode_disable_rectangle_path_4_position_animation" />
-    <target
-        android:name="rectangle_path_4"
-        android:animation="@anim/ic_signal_workmode_disable_rectangle_path_4_animation" />
-    <target
-        android:name="left"
-        android:animation="@anim/ic_signal_workmode_disable_left_animation" />
-    <target
-        android:name="right"
-        android:animation="@anim/ic_signal_workmode_disable_right_animation" />
-    <target
-        android:name="stick"
-        android:animation="@anim/ic_signal_workmode_disable_stick_animation" />
-    <target
-        android:name="ic_signal_workmode_disable"
-        android:animation="@anim/ic_signal_workmode_disable_stickito_animation" />
-</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_workmode_enable.xml b/packages/SystemUI/res/drawable/ic_signal_workmode_enable.xml
deleted file mode 100644
index 2d4f0b5..0000000
--- a/packages/SystemUI/res/drawable/ic_signal_workmode_enable.xml
+++ /dev/null
@@ -1,128 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:name="ic_signal_workmode_enable"
-    android:width="42dp"
-    android:viewportWidth="42"
-    android:height="42dp"
-    android:viewportHeight="42" >
-    <group
-        android:name="suitcase"
-        android:translateX="21"
-        android:translateY="36.9375"
-        android:scaleX="0.1"
-        android:scaleY="0.1" >
-        <group
-            android:name="suitcase_pivot"
-            android:translateY="-158" >
-            <clip-path
-                android:name="mask_1"
-                android:pathData="M 366.5,-269.5 c 0.0,0.0 -578.0,2.0 -578.0,2.0 c 0.0,0.0 480.0,480.0 480.0,480.0 c 0.0,0.0 -20.7500915527,20.75 -20.7500915527,20.75 c 0.0,0.0 -479.999908447,-480.000015259 -479.999908447,-480.000015259 c 0.0,0.0 -7.25,539.250015259 -7.25,539.250015259 c 0.0,0.0 606.0,0.0 606.0,0.0 c 0.0,0.0 0.0,-562.0 0.0,-562.0 Z" />
-            <group
-                android:name="whole"
-                android:translateX="-1.11523"
-                android:translateY="32.0918"
-                android:scaleX="0"
-                android:scaleY="0" >
-                <path
-                    android:name="rectangle_path_1"
-                    android:strokeColor="#FFFFFFFF"
-                    android:strokeWidth="65"
-                    android:pathData="M 0.0,-66.5 l 0.0,0.0 c 36.7269358617,0.0 66.5,29.7730641383 66.5,66.5 l 0.0,0.0 c 0.0,36.7269358617 -29.7730641383,66.5 -66.5,66.5 l 0.0,0.0 c -36.7269358617,0.0 -66.5,-29.7730641383 -66.5,-66.5 l 0.0,0.0 c 0.0,-36.7269358617 29.7730641383,-66.5 66.5,-66.5 Z" />
-            </group>
-            <group
-                android:name="handle"
-                android:translateY="-100" >
-                <path
-                    android:name="rectangle_path_2"
-                    android:strokeColor="#FFFFFFFF"
-                    android:strokeWidth="35"
-                    android:pathData="M -34.0,-50.0 l 68.0,0.0 c 8.8365559968,0.0 16.0,7.1634440032 16.0,16.0 l 0.0,68.0 c 0.0,8.8365559968 -7.1634440032,16.0 -16.0,16.0 l -68.0,0.0 c -8.8365559968,0.0 -16.0,-7.1634440032 -16.0,-16.0 l 0.0,-68.0 c 0.0,-8.8365559968 7.1634440032,-16.0 16.0,-16.0 Z" />
-            </group>
-            <group
-                android:name="case"
-                android:translateY="32.68518" >
-                <group
-                    android:name="case_pivot"
-                    android:translateY="-32.68518" >
-                    <group
-                        android:name="bottom"
-                        android:translateY="-97.62964" >
-                        <group
-                            android:name="bottom_pivot"
-                            android:translateY="40" >
-                            <group
-                                android:name="rectangle_path_3_position"
-                                android:translateY="25" >
-                                <path
-                                    android:name="rectangle_path_3"
-                                    android:fillColor="#FFFFFFFF"
-                                    android:pathData="M -143.0,-65.0 l 286.0,0.0 c 17.6731119936,0.0 32.0,14.3268880064 32.0,32.0 l 0.0,66.0 c 0.0,17.6731119936 -14.3268880064,32.0 -32.0,32.0 l -286.0,0.0 c -17.6731119936,0.0 -32.0,-14.3268880064 -32.0,-32.0 l 0.0,-66.0 c 0.0,-17.6731119936 14.3268880064,-32.0 32.0,-32.0 Z" />
-                            </group>
-                        </group>
-                    </group>
-                    <group
-                        android:name="top"
-                        android:translateY="163" >
-                        <group
-                            android:name="top_pivot"
-                            android:translateY="-40" >
-                            <group
-                                android:name="rectangle_path_4_position"
-                                android:translateY="-25" >
-                                <path
-                                    android:name="rectangle_path_4"
-                                    android:fillColor="#FFFFFFFF"
-                                    android:pathData="M -143.0,-65.0 l 286.0,0.0 c 17.6731119936,0.0 32.0,14.3268880064 32.0,32.0 l 0.0,66.0 c 0.0,17.6731119936 -14.3268880064,32.0 -32.0,32.0 l -286.0,0.0 c -17.6731119936,0.0 -32.0,-14.3268880064 -32.0,-32.0 l 0.0,-66.0 c 0.0,-17.6731119936 14.3268880064,-32.0 32.0,-32.0 Z" />
-                            </group>
-                        </group>
-                    </group>
-                    <group
-                        android:name="left"
-                        android:translateX="-175.00635"
-                        android:translateY="30"
-                        android:scaleX="1.8" >
-                        <group
-                            android:name="left_pivot"
-                            android:translateX="50.00635" >
-                            <path
-                                android:name="rectangle_path_5"
-                                android:fillColor="#FFFFFFFF"
-                                android:pathData="M -50.0,-88.0 l 100.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,176.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -100.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-176.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
-                        </group>
-                    </group>
-                    <group
-                        android:name="right"
-                        android:translateX="174.97778"
-                        android:translateY="30"
-                        android:scaleX="1.8" >
-                        <group
-                            android:name="right_pivot"
-                            android:translateX="-49.97778" >
-                            <path
-                                android:name="rectangle_path_6"
-                                android:fillColor="#FFFFFFFF"
-                                android:pathData="M -50.0,-88.0 l 100.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,176.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -100.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-176.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
-                        </group>
-                    </group>
-                </group>
-            </group>
-            <group
-                android:name="stick"
-                android:translateX="-166.59082"
-                android:translateY="-156.51367"
-                android:rotation="-45" >
-                <group
-                    android:name="stick_pivot"
-                    android:translateX="0.18161"
-                    android:translateY="243.8158" >
-                    <path
-                        android:name="stickito"
-                        android:fillColor="#FFFFFFFF"
-                        android:fillAlpha="1"
-                        android:pathData="M -16.5,-243.999885 l 33.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,487.99977 c 0.0,0.0 0.0,0.0 0.0,0.0 l -33.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-487.99977 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
-                </group>
-            </group>
-        </group>
-    </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_workmode_enable_animation.xml b/packages/SystemUI/res/drawable/ic_signal_workmode_enable_animation.xml
deleted file mode 100644
index c98f800..0000000
--- a/packages/SystemUI/res/drawable/ic_signal_workmode_enable_animation.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<animated-vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:drawable="@drawable/ic_signal_workmode_enable" >
-    <target
-        android:name="mask_1"
-        android:animation="@anim/ic_signal_workmode_enable_mask_1_animation" />
-    <target
-        android:name="whole"
-        android:animation="@anim/ic_signal_workmode_enable_whole_animation" />
-    <target
-        android:name="rectangle_path_3_position"
-        android:animation="@anim/ic_signal_workmode_enable_rectangle_path_3_position_animation" />
-    <target
-        android:name="rectangle_path_3"
-        android:animation="@anim/ic_signal_workmode_enable_rectangle_path_3_animation" />
-    <target
-        android:name="rectangle_path_4_position"
-        android:animation="@anim/ic_signal_workmode_enable_rectangle_path_4_position_animation" />
-    <target
-        android:name="rectangle_path_4"
-        android:animation="@anim/ic_signal_workmode_enable_rectangle_path_4_animation" />
-    <target
-        android:name="left"
-        android:animation="@anim/ic_signal_workmode_enable_left_animation" />
-    <target
-        android:name="right"
-        android:animation="@anim/ic_signal_workmode_enable_right_animation" />
-    <target
-        android:name="stick"
-        android:animation="@anim/ic_signal_workmode_enable_stick_animation" />
-    <target
-        android:name="ic_signal_workmode_enable"
-        android:animation="@anim/ic_signal_workmode_enable_stickito_animation" />
-</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_sim.xml b/packages/SystemUI/res/drawable/ic_sim.xml
deleted file mode 100644
index a9a1902..0000000
--- a/packages/SystemUI/res/drawable/ic_sim.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
-    Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24.0dp"
-        android:height="24.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M18,2h-8L4,8v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4C20,2.9 19.1,2 18,2zM18,4v16H6V8.83L10.83,4L18,4L18,4z"/>
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M7,17h2v2h-2z"/>
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M15,17h2v2h-2z"/>
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M7,11h2v4h-2z"/>
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M11,15h2v4h-2z"/>
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M11,11h2v2h-2z"/>
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M15,11h2v4h-2z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_speaker.xml b/packages/SystemUI/res/drawable/ic_speaker.xml
deleted file mode 100644
index 1ea293c..0000000
--- a/packages/SystemUI/res/drawable/ic_speaker.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-  Copyright (C) 2017 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal" >
-    <path
-        android:pathData="M17,2L7,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,1.99 2,1.99L17,22c1.1,0 2,-0.9 2,-2L19,4c0,-1.1 -0.9,-2 -2,-2zM12,4c1.1,0 2,0.9 2,2s-0.9,2 -2,2c-1.11,0 -2,-0.9 -2,-2s0.89,-2 2,-2zM12,20c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,12c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"
-        android:fillColor="#FFFFFFFF"/>
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_speaker_group.xml b/packages/SystemUI/res/drawable/ic_speaker_group.xml
deleted file mode 100644
index d6867d7..0000000
--- a/packages/SystemUI/res/drawable/ic_speaker_group.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<!--
-  Copyright (C) 2017 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal" >
-    <path
-        android:pathData="M18.2,1L9.8,1C8.81,1 8,1.81 8,2.8v14.4c0,0.99 0.81,1.79 1.8,1.79l8.4,0.01c0.99,0 1.8,-0.81 1.8,-1.8L20,2.8c0,-0.99 -0.81,-1.8 -1.8,-1.8zM14,3c1.1,0 2,0.89 2,2s-0.9,2 -2,2 -2,-0.89 -2,-2 0.9,-2 2,-2zM14,16.5c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4z"
-        android:fillColor="#FFFFFFFF"/>
-    <path
-        android:pathData="M14,12.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"
-        android:fillColor="#FFFFFFFF"/>
-    <path
-        android:pathData="M6,5H4v16c0,1.1 0.89,2 2,2h10v-2H6V5z"
-        android:fillColor="#FFFFFFFF"/>
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_tv.xml b/packages/SystemUI/res/drawable/ic_tv.xml
deleted file mode 100644
index cc2ae91..0000000
--- a/packages/SystemUI/res/drawable/ic_tv.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-  Copyright (C) 2017 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal" >
-    <path
-        android:pathData="M21,3L3,3c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h5v2h8v-2h5c1.1,0 1.99,-0.9 1.99,-2L23,5c0,-1.1 -0.9,-2 -2,-2zM21,17L3,17L3,5h18v12z"
-        android:fillColor="#FFFFFFFF"/>
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_zen_all.xml b/packages/SystemUI/res/drawable/ic_zen_all.xml
deleted file mode 100644
index 5df2447..0000000
--- a/packages/SystemUI/res/drawable/ic_zen_all.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="18dp"
-        android:height="18dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M6.6,3.6L5.2,2.2C2.8,4.0 1.2,6.8 1.0,10.0l2.0,0.0C3.2,7.3 4.5,5.0 6.6,3.6zM20.0,10.0l2.0,0.0c-0.2,-3.2 -1.7,-6.0 -4.1,-7.8l-1.4,1.4C18.5,5.0 19.8,7.3 20.0,10.0zM18.0,10.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7c-2.9,0.7 -5.0,3.2 -5.0,6.3L5.0,16.0l-2.0,2.0l0.0,1.0l17.0,0.0l0.0,-1.0l-2.0,-2.0L18.0,10.5zM11.5,22.0c0.1,0.0 0.3,0.0 0.4,0.0c0.7,-0.1 1.2,-0.6 1.4,-1.2c0.1,-0.2 0.2,-0.5 0.2,-0.8l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_zen_important.xml b/packages/SystemUI/res/drawable/ic_zen_important.xml
deleted file mode 100644
index c2a59b8..0000000
--- a/packages/SystemUI/res/drawable/ic_zen_important.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="18dp"
-        android:height="18dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M12.0,17.273l6.1800003,3.7269993 -1.6350002,-7.0290003 5.455,-4.7269993 -7.191,-0.6170006 -2.809,-6.627 -2.809,6.627 -7.191,0.6170006 5.455,4.7269993 -1.6349998,7.0290003z"/>
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_zen_none.xml b/packages/SystemUI/res/drawable/ic_zen_none.xml
deleted file mode 100644
index 99014f2..0000000
--- a/packages/SystemUI/res/drawable/ic_zen_none.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="18dp"
-        android:height="18dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M24.0,4.0C13.0,4.0 4.0,13.0 4.0,24.0c0.0,11.0 9.0,20.0 20.0,20.0c11.0,0.0 20.0,-9.0 20.0,-20.0C44.0,13.0 35.0,4.0 24.0,4.0zM24.0,40.0c-8.8,0.0 -16.0,-7.2 -16.0,-16.0c0.0,-3.7 1.3,-7.1 3.4,-9.8l22.4,22.4C31.1,38.7 27.7,40.0 24.0,40.0zM36.6,33.8L14.2,11.4C16.9,9.3 20.3,8.0 24.0,8.0c8.8,0.0 16.0,7.2 16.0,16.0C40.0,27.7 38.7,31.1 36.6,33.8z"/>
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/keyguard_overflow_number_background.xml b/packages/SystemUI/res/drawable/keyguard_overflow_number_background.xml
deleted file mode 100644
index b812d43..0000000
--- a/packages/SystemUI/res/drawable/keyguard_overflow_number_background.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2014 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
-  -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval">
-    <solid android:color="#1a000000" />
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/notification_expand_more.xml b/packages/SystemUI/res/drawable/notification_expand_more.xml
deleted file mode 100644
index 430fb0d..0000000
--- a/packages/SystemUI/res/drawable/notification_expand_more.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2015 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License._more
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="22.0dp"
-    android:height="22.0dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
-    <path
-        android:pathData="M16.600000,8.600000l-4.600000,4.599999 -4.600000,-4.599999 -1.400000,1.400000 6.000000,6.000000 6.000000,-6.000000z"
-        android:fillColor="#FF000000"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/pip_dismiss.xml b/packages/SystemUI/res/drawable/pip_dismiss.xml
deleted file mode 100644
index f656eeb..0000000
--- a/packages/SystemUI/res/drawable/pip_dismiss.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="42.0dp"
-        android:height="42.0dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M38.000000,12.800000l-2.799999,-2.800000 -11.200001,11.200001 -11.200000,-11.200001 -2.800000,2.800000 11.200001,11.200000 -11.200001,11.200001 2.800000,2.799999 11.200000,-11.200001 11.200001,11.200001 2.799999,-2.799999 -11.200001,-11.200001z"/>
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/pip_notification_icon.xml b/packages/SystemUI/res/drawable/pip_notification_icon.xml
deleted file mode 100644
index 592bc60..0000000
--- a/packages/SystemUI/res/drawable/pip_notification_icon.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M11.99,18.54l-7.37,-5.73L3,14.07l9,7 9,-7 -1.63,-1.27 -7.38,5.74zM12,16l7.36,-5.73L21,9l-9,-7 -9,7 1.63,1.27L12,16z"/>
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/pop_ball.xml b/packages/SystemUI/res/drawable/pop_ball.xml
deleted file mode 100644
index ee1220f..0000000
--- a/packages/SystemUI/res/drawable/pop_ball.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="100.0dp"
-        android:height="100.0dp"
-        android:viewportWidth="100.0"
-        android:viewportHeight="100.0">
-    <path
-        android:pathData="M0,50 a50,50 0 1 1 100,0
-                                a50,50 0 1 1 -100,0"
-        android:fillColor="#FFFF1744"/>
-    <path
-        android:pathData="M16,36 A24,24 0 1 1 64,36
-                          M64,36 A24,24 0 1 1 16,36"
-        android:fillColor="#20FFFFFF"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/pop_belt.xml b/packages/SystemUI/res/drawable/pop_belt.xml
deleted file mode 100644
index e0ea575..0000000
--- a/packages/SystemUI/res/drawable/pop_belt.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="100.0dp"
-        android:height="100.0dp"
-        android:viewportWidth="100.0"
-        android:viewportHeight="100.0">
-    <path
-        android:pathData="M50.000000,50.000000m-47.599998,0.000000a47.599998,47.599998 0.000000,1.000000 1.000000,95.199997 0.000000a47.599998,47.599998 0.000000,1.000000 1.000000,-95.199997 0.000000"
-        android:fillColor="#9C27B0"/>
-    <path
-        android:pathData="M50.000000,2.429000c-26.337999,0.000000 -47.688999,21.351000 -47.688999,47.688999c0.000000,13.168000 5.337000,25.091000 13.968000,33.722000l67.444000,-67.443001C75.092003,7.766000 63.168999,2.429000 50.000000,2.429000z"
-        android:fillColor="#BA68C8"/>
-    <path
-        android:pathData="M0.000000,41.573002l100.000000,0.000000l0.000000,17.090000l-100.000000,0.000000z"
-        android:fillColor="#9C27B0"/>
-    <path
-        android:pathData="M0.000000,58.662998l0.000000,-17.089996 100.000000,0.000000z"
-        android:fillColor="#BA68C8"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/pop_droid.xml b/packages/SystemUI/res/drawable/pop_droid.xml
deleted file mode 100644
index eed325c..0000000
--- a/packages/SystemUI/res/drawable/pop_droid.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="100.0dp"
-        android:height="100.0dp"
-        android:viewportWidth="100.0"
-        android:viewportHeight="100.0">
-    <path
-        android:pathData="M50.000000,50.000000m-50.000000,0.000000a50.000000,50.000000 0.000000,1.000000 1.000000,100.000000 0.000000a50.000000,50.000000 0.000000,1.000000 1.000000,-100.000000 0.000000"
-        android:fillColor="#9E9D24"/>
-    <path
-        android:pathData="M30.775999,24.528000m-4.209000,0.000000a4.209000,4.209000 0.000000,1.000000 1.000000,8.418000 0.000000a4.209000,4.209000 0.000000,1.000000 1.000000,-8.418000 0.000000"
-        android:fillColor="#FFFFFF"/>
-    <path
-        android:pathData="M69.226997,24.528000m-4.210000,0.000000a4.210000,4.210000 0.000000,1.000000 1.000000,8.420000 0.000000a4.210000,4.210000 0.000000,1.000000 1.000000,-8.420000 0.000000"
-        android:fillColor="#FFFFFF"/>
-    <path
-        android:pathData="M85.352997,14.648000C65.829002,-4.877000 34.168999,-4.877000 14.646000,14.646000C4.882000,24.410000 0.002000,37.207001 0.000000,50.000999l99.996002,0.002000C99.996002,37.207001 95.115997,24.410000 85.352997,14.648000z"
-        android:fillColor="#C0CA33"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/pop_pizza.xml b/packages/SystemUI/res/drawable/pop_pizza.xml
deleted file mode 100644
index 1806b5a..0000000
--- a/packages/SystemUI/res/drawable/pop_pizza.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="100.0dp"
-        android:height="100.0dp"
-        android:viewportWidth="100.0"
-        android:viewportHeight="100.0">
-    <path
-        android:pathData="M14.645000,14.645000C5.597000,23.693001 0.000000,36.193001 0.000000,50.000000l50.000000,0.000000L14.645000,14.645000z"
-        android:fillColor="#2979FF"/>
-    <path
-        android:pathData="M100.000000,50.000000c0.000000,-13.807000 -5.597000,-26.306999 -14.645000,-35.355000L50.000000,50.000000L100.000000,50.000000z"
-        android:fillColor="#FF1744"/>
-    <path
-        android:pathData="M85.355003,14.645000C76.306999,5.597000 63.806999,0.000000 50.000000,0.000000l0.000000,50.000000L85.355003,14.645000z"
-        android:fillColor="#0F9D58"/>
-    <path
-        android:pathData="M50.000000,0.000000C36.193001,0.000000 23.693001,5.597000 14.645000,14.645000L50.000000,50.000000L50.000000,0.000000z"
-        android:fillColor="#FFBC00"/>
-    <path
-        android:pathData="M50.000000,50.000000l35.355000,35.355000C94.403000,76.307999 100.000000,63.807999 100.000000,50.000000L50.000000,50.000000z"
-        android:fillColor="#2979FF"/>
-    <path
-        android:pathData="M50.000000,100.000000c13.807000,0.000000 26.306999,-5.596000 35.355000,-14.645000L50.000000,50.000000L50.000000,100.000000z"
-        android:fillColor="#FFBC00"/>
-    <path
-        android:pathData="M14.645000,85.355003C23.693001,94.403999 36.193001,100.000000 50.000000,100.000000L50.000000,50.000000L14.645000,85.355003z"
-        android:fillColor="#0F9D58"/>
-    <path
-        android:pathData="M0.000000,50.000000c0.000000,13.808000 5.597000,26.308001 14.645000,35.355000L50.000000,50.000000L0.000000,50.000000z"
-        android:fillColor="#FF1744"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/pop_stripes.xml b/packages/SystemUI/res/drawable/pop_stripes.xml
deleted file mode 100644
index ef6c8e8..0000000
--- a/packages/SystemUI/res/drawable/pop_stripes.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="100.0dp"
-        android:height="100.0dp"
-        android:viewportWidth="100.0"
-        android:viewportHeight="100.0">
-    <path
-        android:pathData="M50.000000,50.000000m-50.000000,0.000000a50.000000,50.000000 0.000000,1.000000 1.000000,100.000000 0.000000a50.000000,50.000000 0.000000,1.000000 1.000000,-100.000000 0.000000"
-        android:fillColor="#F57C00"/>
-    <path
-        android:pathData="M50.000999,100.000000c14.136000,0.000000 26.893000,-5.876000 35.987000,-15.308000L14.013000,84.692001C23.106001,94.124001 35.862999,100.000000 50.000999,100.000000z"
-        android:fillColor="#E65100"/>
-    <path
-        android:pathData="M50.000999,0.000000C35.862999,0.000000 23.106001,5.876000 14.013000,15.308000l71.974998,0.000000C76.893997,5.876000 64.137001,0.000000 50.000999,0.000000z"
-        android:fillColor="#FFA726"/>
-    <path
-        android:pathData="M96.501999,31.631001c-2.423000,-6.127000 -6.020000,-11.660000 -10.514000,-16.323000L14.013000,15.308001C9.517000,19.971001 5.922000,25.503000 3.499000,31.631001L96.501999,31.631001z"
-        android:fillColor="#FB8C00"/>
-    <path
-        android:pathData="M3.499000,68.370003c2.423000,6.128000 6.018000,11.658000 10.514000,16.322001l71.974998,0.000000c4.494000,-4.664000 8.091000,-10.194000 10.514000,-16.322001L3.499000,68.370003z"
-        android:fillColor="#EF6C00"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/pop_swirl.xml b/packages/SystemUI/res/drawable/pop_swirl.xml
deleted file mode 100644
index f87569b..0000000
--- a/packages/SystemUI/res/drawable/pop_swirl.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="100.0dp"
-        android:height="100.0dp"
-        android:viewportWidth="100.0"
-        android:viewportHeight="100.0">
-    <path
-        android:pathData="M50.000000,50.000000C86.898003,27.834999 79.244003,11.688000 76.177002,7.399000c-7.240000,-4.459000 -15.703000,-7.112000 -24.770000,-7.363000C56.247002,2.253000 70.815002,12.456000 50.000000,50.000000z"
-        android:fillColor="#FFFFFA"/>
-    <path
-        android:pathData="M50.000000,50.000000c20.815001,-37.543999 6.247000,-47.747002 1.407000,-49.964001C50.938000,0.022000 50.472000,0.000000 50.000000,0.000000c-8.627000,0.000000 -16.743999,2.186000 -23.827000,6.032000C31.392000,5.514000 49.251999,6.903000 50.000000,50.000000z"
-        android:fillColor="#76FF03"/>
-    <path
-        android:pathData="M50.000000,50.000000c37.543999,20.816000 47.747002,6.248000 49.965000,1.408000C99.977997,50.938000 100.000000,50.473000 100.000000,50.000000c0.000000,-8.627000 -2.186000,-16.743999 -6.032000,-23.827000C94.486000,31.393000 93.098000,49.251999 50.000000,50.000000z"
-        android:fillColor="#76FF03"/>
-    <path
-        android:pathData="M50.000000,50.000000c43.098000,-0.748000 44.486000,-18.607000 43.967999,-23.827000c-4.186000,-7.708000 -10.344000,-14.188000 -17.791000,-18.773001C79.244003,11.688000 86.898003,27.834999 50.000000,50.000000z"
-        android:fillColor="#303F9F"/>
-    <path
-        android:pathData="M50.000000,50.000000C27.834000,13.103000 11.687000,20.757000 7.398000,23.823999C2.940000,31.063000 0.287000,39.527000 0.035000,48.592999C2.253000,43.752998 12.456000,29.184999 50.000000,50.000000z"
-        android:fillColor="#FFFFFA"/>
-    <path
-        android:pathData="M50.000000,50.000000C49.251999,6.903000 31.392000,5.514000 26.173000,6.032000c-7.709000,4.187000 -14.188000,10.344000 -18.774000,17.792000C11.687000,20.757000 27.834000,13.103000 50.000000,50.000000z"
-        android:fillColor="#303F9F"/>
-    <path
-        android:pathData="M50.000000,50.000000C12.456000,29.184999 2.253000,43.752998 0.035000,48.592999C0.022000,49.062000 0.000000,49.528000 0.000000,50.000000c0.000000,8.628000 2.186000,16.743999 6.032000,23.827999C5.514000,68.609001 6.902000,50.749001 50.000000,50.000000z"
-        android:fillColor="#76FF03"/>
-    <path
-        android:pathData="M50.000000,50.000000c0.748000,43.098000 18.608000,44.486000 23.827000,43.969002c7.709000,-4.187000 14.188000,-10.344000 18.774000,-17.791000C88.313004,79.244003 72.166000,86.898003 50.000000,50.000000z"
-        android:fillColor="#303F9F"/>
-    <path
-        android:pathData="M50.000000,50.000000c22.166000,36.897999 38.313000,29.243999 42.602001,26.177999c4.458000,-7.240000 7.111000,-15.703000 7.363000,-24.770000C97.747002,56.248001 87.543999,70.816002 50.000000,50.000000z"
-        android:fillColor="#FFFFFA"/>
-    <path
-        android:pathData="M50.000000,50.000000c-20.815001,37.544998 -6.247000,47.748001 -1.407000,49.965000C49.062000,99.978996 49.528000,100.000000 50.000000,100.000000c8.627000,0.000000 16.743999,-2.185000 23.827000,-6.031000C68.608002,94.486000 50.748001,93.098000 50.000000,50.000000z"
-        android:fillColor="#76FF03"/>
-    <path
-        android:pathData="M50.000000,50.000000C13.103000,72.166000 20.757000,88.313004 23.823000,92.601997c7.240000,4.459000 15.703000,7.112000 24.770000,7.363000C43.752998,97.748001 29.184999,87.544998 50.000000,50.000000z"
-        android:fillColor="#FFFFFA"/>
-    <path
-        android:pathData="M50.000000,50.000000C6.902000,50.749001 5.514000,68.609001 6.032000,73.828003c4.186000,7.708000 10.344000,14.188000 17.791000,18.773001C20.757000,88.313004 13.103000,72.166000 50.000000,50.000000z"
-        android:fillColor="#303F9F"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/pop_vortex.xml b/packages/SystemUI/res/drawable/pop_vortex.xml
deleted file mode 100644
index 2380e68..0000000
--- a/packages/SystemUI/res/drawable/pop_vortex.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="100.0dp"
-        android:height="100.0dp"
-        android:viewportWidth="100.0"
-        android:viewportHeight="100.0">
-    <path
-        android:pathData="M50.000000,50.000000m-50.000000,0.000000a50.000000,50.000000 0.000000,1.000000 1.000000,100.000000 0.000000a50.000000,50.000000 0.000000,1.000000 1.000000,-100.000000 0.000000"
-        android:fillColor="#F8F8FF"/>
-    <path
-        android:pathData="M58.658001,89.648003c-19.330000,0.000000 -35.000000,-15.670000 -35.000000,-35.000000c0.000000,-13.531000 10.969000,-24.500000 24.500000,-24.500000c9.472000,0.000000 17.150000,7.679000 17.150000,17.150000c0.000000,6.631000 -5.375000,12.006000 -12.006000,12.006000c-3.798000,0.000000 -7.004000,-2.522000 -8.045000,-5.982000c1.021000,1.136000 2.497000,1.854000 4.145000,1.854000c2.644000,0.000000 4.853000,-1.841000 5.428000,-4.310000c0.175000,-0.558000 0.271000,-1.150000 0.271000,-1.766000c0.000000,-4.642000 -3.763000,-8.404000 -8.403000,-8.404000c-6.631000,0.000000 -12.006000,5.375000 -12.006000,12.006000c0.000000,9.472000 7.679000,17.149000 17.150000,17.149000c13.531000,0.000000 24.500000,-10.969000 24.500000,-24.500000c0.000000,-19.330000 -15.670000,-35.000000 -35.000000,-35.000000c-12.963000,0.000000 -24.773001,4.935000 -33.657001,13.025000C2.824000,31.087000 0.000000,40.212002 0.000000,50.000000c0.000000,27.615000 22.386000,50.000000 50.000000,50.000000c17.825001,0.000000 33.462002,-9.335000 42.313999,-23.375999C83.431000,84.714996 71.621002,89.648003 58.658001,89.648003z"
-        android:fillColor="#7BAAF7"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/pop_vortex2.xml b/packages/SystemUI/res/drawable/pop_vortex2.xml
deleted file mode 100644
index 0a96b74..0000000
--- a/packages/SystemUI/res/drawable/pop_vortex2.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="100.0dp"
-        android:height="100.0dp"
-        android:viewportWidth="100.0"
-        android:viewportHeight="100.0">
-    <path
-        android:pathData="M50.000000,50.000000m-50.000000,0.000000a50.000000,50.000000 0.000000,1.000000 1.000000,100.000000 0.000000a50.000000,50.000000 0.000000,1.000000 1.000000,-100.000000 0.000000"
-        android:fillColor="#D81B60"/>
-    <path
-        android:pathData="M21.250000,78.369003c-13.200000,-16.000000 -10.930000,-39.671001 5.070000,-52.870998c12.799000,-10.560000 31.737000,-8.743000 42.294998,4.057000c8.448000,10.239000 6.996000,25.389000 -3.244000,33.837002c-8.191000,6.759000 -20.311001,5.596000 -27.068001,-2.596000c-5.408000,-6.554000 -4.478000,-16.249001 2.076000,-21.656000c5.242000,-4.325000 12.998000,-3.581000 17.323999,1.661000c3.460000,4.194000 2.865000,10.399000 -1.330000,13.859000c-3.354000,2.769000 -8.318000,2.293000 -11.087000,-1.062000c-2.214000,-2.685000 -1.833000,-6.655000 0.851000,-8.870000c2.147000,-1.771000 5.324000,-1.468000 7.096000,0.681000c1.393000,1.688000 1.174000,4.165000 -0.464000,5.596000c0.409000,-0.564000 0.657000,-1.253000 0.657000,-2.004000c0.000000,-1.021000 -0.455000,-1.928000 -1.165000,-2.556000c-0.067000,-0.112000 -0.134000,-0.226000 -0.220000,-0.329000c-1.135000,-1.373000 -3.168000,-1.568000 -4.542000,-0.435000c-1.719000,1.417000 -1.962000,3.958000 -0.544000,5.677000c1.771000,2.146000 4.949000,2.451000 7.096000,0.680000c2.684000,-2.215000 3.064000,-6.186000 0.851000,-8.870000c-2.769000,-3.356000 -7.732000,-3.831000 -11.087000,-1.063000c-4.195000,3.460000 -4.790000,9.665000 -1.330000,13.859000c4.326000,5.244000 12.082000,5.987000 17.323999,1.662000c6.554000,-5.407000 7.484000,-15.102000 2.076000,-21.656000c-6.758000,-8.191000 -18.875999,-9.354000 -27.069000,-2.596000c-10.239000,8.448000 -11.691000,23.598000 -3.244000,33.837002c10.560000,12.800000 29.497000,14.616000 42.296001,4.056000c16.000000,-13.199000 18.270000,-36.869999 5.070000,-52.868999C68.397003,5.620000 52.516998,-0.139000 37.205002,1.659000c-8.665000,2.287000 -16.410999,6.836000 -22.561001,12.985000C5.597000,23.693001 0.000000,36.193001 0.000000,50.000000c0.000000,13.808000 5.597000,26.308001 14.645000,35.355000C23.693001,94.403999 36.193001,100.000000 50.000000,100.000000c11.935000,0.000000 22.886999,-4.187000 31.482000,-11.164000C61.909000,100.523003 36.202999,96.495003 21.250000,78.369003z"
-        android:fillColor="#F06292"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/qs_subhead_caret.xml b/packages/SystemUI/res/drawable/qs_subhead_caret.xml
deleted file mode 100644
index 13a168d..0000000
--- a/packages/SystemUI/res/drawable/qs_subhead_caret.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24.0dp"
-        android:height="24.0dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-
-    <path
-        android:fillColor="@color/qs_subhead"
-        android:pathData="M14.0,20.0l10.0,10.0 10.0,-10.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/quick_header_bg.xml b/packages/SystemUI/res/drawable/quick_header_bg.xml
deleted file mode 100644
index 920e6f5..0000000
--- a/packages/SystemUI/res/drawable/quick_header_bg.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2015 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="?android:attr/colorControlHighlight" >
-    <item android:drawable="?android:attr/colorPrimary"/>
-</ripple>
diff --git a/packages/SystemUI/res/drawable/scorecard_gameover.xml b/packages/SystemUI/res/drawable/scorecard_gameover.xml
deleted file mode 100644
index f663a66..0000000
--- a/packages/SystemUI/res/drawable/scorecard_gameover.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle"
-    >
-    <corners
-        android:radius="8dp" />
-    <solid
-        android:color="#ffff0000" />
-</shape>
diff --git a/packages/SystemUI/res/drawable/stat_notify_more.xml b/packages/SystemUI/res/drawable/stat_notify_more.xml
deleted file mode 100644
index 50f1286..0000000
--- a/packages/SystemUI/res/drawable/stat_notify_more.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
-  ~ Copyright (C) 2014 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="16dp"
-        android:height="16dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:pathData="M22.000000,3.000000L7.000000,3.000000C6.300000,3.000000 5.800000,3.400000 5.400000,3.900000L0.000000,12.000000l5.400000,8.100000c0.400000,0.500000 1.000000,0.900000 1.700000,0.900000L22.000000,21.000000c1.100000,0.000000 2.000000,-0.900000 2.000000,-2.000000L24.000000,5.000000C24.000000,3.900000 23.100000,3.000000 22.000000,3.000000zM9.000000,13.500000c-0.800000,0.000000 -1.500000,-0.700000 -1.500000,-1.500000s0.700000,-1.500000 1.500000,-1.500000c0.800000,0.000000 1.500000,0.700000 1.500000,1.500000S9.800000,13.500000 9.000000,13.500000zM14.000000,13.500000c-0.800000,0.000000 -1.500000,-0.700000 -1.500000,-1.500000s0.700000,-1.500000 1.500000,-1.500000c0.800000,0.000000 1.500000,0.700000 1.500000,1.500000S14.800000,13.500000 14.000000,13.500000zM19.000000,13.500000c-0.800000,0.000000 -1.500000,-0.700000 -1.500000,-1.500000s0.700000,-1.500000 1.500000,-1.500000c0.800000,0.000000 1.500000,0.700000 1.500000,1.500000S19.799999,13.500000 19.000000,13.500000z"
-        android:fillColor="#FFFFFFFF"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_auto_rotate_landscape.xml b/packages/SystemUI/res/drawable/stat_sys_auto_rotate_landscape.xml
deleted file mode 100644
index ba0709e..0000000
--- a/packages/SystemUI/res/drawable/stat_sys_auto_rotate_landscape.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-    android:insetLeft="2.5dp"
-    android:insetRight="2.5dp">
-    <vector
-        android:height="17dp"
-        android:width="17dp"
-        android:viewportHeight="48"
-        android:viewportWidth="48" >
-        <group
-            android:name="ic_screen_rotation_48px_outlines"
-            android:translateX="24"
-            android:translateY="24" >
-            <group
-                android:name="ic_screen_rotation_48px_outlines_pivot"
-                android:translateX="-24.15"
-                android:translateY="-24.25" >
-                <group
-                    android:name="arrows"
-                    android:translateX="24.1"
-                    android:translateY="24.1" >
-                    <group
-                        android:name="arrows_pivot"
-                        android:translateX="-24.1"
-                        android:translateY="-24.1" >
-                        <path
-                            android:name="arrow_top"
-                            android:pathData="M 33.1499938965,5.25 c 6.5,3.10000610352 11.1999969482,9.40000915527 11.8999938965,17.0 c 0.0,0.0 3.00001525879,0.0 3.00001525879,0.0 c -1.00001525879,-12.3000030518 -11.3000030518,-22.0 -23.9000091553,-22.0 c -0.399993896484,0.0 -0.899993896484,0.0 -1.30000305176,0.100006103516 c 0.0,0.0 7.60000610352,7.59999084473 7.60000610352,7.59999084473 c 0.0,0.0 2.69999694824,-2.69999694824 2.69999694824,-2.69999694824 Z"
-                            android:fillColor="#FFFFFFFF"
-                            android:fillAlpha="1" />
-                        <path
-                            android:name="arrow_bottom"
-                            android:pathData="M 15.1499938965,43.25 c -6.5,-3.09999084473 -11.1999969482,-9.5 -11.8999938965,-17.0 c 0.0,0.0 -3.0,0.0 -3.0,0.0 c 1.0,12.3000030518 11.299987793,22.0 23.8999938965,22.0 c 0.399993896484,0.0 0.899993896484,0.0 1.30000305176,-0.0999908447266 c 0.0,0.0 -7.60000610352,-7.60000610352 -7.60000610352,-7.60000610352 c 0.0,0.0 -2.69999694824,2.69999694824 -2.69999694824,2.69999694824 Z"
-                            android:fillColor="#FFFFFFFF"
-                            android:fillAlpha="1" />
-                    </group>
-                </group>
-                <group
-                    android:name="device"
-                    android:translateX="24.14999"
-                    android:translateY="24.25" >
-                    <path
-                        android:name="body"
-                        android:pathData="M -3.5,-20.5 c -1.19999694824,-1.19999694824 -3.10000610352,-1.19999694824 -4.19999694824,0.0 c 0.0,0.0 -12.8000030518,12.6999969482 -12.8000030518,12.6999969482 c -1.19999694824,1.19999694824 -1.19999694824,3.10000610352 0.0,4.19999694824 c 0.0,0.0 24.0,24.0000152588 24.0,24.0000152588 c 1.19999694824,1.19999694824 3.10000610352,1.19999694824 4.19999694824,0.0 c 0.0,0.0 12.6999969482,-12.700012207 12.6999969482,-12.700012207 c 1.20001220703,-1.19999694824 1.20001220703,-3.09999084473 0.0,-4.19999694824 c 0.0,0.0 -23.8999938965,-24.0 -23.8999938965,-24.0 Z M 2.84999084473,15.5500183105 c 0.0,0.0 -18.6000061035,-18.5000457764 -18.6000061035,-18.5000457764 c 0.0,0.0 12.5999908447,-12.8000030518 12.5999908447,-12.8000030518 c 0.0,0.0 18.6000213623,18.5000457764 18.6000213623,18.5000457764 c 0.0,0.0 -12.6000061035,12.8000030518 -12.6000061035,12.8000030518 Z"
-                        android:fillColor="#FFFFFFFF"
-                        android:fillAlpha="1" />
-                </group>
-            </group>
-        </group>
-    </vector>
-</inset>
diff --git a/packages/SystemUI/res/drawable/stat_sys_auto_rotate_portrait.xml b/packages/SystemUI/res/drawable/stat_sys_auto_rotate_portrait.xml
deleted file mode 100644
index 46a1f35..0000000
--- a/packages/SystemUI/res/drawable/stat_sys_auto_rotate_portrait.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-    android:insetLeft="2.5dp"
-    android:insetRight="2.5dp">
-    <vector
-        android:height="17dp"
-        android:width="17dp"
-        android:viewportHeight="48"
-        android:viewportWidth="48" >
-        <group
-            android:name="icon"
-            android:translateX="24"
-            android:translateY="24" >
-            <group
-                android:name="icon_pivot"
-                android:translateX="-24.15"
-                android:translateY="-24.25" >
-                <group
-                    android:name="arrows"
-                    android:translateX="24.1"
-                    android:translateY="24.1" >
-                    <group
-                        android:name="arrows_pivot"
-                        android:translateX="-24.1"
-                        android:translateY="-24.1" >
-                        <path
-                            android:name="arrow_top"
-                            android:pathData="M 33.1499938965,5.25 c 6.5,3.10000610352 11.1999969482,9.40000915527 11.8999938965,17.0 c 0.0,0.0 3.00001525879,0.0 3.00001525879,0.0 c -1.00001525879,-12.3000030518 -11.3000030518,-22.0 -23.9000091553,-22.0 c -0.399993896484,0.0 -0.899993896484,0.0 -1.30000305176,0.100006103516 c 0.0,0.0 7.60000610352,7.59999084473 7.60000610352,7.59999084473 c 0.0,0.0 2.69999694824,-2.69999694824 2.69999694824,-2.69999694824 Z"
-                            android:fillColor="#FFFFFFFF"
-                            android:fillAlpha="1" />
-                        <path
-                            android:name="arrow_bottom"
-                            android:pathData="M 15.1499938965,43.25 c -6.5,-3.09999084473 -11.1999969482,-9.5 -11.8999938965,-17.0 c 0.0,0.0 -3.0,0.0 -3.0,0.0 c 1.0,12.3000030518 11.299987793,22.0 23.8999938965,22.0 c 0.399993896484,0.0 0.899993896484,0.0 1.30000305176,-0.0999908447266 c 0.0,0.0 -7.60000610352,-7.60000610352 -7.60000610352,-7.60000610352 c 0.0,0.0 -2.69999694824,2.69999694824 -2.69999694824,2.69999694824 Z"
-                            android:fillColor="#FFFFFFFF"
-                            android:fillAlpha="1" />
-                    </group>
-                </group>
-                <group
-                    android:name="device"
-                    android:translateX="24.14999"
-                    android:translateY="24.25" >
-                    <path
-                        android:name="device_1"
-                        android:pathData="M -3.5,-20.5 c -1.19999694824,-1.19999694824 -3.10000610352,-1.19999694824 -4.19999694824,0.0 c 0.0,0.0 -12.8000030518,12.6999969482 -12.8000030518,12.6999969482 c -1.19999694824,1.19999694824 -1.19999694824,3.10000610352 0.0,4.19999694824 c 0.0,0.0 24.0,24.0000152588 24.0,24.0000152588 c 1.19999694824,1.19999694824 3.10000610352,1.19999694824 4.19999694824,0.0 c 0.0,0.0 12.6999969482,-12.700012207 12.6999969482,-12.700012207 c 1.20001220703,-1.19999694824 1.20001220703,-3.09999084473 0.0,-4.19999694824 c 0.0,0.0 -23.8999938965,-24.0 -23.8999938965,-24.0 Z M 2.84999084473,15.5500183105 c 0.0,0.0 -18.6000061035,-18.5000457764 -18.6000061035,-18.5000457764 c 0.0,0.0 12.5999908447,-12.8000030518 12.5999908447,-12.8000030518 c 0.0,0.0 18.6000213623,18.5000457764 18.6000213623,18.5000457764 c 0.0,0.0 -12.6000061035,12.8000030518 -12.6000061035,12.8000030518 Z"
-                        android:fillColor="#FFFFFFFF"
-                        android:fillAlpha="1" />
-                </group>
-            </group>
-        </group>
-    </vector>
-</inset>
diff --git a/packages/SystemUI/res/drawable/stat_sys_managed_profile_status_off.xml b/packages/SystemUI/res/drawable/stat_sys_managed_profile_status_off.xml
deleted file mode 100644
index 1dedd5d..0000000
--- a/packages/SystemUI/res/drawable/stat_sys_managed_profile_status_off.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="23dp"
-        android:height="18dp"
-        android:viewportWidth="23.0"
-        android:viewportHeight="18.0">
-    <path
-        android:fillColor="@android:color/white"
-        android:pathData="M19.4,16.6l-1.1,-1.1L8,5L5.1,2.2L4.2,3.1l2,2H5.7c-0.8,0 -1.4,0.6 -1.4,1.4v8c0,0.8 0.6,1.4 1.4,1.4h11.4l1.5,1.5L19.4,16.6zM18.7,6.5c0,-0.8 -0.6,-1.4 -1.4,-1.4h-2.9V3.6c0,-0.8 -0.6,-1.4 -1.4,-1.4h-3C9.2,2.1 8.6,2.8 8.6,3.6v0.2L18.7,14C18.7,14 18.7,6.5 18.7,6.5zM12.9,5.1H10V3.6h2.9V5.1z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_no_sims.xml b/packages/SystemUI/res/drawable/stat_sys_no_sims.xml
deleted file mode 100644
index 5c9be5c..0000000
--- a/packages/SystemUI/res/drawable/stat_sys_no_sims.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2017, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="17dp"
-    android:height="17dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h6v-3h2V9H12.09zM20,13h-2v3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H20V13z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_in.xml b/packages/SystemUI/res/drawable/stat_sys_signal_in.xml
deleted file mode 100644
index 7e6e09b..0000000
--- a/packages/SystemUI/res/drawable/stat_sys_signal_in.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:autoMirrored="true"
-        android:width="17dp"
-        android:height="17dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:name="in"
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M8.7,12.2l-2.0,3.5l-2.0,-3.5z" />
-</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_inout.xml b/packages/SystemUI/res/drawable/stat_sys_signal_inout.xml
deleted file mode 100644
index b7b6f0f..0000000
--- a/packages/SystemUI/res/drawable/stat_sys_signal_inout.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:autoMirrored="true"
-        android:width="17dp"
-        android:height="17dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:name="in"
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M8.7,12.2l-2.0,3.5l-2.0,-3.5z" />
-    <path
-        android:name="out"
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M0.5,15.7l2.0,-3.5l2.0,3.5z" />
-</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_out.xml b/packages/SystemUI/res/drawable/stat_sys_signal_out.xml
deleted file mode 100644
index 910c035..0000000
--- a/packages/SystemUI/res/drawable/stat_sys_signal_out.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:autoMirrored="true"
-        android:width="17dp"
-        android:height="17dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:name="out"
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M0.5,15.7l2.0,-3.5l2.0,3.5z" />
-</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_in.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_in.xml
deleted file mode 100644
index ba3d4e6..0000000
--- a/packages/SystemUI/res/drawable/stat_sys_wifi_in.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:autoMirrored="true"
-        android:width="16.25dp"
-        android:height="15dp"
-        android:viewportWidth="26.0"
-        android:viewportHeight="24.0">
-    <path
-        android:name="in"
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M8.7,18.3l-2.0,3.5l-2.0,-3.5z" />
-</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_inout.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_inout.xml
deleted file mode 100644
index 1f3b68f..0000000
--- a/packages/SystemUI/res/drawable/stat_sys_wifi_inout.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:autoMirrored="true"
-        android:width="16.25dp"
-        android:height="15dp"
-        android:viewportWidth="26.0"
-        android:viewportHeight="24.0">
-    <path
-        android:name="in"
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M8.7,18.3l-2.0,3.5l-2.0,-3.5z" />
-    <path
-        android:name="out"
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M0.5,21.8l2.0,-3.5l2.0,3.5z" />
-</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_out.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_out.xml
deleted file mode 100644
index 24c6b1e..0000000
--- a/packages/SystemUI/res/drawable/stat_sys_wifi_out.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:autoMirrored="true"
-        android:width="16.25dp"
-        android:height="15dp"
-        android:viewportWidth="26.0"
-        android:viewportHeight="24.0">
-    <path
-        android:name="out"
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M0.5,21.8l2.0,-3.5l2.0,3.5z" />
-</vector>
diff --git a/packages/SystemUI/res/drawable/sun2.xml b/packages/SystemUI/res/drawable/sun2.xml
deleted file mode 100644
index 6d2d504..0000000
--- a/packages/SystemUI/res/drawable/sun2.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="48.0dp"
-        android:height="48.0dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path
-        android:pathData="M40.000000,17.400000L40.000000,8.000000l-9.400000,0.000000L24.000000,1.400000L17.400000,8.000000L8.000000,8.000000l0.000000,9.400000L1.400000,24.000000L8.000000,30.600000L8.000000,40.000000l9.400000,0.000000l6.600000,6.600000l6.600000,-6.600000L40.000000,40.000000l0.000000,-9.400000l6.600000,-6.600000L40.000000,17.400000zM24.000000,36.000000c-6.600000,0.000000 -12.000000,-5.400000 -12.000000,-12.000000s5.400000,-12.000000 12.000000,-12.000000c6.600000,0.000000 12.000000,5.400000 12.000000,12.000000S30.600000,36.000000 24.000000,36.000000zM24.000000,16.000000c-4.400000,0.000000 -8.000000,3.600000 -8.000000,8.000000c0.000000,4.400000 3.600000,8.000000 8.000000,8.000000s8.000000,-3.600000 8.000000,-8.000000C32.000000,19.600000 28.400000,16.000000 24.000000,16.000000z"
-        android:fillColor="#FF000000"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/volume_dialog_background.xml b/packages/SystemUI/res/drawable/volume_dialog_background.xml
deleted file mode 100644
index 996ac5e..0000000
--- a/packages/SystemUI/res/drawable/volume_dialog_background.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<!--
-     Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android" >
-    <solid android:color="?android:attr/colorBackgroundFloating" />
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index ed18dc7..4e0cbe0 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -24,13 +24,14 @@
     android:layout_gravity="@integer/notification_panel_layout_gravity"
     android:background="@android:color/transparent"
     android:baselineAligned="false"
-    android:clickable="false"
+    android:clickable="true"
     android:clipChildren="false"
     android:clipToPadding="false"
     android:paddingTop="0dp"
     android:paddingEnd="0dp"
     android:paddingStart="0dp"
-    android:elevation="4dp" >
+    android:elevation="4dp"
+    android:importantForAccessibility="no" >
 
     <include layout="@layout/quick_status_bar_header_system_icons" />
 
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 2674f07c..75c0ec3 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -25,6 +25,12 @@
     android:layout_height="match_parent"
     android:background="@android:color/transparent" >
 
+    <FrameLayout
+        android:id="@+id/big_clock_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="gone" />
+
     <include
         layout="@layout/keyguard_status_view"
         android:visibility="gone" />
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 6b25387..eed44dd 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Swerwing"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Werkmodus is aangeskakel."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Databespaarder is afgeskakel."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Databespaarder is aangeskakel."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Sensorprivaatheid is afgeskakel."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Sensorprivaatheid is aangeskakel."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Skermhelderheid"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Laai tans"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G-data is laat wag"</string>
@@ -884,4 +888,8 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> ander programme</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> ander program</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensors is af"</string>
+    <string name="device_services" msgid="1191212554435440592">"Toesteldienste"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index db97510..7c09112 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5ጂ"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5ጂ+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"በማዛወር ላይ"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"ኤጅ"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"የሥራ ሁነታ በርቷል።"</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"ውሂብ ቆጣቢ ጠፍቷል።"</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"ውሂብ ቆጣቢ በርቷል።"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"የዳሳሽ ግላዊነት ጠፍቷል።"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"የዳሳሽ ግላዊነት በርቷል።"</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"ብሩህነት ያሳዩ"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"ኃይል በመሙላት ላይ"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2ጂ-3ጂ ውሂብ ላፍታ ቆሟል"</string>
@@ -884,4 +888,8 @@
       <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> ሌሎች መተግበሪያዎች</item>
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> ሌሎች መተግበሪያዎች</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"ዳሳሾች ጠፍተዋል"</string>
+    <string name="device_services" msgid="1191212554435440592">"የመሣሪያ አገልግሎቶች"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index ff98366..16abb68 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"شبكة الجيل الرابع أو أحدث"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+‎"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"‏شبكة 5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"‏شبكة 5G‎ والأحدث"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"التجوال"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"‏شبكة EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"تم تشغيل وضع العمل."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"تم إيقاف توفير البيانات."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"تم تشغيل توفير البيانات."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"تم إيقاف \"خصوصية أجهزة الاستشعار\"."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"تم تفعيل \"خصوصية أجهزة الاستشعار\"."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"سطوع الشاشة"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"جارٍ الشحن"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"بيانات شبكات الجيل الثاني والثالث متوقفة مؤقتًا"</string>
@@ -908,4 +912,8 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> تطبيق آخر</item>
       <item quantity="one">تطبيق واحد (<xliff:g id="NUM_APPS_0">%d</xliff:g>) آخر</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"إيقاف أجهزة الاستشعار"</string>
+    <string name="device_services" msgid="1191212554435440592">"خدمات الأجهزة"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index d7b218d..25f7b5b 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -42,7 +42,7 @@
     <string name="status_bar_settings_mute_label" msgid="554682549917429396">"মিউট"</string>
     <string name="status_bar_settings_auto_brightness_label" msgid="511453614962324674">"স্বয়ং"</string>
     <string name="status_bar_settings_notifications" msgid="397146176280905137">"জাননীসমূহ"</string>
-    <string name="bluetooth_tethered" msgid="7094101612161133267">"ব্লুটুথ টেডাৰিং কৰা হ\'ল"</string>
+    <string name="bluetooth_tethered" msgid="7094101612161133267">"ব্লুটুথ টেডাৰিং কৰা হ’ল"</string>
     <string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ইনপুট পদ্ধতি ছেট আপ কৰক"</string>
     <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"বাস্তৱিক কীব\'ৰ্ড"</string>
     <string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>ত প্ৰৱেশ কৰিবলৈ <xliff:g id="APPLICATION">%1$s</xliff:g>ক অনুমতি দিবনে?"</string>
@@ -64,7 +64,7 @@
     <string name="global_action_screenshot" msgid="8329831278085426283">"স্ক্ৰীণশ্বট"</string>
     <string name="screenshot_saving_ticker" msgid="7403652894056693515">"স্ক্ৰীণশ্বট ছেভ কৰি থকা হৈছে…"</string>
     <string name="screenshot_saving_title" msgid="8242282144535555697">"স্ক্ৰীণশ্বট ছেভ কৰি থকা হৈছে…"</string>
-    <string name="screenshot_saved_title" msgid="5637073968117370753">"স্ক্ৰীণশ্বট ছেভ কৰা হ\'ল"</string>
+    <string name="screenshot_saved_title" msgid="5637073968117370753">"স্ক্ৰীণশ্বট ছেভ কৰা হ’ল"</string>
     <string name="screenshot_saved_text" msgid="7574667448002050363">"আপোনাৰ স্ক্ৰীণশ্বট চাবলৈ টিপক"</string>
     <string name="screenshot_failed_title" msgid="7612509838919089748">"স্ক্ৰীণশ্বট ছেভ কৰিব পৰা নগ\'ল"</string>
     <string name="screenshot_failed_to_save_unknown_text" msgid="3637758096565605541">"স্ক্ৰীণশ্বট আকৌ ল\'বলৈ চেষ্টা কৰক"</string>
@@ -81,10 +81,10 @@
     <string name="screenrecord_cancel_label" msgid="3385204992871088609">"বাতিল কৰক"</string>
     <string name="screenrecord_share_label" msgid="4197867360204019389">"শ্বেয়াৰ কৰক"</string>
     <string name="screenrecord_delete_label" msgid="7893716870917824013">"মচক"</string>
-    <string name="screenrecord_cancel_success" msgid="7768976011702614782">"স্ক্রীণ ৰেকৰ্ড কৰাটো বাতিল কৰা হ\'ল"</string>
-    <string name="screenrecord_save_message" msgid="4733982661301846778">"স্ক্রীণ ৰেকৰ্ডিং ছেভ কৰা হ\'ল, চাবলৈ টিপক"</string>
-    <string name="screenrecord_delete_description" msgid="5743190456090354585">"স্ক্রীণ ৰেকৰ্ডিং মচা হ\'ল"</string>
-    <string name="screenrecord_delete_error" msgid="8154904464563560282">"স্ক্রীণ ৰেকৰ্ডিং মচি থাকোঁতে কিবা আসোঁৱাহ হ\'ল"</string>
+    <string name="screenrecord_cancel_success" msgid="7768976011702614782">"স্ক্রীণ ৰেকৰ্ড কৰাটো বাতিল কৰা হ’ল"</string>
+    <string name="screenrecord_save_message" msgid="4733982661301846778">"স্ক্রীণ ৰেকৰ্ডিং ছেভ কৰা হ’ল, চাবলৈ টিপক"</string>
+    <string name="screenrecord_delete_description" msgid="5743190456090354585">"স্ক্রীণ ৰেকৰ্ডিং মচা হ’ল"</string>
+    <string name="screenrecord_delete_error" msgid="8154904464563560282">"স্ক্রীণ ৰেকৰ্ডিং মচি থাকোঁতে কিবা আসোঁৱাহ হ’ল"</string>
     <string name="screenrecord_permission_error" msgid="1526755299469001000">"অনুমতি পাব পৰা নগ\'ল"</string>
     <string name="usb_preference_title" msgid="6551050377388882787">"ইউএছবিৰে ফাইল স্থানান্তৰণৰ বিকল্পসমূহ"</string>
     <string name="use_mtp_button_title" msgid="4333504413563023626">"মিডিয়া প্লেয়াৰ (এমটিপি) হিচাপে সংলগ্ন কৰক"</string>
@@ -120,8 +120,8 @@
     <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"মুখমণ্ডলৰ আইকন"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"উপযোগিতা অনুসৰি জুম কৰা বুটাম।"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"স্ক্ৰীণৰ আকাৰ ডাঙৰ কৰিবলৈ জুম কৰক।"</string>
-    <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ব্লুটুথ সংযোগ হ\'ল।"</string>
-    <string name="accessibility_bluetooth_disconnected" msgid="7416648669976870175">"ব্লুটুথ সংযোগ বিচ্ছিন্ন কৰা হ\'ল।"</string>
+    <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ব্লুটুথ সংযোগ হ’ল।"</string>
+    <string name="accessibility_bluetooth_disconnected" msgid="7416648669976870175">"ব্লুটুথ সংযোগ বিচ্ছিন্ন কৰা হ’ল।"</string>
     <string name="accessibility_no_battery" msgid="358343022352820946">"বেটাৰি শেষ"</string>
     <string name="accessibility_battery_one_bar" msgid="7774887721891057523">"বেটাৰিৰ এডাল দণ্ড।"</string>
     <string name="accessibility_battery_two_bars" msgid="8500650438735009973">"বেটাৰিৰ দুডাল দণ্ড।"</string>
@@ -137,9 +137,9 @@
     <string name="accessibility_data_two_bars" msgid="6166018492360432091">"ডেটা ছংনেলৰ তিনিডাল দণ্ড।"</string>
     <string name="accessibility_data_three_bars" msgid="9167670452395038520">"ডেটা ছিংগনেলত তিনিডাল দণ্ড আছে।"</string>
     <string name="accessibility_data_signal_full" msgid="2708384608124519369">"ডেটা ছিগনেল পূৰা আছে।"</string>
-    <string name="accessibility_wifi_name" msgid="7202151365171148501">"<xliff:g id="WIFI">%s</xliff:g>ৰ লগত সংযোগ কৰা হ\'ল।"</string>
-    <string name="accessibility_bluetooth_name" msgid="8441517146585531676">"<xliff:g id="BLUETOOTH">%s</xliff:g>ৰ লগত সংযোগ কৰা হ\'ল।"</string>
-    <string name="accessibility_cast_name" msgid="4026393061247081201">"<xliff:g id="CAST">%s</xliff:g>ত সংযোগ হ\'ল।"</string>
+    <string name="accessibility_wifi_name" msgid="7202151365171148501">"<xliff:g id="WIFI">%s</xliff:g>ৰ লগত সংযোগ কৰা হ’ল।"</string>
+    <string name="accessibility_bluetooth_name" msgid="8441517146585531676">"<xliff:g id="BLUETOOTH">%s</xliff:g>ৰ লগত সংযোগ কৰা হ’ল।"</string>
+    <string name="accessibility_cast_name" msgid="4026393061247081201">"<xliff:g id="CAST">%s</xliff:g>ত সংযোগ হ’ল।"</string>
     <string name="accessibility_no_wimax" msgid="4329180129727630368">"কোনো WiMAX নাই।"</string>
     <string name="accessibility_wimax_one_bar" msgid="4170994299011863648">"WiMAXৰ এডাল দণ্ড৷"</string>
     <string name="accessibility_wimax_two_bars" msgid="9176236858336502288">"WiMAXৰ দুডাল দণ্ড আছে।"</string>
@@ -167,14 +167,16 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"এলটিই"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"এলটিই+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"ৰ\'মিং"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
     <string name="accessibility_data_connection_wifi" msgid="2324496756590645221">"ৱাই-ফাই"</string>
     <string name="accessibility_no_sim" msgid="8274017118472455155">"ছিম নাই।"</string>
-    <string name="accessibility_cell_data" msgid="5326139158682385073">"ম\'বাইল ডেটা"</string>
-    <string name="accessibility_cell_data_on" msgid="5927098403452994422">"ম\'বাইল ডেটা অন অৱস্থাত আছে"</string>
-    <string name="cell_data_off_content_description" msgid="4356113230238585072">"ম\'বাইল ডেটা অফ অৱস্থাত আছে"</string>
+    <string name="accessibility_cell_data" msgid="5326139158682385073">"ম’বাইল ডেটা"</string>
+    <string name="accessibility_cell_data_on" msgid="5927098403452994422">"ম’বাইল ডেটা অন অৱস্থাত আছে"</string>
+    <string name="cell_data_off_content_description" msgid="4356113230238585072">"ম’বাইল ডেটা অফ অৱস্থাত আছে"</string>
     <string name="cell_data_off" msgid="1051264981229902873">"অফ অৱস্থাত আছে"</string>
     <string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"ব্লুটুথ টেডাৰিং।"</string>
     <string name="accessibility_airplane_mode" msgid="834748999790763092">"এয়াৰপ্লেইন ম\'ড।"</string>
@@ -206,30 +208,30 @@
     <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"কৰ্মস্থানৰ প্ৰ\'ফাইলৰ লক স্ক্ৰীণ"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"বন্ধ কৰক"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
-    <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"ৱাই-ফাই অফ কৰা হ\'ল।"</string>
-    <string name="accessibility_quick_settings_wifi_changed_on" msgid="6440117170789528622">"ৱাই-ফাই অন কৰা হ\'ল।"</string>
-    <string name="accessibility_quick_settings_mobile" msgid="4876806564086241341">"ম\'বাইল <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"ৱাই-ফাই অফ কৰা হ’ল।"</string>
+    <string name="accessibility_quick_settings_wifi_changed_on" msgid="6440117170789528622">"ৱাই-ফাই অন কৰা হ’ল।"</string>
+    <string name="accessibility_quick_settings_mobile" msgid="4876806564086241341">"ম’বাইল <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_battery" msgid="1480931583381408972">"বেটাৰি <xliff:g id="STATE">%s</xliff:g>।"</string>
     <string name="accessibility_quick_settings_airplane_off" msgid="7786329360056634412">"এয়াৰপ্লেইন ম\'ড অফ হৈ আছে৷"</string>
     <string name="accessibility_quick_settings_airplane_on" msgid="6406141469157599296">"এয়াৰপ্লেইন ম\'ড অন হৈ আছে৷"</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="66846307818850664">"এয়াৰপ্লেইন ম\'ড অফ কৰা হ\'ল।"</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="8983005603505087728">"এয়াৰপ্লেইন ম\'ড অন কৰা হ\'ল।"</string>
+    <string name="accessibility_quick_settings_airplane_changed_off" msgid="66846307818850664">"এয়াৰপ্লেইন ম\'ড অফ কৰা হ’ল।"</string>
+    <string name="accessibility_quick_settings_airplane_changed_on" msgid="8983005603505087728">"এয়াৰপ্লেইন ম\'ড অন কৰা হ’ল।"</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="2960643943620637020">"সম্পূৰ্ণ নিৰৱতা"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3357131899365865386">"কেৱল এলাৰ্মবোৰৰ বাবে"</string>
     <string name="accessibility_quick_settings_dnd" msgid="6607873236717185815">"অসুবিধা নিদিব।"</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="898107593453022935">"অসুবিধা নিদিব বন্ধ কৰা হ\'ল।"</string>
+    <string name="accessibility_quick_settings_dnd_changed_off" msgid="898107593453022935">"অসুবিধা নিদিব বন্ধ কৰা হ’ল।"</string>
     <string name="accessibility_quick_settings_dnd_changed_on" msgid="4483780856613561039">"অসুবিধা নিদিব অন কৰা হৈছে।"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="6341675755803320038">"ব্লুটুথ।"</string>
     <string name="accessibility_quick_settings_bluetooth_off" msgid="2133631372372064339">"ব্লুটুথ অফ হৈ আছে।"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="7681999166216621838">"ব্লুটুথ অন হৈ আছে।"</string>
     <string name="accessibility_quick_settings_bluetooth_connecting" msgid="6953242966685343855">"ব্লুটুথ সংযোগ কৰি থকা হৈছে।"</string>
-    <string name="accessibility_quick_settings_bluetooth_connected" msgid="4306637793614573659">"ব্লুটুথ সংযোগ কৰা হ\'ল।"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="2730003763480934529">"ব্লুটুথ অফ কৰা হ\'ল।"</string>
-    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="8722351798763206577">"ব্লুটুথ অন কৰা হ\'ল।"</string>
+    <string name="accessibility_quick_settings_bluetooth_connected" msgid="4306637793614573659">"ব্লুটুথ সংযোগ কৰা হ’ল।"</string>
+    <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="2730003763480934529">"ব্লুটুথ অফ কৰা হ’ল।"</string>
+    <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="8722351798763206577">"ব্লুটুথ অন কৰা হ’ল।"</string>
     <string name="accessibility_quick_settings_location_off" msgid="5119080556976115520">"অৱস্থান সবিশেষ অফ হৈ আছে।"</string>
     <string name="accessibility_quick_settings_location_on" msgid="5809937096590102036">"অৱস্থান সবিশেষ অন হৈ আছে।"</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="8526845571503387376">"অৱস্থান সবিশেষ অফ কৰা হ\'ল।"</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="339403053079338468">"অৱস্থান সবিশেষ অন কৰা হ\'ল।"</string>
+    <string name="accessibility_quick_settings_location_changed_off" msgid="8526845571503387376">"অৱস্থান সবিশেষ অফ কৰা হ’ল।"</string>
+    <string name="accessibility_quick_settings_location_changed_on" msgid="339403053079338468">"অৱস্থান সবিশেষ অন কৰা হ’ল।"</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"<xliff:g id="TIME">%s</xliff:g>ৰ বাবে এলাৰ্ম ছেট কৰা হৈছে।"</string>
     <string name="accessibility_quick_settings_close" msgid="3115847794692516306">"পেনেল বন্ধ কৰক।"</string>
     <string name="accessibility_quick_settings_more_time" msgid="3659274935356197708">"অধিক সময়।"</string>
@@ -237,26 +239,28 @@
     <string name="accessibility_quick_settings_flashlight_off" msgid="4936432000069786988">"ফ্লাশ্বলাইট অফ হৈ আছে।"</string>
     <string name="accessibility_quick_settings_flashlight_unavailable" msgid="8012811023312280810">"ফ্লাশ্বলাইট উপলব্ধ নহয়।"</string>
     <string name="accessibility_quick_settings_flashlight_on" msgid="2003479320007841077">"ফ্লাশ্বলাইট অন হৈ আছে৷"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3303701786768224304">"ফ্লাশ্বলাইট অফ কৰা হ\'ল।"</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="6531793301533894686">"ফ্লাশ্বলাইট অন কৰা হ\'ল।"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="4406577213290173911">"ৰং বিপৰীতকৰণ অফ কৰা হ\'ল।"</string>
-    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="6897462320184911126">"ৰং বিপৰীতকৰণ অন কৰা হ\'ল।"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="5004708003447561394">"ম\'বাইল হ\'টস্প\'ট  অফ কৰা হ\'ল।"</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2890951609226476206">"ম\'বাইল হ\'টস্প\'ট  অন কৰা হ\'ল।"</string>
-    <string name="accessibility_casting_turned_off" msgid="1430668982271976172">"স্ক্ৰীণ কাষ্টিং বন্ধ কৰা হ\'ল।"</string>
+    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3303701786768224304">"ফ্লাশ্বলাইট অফ কৰা হ’ল।"</string>
+    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="6531793301533894686">"ফ্লাশ্বলাইট অন কৰা হ’ল।"</string>
+    <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="4406577213290173911">"ৰং বিপৰীতকৰণ অফ কৰা হ’ল।"</string>
+    <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="6897462320184911126">"ৰং বিপৰীতকৰণ অন কৰা হ’ল।"</string>
+    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="5004708003447561394">"ম’বাইল হ\'টস্প\'ট  অফ কৰা হ’ল।"</string>
+    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2890951609226476206">"ম’বাইল হ\'টস্প\'ট  অন কৰা হ’ল।"</string>
+    <string name="accessibility_casting_turned_off" msgid="1430668982271976172">"স্ক্ৰীণ কাষ্টিং বন্ধ কৰা হ’ল।"</string>
     <string name="accessibility_quick_settings_work_mode_off" msgid="7045417396436552890">"কৰ্মস্থান ম\'ড অফ হৈ আছে।"</string>
     <string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"কৰ্মস্থান ম\'ড অন হৈ আছে।"</string>
-    <string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"কৰ্মস্থান ম\'ড অফ কৰা হ\'ল।"</string>
-    <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"কৰ্মস্থান ম\'ড অন কৰা হ\'ল।"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"ডেটা সঞ্চয়কাৰী সুবিধা অফ কৰা হ\'ল।"</string>
-    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"ডেটা সঞ্চয়কাৰী সুবিধা অন কৰা হ\'ল।"</string>
+    <string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"কৰ্মস্থান ম\'ড অফ কৰা হ’ল।"</string>
+    <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"কৰ্মস্থান ম\'ড অন কৰা হ’ল।"</string>
+    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"ডেটা সঞ্চয়কাৰী সুবিধা অফ কৰা হ’ল।"</string>
+    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"ডেটা সঞ্চয়কাৰী সুবিধা অন কৰা হ’ল।"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"ছেন্সৰৰ গোপনীয়তা অফ কৰা হৈছে।"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"ছেন্সৰৰ গোপনীয়তা অন কৰা হৈছে।"</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"ডিছপ্লেৰ উজ্জ্বলতা"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"চ্চার্জ কৰি থকা হৈছে"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G ডেটা পজ কৰা হৈছে"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G ডেটা পজ কৰা হৈছে"</string>
-    <string name="data_usage_disabled_dialog_mobile_title" msgid="6801382439018099779">"ম\'বাইল ডেটা পজ কৰা হৈছে"</string>
+    <string name="data_usage_disabled_dialog_mobile_title" msgid="6801382439018099779">"ম’বাইল ডেটা পজ কৰা হৈছে"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"ডেটা পজ কৰা হৈছে"</string>
-    <string name="data_usage_disabled_dialog" msgid="4919541636934603816">"আপুনি নির্ধাৰণ কৰা ডেটাৰ সীমা শেষ হৈছে। আপুনি এতিয়া ম\'বাইল ডেটা ব্যৱহাৰ কৰিব নোৱাৰে। \n\nযদিহে আপুনি আকৌ ম\'বাইল ডেটা ব্যৱহাৰ কৰে তেন্তে ডেটাৰ ব্যৱহাৰৰ বাবে মাচুল ভৰিবলগীয়া হ\'ব পাৰে।"</string>
+    <string name="data_usage_disabled_dialog" msgid="4919541636934603816">"আপুনি নির্ধাৰণ কৰা ডেটাৰ সীমা শেষ হৈছে। আপুনি এতিয়া ম’বাইল ডেটা ব্যৱহাৰ কৰিব নোৱাৰে। \n\nযদিহে আপুনি আকৌ ম’বাইল ডেটা ব্যৱহাৰ কৰে তেন্তে ডেটাৰ ব্যৱহাৰৰ বাবে মাচুল ভৰিবলগীয়া হ’ব পাৰে।"</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"পুনৰ আৰম্ভ কৰক"</string>
     <string name="gps_notification_searching_text" msgid="8574247005642736060">"জিপিএছ সন্ধান কৰি থকা হৈছে"</string>
     <string name="gps_notification_found_text" msgid="4619274244146446464">"জিপিএছএ অৱস্থান ছেট কৰিছে"</string>
@@ -271,8 +275,8 @@
     <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"জাননীৰ ছেটিংসমূহ"</string>
     <string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> ছেটিংসমূহ"</string>
     <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"আপোনাৰ ফ\'নৰ স্ক্ৰীণ স্বয়ংক্ৰিয়ভাৱে ঘূৰিব৷"</string>
-    <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"স্ক্ৰীণ লেণ্ডস্কেপ দিশত লক কৰা হ\'ল।"</string>
-    <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"স্ক্ৰীণ প\'ৰ্ট্ৰেইট দিশত লক কৰা হ\'ল।"</string>
+    <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"স্ক্ৰীণ লেণ্ডস্কেপ দিশত লক কৰা হ’ল।"</string>
+    <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"স্ক্ৰীণ প\'ৰ্ট্ৰেইট দিশত লক কৰা হ’ল।"</string>
     <string name="accessibility_rotation_lock_off_changed" msgid="8134601071026305153">"আপোনাৰ ফ\'নৰ স্ক্ৰীণ এতিয়া স্বয়ংক্ৰিয়ভাৱে ঘূৰিব৷"</string>
     <string name="accessibility_rotation_lock_on_landscape_changed" msgid="3135965553707519743">"স্ক্ৰীণখন এতিয়া লেণ্ডস্কেইপ দিশত লক কৰা অৱস্থাত আছে।"</string>
     <string name="accessibility_rotation_lock_on_portrait_changed" msgid="8922481981834012126">"স্ক্ৰীণখন এতিয়া প\'ৰ্ট্ৰেইট দিশত লক কৰা অৱস্থাত আছে।"</string>
@@ -299,7 +303,7 @@
     <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"স্বয়ং-ঘূৰ্ণন"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4231661040698488779">"স্বয়ং-ঘূৰ্ণন স্ক্ৰীণ"</string>
     <string name="accessibility_quick_settings_rotation_value" msgid="8187398200140760213">"<xliff:g id="ID_1">%s</xliff:g> ম\'ড"</string>
-    <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"ঘূৰ্ণন লক কৰা হ\'ল"</string>
+    <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"ঘূৰ্ণন লক কৰা হ’ল"</string>
     <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"প\'ৰ্ট্ৰেইট"</string>
     <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"লেণ্ডস্কেইপ"</string>
     <string name="quick_settings_ime_label" msgid="7073463064369468429">"ইনপুট পদ্ধতি"</string>
@@ -331,7 +335,7 @@
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"ৰং ওলোটা কৰক"</string>
     <string name="quick_settings_color_space_label" msgid="853443689745584770">"ৰং শুধৰণী কৰা ম\'ড"</string>
     <string name="quick_settings_more_settings" msgid="326112621462813682">"অধিক ছেটিং"</string>
-    <string name="quick_settings_done" msgid="3402999958839153376">"সম্পন্ন কৰা হ\'ল"</string>
+    <string name="quick_settings_done" msgid="3402999958839153376">"সম্পন্ন কৰা হ’ল"</string>
     <string name="quick_settings_connected" msgid="1722253542984847487">"সংযোগ কৰা হ’ল"</string>
     <string name="quick_settings_connected_battery_level" msgid="4136051440381328892">"সংযুক্ত, বেটাৰি <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="quick_settings_connecting" msgid="47623027419264404">"সংযোগ কৰি থকা হৈছে..."</string>
@@ -345,7 +349,7 @@
     </plurals>
     <string name="quick_settings_notifications_label" msgid="4818156442169154523">"জাননীসমূহ"</string>
     <string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ফ্লাশ্বলাইট"</string>
-    <string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"ম\'বাইল ডেটা"</string>
+    <string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"ম’বাইল ডেটা"</string>
     <string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"ডেটা ব্যৱহাৰ"</string>
     <string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"বাকী থকা ডেটা"</string>
     <string name="quick_settings_cellular_detail_over_limit" msgid="967669665390990427">"সর্ব্বোচ সীমা"</string>
@@ -364,7 +368,7 @@
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"আনটো এপ্ ব্য়ৱহাৰ কৰিবলৈ ওপৰলৈ ছোৱাইপ কৰক"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"খৰতকীয়াকৈ আনটো এপ্ ব্য়ৱহাৰ কৰিবলৈ সোঁফালে টানক"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"অৱলোকন ট’গল কৰক"</string>
-    <string name="expanded_header_battery_charged" msgid="5945855970267657951">"চ্চার্জ হ\'ল"</string>
+    <string name="expanded_header_battery_charged" msgid="5945855970267657951">"চ্চার্জ হ’ল"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"চ্চার্জ হৈ আছে"</string>
     <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"বেটাৰিৰ চ্চাৰ্জ সম্পূর্ণ হ\'বলৈ <xliff:g id="CHARGING_TIME">%s</xliff:g> বাকী"</string>
     <string name="expanded_header_battery_not_charging" msgid="4798147152367049732">"চ্চার্জ কৰি থকা নাই"</string>
@@ -419,7 +423,7 @@
     <string name="user_logout_notification_text" msgid="3350262809611876284">"বর্তমানৰ ব্যৱহাৰকাৰীক লগ আউট কৰক"</string>
     <string name="user_logout_notification_action" msgid="1195428991423425062">"ব্যৱহাৰকাৰীক লগ আউট কৰক"</string>
     <string name="user_add_user_title" msgid="4553596395824132638">"নতুন ব্যৱহাৰকাৰী যোগ কৰিবনে?"</string>
-    <string name="user_add_user_message_short" msgid="2161624834066214559">"আপুনি যেতিয়া এজন নতুন ব্যৱহাৰকাৰী যোগ কৰে, তেওঁ নিজৰ স্থান ছেট আপ কৰা প্ৰয়োজন।\n\nযিকোনো ব্যৱহাৰকাৰীয়ে নিজৰ লগতে আন ব্যৱহাৰকাৰীৰো এপ্ আপডেট কৰিব পাৰে।"</string>
+    <string name="user_add_user_message_short" msgid="2161624834066214559">"আপুনি যেতিয়া এজন নতুন ব্যৱহাৰকাৰী যোগ কৰে, তেওঁ নিজৰ স্থান ছেট আপ কৰা প্ৰয়োজন।\n\nযিকোনো ব্যৱহাৰকাৰীয়ে নিজৰ লগতে আন ব্যৱহাৰকাৰীৰো এপ্ আপডে’ট কৰিব পাৰে।"</string>
     <string name="user_limit_reached_title" msgid="7374910700117359177">"অধিকতম ব্যৱহাৰকাৰী সৃষ্টি কৰা হ’ল"</string>
     <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
       <item quantity="one">আপুনি <xliff:g id="COUNT">%d</xliff:g> জনলৈকে ব্যৱহাৰকাৰী যোগ কৰিব পাৰে।</item>
@@ -507,7 +511,7 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"সম্প্ৰসাৰণ কৰক"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"সংকুচিত কৰক"</string>
     <string name="accessibility_output_chooser" msgid="8185317493017988680">"আউটপুট ডিভাইচ সলনি কৰক"</string>
-    <string name="screen_pinning_title" msgid="3273740381976175811">"স্ক্ৰীণ পিন কৰা হ\'ল"</string>
+    <string name="screen_pinning_title" msgid="3273740381976175811">"স্ক্ৰীণ পিন কৰা হ’ল"</string>
     <string name="screen_pinning_description" msgid="8909878447196419623">"এই কাৰ্যই আপুনি আনপিন নকৰালৈকে ইয়াক দেখা পোৱা অৱস্থাত ৰাখে। আনপিন কৰিবলৈ \'পিছলৈ যাওক\' আৰু \'অৱলোকন\'-ত স্পৰ্শ কৰি থাকক।"</string>
     <string name="screen_pinning_description_recents_invisible" msgid="8281145542163727971">"এই কাৰ্যই আপুনি আনপিন নকৰালৈকে ইয়াক দেখা পোৱা অৱস্থাত ৰাখে। আনপিন কৰিবলৈ পিছলৈ যাওক আৰু হ\'মত স্পৰ্শ কৰি সেঁচি ধৰক।"</string>
     <string name="screen_pinning_description_accessible" msgid="426190689254018656">"এই কাৰ্যই আপুনি আনপিন নকৰালৈকে ইয়াক দেখা পোৱা অৱস্থাত ৰাখে। আনপিন কৰিবলৈ \'অৱলোকন\'-ত স্পৰ্শ কৰি থাকক।"</string>
@@ -516,8 +520,8 @@
     <string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"এই স্ক্ৰীণখন আনপিন কৰিবলৈ পিছলৈ যাওক আৰু হ\'ম বুটামত স্পৰ্শ কৰি হেঁচি ধৰক।"</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"বুজি পালোঁ"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"নালাগে, ধন্যবাদ"</string>
-    <string name="screen_pinning_start" msgid="1022122128489278317">"স্ক্ৰীণ পিন কৰা হ\'ল"</string>
-    <string name="screen_pinning_exit" msgid="5187339744262325372">"স্ক্ৰীণ আনপিন কৰা হ\'ল"</string>
+    <string name="screen_pinning_start" msgid="1022122128489278317">"স্ক্ৰীণ পিন কৰা হ’ল"</string>
+    <string name="screen_pinning_exit" msgid="5187339744262325372">"স্ক্ৰীণ আনপিন কৰা হ’ল"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> লুকুৱাবনে?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"আপুনি ইয়াক পৰৱৰ্তী সময়ত ছেটিংসমূহত অন কৰিলে ই পুনৰ প্ৰকট হ\'ব।"</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"লুকুৱাওক"</string>
@@ -576,8 +580,8 @@
     <string name="accessibility_status_bar_hotspot" msgid="4099381329956402865">"হ\'টস্প\'ট"</string>
     <string name="accessibility_managed_profile" msgid="6613641363112584120">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
     <string name="tuner_warning_title" msgid="7094689930793031682">"কিছুমানৰ বাবে আমোদজনক হয় কিন্তু সকলোৰে বাবে নহয়"</string>
-    <string name="tuner_warning" msgid="8730648121973575701">"System UI Tunerএ আপোনাক Android ব্যৱহাৰকাৰী ইণ্টাৰফেইচ সলনি কৰিবলৈ আৰু নিজৰ উপযোগিতা অনুসৰি ব্যৱহাৰ কৰিবলৈ অতিৰিক্ত সুবিধা প্ৰদান কৰে। এই পৰীক্ষামূলক সুবিধাসমূহ সলনি হ\'ব পাৰে, সেইবোৰে কাম নকৰিব পাৰে বা আগন্তুক সংস্কৰণসমূহত সেইবোৰ অন্তৰ্ভুক্ত কৰা নহ\'ব পাৰে। সাৱধানেৰে আগবাঢ়ক।"</string>
-    <string name="tuner_persistent_warning" msgid="8597333795565621795">"এই পৰীক্ষামূলক সুবিধাসমূহ সলনি হ\'ব পাৰে, সেইবোৰে কাম নকৰিব পাৰে বা আগন্তুক সংস্কৰণসমূহত সেইবোৰ অন্তৰ্ভুক্ত কৰা নহ\'ব পাৰে। সাৱধানেৰে আগবাঢ়ক।"</string>
+    <string name="tuner_warning" msgid="8730648121973575701">"System UI Tunerএ আপোনাক Android ব্যৱহাৰকাৰী ইণ্টাৰফেইচ সলনি কৰিবলৈ আৰু নিজৰ উপযোগিতা অনুসৰি ব্যৱহাৰ কৰিবলৈ অতিৰিক্ত সুবিধা প্ৰদান কৰে। এই পৰীক্ষামূলক সুবিধাসমূহ সলনি হ\'ব পাৰে, সেইবোৰে কাম নকৰিব পাৰে বা আগন্তুক সংস্কৰণসমূহত সেইবোৰ অন্তৰ্ভুক্ত কৰা নহ’ব পাৰে। সাৱধানেৰে আগবাঢ়ক।"</string>
+    <string name="tuner_persistent_warning" msgid="8597333795565621795">"এই পৰীক্ষামূলক সুবিধাসমূহ সলনি হ\'ব পাৰে, সেইবোৰে কাম নকৰিব পাৰে বা আগন্তুক সংস্কৰণসমূহত সেইবোৰ অন্তৰ্ভুক্ত কৰা নহ’ব পাৰে। সাৱধানেৰে আগবাঢ়ক।"</string>
     <string name="got_it" msgid="2239653834387972602">"বুজি পালোঁ"</string>
     <string name="tuner_toast" msgid="603429811084428439">"অভিনন্দন! ছেটিংসমূহত System UI Tuner যোগ কৰা হৈছে"</string>
     <string name="remove_from_settings" msgid="8389591916603406378">"ছেটিংসমূহৰ পৰা আঁতৰাওক"</string>
@@ -600,7 +604,7 @@
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"অফ"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"জাননী নিয়ন্ত্ৰণৰ অধিক কৰ্তৃত্বৰ সৈতে আপুনি এটা এপৰ জাননীৰ গুৰুত্বৰ স্তৰ ০ৰ পৰা ৫লৈ ছেট কৰিব পাৰে।\n\n"<b>"স্তৰ ৫"</b>" \n- জাননী তালিকাৰ একেবাৰে ওপৰত দেখুৱাওক \n- সম্পূৰ্ণ স্ক্ৰীণত থাকোঁতে ব্যাঘাত জন্মাবলৈ অনুমতি দিয়ক\n- সদায় ভুমুকি মাৰিবলৈ দিয়ক\n\n"<b>"স্তৰ ৪"</b>" \n- সম্পূৰ্ণ স্ক্ৰীণত থাকোঁতে ব্যাঘাত জন্মাবলৈ নিদিব\n- সদায় ভুমুকি মাৰিবলৈ দিয়ক\n\n"<b>"স্তৰ ৩"</b>" \n- সম্পূৰ্ণ স্ক্ৰীণত থাকোঁতে ব্যাঘাত জন্মাবলৈ নিদিব\n- কেতিয়াও ভুমুকি মাৰিবলৈ নিদিব\n\n"<b>"স্তৰ ২"</b>" \n- সম্পূর্ণ স্ক্ৰীণত থাকোঁতে ব্যাঘাত জন্মাবলৈ নিদিব \n- কেতিয়াও ভুমুকি মাৰিবলৈ নিদিব\n- কেতিয়াও শব্দ আৰু কম্পন কৰিবলৈ নিদিব\n\n"<b>" স্তৰ ১"</b>" \n- সম্পূৰ্ণ স্ক্ৰীণত থাকোঁতে ব্যাঘাত জন্মাবলৈ নিদিব\n- কেতিয়াও ভুমুকি মাৰিবলৈ নিদিব\n-কেতিয়াও শব্দ আৰু কম্পন কৰিবলৈ নিদিব \n- লক স্ক্ৰীণ আৰু স্থিতি দণ্ডৰ পৰা লুকুৱাই ৰাখক \n- জাননী তালিকাৰ একেবাৰে তলত দেখুৱাওক\n\n"<b>"স্তৰ ০"</b>" \n- এই এপৰ সকলো জাননী অৱৰোধ কৰক"</string>
     <string name="notification_header_default_channel" msgid="7506845022070889909">"জাননীসমূহ"</string>
-    <string name="notification_channel_disabled" msgid="344536703863700565">"আপোনাক এই জাননীসমূহ আৰু দেখুওৱা নহ\'ব"</string>
+    <string name="notification_channel_disabled" msgid="344536703863700565">"আপোনাক এই জাননীসমূহ আৰু দেখুওৱা নহ’ব"</string>
     <string name="notification_channel_minimized" msgid="1664411570378910931">"এই জাননীসমূহ মিনিমাইজ কৰি থোৱা হ\'ব"</string>
     <string name="notification_channel_silenced" msgid="2877199534497961942">"এই জাননী নিৰৱে দেখুওৱা হ’ব"</string>
     <string name="notification_channel_unsilenced" msgid="4790904571552394137">"এই জাননীবোৰে আপোনাক সতৰ্ক কৰিব"</string>
@@ -635,7 +639,7 @@
     <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"এই চ্চেনেলৰ পৰা জাননী দিবলৈ অনুমতি দিয়ক"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"অধিক ছেটিং"</string>
     <string name="notification_app_settings" msgid="420348114670768449">"নিজৰ উপযোগিতা অনুসৰি"</string>
-    <string name="notification_done" msgid="5279426047273930175">"সম্পন্ন হ\'ল"</string>
+    <string name="notification_done" msgid="5279426047273930175">"সম্পন্ন হ’ল"</string>
     <string name="inline_undo" msgid="558916737624706010">"আনডু কৰক"</string>
     <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="notification_menu_gear_description" msgid="2204480013726775108">"জাননীৰ নিয়ন্ত্ৰণসমূহ"</string>
@@ -783,8 +787,8 @@
     <string name="accessibility_quick_settings_settings" msgid="6132460890024942157">"ছেটিংসমূহ খোলক।"</string>
     <string name="accessibility_quick_settings_expand" msgid="2375165227880477530">"ক্ষিপ্ৰ ছেটিংসমূহ খোলক।"</string>
     <string name="accessibility_quick_settings_collapse" msgid="1792625797142648105">"ক্ষিপ্ৰ ছেটিংসমূহ বন্ধ কৰক।"</string>
-    <string name="accessibility_quick_settings_alarm_set" msgid="1863000242431528676">"এলার্ম ছেট কৰা হ\'ল।"</string>
-    <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> হিচাপে ছাইন ইন হ\'ল"</string>
+    <string name="accessibility_quick_settings_alarm_set" msgid="1863000242431528676">"এলার্ম ছেট কৰা হ’ল।"</string>
+    <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> হিচাপে ছাইন ইন হ’ল"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"ইণ্টাৰনেট সংযোগ নাই"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"বিৱৰণসমূহ খোলক।"</string>
     <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g>ৰ বাবে উপলব্ধ নহয়"</string>
@@ -806,9 +810,9 @@
     <string name="pip_skip_to_prev" msgid="1955311326688637914">"আগৰটো মিডিয়ালৈ যাওক"</string>
     <string name="thermal_shutdown_title" msgid="4458304833443861111">"আপোনাৰ ফ\'নটো গৰম হোৱাৰ কাৰণে অফ কৰা হৈছিল"</string>
     <string name="thermal_shutdown_message" msgid="9006456746902370523">"আপোনাৰ ফ\'নটো এতিয়া স্বাভাৱিকভাৱে চলি আছে"</string>
-    <string name="thermal_shutdown_dialog_message" msgid="566347880005304139">"আপোনাৰ ফ\'নটো অত্যধিক গৰম হোৱাৰ বাবে ইয়াক ঠাণ্ডা কৰিবলৈ অফ কৰা হৈছিল। আপোনাৰ ফ\'নটো এতিয়া স্বাভাৱিকভাৱে চলি আছে।\n\nআপোনাৰ ফ\'নটো গৰম হ\'ব পাৰে, যদিহে আপুনি:\n	• ফ\'নটোৰ হাৰ্ডৱেৰ অত্যধিক মাত্ৰাত ব্যৱহাৰ কৰা এপসমূহ চলালে (যেনে, ভিডিঅ\' গেইম, ভিডিঅ\', দিক্-নিৰ্দেশনা এপসমূহ)\n	• খুউব ডাঙৰ আকাৰৰ ফাইল আপল\'ড বা ডাউনল\'ড কৰিলে\n	• আপোনাৰ ফ\'নটো উচ্চ তাপমাত্ৰাৰ পৰিৱেশত ব্যৱহাৰ কৰিলে"</string>
+    <string name="thermal_shutdown_dialog_message" msgid="566347880005304139">"আপোনাৰ ফ\'নটো অত্যধিক গৰম হোৱাৰ বাবে ইয়াক ঠাণ্ডা কৰিবলৈ অফ কৰা হৈছিল। আপোনাৰ ফ\'নটো এতিয়া স্বাভাৱিকভাৱে চলি আছে।\n\nআপোনাৰ ফ\'নটো গৰম হ\'ব পাৰে, যদিহে আপুনি:\n	• ফ\'নটোৰ হাৰ্ডৱেৰ অত্যধিক মাত্ৰাত ব্যৱহাৰ কৰা এপসমূহ চলালে (যেনে, ভিডিঅ\' গেইম, ভিডিঅ\', দিক্-নিৰ্দেশনা এপসমূহ)\n	• খুউব ডাঙৰ আকাৰৰ ফাইল আপল\'ড বা ডাউনল’ড কৰিলে\n	• আপোনাৰ ফ\'নটো উচ্চ তাপমাত্ৰাৰ পৰিৱেশত ব্যৱহাৰ কৰিলে"</string>
     <string name="high_temp_title" msgid="4589508026407318374">"ফ\'নটো গৰম হ\'বলৈ ধৰিছে"</string>
-    <string name="high_temp_notif_message" msgid="5642466103153429279">"ফ\'নটো ঠাণ্ডা হৈ থকা সময়ত কিছুমান সুবিধা উপলব্ধ নহ\'ব"</string>
+    <string name="high_temp_notif_message" msgid="5642466103153429279">"ফ\'নটো ঠাণ্ডা হৈ থকা সময়ত কিছুমান সুবিধা উপলব্ধ নহ’ব"</string>
     <string name="high_temp_dialog_message" msgid="6840700639374113553">"আপোনাৰ ফ\'নটোৱে নিজে নিজে ঠাণ্ডা হ\'বলৈ স্বয়ংক্ৰিয়ভাৱে চেষ্টা কৰিব। আপুনি ফ\'নটো ব্যৱহাৰ কৰি থাকিব পাৰে কিন্তু ই লাহে লাহে চলিব পাৰে।\n\nফ\'নটো সম্পূৰ্ণভাৱে ঠাণ্ডা হোৱাৰ পিছত ই আগৰ নিচিনাকৈয়েই চলিব।"</string>
     <string name="lockscreen_shortcut_left" msgid="2182769107618938629">"বাওঁ শ্বৰ্টকাট"</string>
     <string name="lockscreen_shortcut_right" msgid="3328683699505226536">"সোঁ শ্বৰ্টকাট"</string>
@@ -836,7 +840,7 @@
     <string name="instant_apps_message_with_help" msgid="6179830437630729747">"ইনষ্ট\'ল নকৰাকৈয়েই এপটো খোলা হৈছে। অধিক জানিবলৈ টিপক।"</string>
     <string name="app_info" msgid="6856026610594615344">"এপ্ সম্পৰ্কীয় তথ্য"</string>
     <string name="go_to_web" msgid="2650669128861626071">"ব্ৰাউজাৰলৈ যাওক"</string>
-    <string name="mobile_data" msgid="7094582042819250762">"ম\'বাইল ডেটা"</string>
+    <string name="mobile_data" msgid="7094582042819250762">"ম’বাইল ডেটা"</string>
     <string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="wifi_is_off" msgid="1838559392210456893">"ৱাই-ফাই অফ অৱস্থাত আছে"</string>
     <string name="bt_is_off" msgid="2640685272289706392">"ব্লুটুথ অফ অৱস্থাত আছে"</string>
@@ -884,4 +888,9 @@
       <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g>টা অন্য এপ্</item>
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g>টা অন্য এপ্</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"ছেন্সৰ অফ হৈ আছে"</string>
+    <!-- no translation found for device_services (1191212554435440592) -->
+    <skip />
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 9c2cbb9..907e19d 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Rominq"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"İş rejimi yanılıdır."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Data Qənaəti deaktiv edildi."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Data Qənaəti aktiv edildi."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Sensor Məxfiliyi deaktivdir."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Sensor Məxfiliyi aktivdir."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Display brightness"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Enerji yığır"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G məlumatlarına fasilə verildi"</string>
@@ -884,4 +888,7 @@
       <item quantity="other">Digər <xliff:g id="NUM_APPS_1">%d</xliff:g> tətbiq</item>
       <item quantity="one">Digər <xliff:g id="NUM_APPS_0">%d</xliff:g> tətbiq</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensorlar deaktivdir"</string>
+    <string name="device_services" msgid="1191212554435440592">"Cihaz Xidmətləri"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Başlıq yoxdur"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 13919bb..8668c6b 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Režim rada je uključen."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Ušteda podataka je isključena."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Ušteda podataka je uključena."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Isključena je privatnost senzora."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Uključena je privatnost senzora."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Osvetljenost ekrana"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Puni se"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G–3G podaci su pauzirani"</string>
@@ -890,4 +894,7 @@
       <item quantity="few">I još <xliff:g id="NUM_APPS_1">%d</xliff:g> aplikacije</item>
       <item quantity="other">I još <xliff:g id="NUM_APPS_1">%d</xliff:g> aplikacija</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Senzori su isključeni"</string>
+    <string name="device_services" msgid="1191212554435440592">"Usluge za uređaje"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Bez naslova"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 2f2f560..4cc91d2 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Роўмінг"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -252,6 +254,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Рэжым працы ўключаны."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Эканомія трафіку адключана."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Эканомія трафіку ўключана."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Прыватнасць датчыкаў выключана."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Прыватнасць датчыкаў уключана."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Яркасць дысплэя"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Ідзе зарадка"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Перадача даных 2G-3G прыпынена"</string>
@@ -898,4 +902,8 @@
       <item quantity="many">яшчэ <xliff:g id="NUM_APPS_1">%d</xliff:g> праграм</item>
       <item quantity="other">яшчэ <xliff:g id="NUM_APPS_1">%d</xliff:g> праграмы</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Датчыкі выкл."</string>
+    <string name="device_services" msgid="1191212554435440592">"Сэрвісы прылады"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index d985976..ede7af9 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Роуминг"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Работният режим е включен."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Функцията „Икономия на данни“ е изключена."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Функцията „Икономия на данни“ е включена."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Настройката Sensor Privacy е изключена."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Настройката Sensor Privacy е включена."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Яркост на екрана"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Зарежда се"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Данните от 2G – 3G са поставени на пауза"</string>
@@ -884,4 +888,8 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> други приложения</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> друго приложение</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Сензорите са изключени"</string>
+    <string name="device_services" msgid="1191212554435440592">"Услуги за устройството"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 504b3fa..76a344f 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"রোমিং"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"কাজের মোড চালু আছে"</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"ডেটা সেভার বন্ধ আছে।"</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"ডেটা সেভার চালু আছে।"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"সেন্সরের গোপনীয়তা বন্ধ আছে।"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"সেন্সরের গোপনীয়তা চালু আছে।"</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"প্রদর্শনের উজ্জ্বলতা"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"চার্জ হচ্ছে"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G ডেটা বিরতি দেওয়া হয়েছে"</string>
@@ -884,4 +888,8 @@
       <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g>টি অন্য অ্যাপ</item>
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g>টি অন্য অ্যাপ</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"সেন্সর বন্ধ"</string>
+    <string name="device_services" msgid="1191212554435440592">"ডিভাইস সংক্রান্ত পরিষেবা"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index cdb0c65..77429a0d 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Poslovni režim je uključen."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Ušteda podataka je isključena."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Ušteda podataka je uključena."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Privatnost senzora je isključena."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Privatnost senzora je uključena."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Osvjetljenje ekrana"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Punjenje"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G–3G prijenos podataka je pauzirano"</string>
@@ -892,4 +896,7 @@
       <item quantity="few"><xliff:g id="NUM_APPS_1">%d</xliff:g> druge aplikacije​</item>
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> drugih aplikacija​</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Senzori su isključeni"</string>
+    <string name="device_services" msgid="1191212554435440592">"Usluge uređaja"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Bez naslova"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 16770dc..69550b0 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Itinerància"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"S\'ha activat el mode de feina."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"S\'ha desactivat l\'Economitzador de dades."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"S\'ha activat l\'Economitzador de dades."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"El sensor de privadesa està desactivat."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"El sensor de privadesa està activat."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Brillantor de la pantalla"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"S\'està carregant"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Les dades 2G-3G estan aturades"</string>
@@ -884,4 +888,8 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> aplicacions més</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> aplicació més</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensors desactivats"</string>
+    <string name="device_services" msgid="1191212554435440592">"Serveis per a dispositius"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 7d1976b..cf116e3 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Pracovní režim je zapnutý."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Spořič dat byl vypnut."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Spořič dat byl zapnut."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Ochrana soukromí obsahu ze senzorů je vypnuta."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Ochrana soukromí obsahu ze senzorů je zapnuta."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Jas displeje"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Nabíjení"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Data 2G a 3G jsou pozastavena"</string>
@@ -896,4 +900,8 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> dalších aplikací​</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> dalších aplikací</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Senzory jsou vypnuty"</string>
+    <string name="device_services" msgid="1191212554435440592">"Služby zařízení"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 0a9ca2e..ce9a821 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Arbejdstilstand er slået til."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Datasparefunktionen er slået fra."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Datasparefunktionen er slået til."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Privat sensortilstand blev deaktiveret."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Privat sensortilstand blev aktiveret."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Skærmens lysstyrke"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Oplader"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G-data er sat på pause"</string>
@@ -884,4 +888,8 @@
       <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> anden app</item>
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> andre apps​</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Deaktiver sensorer"</string>
+    <string name="device_services" msgid="1191212554435440592">"Enhedstjenester"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 23336de..447f8b9 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -254,6 +256,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Arbeitsmodus aktiviert."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Der Datensparmodus ist deaktiviert."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Der Datensparmodus ist aktiviert."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Sensor Privacy deaktiviert."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Sensor Privacy aktiviert."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Helligkeit des Displays"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Wird aufgeladen"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-/3G-Daten pausiert"</string>
@@ -888,4 +892,8 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> weitere Apps</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> weitere App</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensoren aus"</string>
+    <string name="device_services" msgid="1191212554435440592">"Gerätedienste"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 48f6d36..7468b3a 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Περιαγωγή"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Η λειτουργία εργασίας ενεργοποιήθηκε."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Η Εξοικονόμηση δεδομένων είναι ανενεργή."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Η Εξοικονόμηση δεδομένων είναι ενεργή."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Το απόρρητο του αισθητήρα είναι ανενεργό."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Το απόρρητο του αισθητήρα είναι ενεργό."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Φωτεινότητα οθόνης"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Φόρτιση"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Τα δεδομένα 2G-3G τέθηκαν σε παύση"</string>
@@ -884,4 +888,7 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> εφαρμογές ακόμα</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> εφαρμογή ακόμα</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Αισθητήρες ανενεργοί"</string>
+    <string name="device_services" msgid="1191212554435440592">"Υπηρεσίες συσκευής"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Χωρίς τίτλο"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index c9a984b..5e2fb03 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Work mode turned on."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Data Saver turned off."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Data Saver turned on."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Sensor Privacy turned off."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Sensor Privacy turned on."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Display brightness"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Charging"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G data is paused"</string>
@@ -884,4 +888,8 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> other apps</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> other app</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensors off"</string>
+    <string name="device_services" msgid="1191212554435440592">"Device Services"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index effbb0b..3891569 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Work mode turned on."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Data Saver turned off."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Data Saver turned on."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Sensor Privacy turned off."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Sensor Privacy turned on."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Display brightness"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Charging"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G data is paused"</string>
@@ -884,4 +888,8 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> other apps</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> other app</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensors off"</string>
+    <string name="device_services" msgid="1191212554435440592">"Device Services"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index c9a984b..5e2fb03 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Work mode turned on."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Data Saver turned off."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Data Saver turned on."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Sensor Privacy turned off."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Sensor Privacy turned on."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Display brightness"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Charging"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G data is paused"</string>
@@ -884,4 +888,8 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> other apps</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> other app</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensors off"</string>
+    <string name="device_services" msgid="1191212554435440592">"Device Services"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index c9a984b..5e2fb03 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Work mode turned on."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Data Saver turned off."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Data Saver turned on."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Sensor Privacy turned off."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Sensor Privacy turned on."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Display brightness"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Charging"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G data is paused"</string>
@@ -884,4 +888,8 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> other apps</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> other app</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensors off"</string>
+    <string name="device_services" msgid="1191212554435440592">"Device Services"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 094aa51..d53d61b 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -166,6 +166,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‏‎‎‏‏‏‎‏‏‎‎‏‎‎‏‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‏‎‏‏‏‏‏‏‎‎‎4G+‎‏‎‎‏‎"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‏‏‎‎‏‏‎‎‎‏‎‎‎‎‎‎‎‏‎‏‎‏‏‎‏‏‎‎‎‎‏‏‏‎‎‏‎‎‎‎‏‏‎‏‏‏‎‏‏‎‎‎‏‏‎‎LTE‎‏‎‎‏‎"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‏‏‏‎‎‎‏‏‎‎‎‎‎‏‎‎‏‎‏‏‎‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‎‎‏‎‏‎‎‎‎‎LTE+‎‏‎‎‏‎"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‏‏‎‏‏‎‎‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‎‏‎‎‎‎‎‎‏‎‎‎‎‏‎‏‎‏‏‎‏‏‏‏‏‏‏‏‎‎‎‎5G‎‏‎‎‏‎"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‎‏‎‎‏‏‏‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‏‎‎‏‏‏‎‎‏‏‎‏‏‎‎‏‎‎‏‏‏‎‎‏‎‏‎5G+‎‏‎‎‏‎"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‏‎‎‏‎‎‎‏‏‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‏‎‏‎‎‎‎‎‎‎‏‏‎‎‎‎‏‎‎‏‎‏‏‏‏‎‎‎1X‎‏‎‎‏‎"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‎‏‎‎‎‏‎‎‎‏‏‏‎‏‎‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‎‏‎‏‎‎‎‎‏‎‎‏‎‏‎‎Roaming‎‏‎‎‏‎"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‎‎‏‏‎‎‏‎‏‏‎‎‎‎‏‏‎‎‎‎‏‎‏‎‏‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‏‎‎‏‎‏‎‏‎‎EDGE‎‏‎‎‏‎"</string>
@@ -249,6 +251,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‏‏‏‎‏‏‏‏‎‎‏‏‏‎‎‎‏‏‏‎‏‎‏‎‎‎‎‎‏‎‏‎‏‎‏‎‏‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‎‎‎Work mode turned on.‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‎‎‎‎‎‏‏‎‎‎‎‏‎‏‏‎‏‎‎‎‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‎‎‏‎‏‏‎‏‎‏‏‎‎‏‎‏‎‏‎‎‏‎Data Saver turned off.‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‏‎‏‎‏‎‏‏‎‏‎‏‎‎‏‎‎‏‏‎‎‎‏‎‎‎‏‏‏‎Data Saver turned on.‎‏‎‎‏‎"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‎‎‎‎‎‏‎‎‏‏‏‏‏‏‎‎‎‎‏‏‏‎‏‏‎‎‎‎‏‏‏‎‏‎‏‏‎‎‎‏‏‎‎‏‎‎‏‏‏‎‏‎‎‏‎‎Sensor Privacy turned off.‎‏‎‎‏‎"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‏‏‎‎‏‏‏‏‎‎‏‎‎‎‎‏‎‎‎‎‎‏‎‎‎‏‎‎‎‎‎‎‏‎‎‏‏‏‏‎‎‎‎‎‏‎‎‏‏‎‎‏‏‎Sensor Privacy turned on.‎‏‎‎‏‎"</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‎‎‏‎‏‏‎‎‏‎‎‏‏‎‏‏‏‎‎‏‎‏‎‏‎‎‎‎‏‎‎‎‎‏‎‎‏‏‎‎‎‎‎‏‏‏‎‏‎‏‏‏‎Display brightness‎‏‎‎‏‎"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‎‏‎‏‏‎‏‎‎‎‏‎‏‎‏‎‏‏‏‏‎‎‏‏‎‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎Charging‎‏‎‎‏‎"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‏‎‎‏‏‎‎‏‎‎‏‏‏‏‎‎‏‎‏‏‏‏‎‏‏‏‎‏‎‎‎‎‏‎‎‎‏‏‏‎‎‏‎‏‎‏‏‎‏‏‎‎‎‎‏‎2G-3G data is paused‎‏‎‎‏‎"</string>
@@ -884,4 +888,7 @@
       <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‎‎‎‏‎‎‎‎‎‏‎‏‎‎‏‏‎‎‏‏‎‏‏‎‏‏‎‎‏‎‎‎‎‎‎‏‏‎‎‏‎‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="NUM_APPS_1">%d</xliff:g>‎‏‎‎‏‏‏‎ other apps‎‏‎‎‏‎</item>
       <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‎‎‎‏‎‎‎‎‎‏‎‏‎‎‏‏‎‎‏‏‎‏‏‎‏‏‎‎‏‎‎‎‎‎‎‏‏‎‎‏‎‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="NUM_APPS_0">%d</xliff:g>‎‏‎‎‏‏‏‎ other app‎‏‎‎‏‎</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‎‎‏‎‎‏‏‎‏‏‎‏‏‎‏‏‏‎‎‏‎‏‎‎‎‏‎‏‏‏‎‎‏‎‎‏‎‏‏‏‏‎‏‎‎‏‏‏‏‏‎‎Sensors off‎‏‎‎‏‎"</string>
+    <string name="device_services" msgid="1191212554435440592">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‏‎‎‎‎‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‎‎‎‏‏‏‎‎‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‎‎‎‎‎Device Services‎‏‎‎‏‎"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‏‎‏‏‎‏‎‎‏‏‎‎‎‎‏‎‎‎‏‎‎‏‎‎‎‏‏‎‎‎‏‏‎‏‎‏‎‎‏‎‏‏‏‎‎‏‏‎‎‏‏‎‏‏‎No title‎‏‎‎‏‎"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 4d44e0d..9ef6a83 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Se activó el modo de trabajo."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Se desactivó el Ahorro de datos."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Se activó el Ahorro de datos."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Se desactivó el modo de privacidad del sensor."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Se activó el modo de privacidad del sensor."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Brillo de pantalla"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Cargando"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Datos 2G-3G pausados"</string>
@@ -884,4 +888,9 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> apps más</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> app más</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Se desactivaron los sensores"</string>
+    <!-- no translation found for device_services (1191212554435440592) -->
+    <skip />
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 9c09196..f4e107d 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Itinerancia"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Modo de trabajo activado."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Ahorro de datos desactivado."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Ahorro de datos activado."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Se ha desactivado el sensor de privacidad."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Se ha activado el sensor de privacidad."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Brillo de la pantalla"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Cargando"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Datos 2G-3G pausados"</string>
@@ -884,4 +888,8 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> aplicaciones más</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> aplicación más</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensores desactivados"</string>
+    <string name="device_services" msgid="1191212554435440592">"Servicios del dispositivo"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 8c755b9..d8568df 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Rändlus"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Töörežiim on sisse lülitatud."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Andmeside mahu säästja on välja lülitatud."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Andmeside mahu säästja on sisse lülitatud."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Andur Privaatsus on välja lülitatud."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Andur Privaatsus on sisse lülitatud."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Ekraani heledus"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Laadimine"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G–3G andmekasutus on peatatud"</string>
@@ -884,4 +888,8 @@
       <item quantity="other">Veel <xliff:g id="NUM_APPS_1">%d</xliff:g> rakendust</item>
       <item quantity="one">Veel <xliff:g id="NUM_APPS_0">%d</xliff:g> rakendus</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Andurid on välja lülitatud"</string>
+    <string name="device_services" msgid="1191212554435440592">"Seadme teenused"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 51a7935..cfdf7a5 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Ibiltaritza"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Aktibatuta dago lan modua."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Desaktibatuta dago datu-aurrezlea."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Aktibatuta dago datu-aurrezlea."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Desaktibatu da sentsoreen pribatutasuna."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Aktibatu da sentsoreen pribatutasuna."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Bistaratu distira"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Kargatzen"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G datuen erabilera eten da"</string>
@@ -884,4 +888,8 @@
       <item quantity="other">beste <xliff:g id="NUM_APPS_1">%d</xliff:g> aplikazio</item>
       <item quantity="one">beste <xliff:g id="NUM_APPS_0">%d</xliff:g> aplikazio</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sentsoreak desaktibatuta daude"</string>
+    <string name="device_services" msgid="1191212554435440592">"Gailuetarako zerbitzuak"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index e31a07b..e89f906 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+‎"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+‎"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"رومینگ"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"حالت کار روشن شد."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"صرفه‌جویی داده خاموش شد."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"صرفه‌جویی داده روشن شد."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"«حریم خصوصی حسگر» خاموش است."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"«حریم خصوصی حسگر» روشن است."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"روشنایی نمایشگر"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"درحال شارژ شدن"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"‏داده 2G-3G موقتاً متوقف شده است"</string>
@@ -884,4 +888,7 @@
       <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> برنامه دیگر</item>
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> برنامه دیگر</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"حسگرها خاموش است"</string>
+    <string name="device_services" msgid="1191212554435440592">"سرویس‌های دستگاه"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"بدون عنوان"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index d4e59ae..cd92d45 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Työtila otettiin käyttöön."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Data Saver poistettiin käytöstä."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Data Saver otettiin käyttöön."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Anturien yksityisyystila ei käytössä"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Anturien yksityisyystila käytössä"</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Näytön kirkkaus"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Ladataan"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G–3G-tiedonsiirto keskeytettiin"</string>
@@ -884,4 +888,8 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> muuta sovellusta</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> muu sovellus</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Anturit pois päältä"</string>
+    <string name="device_services" msgid="1191212554435440592">"Laitepalvelut"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 31cc6d3..695a6a5 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Itinérance"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Le mode Travail est activé."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Mode Économiseur de données désactivé."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Mode Économiseur de données activé."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"La confidentialité des capteurs est désactivée."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"La confidentialité des capteurs est désactivée."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Luminosité de l\'écran"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"En cours de charge"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Données 2G/3G désactivées"</string>
@@ -884,4 +888,8 @@
       <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> autre application</item>
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> autres applications</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Capteurs désactivés"</string>
+    <string name="device_services" msgid="1191212554435440592">"Services de l\'appareil"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index db6ffd9..a1a0352 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Itinérance"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Le mode Travail est activé."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"L\'économiseur de données est désactivé."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"L\'économiseur de données est activé."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Confidentialité des capteurs désactivée."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Confidentialité des capteurs activée."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Luminosité de l\'affichage"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"En charge…"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Données 2G-3G désactivées"</string>
@@ -884,4 +888,8 @@
       <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> autre application</item>
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> autres applications</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Capteurs désactivés"</string>
+    <string name="device_services" msgid="1191212554435440592">"Services pour l\'appareil"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 5dccba3..7f111e8 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Itinerancia"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Activouse o modo de traballo."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Desactivouse o Economizador de datos."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Activouse o Economizador de datos."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Desactivouse a privacidade dos sensores."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Activouse a privacidade dos sensores."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Brillo de pantalla"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Cargando"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Os datos 2G-3G están en pausa"</string>
@@ -884,4 +888,8 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> aplicacións máis</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> aplicación máis</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Desactivar sensores"</string>
+    <string name="device_services" msgid="1191212554435440592">"Servizos do dispositivo"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 1e6e733..8525094 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"રોમિંગ"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"કાર્ય મોડ ચાલુ કર્યો."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"ડેટા સેવર બંધ કર્યું."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"ડેટા સેવર ચાલુ કર્યું."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"સેન્સર પ્રાઇવસી બંધ કરવામાં આવી છે."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"સેન્સર પ્રાઇવસી ચાલુ કરવામાં આવી છે."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"પ્રદર્શન તેજ"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"ચાર્જ થઈ રહ્યું છે"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G ડેટા થોભાવ્યો છે"</string>
@@ -884,4 +888,9 @@
       <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> અન્ય ઍપ</item>
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> અન્ય ઍપ</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"સેન્સર બંધ છે"</string>
+    <!-- no translation found for device_services (1191212554435440592) -->
+    <skip />
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 57709aa..1c69910 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"एलटीई"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"रोमिंग"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"कार्य मोड चालू किया गया."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"डेटा बचाने की सेटिंग बंद कर दी गई है."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"डेटा बचाने की सेटिंग चालू कर दी गई है."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"सेंसर निजता को बंद कर दिया गया है."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"सेंसर निजता को चालू कर दिया गया है."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"स्क्रीन की स्क्रीन की रोशनी"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"चार्ज हो रही है"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G डेटा रोक दिया गया है"</string>
@@ -599,7 +603,7 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"चालू"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"बंद"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"पावर सूचना नियंत्रण के ज़रिये, आप किसी ऐप की सूचना को उसकी अहमियत के हिसाब से 0 से 5 के लेवल पर सेट कर सकते हैं.\n\n"<b>"लेवल 5"</b>" \n- सूचना सूची में सबसे ऊपर दिखाएं \n- पूरे स्क्रीन को ढंकने की अनुमति दें \n- लगातार देखते रहें \n\n"<b>" लेवल 4"</b>" \n- पूरे स्क्रीन को ढंकें \n- लगातार देखते रहें \n\n"<b>"लेवल 3"</b>" \n- पूरे स्क्रीन को ढंकने से रोकें \n-कभी भी न देखें \n\n"<b>"लेवल 2"</b>" \n- पूरे स्क्रीन को ढंकने से रोकें \n- कभी भी देखें \n- कभी भी आवाज़ या कंपन (वाइब्रेशन) न करें \n\n"<b>"लेवल 1"</b>" \n- पूरे स्क्रीन को ढंकने से रोकें \n- कभी भी न देखें \n- कभी भी आवाज़ या कंपन (वाइब्रेशन) न करें \n- लॉक स्क्रीन और स्टेटस बार से छिपाएं \n- सूचना सूची के नीचे दिखाएं \n\n"<b>"लेवल 0"</b>" \n- ऐप्लिकेशन की सभी सूचनाएं रोक दें"</string>
-    <string name="notification_header_default_channel" msgid="7506845022070889909">"सूचना"</string>
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"सूचनाएं"</string>
     <string name="notification_channel_disabled" msgid="344536703863700565">"अब आपको ये सूचनाएं दिखाई नहीं देंगी"</string>
     <string name="notification_channel_minimized" msgid="1664411570378910931">"इन सूचनाओं को छोटा कर दिया जाएगा"</string>
     <string name="notification_channel_silenced" msgid="2877199534497961942">"ये सूचनाएं बिना आवाज़ के दिखाई जाएंगी"</string>
@@ -834,7 +838,7 @@
     <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> चल रहा है"</string>
     <string name="instant_apps_message" msgid="1183313016396018086">"ऐप्लिकेशन इंस्टॉल किए बिना ही खुल गया है."</string>
     <string name="instant_apps_message_with_help" msgid="6179830437630729747">"ऐप्लिकेशन इंस्टॉल किए बिना ही खुल गया है. ज़्यादा जानने के लिए टैप करें."</string>
-    <string name="app_info" msgid="6856026610594615344">"ऐप की जानकारी"</string>
+    <string name="app_info" msgid="6856026610594615344">"ऐप्लिकेशन की जानकारी"</string>
     <string name="go_to_web" msgid="2650669128861626071">"ब्राउज़र पर जाएं"</string>
     <string name="mobile_data" msgid="7094582042819250762">"मोबाइल डेटा"</string>
     <string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
@@ -884,4 +888,7 @@
       <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> दूसरे ऐप्लिकेशन</item>
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> दूसरे ऐप्लिकेशन</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"सेंसर बंद हैं"</string>
+    <string name="device_services" msgid="1191212554435440592">"डिवाइस सेवाएं"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"कोई शीर्षक नहीं"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 40d9932..8f3a11b 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G i više"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Način rada uključen."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Štednja podatkovnog prometa isključena."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Štednja podatkovnog prometa uključena."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Isključena je privatnost senzora."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Uključena je privatnost senzora."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Svjetlina zaslona"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Punjenje"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G – 3G podaci pauzirani"</string>
@@ -890,4 +894,7 @@
       <item quantity="few">Još <xliff:g id="NUM_APPS_1">%d</xliff:g> aplikacije</item>
       <item quantity="other">Još <xliff:g id="NUM_APPS_1">%d</xliff:g> aplikacija​</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Senzori su isključeni"</string>
+    <string name="device_services" msgid="1191212554435440592">"Usluge uređaja"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Bez naslova"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index e1cdc0b..ae12412 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Barangolás"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Munka mód bekapcsolva."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Adatforgalom-csökkentő kikapcsolva."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Adatforgalom-csökkentő bekapcsolva."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Kikapcsolta a Szenzoradatok védelmét."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Bekapcsolta a Szenzoradatok védelmét."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"A kijelző fényereje"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Töltés folyamatban…"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"A 2G és 3G adatforgalom szünetel."</string>
@@ -884,4 +888,8 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> további alkalmazás</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> további alkalmazás</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Érzékelők kikapcsolva"</string>
+    <string name="device_services" msgid="1191212554435440592">"Eszközszolgáltatások"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index f5921dc..ffcc762 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Ռոումինգ"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Աշխատանքային ռեժիմը միացվեց:"</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Տվյալների խնայումն անջատվեց:"</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Թրաֆիկի տնտեսումը միացվեց:"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Տվիչների գաղտնիությունն անջատած է:"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Տվիչների գաղտնիությունը միացված է:"</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Ցուցադրել պայծառությունը"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Լիցքավորում"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2Գ-3Գ տվյալների օգտագործումը դադարեցված է"</string>
@@ -884,4 +888,8 @@
       <item quantity="one">Եվս <xliff:g id="NUM_APPS_1">%d</xliff:g> հավելված</item>
       <item quantity="other">Եվս <xliff:g id="NUM_APPS_1">%d</xliff:g> հավելված</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Տվիչներն անջատած են"</string>
+    <string name="device_services" msgid="1191212554435440592">"Սարքի ծառայություններ"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 06609e3..eb99a6c 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Mode kerja diaktifkan."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Penghemat Kuota nonaktif."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Penghemat Kuota diaktifkan."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Privasi Sensor dinonaktifkan."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Privasi Sensor diaktifkan."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Kecerahan tampilan"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Mengisi daya"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Data 2G-3G dijeda"</string>
@@ -884,4 +888,8 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> aplikasi lain</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> aplikasi lain</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensor nonaktif"</string>
+    <string name="device_services" msgid="1191212554435440592">"Layanan Perangkat"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 83c75e4..0040c3c 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Reiki"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Kveikt á vinnustillingu."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Slökkt var á gagnasparnaði."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Kveikt var á gagnasparnaði."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Slökkt á persónvuernd skynjara."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Kveikt á persónuvernd fyrir skynjara."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Birtustig skjás"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Í hleðslu"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Slökkt er á 2G- og 3G-gögnum"</string>
@@ -884,4 +888,8 @@
       <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> forrit í viðbót</item>
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> forrit í viðbót</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Slökkt á skynjurum"</string>
+    <string name="device_services" msgid="1191212554435440592">"Tækjaþjónusta"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 4c512fe..83956e8 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Modalità Lavoro attivata."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Funzione Risparmio dati disattivata."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Funzione Risparmio dati attivata."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Privacy per i sensori disattivata."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Privacy per i sensori attiva."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Luminosità dello schermo"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"In carica"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Dati 2G-3G sospesi"</string>
@@ -884,4 +888,8 @@
       <item quantity="other">Altre <xliff:g id="NUM_APPS_1">%d</xliff:g> app</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> altra app</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensori disattivati"</string>
+    <string name="device_services" msgid="1191212554435440592">"Servizi del dispositivo"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 37fb1f7..d43551d 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"+4G"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"+LTE"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"‏+G‏5"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"נדידה"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"מצב עבודה הופעל."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"‏חוסך הנתונים (Data Saver) כובה."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"‏חוסך הנתונים (Data Saver) הופעל."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"התכונה \'פרטיות חיישנים\' כבויה."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"התכונה \'פרטיות חיישנים\' פועלת."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"בהירות תצוגה"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"הסוללה נטענת"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"‏השימוש בנתוני 2G-3G מושהה"</string>
@@ -896,4 +900,7 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> אפליקציות נוספות</item>
       <item quantity="one">אפליקציה נוספת (<xliff:g id="NUM_APPS_0">%d</xliff:g>)</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"החיישנים כבויים"</string>
+    <string name="device_services" msgid="1191212554435440592">"שירותים למכשיר"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"ללא שם"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 519d333..f7d52ed 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"ローミング"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Work モードをオンにしました。"</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"データセーバーが OFF になりました。"</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"データセーバーが ON になりました。"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"センサー プライバシーを OFF にしました。"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"センサー プライバシーを ON にしました。"</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"ディスプレイの明るさ"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"充電しています"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G~3Gデータは一時停止中です"</string>
@@ -884,4 +888,8 @@
       <item quantity="other">他 <xliff:g id="NUM_APPS_1">%d</xliff:g> 個のアプリ</item>
       <item quantity="one">他 <xliff:g id="NUM_APPS_0">%d</xliff:g> 個のアプリ</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"センサー OFF"</string>
+    <string name="device_services" msgid="1191212554435440592">"デバイス サービス"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 50a25bd..e1ee20e 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"როუმინგი"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"სამსახურის რეჟიმი ჩართულია."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"მონაცემთა დამზოგველი გამორთულია."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"მონაცემთა დამზოგველი ჩართულია."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"სენსორების კონფიდენციალურობა გამორთულია."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"სენსორების კონფიდენციალურობა ჩართულია."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"ეკრანის სიკაშკაშე"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"იტენება"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G მონაცემები შეჩერებულია"</string>
@@ -884,4 +888,7 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> სხვა აპი</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> სხვა აპი</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"სენსორების გამორთვა"</string>
+    <string name="device_services" msgid="1191212554435440592">"მოწყობილობის სერვისები"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"უსათაურო"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index b7e02a2..a1788fc 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Роуминг"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Жұмыс режимі қосылды."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Трафикті үнемдеу функциясы өшірілді."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Трафикті үнемдеу функциясы қосылды."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Sensor Privacy функциясы өшірулі."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Sensor Privacy функциясы қосулы."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Дисплей жарықтығы"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Зарядтау"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G деректері кідіртілді"</string>
@@ -884,4 +888,8 @@
       <item quantity="other">Тағы <xliff:g id="NUM_APPS_1">%d</xliff:g> қолданба</item>
       <item quantity="one">Тағы <xliff:g id="NUM_APPS_0">%d</xliff:g> қолданба</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Датчиктер өшірулі"</string>
+    <string name="device_services" msgid="1191212554435440592">"Құрылғы қызметтері"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 1330f36..0cbd06c 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"រ៉ូ​មីង"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"បានបើករបៀបការងារ"</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"បានបិទកម្មវិធីសន្សំសំចៃទិន្នន័យ"</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"បានបើកកម្មវិធីសន្សំសំចៃទិន្នន័យ"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"បានបិទ​ឯកជនភាព​ឧបករណ៍​ចាប់សញ្ញា។"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"បានបើក​ឯកជនភាព​ឧបករណ៍​ចាប់សញ្ញា។"</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"ពន្លឺ​ការ​បង្ហាញ"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"កំពុង​សាក​ថ្ម"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"ទិន្នន័យ 2G-3G ត្រូវបានផ្អាក"</string>
@@ -884,4 +888,8 @@
       <item quantity="other">កម្មវិធី <xliff:g id="NUM_APPS_1">%d</xliff:g> ទៀត</item>
       <item quantity="one">កម្មវិធី <xliff:g id="NUM_APPS_0">%d</xliff:g> ទៀត</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"បិទឧបករណ៍​ចាប់សញ្ញា"</string>
+    <string name="device_services" msgid="1191212554435440592">"សេវាកម្មឧបករណ៍"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 58d2fb8..9f46742 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"ರೋಮಿಂಗ್"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"ಕೆಲಸದ ಮೋಡ್ ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"ಡೇಟಾ ಸೇವರ್ ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"ಡೇಟಾ ಸೇವರ್ ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"ಸೆನ್ಸರ್ ಗೌಪ್ಯತೆ ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"ಸೆನ್ಸರ್ ಗೌಪ್ಯತೆ ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"ಹೊಳಪನ್ನು ಪ್ರದರ್ಶಿಸಿ"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G ಡೇಟಾವನ್ನು ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ"</string>
@@ -884,4 +888,9 @@
       <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> ಇತರ ಆ್ಯಪ್‌ಗಳು</item>
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> ಇತರ ಆ್ಯಪ್‌ಗಳು</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"ಸೆನ್ಸರ್‌ಗಳು ಆಫ್"</string>
+    <!-- no translation found for device_services (1191212554435440592) -->
+    <skip />
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 76fcaf5..581ffa9 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G 이상"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"로밍"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"작업 모드가 사용 설정되었습니다."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"데이터 절약 모드를 사용 중지했습니다."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"데이터 절약 모드를 사용 설정했습니다."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"센서 개인정보 보호가 사용 중지되었습니다."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"센서 개인정보 보호가 사용 설정되었습니다."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"디스플레이 밝기"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"충전 중"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G 데이터 사용 중지됨"</string>
@@ -884,4 +888,8 @@
       <item quantity="other">기타 앱 <xliff:g id="NUM_APPS_1">%d</xliff:g>개</item>
       <item quantity="one">기타 앱 <xliff:g id="NUM_APPS_0">%d</xliff:g>개</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"센서 사용 안함"</string>
+    <string name="device_services" msgid="1191212554435440592">"기기 서비스"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 54f8da3..ba5a0ac 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Роуминг"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Иштөө режими күйгүзүлдү."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Трафикти үнөмдөө режими өчүрүлдү."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Трафикти үнөмдөө режими күйгүзүлдү."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Сенсордун купуялыгы өчүрүлдү."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Сенсордун купуялыгы күйгүзүлдү."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Жарыктыгын көрсөтүү"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Кубатталууда"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G дайындары тындырылды."</string>
@@ -884,4 +888,8 @@
       <item quantity="other">Дагы <xliff:g id="NUM_APPS_1">%d</xliff:g> колдонмо</item>
       <item quantity="one">Дагы <xliff:g id="NUM_APPS_0">%d</xliff:g> колдонмо</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Сенсорлорду өчүрүү"</string>
+    <string name="device_services" msgid="1191212554435440592">"Түзмөк кызматтары"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 8cc18e9..a4ffffb 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"ໂຣມມິງ"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"ໂໝດການເຮັດວຽກເປີດຢູ່."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"ປິດຕົວປະຢັດອິນເຕີເນັດແລ້ວ."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"ເປີດຕົວປະຢັດອິນເຕີເນັດແລ້ວ."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"ປິດການຮັບຮູ້ຄວາມເປັນສ່ວນຕົວແລ້ວ."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"ເປີດການຮັບຮູ້ຄວາມເປັນສ່ວນຕົວແລ້ວ."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"​ຄວາມ​ແຈ້ງ​​ຂອງ​ຈໍ"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"ກຳລັງສາກໄຟ"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"ຂໍ້​ມູນ 2G​-3G ຢຸດ​ຊົ່ວ​ຄາວແລ້ວ"</string>
@@ -884,4 +888,7 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> ແອັບອື່ນໆ</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> ແອັບອື່ນໆ</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"ປິດການຮັບຮູ້ຢູ່"</string>
+    <string name="device_services" msgid="1191212554435440592">"ບໍລິການອຸປະກອນ"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"ບໍ່ມີຊື່"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index e8d6438..46cd3f0 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Tarptinklinis ryšys"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Darbo režimas įjungtas."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Duomenų taupymo priemonė išjungta."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Duomenų taupymo priemonė įjungta."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Jutiklių privatumo funkcija išjungta."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Jutiklių privatumo funkcija įjungta."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Ekrano šviesumas"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Įkrovimas"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G–3G duomenys pristabdyti"</string>
@@ -896,4 +900,9 @@
       <item quantity="many">Dar <xliff:g id="NUM_APPS_1">%d</xliff:g> programos</item>
       <item quantity="other">Dar <xliff:g id="NUM_APPS_1">%d</xliff:g> programų</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Jutikliai išjungti"</string>
+    <!-- no translation found for device_services (1191212554435440592) -->
+    <skip />
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 4c6282d..a1203d5 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Viesabonēšana"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Darba režīms ir ieslēgts."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Datu lietojuma samazinātājs ir izslēgts."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Datu lietojuma samazinātājs ir ieslēgts."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Sensoru konfidencialitāte ir izslēgta."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Sensoru konfidencialitāte ir ieslēgta."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Ekrāna spilgtums"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Notiek uzlāde"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G–3G datu lietojums ir apturēts"</string>
@@ -890,4 +894,8 @@
       <item quantity="one">Vēl <xliff:g id="NUM_APPS_1">%d</xliff:g> lietotne</item>
       <item quantity="other">Vēl <xliff:g id="NUM_APPS_1">%d</xliff:g> lietotnes</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensori izslēgti"</string>
+    <string name="device_services" msgid="1191212554435440592">"Ierīces pakalpojumi"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 8591932..a3fd2d5 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Роаминг"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Режимот на работа е вклучен."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Штедачот на интернет е исклучен."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Штедачот на интернет е вклучен."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Сензорот „Приватност“ е исклучен."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Сензорот „Приватност“ е вклучен."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Осветленост на екранот"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Се полни"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Податоците 2G-3G се паузирани"</string>
@@ -884,4 +888,8 @@
       <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> друга апликација</item>
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> други апликации</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Сензорите се исклучени"</string>
+    <string name="device_services" msgid="1191212554435440592">"Услуги за уредот"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 51ebb2e..f3075b8 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"റോമിംഗ്"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDG"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"പ്രവർത്തന മോഡ് ഓണാക്കി."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"ഡാറ്റ സേവർ ഓഫാക്കി."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"ഡാറ്റ സേവർ ഓണാക്കി."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"സെൻസർ സ്വകാര്യത ഓഫാക്കി."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"സെൻസർ സ്വകാര്യത ഓഫാണ്."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"ഡിസ്പ്ലേ തെളിച്ചം"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"ചാർജ്ജുചെയ്യുന്നു"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G ഡാറ്റ താൽക്കാലികമായി നിർത്തി"</string>
@@ -884,4 +888,8 @@
       <item quantity="other">മറ്റ് <xliff:g id="NUM_APPS_1">%d</xliff:g> ആപ്പുകൾ</item>
       <item quantity="one">മറ്റ് <xliff:g id="NUM_APPS_0">%d</xliff:g> ആപ്പ്</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"സെൻസറുകൾ ഓഫാണ്"</string>
+    <string name="device_services" msgid="1191212554435440592">"ഉപകരണ സേവനങ്ങള്‍"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 6be0e71..7236893 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Роуминг"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Ажлын горимыг асаасан."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Дата хэмнэгчийг унтраасан."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Дата хэмнэгчийг асаасан."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Мэдрэгчийн нууцлалыг унтраасан."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Мэдрэгчийн нууцлалыг асаасан."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Дэлгэцийн гэрэлтэлт"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Цэнэглэж байна"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G дата-г түр зогсоосон байна"</string>
@@ -884,4 +888,8 @@
       <item quantity="other">Бусад <xliff:g id="NUM_APPS_1">%d</xliff:g> апп</item>
       <item quantity="one">Бусад <xliff:g id="NUM_APPS_0">%d</xliff:g> апп</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Мэдрэгчийг унтраах"</string>
+    <string name="device_services" msgid="1191212554435440592">"Төхөөрөмжийн үйлчилгээ"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 6fb44cc..59e481c 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"४G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"१X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"रोमिंग"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"कार्य मोड चालू केला."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"डेटा सर्व्हर बंद केला."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"डेटा सर्व्हर चालू केला."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"सेन्सर गोपनीयता बंद केली आहे."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"सेन्सर गोपनीयता सुरू केली आहे."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"डिस्प्ले चमक"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"चार्ज होत आहे"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G डेटास विराम दिला आहे"</string>
@@ -884,4 +888,9 @@
       <item quantity="one">इतर <xliff:g id="NUM_APPS_1">%d</xliff:g> अॅ​प</item>
       <item quantity="other">इतर <xliff:g id="NUM_APPS_1">%d</xliff:g> अ‍ॅप्स</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"सेन्सर बंद आहेत"</string>
+    <!-- no translation found for device_services (1191212554435440592) -->
+    <skip />
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 58a417d..e77771d 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Perayauan"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Mod kerja dihidupkan."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Penjimat Data dimatikan."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Penjimat Data dihidupkan."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Privasi penderia dimatikan."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Privasi penderia dihidupkan."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Kecerahan paparan"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Mengecas"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Data 2G-3G dijeda"</string>
@@ -884,4 +888,8 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> apl lain</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g>apl lain</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Penderia dimatikan"</string>
+    <string name="device_services" msgid="1191212554435440592">"Perkhidmatan Peranti"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index aa52f7b..e5561e8 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"ပြင်ပကွန်ရက်နှင့် ချိတ်ဆက်ခြင်း"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"အလုပ် မုဒ်ကို ဖွင့်ထားပါသည်။"</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"ဒေတာချွေတာမှု ပိတ်ထားသည်။"</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"ဒေတာချွေတာမှု ဖွင့်ထားသည်။"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"\'အာရုံခံကိရိယာ ကန့်သတ်ရန်ဆက်တင်\' ကို ပိတ်ထားသည်။"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"\'အာရုံခံကိရိယာ ကန့်သတ်ရန်ဆက်တင်\' ကို ဖွင့်ထားသည်။"</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"တောက်ပမှုကို ပြရန်"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"အားသွင်းနေသည်"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G ဒေတာ ခေတ္တရပ်တန့်သည်"</string>
@@ -884,4 +888,8 @@
       <item quantity="other">အခြားအက်ပ် <xliff:g id="NUM_APPS_1">%d</xliff:g> ခု</item>
       <item quantity="one">အခြားအက်ပ် <xliff:g id="NUM_APPS_0">%d</xliff:g> ခု</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"အာရုံခံကိရိယာများ ပိတ်ထားသည်"</string>
+    <string name="device_services" msgid="1191212554435440592">"စက်ပစ္စည်းဝန်ဆောင်မှုများ"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 558fd9b..a1a12bd 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Arbeidsmodusen er slått på."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Datasparing er slått av."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Datasparing er slått på."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Sensor Privacy er slått av."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Sensor Privacy er slått på."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Lysstyrken på skjermen"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Lader"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G- og 3G-data er satt på pause"</string>
@@ -884,4 +888,8 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> andre apper</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> annen app</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensorer er av"</string>
+    <string name="device_services" msgid="1191212554435440592">"Enhetstjenester"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index b826420..096db56 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"रोमिङ"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"कार्य मोड सक्रिय भयो।"</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"डेटा सेभरलाई निष्क्रिय पारियो।"</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"डेटा सेभरलाई सक्रिय गरियो।"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"सेन्सरको गोपनियता निष्क्रिय पारिएको छ।"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"सेन्सरको गोपनियता सक्रिय गरिएको छ।"</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"प्रदर्शन चमक"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"चार्ज गर्दै"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G डेटा रोकिएको छ"</string>
@@ -431,7 +435,7 @@
     <string name="battery_saver_notification_title" msgid="8614079794522291840">"ब्याट्री सेभर सक्रिय छ"</string>
     <string name="battery_saver_notification_text" msgid="820318788126672692">"प्रदर्शन र पृष्ठभूमि डेटा घटाउँनुहोस्"</string>
     <string name="battery_saver_notification_action_text" msgid="132118784269455533">"ब्याट्री सेभर निष्क्रिय पार्नुहोस्"</string>
-    <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ले आफ्नो स्क्रीनमा प्रदर्शित हुने सबै खिच्न शुरू गर्नेछ।"</string>
+    <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ले आफ्नो स्क्रीनमा प्रदर्शित हुने सबै खिच्न शुरू गर्ने छ।"</string>
     <string name="media_projection_remember_text" msgid="3103510882172746752">"फेरि नदेखाउनुहोस्"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"सबै हटाउनुहोस्"</string>
     <string name="manage_notifications_text" msgid="2386728145475108753">"व्यवस्थित गर्नुहोस्"</string>
@@ -700,7 +704,7 @@
     <string name="tuner_full_zen_title" msgid="4540823317772234308">"भोल्युम नियन्त्रणसहित देखाउनुहोस्"</string>
     <string name="volume_and_do_not_disturb" msgid="3373784330208603030">"बाधा नपुर्याउनुहोस्"</string>
     <string name="volume_dnd_silent" msgid="4363882330723050727">"भोल्युम बटनका सर्टकट"</string>
-    <string name="volume_up_silent" msgid="7141255269783588286">"भोल्युम बढाउनेमा बाधा नपुर्याउनुहोस् प्रविष्ट गर्नुहोस्"</string>
+    <string name="volume_up_silent" msgid="7141255269783588286">"भोल्युम बढाउनेमा बाधा नपुर्याउनुहोस् प्रविष्टि गर्नुहोस्"</string>
     <string name="battery" msgid="7498329822413202973">"ब्याट्री"</string>
     <string name="clock" msgid="7416090374234785905">"घडी"</string>
     <string name="headset" msgid="4534219457597457353">"हेडसेट"</string>
@@ -809,7 +813,7 @@
     <string name="thermal_shutdown_dialog_message" msgid="566347880005304139">"तपाईंको फोन अति नै तातिएकाले चिसिन बन्द भयो। तपाईंको फोन अब सामान्य ढंगले चल्दै छ।\n\nतपाईंले निम्न कुराहरू गर्नुभयो भने तपाईंको फोन अत्यन्त तातो हुनसक्छ:\n	• धेरै संसाधन खपत गर्ने अनुप्रयोगहरूको प्रयोग (जस्तै गेमिङ, भिडियो वा नेभिगेसन अनुप्रयोगहरू)\n	• ठूला फाइलहरूको डाउनलोड वा अपलोड\n	• उच्च तापक्रममा फोनको प्रयोग"</string>
     <string name="high_temp_title" msgid="4589508026407318374">"फोन तातो भइरहेको छ"</string>
     <string name="high_temp_notif_message" msgid="5642466103153429279">"फोन चिसो हुँदै गर्दा केही विशेषताहरूलाई सीमित गरिन्छ"</string>
-    <string name="high_temp_dialog_message" msgid="6840700639374113553">"तपाईंको फोन स्वतः चिसो हुने प्रयास गर्नेछ। तपाईं अझै पनि आफ्नो फोनको प्रयोग गर्न सक्नुहुन्छ तर त्यो अझ ढिलो चल्न सक्छ।\n\nचिसो भएपछि तपाईंको फोन सामान्य गतिमा चल्नेछ।"</string>
+    <string name="high_temp_dialog_message" msgid="6840700639374113553">"तपाईंको फोन स्वतः चिसो हुने प्रयास गर्ने छ। तपाईं अझै पनि आफ्नो फोनको प्रयोग गर्न सक्नुहुन्छ तर त्यो अझ ढिलो चल्न सक्छ।\n\nचिसो भएपछि तपाईंको फोन सामान्य गतिमा चल्नेछ।"</string>
     <string name="lockscreen_shortcut_left" msgid="2182769107618938629">"बायाँतिरको सर्टकट"</string>
     <string name="lockscreen_shortcut_right" msgid="3328683699505226536">"दायाँतिरको सर्टकट"</string>
     <string name="lockscreen_unlock_left" msgid="2043092136246951985">"बायाँतिरको सर्टकटले पनि अनलक गर्छ"</string>
@@ -884,4 +888,9 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g>अन्य अनुप्रयोगहरू</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> अन्य अनुप्रयोग</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"सेन्सरहरू निष्क्रिय छन्"</string>
+    <!-- no translation found for device_services (1191212554435440592) -->
+    <skip />
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 06467b8..3607ff8 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Werkmodus ingeschakeld."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Databesparing is uitgeschakeld."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Databesparing is ingeschakeld."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Sensorprivacy uitgeschakeld."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Sensorprivacy ingeschakeld."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Helderheid van het scherm"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Opladen"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G/3G-data zijn onderbroken"</string>
@@ -884,4 +888,8 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> andere apps</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> andere app</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensoren uit"</string>
+    <string name="device_services" msgid="1191212554435440592">"Apparaatservices"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index de7f3b2..5cb98ee 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"ରୋମିଙ୍ଗ"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"ୱର୍କ ମୋଡ୍‌କୁ ଅନ୍‍ କରାଯାଇଛି।"</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"ଡାଟା ସେଭର୍‌ ଅଫ୍‍ କରାଗଲା।"</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"ଡାଟା ସେଭର୍‌ ଅନ୍‍ କରାଗଲା।"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"ସେନ୍ସର୍‍ ଗୋପନୀୟତାକୁ ବନ୍ଦ କରିଦିଆଯାଇଛି।"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"ସେନ୍ସର୍‍ ଗୋପନୀୟତାକୁ ଚାଲୁ କରାଯାଇଛି।"</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"ଡିସ୍‌ପ୍ଲେ ଉଜ୍ଜ୍ୱଳତା"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"ଚାର୍ଜ କରାଯାଉଛି"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G ଡାଟା ପଜ୍‍ କରାଯାଇଛି"</string>
@@ -884,4 +888,9 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g>ଟି ଅନ୍ୟ ଆପ୍‍</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g>ଟି ଅନ୍ୟ ଆପ୍‍</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"ସେନ୍ସର୍‍ଗୁଡ଼ିକ ବନ୍ଦ ଅଛି"</string>
+    <!-- no translation found for device_services (1191212554435440592) -->
+    <skip />
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 7883bbd..3702e2b 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"ਰੋਮਿੰਗ"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"ਕੰਮ ਮੋਡ ਚਾਲੂ ਕੀਤਾ ਗਿਆ।"</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"ਡਾਟਾ ਸੇਵਰ ਬੰਦ ਕੀਤਾ ਗਿਆ।"</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"ਡਾਟਾ ਸੇਵਰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ।"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"ਸੈਂਸਰ ਪਰਦੇਦਾਰੀ ਬੰਦ ਹੈ।"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"ਸੈਂਸਰ ਪਰਦੇਦਾਰੀ ਚਾਲੂ ਹੈ।"</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"ਡਿਸਪਲੇ ਚਮਕ"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G ਡਾਟਾ ਰੁਕ ਗਿਆ ਹੈ"</string>
@@ -884,4 +888,9 @@
       <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> ਹੋਰ ਐਪ</item>
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> ਹੋਰ ਐਪਾਂ</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"ਸੈਂਸਰ ਬੰਦ ਕਰੋ"</string>
+    <!-- no translation found for device_services (1191212554435440592) -->
+    <skip />
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 81c2689..4ece82c 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Tryb pracy włączony."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Oszczędzanie danych jest wyłączone."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Oszczędzanie danych jest włączone."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Wyłączono ustawienia prywatności czujników."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Włączono ustawienia prywatności czujników."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Jasność wyświetlacza"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Ładowanie"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Transmisja danych 2G-3G została wstrzymana"</string>
@@ -896,4 +900,9 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> inne aplikacje</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> inna aplikacja</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Wyłącz czujniki"</string>
+    <!-- no translation found for device_services (1191212554435440592) -->
+    <skip />
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 05fcfb8..ac69917 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Modo de trabalho ativado."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Economia de dados desativada."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Economia de dados ativada."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Sensor de privacidade desativado."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Sensor de privacidade ativado."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Brilho da tela"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Carregando"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Os dados 2G e 3G foram pausados"</string>
@@ -884,4 +888,7 @@
       <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> outro app</item>
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> outros apps</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensores desativados"</string>
+    <string name="device_services" msgid="1191212554435440592">"Serviços do dispositivo"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Sem título"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index fafdad0..375f5e8 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"O modo de trabalho foi ativado."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Poupança de dados desativada."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Poupança de dados ativada."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Sensor Privacidade desativado."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Sensor Privacidade ativado."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Brilho do visor"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"A carregar…"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Dados 2G-3G em pausa"</string>
@@ -884,4 +888,9 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> outras aplicações</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> outra aplicação</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensores desativados"</string>
+    <!-- no translation found for device_services (1191212554435440592) -->
+    <skip />
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 05fcfb8..ac69917 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Modo de trabalho ativado."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Economia de dados desativada."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Economia de dados ativada."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Sensor de privacidade desativado."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Sensor de privacidade ativado."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Brilho da tela"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Carregando"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Os dados 2G e 3G foram pausados"</string>
@@ -884,4 +888,7 @@
       <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> outro app</item>
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> outros apps</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensores desativados"</string>
+    <string name="device_services" msgid="1191212554435440592">"Serviços do dispositivo"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Sem título"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index cb9b574..24254f6 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Modul de lucru a fost activat."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Economizorul de date a fost dezactivat."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Economizorul de date a fost activat."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Confidențialitatea senzorilor este dezactivată."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Confidențialitatea senzorilor este activată."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Luminozitatea ecranului"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Se încarcă"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Conexiunea de date 2G – 3G este întreruptă"</string>
@@ -890,4 +894,8 @@
       <item quantity="other">Alte <xliff:g id="NUM_APPS_1">%d</xliff:g> de aplicații</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> altă aplicație</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Senzori dezactivați"</string>
+    <string name="device_services" msgid="1191212554435440592">"Servicii pentru dispozitiv"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index f9de77c..a5d4059 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Роуминг"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Рабочий режим включен."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Режим экономии трафика отключен."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Режим экономии трафика включен."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Функция Sensor Privacy отключена."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Функция Sensor Privacy включена."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Яркость экрана"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Зарядка батареи"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Передача данных 2G и 3G приостановлена"</string>
@@ -896,4 +900,8 @@
       <item quantity="many">Ещё <xliff:g id="NUM_APPS_1">%d</xliff:g> приложений</item>
       <item quantity="other">Ещё <xliff:g id="NUM_APPS_1">%d</xliff:g> другого приложения</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Датчики отключены"</string>
+    <string name="device_services" msgid="1191212554435440592">"Сервисы устройства"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index cd24df6..cb22f3d 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"රෝමිං"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"වැඩ ප්‍රකාරය ක්‍රියාත්මක කරන ලදී."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"දත්ත සුරැකුම ක්‍රියාවිරහිත කරන ලදී."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"දත්ත සුරැකුම ක්‍රියාත්මක කරන ලදී."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"සංවේදක රහස්‍යතාව ක්‍රියාවිරහිතයි."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"සංවේදක රහස්‍යතාව ක්‍රියාත්මකයි."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"සංදර්ශක දීප්තිය"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"ආරෝපණය වෙමින්"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G දත්ත විරාම කර ඇත"</string>
@@ -884,4 +888,7 @@
       <item quantity="one">වෙනත් යෙදුම් <xliff:g id="NUM_APPS_1">%d</xliff:g></item>
       <item quantity="other">වෙනත් යෙදුම් <xliff:g id="NUM_APPS_1">%d</xliff:g></item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"සංවේදක ක්‍රියාවිරහිතයි"</string>
+    <string name="device_services" msgid="1191212554435440592">"උපාංග සේවා"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"මාතෘකාවක් නැත"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 941e9c7..d9ba8de 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Pracovný režim je zapnutý."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Šetrič dát bol vypnutý."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Šetrič dát bol zapnutý."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Senzor ochrany súkromia je vypnutý."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Senzor ochrany súkromia je zapnutý."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Jas displeja"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Nabíja sa"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Dátové prenosy 2G a 3G sú pozastavené"</string>
@@ -896,4 +900,9 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> ďalších aplikácií</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> ďalšia aplikácia</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Senzory sú vypnuté"</string>
+    <!-- no translation found for device_services (1191212554435440592) -->
+    <skip />
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index e5555c7..5048227 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Gostovanje"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Način za delo je vklopljen."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Varčevanje s podatki je izklopljeno."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Varčevanje s podatki je vklopljeno."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Zasebnost tipal je izklopljena."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Zasebnost tipal je vklopljena."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Svetlost zaslona"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Polnjenje"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Prenos podatkov v omrežju 2G/3G je zaustavljen"</string>
@@ -896,4 +900,8 @@
       <item quantity="few">in še <xliff:g id="NUM_APPS_1">%d</xliff:g> aplikacije</item>
       <item quantity="other">in še <xliff:g id="NUM_APPS_1">%d</xliff:g> aplikacij​</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Izklop za tipala"</string>
+    <string name="device_services" msgid="1191212554435440592">"Storitve naprave"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index ba1f117..66a5778 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Modaliteti i punës është i aktivizuar."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Kursyesi i të dhënave është çaktivizuar."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Kursyesi i të dhënave është aktivizuar."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Privatësia e sensorëve është çaktivizuar."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Privatësia e sensorëve është aktivizuar."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Ndriçimi i ekranit"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Po ngarkohet"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Të dhënat 2G-3G janë ndërprerë"</string>
@@ -884,4 +888,7 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> aplikacione të tjera</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> aplikacion tjetër</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensorët joaktivë"</string>
+    <string name="device_services" msgid="1191212554435440592">"Shërbimet e pajisjes"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Pa titull"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 64dd9a4..1ff65d7 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Роминг"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Режим рада је укључен."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Уштеда података је искључена."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Уштеда података је укључена."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Искључена је приватност сензора."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Укључена је приватност сензора."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Осветљеност екрана"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Пуни се"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G–3G подаци су паузирани"</string>
@@ -890,4 +894,7 @@
       <item quantity="few">И још <xliff:g id="NUM_APPS_1">%d</xliff:g> апликације</item>
       <item quantity="other">И још <xliff:g id="NUM_APPS_1">%d</xliff:g> апликација</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Сензори су искључени"</string>
+    <string name="device_services" msgid="1191212554435440592">"Услуге за уређаје"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Без наслова"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index dd3af05..22ffa81 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Arbetsläget har aktiverats."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Databesparing har inaktiverats."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Databesparing har aktiverats."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Sensorsekretess har inaktiverats."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Sensorsekretess har aktiverats."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Skärmens ljusstyrka"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Laddas"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G- och 3G-data har pausats"</string>
@@ -884,4 +888,8 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> andra appar</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> annan app</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensorer har inaktiverats"</string>
+    <string name="device_services" msgid="1191212554435440592">"Enhetstjänster"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 5170095..69b3be7 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Mitandao ya ng\'ambo"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Hali ya kazi imewashwa."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Kiokoa Data kimezimwa."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Kiokoa Data kimewashwa."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Umezima mipangilio ya Faragha ya Vitambuzi."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Umewasha mipangilio ya Faragha ya Vitambuzi."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Ung\'aavu wa skrini"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Inachaji"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Data ya 2G-3G imesitishwa"</string>
@@ -884,4 +888,7 @@
       <item quantity="other">Programu zingine <xliff:g id="NUM_APPS_1">%d</xliff:g></item>
       <item quantity="one">Programu nyingine <xliff:g id="NUM_APPS_0">%d</xliff:g></item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Umezima vitambuzi"</string>
+    <string name="device_services" msgid="1191212554435440592">"Huduma za Kifaa"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Wimbo hauna jina"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 9f3d7ca..a35c78c 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"ரோமிங்"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"பணிப் பயன்முறை இயக்கப்பட்டது."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"டேட்டா சேமிப்பான் முடக்கப்பட்டது."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"டேட்டா சேமிப்பான் இயக்கப்பட்டது."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"\'சென்சார் தனியுரிமை\' ஆஃப் செய்யப்பட்டது."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"’சென்சார் தனியுரிமை’ ஆன் செய்யப்பட்டது."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"திரை பிரகாசம்"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"சார்ஜ் ஆகிறது"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G டேட்டா இடைநிறுத்தப்பட்டது"</string>
@@ -884,4 +888,9 @@
       <item quantity="other">வேறு <xliff:g id="NUM_APPS_1">%d</xliff:g> ஆப்ஸ்</item>
       <item quantity="one">வேறு <xliff:g id="NUM_APPS_0">%d</xliff:g> ஆப்ஸ்</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"சென்சார்களை ஆஃப் செய்தல்"</string>
+    <!-- no translation found for device_services (1191212554435440592) -->
+    <skip />
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 3184107..e7eafbe 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"రోమింగ్"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"పని మోడ్ ఆన్ చేయబడింది."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"డేటా సేవర్ ఆఫ్ చేయబడింది."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"డేటా సేవర్ ఆన్ చేయబడింది."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"సెన్సార్ గోప్యత ఆఫ్ చేయబడింది."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"సెన్సార్ గోప్యత ఆన్ చేయబడింది."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"ప్రదర్శన ప్రకాశం"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"ఛార్జ్ అవుతోంది"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G డేటా పాజ్ చేయబడింది"</string>
@@ -884,4 +888,9 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> ఇతర యాప్‌లు</item>
       <item quantity="one">మరో <xliff:g id="NUM_APPS_0">%d</xliff:g> యాప్</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"సెన్సార్‌లు ఆఫ్"</string>
+    <!-- no translation found for device_services (1191212554435440592) -->
+    <skip />
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index bf3b131..05df7bd 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"โรมมิ่ง"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"เปิดโหมดการทำงานแล้ว"</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"ปิดโปรแกรมประหยัดอินเทอร์เน็ตแล้ว"</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"เปิดโปรแกรมประหยัดอินเทอร์เน็ตแล้ว"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"ปิดความเป็นส่วนตัวสำหรับเซ็นเซอร์แล้ว"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"เปิดความเป็นส่วนตัวสำหรับเซ็นเซอร์แล้ว"</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"ความสว่างของหน้าจอ"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"กำลังชาร์จ"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"หยุดการใช้ข้อมูล 2G-3G ชั่วคราวแล้ว"</string>
@@ -884,4 +888,8 @@
       <item quantity="other">แอปอื่น <xliff:g id="NUM_APPS_1">%d</xliff:g> แอป</item>
       <item quantity="one">แอปอื่น <xliff:g id="NUM_APPS_0">%d</xliff:g> แอป</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"เซ็นเซอร์ปิดอยู่"</string>
+    <string name="device_services" msgid="1191212554435440592">"บริการของอุปกรณ์"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index fcfd1cd..3bd6ea6 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Na-on ang work mode."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Na-off ang Data Saver."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Na-on ang Data Saver."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Naka-off ang Privacy ng Sensor."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Naka-on ang Privacy ng Sensor."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Liwanag ng display"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Nagcha-charge"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Naka-pause ang 2G-3G data"</string>
@@ -884,4 +888,8 @@
       <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> (na) iba pang app</item>
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> (na) iba pang app</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Naka-off ang mga sensor"</string>
+    <string name="device_services" msgid="1191212554435440592">"Mga Serbisyo ng Device"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index c836875..4962b16 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Dolaşım"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Çalışma modu açıldı."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Veri Tasarrufu kapatıldı."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Veri Tasarrufu açıldı."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Sensör Gizliliği kapatıldı."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Sensör Gizliliği açıldı."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Ekran parlaklığı"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Şarj oluyor"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G veri kullanımı duraklatıldı"</string>
@@ -884,4 +888,8 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> diğer uygulama</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> diğer uygulama</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensörler kapalı"</string>
+    <string name="device_services" msgid="1191212554435440592">"Cihaz Hizmetleri"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 6864034..a298c6a 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Роумінг"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Робочий режим увімкнено."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Заощадження трафіку вимкнено."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Заощадження трафіку ввімкнено."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Конфіденційність датчиків вимкнено."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Конфіденційність датчиків увімкнено."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Яскравість дисплея"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Заряджається"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Передавання даних 2G–3G призупинено"</string>
@@ -896,4 +900,7 @@
       <item quantity="many">Ще <xliff:g id="NUM_APPS_1">%d</xliff:g> додатків​</item>
       <item quantity="other">Щ​е <xliff:g id="NUM_APPS_1">%d</xliff:g> додатка</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Датчики вимкнено"</string>
+    <string name="device_services" msgid="1191212554435440592">"Сервіси на пристрої"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Без назви"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 4738bcd..3a59029 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+‎"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+‎"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+‎"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X‎"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"رومنگ"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"کام موڈ آن ہو گیا۔"</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"ڈیٹا سیور آف ہو گیا۔"</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"ڈیٹا سرور آن ہو گیا۔"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"سینسر کی رازداری آف ہے۔"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"سینسر کی رازداری آن ہے۔"</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"ڈسپلے کی چمک"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"چارج ہو رہا ہے"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"‏2G-3G ڈیٹا موقوف کر دیا گیا"</string>
@@ -884,4 +888,8 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> دیگر ایپس</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> دیگر ایپ</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"سینسرز آف ہیں"</string>
+    <string name="device_services" msgid="1191212554435440592">"آلہ کی سروس"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 53a29e0..3388bcb 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Rouming"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Ishchi rejim yoqildi."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Trafik tejash rejimi o‘chirib qo‘yildi."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Trafik tejash rejimi yoqildi."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Sensor Privacy funksiyasi nofaol."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Sensor Privacy funksiyasi nofaol."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Ekran yorqinligi"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Quvvat olmoqda"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G internet to‘xtatib qo‘yildi"</string>
@@ -884,4 +888,8 @@
       <item quantity="other">Yana <xliff:g id="NUM_APPS_1">%d</xliff:g> ta ilova</item>
       <item quantity="one">Yana <xliff:g id="NUM_APPS_0">%d</xliff:g> ta ilova</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Sensorlar nofaol"</string>
+    <string name="device_services" msgid="1191212554435440592">"Qurilma xizmatlari"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index ce3da2a..3ebb8dd 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G trở lên"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Chuyển vùng"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Chế độ làm việc đã bật."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Đã tắt Trình tiết kiệm dữ liệu."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Đã bật Trình tiết kiệm dữ liệu."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Đã tắt Bảo mật cảm biến."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Đã bật Bảo mật cảm biến."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Độ sáng màn hình"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Đang sạc"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Đã tạm dừng dữ liệu 2G-3G"</string>
@@ -884,4 +888,8 @@
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> ứng dụng khác</item>
       <item quantity="one"><xliff:g id="NUM_APPS_0">%d</xliff:g> ứng dụng khác</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Tắt cảm biến"</string>
+    <string name="device_services" msgid="1191212554435440592">"Dịch vụ cho thiết bị"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index b9abd40..d480a8a 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"漫游"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"工作模式已开启。"</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"流量节省程序已关闭。"</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"流量节省程序已开启。"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"已关闭传感器隐私设置。"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"已开启传感器隐私设置。"</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"屏幕亮度"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"正在充电"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G 数据网络已暂停使用"</string>
@@ -296,7 +300,7 @@
     <skip />
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"正在开启…"</string>
     <string name="quick_settings_brightness_label" msgid="6968372297018755815">"亮度"</string>
-    <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"自动旋转"</string>
+    <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"自动旋转屏幕"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4231661040698488779">"自动旋转屏幕"</string>
     <string name="accessibility_quick_settings_rotation_value" msgid="8187398200140760213">"<xliff:g id="ID_1">%s</xliff:g>模式"</string>
     <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"屏幕方向:锁定"</string>
@@ -884,4 +888,8 @@
       <item quantity="other">另外 <xliff:g id="NUM_APPS_1">%d</xliff:g> 个应用</item>
       <item quantity="one">另外 <xliff:g id="NUM_APPS_0">%d</xliff:g> 个应用</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"已关闭传感器"</string>
+    <string name="device_services" msgid="1191212554435440592">"设备服务"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 5d89c84..5c2a5d2 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"漫遊"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"已開啟工作模式。"</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"已關閉數據節省模式。"</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"已開啟數據節省模式。"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"閂咗感應器私隱功能。"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"開咗感應器私隱功能。"</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"顯示光暗度"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"正在充電"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"已暫停 2G-3G 數據"</string>
@@ -884,4 +888,7 @@
       <item quantity="other">其他 <xliff:g id="NUM_APPS_1">%d</xliff:g> 個應用程式</item>
       <item quantity="one">其他 <xliff:g id="NUM_APPS_0">%d</xliff:g> 個應用程式</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"感應器已關閉"</string>
+    <string name="device_services" msgid="1191212554435440592">"裝置服務"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"無標題"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index bffa9ce..7ff70d9 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"漫遊"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"工作模式已開啟。"</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Data Saver 已關閉。"</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Data Saver 已開啟。"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"已關閉感應器隱私設定。"</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"已開啟感應器隱私設定。"</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"螢幕亮度"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"充電中"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"已暫停 2G-3G 數據連線"</string>
@@ -884,4 +888,8 @@
       <item quantity="other">另外 <xliff:g id="NUM_APPS_1">%d</xliff:g> 個應用程式</item>
       <item quantity="one">另外 <xliff:g id="NUM_APPS_0">%d</xliff:g> 個應用程式</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"已關閉感應器"</string>
+    <string name="device_services" msgid="1191212554435440592">"裝置服務"</string>
+    <!-- no translation found for music_controls_no_title (5236895307087002011) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index f7369a8..098ed01 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -167,6 +167,8 @@
     <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
     <string name="data_connection_lte" msgid="2694876797724028614">"I-LTE"</string>
     <string name="data_connection_lte_plus" msgid="3423013208570937424">"I-LTE+"</string>
+    <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string>
+    <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string>
     <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
     <string name="data_connection_roaming" msgid="6037232010953697354">"Iyazulazula"</string>
     <string name="data_connection_edge" msgid="871835227939216682">"I-EDGE"</string>
@@ -250,6 +252,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Imodi yomsebenzi ivuliwe."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Iseva yedatha ivaliwe."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Iseva yedatha ivuliwe."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"Inzwa yobumfihlo ivaliwe."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"Inzwa yobumfihlo ivuliwe."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Bonisa ukukhanya"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Iyashaja"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G idatha imisiwe"</string>
@@ -884,4 +888,7 @@
       <item quantity="one"><xliff:g id="NUM_APPS_1">%d</xliff:g> ezinye izinhlelo zokusebenza</item>
       <item quantity="other"><xliff:g id="NUM_APPS_1">%d</xliff:g> ezinye izinhlelo zokusebenza</item>
     </plurals>
+    <string name="sensor_privacy_mode" msgid="8982771253020769598">"Izinzwa zivaliwe"</string>
+    <string name="device_services" msgid="1191212554435440592">"Amasevisi edivayisi"</string>
+    <string name="music_controls_no_title" msgid="5236895307087002011">"Asikho isihloko"</string>
 </resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index b3567f8..d5f29ba 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -130,6 +130,7 @@
 
     <!-- Biometric dialog colors -->
     <color name="biometric_dialog_dim_color">#80000000</color> <!-- 50% black -->
+    <color name="biometric_face_icon_gray">#ffbdc1c6</color>
 
     <!-- Logout button -->
     <color name="logout_button_bg_color">#ccffffff</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 9e97cd8..889db66 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -111,7 +111,7 @@
 
     <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
     <string name="quick_settings_tiles_stock" translatable="false">
-        wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,work,cast,night
+        wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,work,cast,night,sensorprivacy
     </string>
 
     <!-- The tiles to display in QuickSettings -->
@@ -290,7 +290,7 @@
 
     <!-- SystemUI Services: The classes of the stuff to start. -->
     <string-array name="config_systemUIServiceComponents" translatable="false">
-        <item>com.android.systemui.Dependency</item>
+        <item>com.android.systemui.Dependency$DependencyCreator</item>
         <item>com.android.systemui.util.NotificationChannels</item>
         <item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item>
         <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
@@ -318,7 +318,7 @@
 
     <!-- SystemUI Services (per user): The classes of the stuff to start for each user. This is a subset of the config_systemUIServiceComponents -->
     <string-array name="config_systemUIServiceComponentsPerUser" translatable="false">
-        <item>com.android.systemui.Dependency</item>
+        <item>com.android.systemui.Dependency$DependencyCreator</item>
         <item>com.android.systemui.util.NotificationChannels</item>
     </string-array>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6037dfc..06df0e7 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -861,9 +861,6 @@
     <dimen name="bottom_padding">48dp</dimen>
     <dimen name="edge_margin">8dp</dimen>
 
-    <dimen name="rounded_corner_radius">@*android:dimen/rounded_corner_radius</dimen>
-    <dimen name="rounded_corner_radius_top">@*android:dimen/rounded_corner_radius_top</dimen>
-    <dimen name="rounded_corner_radius_bottom">@*android:dimen/rounded_corner_radius_bottom</dimen>
     <dimen name="rounded_corner_content_padding">0dp</dimen>
     <dimen name="nav_content_padding">0dp</dimen>
     <dimen name="nav_quick_scrub_track_edge_padding">24dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 9917257..f384d8f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -406,6 +406,12 @@
     <!-- Content description of the data connection type LTE+. [CHAR LIMIT=NONE] -->
     <string name="data_connection_lte_plus">LTE+</string>
 
+    <!-- Content description of the data connection type 5G. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g" translate="false">5G</string>
+
+    <!-- Content description of the data connection type 5G+. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translate="false">5G+</string>
+
     <!-- Content description of the data connection type CDMA. [CHAR LIMIT=NONE] -->
     <string name="data_connection_cdma">1X</string>
 
@@ -598,6 +604,10 @@
     <string name="accessibility_quick_settings_data_saver_changed_off">Data Saver turned off.</string>
     <!-- Announcement made when the Data Saver changes to on (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_quick_settings_data_saver_changed_on">Data Saver turned on.</string>
+    <!-- Announcement made when the Sensor Privacy changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off">Sensor Privacy turned off.</string>
+    <!-- Announcement made when the Sensor Privacy changes to on (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on">Sensor Privacy turned on.</string>
 
     <!-- Content description of the display brightness slider (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_brightness">Display brightness</string>
@@ -2318,4 +2328,12 @@
         <item quantity="one"><xliff:g id="num_apps" example="1">%d</xliff:g> other app</item>
         <item quantity="other"><xliff:g id="num_apps" example="3">%d</xliff:g> other apps</item>
     </plurals>
+    <!-- Text for the quick setting tile for sensor privacy [CHAR LIMIT=30] -->
+    <string name="sensor_privacy_mode">Sensors off</string>
+
+    <!-- Name for device services grouping system uid apps in Ongoing Privacy Dialog [CHAR_LIMIT=NONE] -->
+    <string name="device_services">Device Services</string>
+
+    <!-- What to show on the ambient display player when song doesn't have a title. [CHAR LIMIT=20] -->
+    <string name="music_controls_no_title">No title</string>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 87155c4..8a5a69b 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -125,7 +125,7 @@
 
     <style name="TextAppearance.StatusBar.Clock" parent="@*android:style/TextAppearance.StatusBar.Icon">
         <item name="android:textSize">@dimen/status_bar_clock_size</item>
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+        <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
         <item name="android:textColor">@color/status_bar_clock_color</item>
     </style>
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java
index 74fd13f..01b012d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java
@@ -14,12 +14,40 @@
 
 package com.android.systemui.shared.plugins;
 
+import android.annotation.IntDef;
 import android.content.ComponentName;
 
 /**
  * Enables and disables plugins.
  */
 public interface PluginEnabler {
-    void setEnabled(ComponentName component, boolean enabled);
+
+    int ENABLED = 0;
+    int DISABLED_MANUALLY = 1;
+    int DISABLED_INVALID_VERSION = 1;
+    int DISABLED_FROM_EXPLICIT_CRASH = 2;
+    int DISABLED_FROM_SYSTEM_CRASH = 3;
+
+    @IntDef({ENABLED, DISABLED_MANUALLY, DISABLED_INVALID_VERSION, DISABLED_FROM_EXPLICIT_CRASH,
+            DISABLED_FROM_SYSTEM_CRASH})
+    @interface DisableReason {
+    }
+
+    /** Enables plugin via the PackageManager. */
+    void setEnabled(ComponentName component);
+
+    /** Disables a plugin via the PackageManager and records the reason for disabling. */
+    void setDisabled(ComponentName component, @DisableReason int reason);
+
+    /** Returns true if the plugin is enabled in the PackageManager. */
     boolean isEnabled(ComponentName component);
+
+    /**
+     * Returns the reason that a plugin is disabled, (if it is).
+     *
+     * It should return {@link #ENABLED} if the plugin is turned on.
+     * It should return {@link #DISABLED_MANUALLY} if the plugin is off but the reason is unknown.
+     */
+    @DisableReason
+    int getDisableReason(ComponentName componentName);
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
index 8e7fadb..523720d5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
@@ -136,7 +136,7 @@
         ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
         for (PluginInfo info : plugins) {
             if (className.startsWith(info.mPackage)) {
-                disable(info);
+                disable(info, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH);
                 disableAny = true;
             }
         }
@@ -146,12 +146,13 @@
     public boolean disableAll() {
         ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
         for (int i = 0; i < plugins.size(); i++) {
-            disable(plugins.get(i));
+            disable(plugins.get(i), PluginEnabler.DISABLED_FROM_SYSTEM_CRASH);
         }
         return plugins.size() != 0;
     }
 
-    private void disable(PluginInfo info) {
+    private void disable(PluginInfo info,
+            @PluginEnabler.DisableReason int reason) {
         // Live by the sword, die by the sword.
         // Misbehaving plugins get disabled and won't come back until uninstall/reinstall.
 
@@ -162,9 +163,9 @@
             // Don't disable whitelisted plugins as they are a part of the OS.
             return;
         }
-        Log.w(TAG, "Disabling plugin " + info.mPackage + "/" + info.mClass);
-        mManager.getPluginEnabler().setEnabled(new ComponentName(info.mPackage, info.mClass),
-                false);
+        ComponentName pluginComponent = new ComponentName(info.mPackage, info.mClass);
+        Log.w(TAG, "Disabling plugin " + pluginComponent.flattenToShortString());
+        mManager.getPluginEnabler().setDisabled(pluginComponent, reason);
     }
 
     public <T> boolean dependsOn(Plugin p, Class<T> cls) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
index dc2a9bd..da143f9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
@@ -184,6 +184,7 @@
         mListening = true;
         IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         filter.addAction(PLUGIN_CHANGED);
         filter.addAction(DISABLE_PLUGIN);
@@ -214,12 +215,13 @@
                 // Don't disable whitelisted plugins as they are a part of the OS.
                 return;
             }
-            getPluginEnabler().setEnabled(component, false);
+            getPluginEnabler().setDisabled(component, PluginEnabler.DISABLED_INVALID_VERSION);
             mContext.getSystemService(NotificationManager.class).cancel(component.getClassName(),
                     SystemMessage.NOTE_PLUGIN);
         } else {
             Uri data = intent.getData();
             String pkg = data.getEncodedSchemeSpecificPart();
+            ComponentName componentName = ComponentName.unflattenFromString(pkg);
             if (mOneShotPackages.contains(pkg)) {
                 int icon = mContext.getResources().getIdentifier("tuner", "drawable",
                         mContext.getPackageName());
@@ -256,6 +258,18 @@
                     Log.v(TAG, "Reloading " + pkg);
                 }
             }
+            if (Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction())
+                    && componentName != null) {
+                @PluginEnabler.DisableReason int disableReason =
+                        getPluginEnabler().getDisableReason(componentName);
+                if (disableReason == PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH
+                        || disableReason == PluginEnabler.DISABLED_FROM_SYSTEM_CRASH
+                        || disableReason == PluginEnabler.DISABLED_INVALID_VERSION) {
+                    Log.i(TAG, "Re-enabling previously disabled plugin that has been "
+                            + "updated: " + componentName.flattenToShortString());
+                    getPluginEnabler().setEnabled(componentName);
+                }
+            }
             if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
                 for (PluginInstanceManager manager : mPluginMap.values()) {
                     manager.onPackageChange(pkg);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextCompat.java
new file mode 100644
index 0000000..51fcb0a
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextCompat.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.system;
+
+import android.content.Context;
+
+/**
+ * Wraps a context to expose some methods for launcher to call.
+ */
+public class ContextCompat {
+    private final Context mWrapped;
+
+    public ContextCompat(Context context) {
+        mWrapped = context;
+    }
+
+    public int getUserId() {
+        return mWrapped.getUserId();
+    }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
new file mode 100644
index 0000000..c0a1d89
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+import android.view.SyncRtSurfaceTransactionApplier;
+import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
+import android.view.ThreadedRenderer;
+import android.view.View;
+import android.view.ViewRootImpl;
+
+import java.util.function.Consumer;
+
+/**
+ * Helper class to apply surface transactions in sync with RenderThread.
+ */
+public class SyncRtSurfaceTransactionApplierCompat {
+
+    private final SyncRtSurfaceTransactionApplier mApplier;
+
+    /**
+     * @param targetView The view in the surface that acts as synchronization anchor.
+     */
+    public SyncRtSurfaceTransactionApplierCompat(View targetView) {
+        mApplier = new SyncRtSurfaceTransactionApplier(targetView);
+    }
+
+    private SyncRtSurfaceTransactionApplierCompat(SyncRtSurfaceTransactionApplier applier) {
+        mApplier = applier;
+    }
+
+    /**
+     * Schedules applying surface parameters on the next frame.
+     *
+     * @param params The surface parameters to apply. DO NOT MODIFY the list after passing into
+     *               this method to avoid synchronization issues.
+     */
+    public void scheduleApply(final SurfaceParams... params) {
+        mApplier.scheduleApply(convert(params));
+    }
+
+    private SyncRtSurfaceTransactionApplier.SurfaceParams[] convert(SurfaceParams[] params) {
+        SyncRtSurfaceTransactionApplier.SurfaceParams[] result =
+                new SyncRtSurfaceTransactionApplier.SurfaceParams[params.length];
+        for (int i = 0; i < params.length; i++) {
+            result[i] = params[i].mParams;
+        }
+        return result;
+    }
+
+    public static void applyParams(TransactionCompat t, SurfaceParams params) {
+        SyncRtSurfaceTransactionApplier.applyParams(t.mTransaction, params.mParams, t.mTmpValues);
+    }
+
+    public static void create(final View targetView,
+            final Consumer<SyncRtSurfaceTransactionApplierCompat> callback) {
+        SyncRtSurfaceTransactionApplier.create(targetView,
+                new Consumer<SyncRtSurfaceTransactionApplier>() {
+                    @Override
+                    public void accept(SyncRtSurfaceTransactionApplier applier) {
+                        callback.accept(new SyncRtSurfaceTransactionApplierCompat(applier));
+                    }
+                });
+    }
+
+    public static class SurfaceParams {
+
+        private final SyncRtSurfaceTransactionApplier.SurfaceParams mParams;
+
+        /**
+         * Constructs surface parameters to be applied when the current view state gets pushed to
+         * RenderThread.
+         *
+         * @param surface The surface to modify.
+         * @param alpha Alpha to apply.
+         * @param matrix Matrix to apply.
+         * @param windowCrop Crop to apply.
+         */
+        public SurfaceParams(SurfaceControlCompat surface, float alpha, Matrix matrix,
+                Rect windowCrop, int layer, float cornerRadius) {
+            mParams = new SyncRtSurfaceTransactionApplier.SurfaceParams(surface.mSurfaceControl,
+                    alpha, matrix, windowCrop, layer, cornerRadius);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 0ec9014..27d624a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -7,6 +7,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
 import android.widget.TextClock;
 
 import androidx.annotation.VisibleForTesting;
@@ -15,6 +16,8 @@
 import com.android.systemui.plugins.ClockPlugin;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateController;
 
 import java.util.Objects;
 import java.util.TimeZone;
@@ -22,7 +25,7 @@
 /**
  * Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
  */
-public class KeyguardClockSwitch extends FrameLayout {
+public class KeyguardClockSwitch extends RelativeLayout {
     /**
      * Optional/alternative clock injected via plugin.
      */
@@ -31,24 +34,45 @@
      * Default clock.
      */
     private TextClock mClockView;
+    /**
+     * Frame for default and custom clock.
+     */
+    private FrameLayout mSmallClockFrame;
+    /**
+     * Container for big custom clock.
+     */
+    private ViewGroup mBigClockContainer;
+    /**
+     * Status area (date and other stuff) shown below the clock. Plugin can decide whether
+     * or not to show it below the alternate clock.
+     */
+    private View mKeyguardStatusArea;
 
     private final PluginListener<ClockPlugin> mClockPluginListener =
             new PluginListener<ClockPlugin>() {
                 @Override
                 public void onPluginConnected(ClockPlugin plugin, Context pluginContext) {
-                    View view = plugin.getView();
-                    if (view != null) {
-                        disconnectPlugin();
+                    disconnectPlugin();
+                    View smallClockView = plugin.getView();
+                    if (smallClockView != null) {
                         // For now, assume that the most recently connected plugin is the
                         // selected clock face. In the future, the user should be able to
                         // pick a clock face from the available plugins.
-                        mClockPlugin = plugin;
-                        addView(view, -1,
+                        mSmallClockFrame.addView(smallClockView, -1,
                                 new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                         ViewGroup.LayoutParams.WRAP_CONTENT));
                         initPluginParams();
                         mClockView.setVisibility(View.GONE);
                     }
+                    View bigClockView = plugin.getBigClockView();
+                    if (bigClockView != null && mBigClockContainer != null) {
+                        mBigClockContainer.addView(bigClockView);
+                        mBigClockContainer.setVisibility(View.VISIBLE);
+                    }
+                    if (!plugin.shouldShowStatusArea()) {
+                        mKeyguardStatusArea.setVisibility(View.GONE);
+                    }
+                    mClockPlugin = plugin;
                 }
 
                 @Override
@@ -56,9 +80,28 @@
                     if (Objects.equals(plugin, mClockPlugin)) {
                         disconnectPlugin();
                         mClockView.setVisibility(View.VISIBLE);
+                        mKeyguardStatusArea.setVisibility(View.VISIBLE);
                     }
                 }
             };
+    private final StatusBarStateController.StateListener mStateListener =
+            new StatusBarStateController.StateListener() {
+                @Override
+                public void onStateChanged(int newState) {
+                    if (mBigClockContainer == null) {
+                        return;
+                    }
+                    if (newState == StatusBarState.SHADE) {
+                        if (mBigClockContainer.getVisibility() == View.VISIBLE) {
+                            mBigClockContainer.setVisibility(View.INVISIBLE);
+                        }
+                    } else {
+                        if (mBigClockContainer.getVisibility() == View.INVISIBLE) {
+                            mBigClockContainer.setVisibility(View.VISIBLE);
+                        }
+                    }
+                }
+    };
 
     public KeyguardClockSwitch(Context context) {
         this(context, null);
@@ -72,6 +115,8 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mClockView = findViewById(R.id.default_clock_view);
+        mSmallClockFrame = findViewById(R.id.clock_view);
+        mKeyguardStatusArea = findViewById(R.id.keyguard_status_area);
     }
 
     @Override
@@ -79,12 +124,28 @@
         super.onAttachedToWindow();
         Dependency.get(PluginManager.class).addPluginListener(mClockPluginListener,
                 ClockPlugin.class);
+        Dependency.get(StatusBarStateController.class).addCallback(mStateListener);
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         Dependency.get(PluginManager.class).removePluginListener(mClockPluginListener);
+        Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
+    }
+
+    /**
+     * Set container for big clock face appearing behind NSSL and KeyguardStatusView.
+     */
+    public void setBigClockContainer(ViewGroup container) {
+        if (mClockPlugin != null && container != null) {
+            View bigClockView = mClockPlugin.getBigClockView();
+            if (bigClockView != null) {
+                container.addView(bigClockView);
+                container.setVisibility(View.VISIBLE);
+            }
+        }
+        mBigClockContainer = container;
     }
 
     /**
@@ -183,9 +244,13 @@
 
     private void disconnectPlugin() {
         if (mClockPlugin != null) {
-            View view = mClockPlugin.getView();
-            if (view != null) {
-                removeView(view);
+            View smallClockView = mClockPlugin.getView();
+            if (smallClockView != null) {
+                mSmallClockFrame.removeView(smallClockView);
+            }
+            if (mBigClockContainer != null) {
+                mBigClockContainer.removeAllViews();
+                mBigClockContainer.setVisibility(View.GONE);
             }
             mClockPlugin = null;
         }
@@ -195,4 +260,9 @@
     PluginListener getClockPluginListener() {
         return mClockPluginListener;
     }
+
+    @VisibleForTesting (otherwise = VisibleForTesting.NONE)
+    StatusBarStateController.StateListener getStateListener() {
+        return mStateListener;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index e051317..583dac7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -28,6 +28,7 @@
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.Display;
+import android.view.DisplayInfo;
 import android.view.View;
 import android.view.WindowManager;
 
@@ -45,6 +46,7 @@
     private final Context mContext;
 
     private boolean mShowing;
+    private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
 
     private final SparseArray<Presentation> mPresentations = new SparseArray<>();
 
@@ -86,6 +88,22 @@
         mDisplayService.registerDisplayListener(mDisplayListener, null /* handler */);
     }
 
+    private boolean isKeyguardShowable(Display display) {
+        if (display == null) {
+            if (DEBUG) Log.i(TAG, "Cannot show Keyguard on null display");
+            return false;
+        }
+        if (display.getDisplayId() == DEFAULT_DISPLAY) {
+            if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on the default display");
+            return false;
+        }
+        display.getDisplayInfo(mTmpDisplayInfo);
+        if ((mTmpDisplayInfo.flags & Display.FLAG_PRIVATE) != 0) {
+            if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on a private display");
+            return false;
+        }
+        return true;
+    }
     /**
      * @param display The display to show the presentation on.
      * @return {@code true} if a presentation was added.
@@ -93,7 +111,7 @@
      *         was already there.
      */
     private boolean showPresentation(Display display) {
-        if (display == null || display.getDisplayId() == DEFAULT_DISPLAY) return false;
+        if (!isKeyguardShowable(display)) return false;
         if (DEBUG) Log.i(TAG, "Keyguard enabled on display: " + display);
         final int displayId = display.getDisplayId();
         Presentation presentation = mPresentations.get(displayId);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index 6528d8c..c0ec405 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -66,17 +66,6 @@
         public void onSimStateChanged(int subId, int slotId, State simState) {
             if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
             switch(simState) {
-                // If the SIM is removed, then we must remove the keyguard. It will be put up
-                // again when the PUK locked SIM is re-entered.
-                case ABSENT: {
-                    KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(mSubId);
-                    // onSimStateChanged callback can fire when the SIM PIN lock is not currently
-                    // active and mCallback is null.
-                    if (mCallback != null) {
-                        mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
-                    }
-                    break;
-                }
                 case READY: {
                     mRemainingAttempts = -1;
                     resetState();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index 1157f86..b17d117 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -69,14 +69,11 @@
         public void onSimStateChanged(int subId, int slotId, State simState) {
             if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
             switch(simState) {
-                // If the SIM is removed, then we must remove the keyguard. It will be put up
-                // again when the PUK locked SIM is re-entered.
-                case ABSENT:
-                // intentional fall-through
                 // If the SIM is unlocked via a key sequence through the emergency dialer, it will
                 // move into the READY state and the PUK lock keyguard should be removed.
                 case READY: {
-                    KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(mSubId);
+                    mRemainingAttempts = -1;
+                    mShowDefaultMessage = true;
                     // mCallback can be null if onSimStateChanged callback is called when keyguard
                     // isn't active.
                     if (mCallback != null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index c41ef0e..669e6ff 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -24,6 +24,7 @@
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.annotation.ColorInt;
+import android.annotation.StyleRes;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.graphics.Color;
@@ -207,9 +208,11 @@
                     android.app.slice.SliceItem.FORMAT_IMAGE);
             if (icon != null) {
                 iconDrawable = icon.getIcon().loadDrawable(mContext);
-                final int width = (int) (iconDrawable.getIntrinsicWidth()
-                        / (float) iconDrawable.getIntrinsicHeight() * mIconSize);
-                iconDrawable.setBounds(0, 0, Math.max(width, 1), mIconSize);
+                if (iconDrawable != null) {
+                    final int width = (int) (iconDrawable.getIntrinsicWidth()
+                            / (float) iconDrawable.getIntrinsicHeight() * mIconSize);
+                    iconDrawable.setBounds(0, 0, Math.max(width, 1), mIconSize);
+                }
             }
             button.setCompoundDrawables(iconDrawable, null, null, null);
             button.setOnClickListener(this);
@@ -444,9 +447,11 @@
     static class KeyguardSliceButton extends Button implements
             ConfigurationController.ConfigurationListener {
 
+        @StyleRes
+        private static int sStyleId = R.style.TextAppearance_Keyguard_Secondary;
+
         public KeyguardSliceButton(Context context) {
-            super(context, null /* attrs */, 0 /* styleAttr */,
-                    com.android.keyguard.R.style.TextAppearance_Keyguard_Secondary);
+            super(context, null /* attrs */, 0 /* styleAttr */, sStyleId);
             onDensityOrFontScaleChanged();
             setEllipsize(TruncateAt.END);
         }
@@ -469,6 +474,11 @@
         }
 
         @Override
+        public void onOverlayChanged() {
+            setTextAppearance(sStyleId);
+        }
+
+        @Override
         public void setText(CharSequence text, BufferType type) {
             super.setText(text, type);
             updatePadding();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 1e9d288..c6f1726 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -37,7 +37,7 @@
 import android.util.TypedValue;
 import android.view.View;
 import android.widget.GridLayout;
-import android.widget.RelativeLayout;
+import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import androidx.core.graphics.ColorUtils;
@@ -173,7 +173,7 @@
             mLogoutView.setOnClickListener(this::onLogoutClicked);
         }
 
-        mClockView = findViewById(R.id.clock_view);
+        mClockView = findViewById(R.id.keyguard_clock_container);
         mClockView.setShowCurrentUserTime(true);
         if (KeyguardClockAccessibilityDelegate.isNeeded(mContext)) {
             mClockView.setAccessibilityDelegate(new KeyguardClockAccessibilityDelegate(mContext));
@@ -205,8 +205,8 @@
      * Moves clock, adjusting margins when slice content changes.
      */
     private void onSliceContentChanged() {
-        RelativeLayout.LayoutParams layoutParams =
-                (RelativeLayout.LayoutParams) mClockView.getLayoutParams();
+        LinearLayout.LayoutParams layoutParams =
+                (LinearLayout.LayoutParams) mClockView.getLayoutParams();
         layoutParams.bottomMargin = mPulsing ? mSmallClockPadding : 0;
         mClockView.setLayoutParams(layoutParams);
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 416441e..bcd41a0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1936,6 +1936,13 @@
                 // Even though the subscription is not valid anymore, we need to notify that the
                 // SIM card was removed so we can update the UI.
                 becameAbsent = true;
+                for (SimData data : mSimDatas.values()) {
+                    // Set the SIM state of all SimData associated with that slot to ABSENT se we
+                    // do not move back into PIN/PUK locked and not detect the change below.
+                    if (data.slotId == slotId) {
+                        data.simState = State.ABSENT;
+                    }
+                }
             } else {
                 return;
             }
diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
index d3dded0..b21bcc9 100644
--- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
@@ -137,7 +137,7 @@
         mDrawPaint.setFlags(Paint.SUBPIXEL_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG);
         mDrawPaint.setTextAlign(Paint.Align.CENTER);
         mDrawPaint.setTypeface(Typeface.create(
-                context.getString(com.android.internal.R.string.config_headlineFontFamilyLight),
+                context.getString(com.android.internal.R.string.config_headlineFontFamily),
                 0));
         mShowPassword = Settings.System.getInt(mContext.getContentResolver(),
                 Settings.System.TEXT_SHOW_PASSWORD, 1) == 1;
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index 9e7c5ba..b461f69 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -19,14 +19,22 @@
 
 import com.android.systemui.plugins.ActivityStarter;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Single common instance of ActivityStarter that can be gotten and referenced from anywhere, but
  * delegates to an actual implementation such as StatusBar, assuming it exists.
  */
+@Singleton
 public class ActivityStarterDelegate implements ActivityStarter {
 
     private ActivityStarter mActualStarter;
 
+    @Inject
+    public ActivityStarterDelegate() {
+    }
+
     @Override
     public void startPendingIntentDismissingKeyguard(PendingIntent intent) {
         if (mActualStarter == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 38dadd4..6864ea1 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -48,14 +48,14 @@
 
 import com.android.settingslib.Utils;
 import com.android.settingslib.graph.BatteryMeterDrawableBase;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.policy.IconLogger;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
@@ -325,6 +325,17 @@
                 .inflate(R.layout.battery_percentage_view, null);
     }
 
+    /**
+     * Updates percent view by removing old one and reinflating if necessary
+     */
+    public void updatePercentView() {
+        if (mBatteryPercentView != null) {
+            removeView(mBatteryPercentView);
+            mBatteryPercentView = null;
+        }
+        updateShowPercent();
+    }
+
     private void updatePercentText() {
         if (mBatteryPercentView != null) {
             mBatteryPercentView.setText(
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 5e6d272..9d81064 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -14,19 +14,15 @@
 
 package com.android.systemui;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Configuration;
-import android.hardware.SensorManager;
+import android.hardware.SensorPrivacyManager;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.Looper;
-import android.os.Process;
-import android.os.ServiceManager;
-import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
 import android.view.IWindowManager;
-import android.view.WindowManagerGlobal;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ColorDisplayController;
@@ -35,92 +31,91 @@
 import com.android.internal.util.Preconditions;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.systemui.appops.AppOpsController;
-import com.android.systemui.appops.AppOpsControllerImpl;
 import com.android.systemui.assist.AssistManager;
+import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.PluginDependencyProvider;
-import com.android.systemui.plugins.PluginInitializerImpl;
 import com.android.systemui.plugins.VolumeDialogController;
 import com.android.systemui.power.EnhancedEstimates;
-import com.android.systemui.power.EnhancedEstimatesImpl;
-import com.android.systemui.power.PowerNotificationWarnings;
 import com.android.systemui.power.PowerUI;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.shared.plugins.PluginManagerImpl;
-import com.android.systemui.statusbar.DisplayNavigationBarController;
+import com.android.systemui.statusbar.AmbientPulseManager;
+import com.android.systemui.statusbar.NavigationBarController;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
-import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
-import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
-import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
+import com.android.systemui.statusbar.notification.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
 import com.android.systemui.statusbar.phone.ManagedProfileController;
-import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
+import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
-import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
 import com.android.systemui.statusbar.policy.AccessibilityController;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BatteryControllerImpl;
 import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
 import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastControllerImpl;
 import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.DataSaverController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
 import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
 import com.android.systemui.statusbar.policy.FlashlightController;
-import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
 import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.HotspotControllerImpl;
 import com.android.systemui.statusbar.policy.IconLogger;
-import com.android.systemui.statusbar.policy.IconLoggerImpl;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
 import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.LocationControllerImpl;
 import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkControllerImpl;
 import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
+import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.statusbar.policy.RotationLockController;
-import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
 import com.android.systemui.statusbar.policy.SecurityController;
-import com.android.systemui.statusbar.policy.SecurityControllerImpl;
+import com.android.systemui.statusbar.policy.SmartReplyConstants;
 import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
 import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
 import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tuner.TunerServiceImpl;
 import com.android.systemui.util.AsyncSensorManager;
 import com.android.systemui.util.leak.GarbageMonitor;
 import com.android.systemui.util.leak.LeakDetector;
 import com.android.systemui.util.leak.LeakReporter;
-import com.android.systemui.volume.VolumeDialogControllerImpl;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.HashMap;
 import java.util.function.Consumer;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import dagger.Lazy;
+import dagger.Subcomponent;
+
 /**
  * Class to handle ugly dependencies throughout sysui until we determine the
  * long-term dependency injection solution.
@@ -142,232 +137,328 @@
     /**
      * Key for getting a background Looper for background work.
      */
-    public static final DependencyKey<Looper> BG_LOOPER = new DependencyKey<>("background_looper");
+    public static final String BG_LOOPER_NAME = "background_looper";
     /**
      * Key for getting a background Handler for background work.
      */
-    public static final DependencyKey<Handler> BG_HANDLER = new DependencyKey<>("background_handler");
+    public static final String BG_HANDLER_NAME = "background_handler";
     /**
      * Key for getting a Handler for receiving time tick broadcasts on.
      */
-    public static final DependencyKey<Handler> TIME_TICK_HANDLER =
-            new DependencyKey<>("time_tick_handler");
+    public static final String TIME_TICK_HANDLER_NAME = "time_tick_handler";
     /**
      * Generic handler on the main thread.
      */
-    public static final DependencyKey<Handler> MAIN_HANDLER = new DependencyKey<>("main_handler");
+    public static final String MAIN_HANDLER_NAME = "main_handler";
 
     /**
      * An email address to send memory leak reports to by default.
      */
-    public static final DependencyKey<String> LEAK_REPORT_EMAIL
-            = new DependencyKey<>("leak_report_email");
+    public static final String LEAK_REPORT_EMAIL_NAME = "leak_report_email";
+
+    /**
+     * Whether this platform supports long-pressing notifications to show notification channel
+     * settings.
+     */
+    public static final String ALLOW_NOTIFICATION_LONG_PRESS_NAME = "allow_notif_longpress";
+
+    /**
+     * Key for getting a background Looper for background work.
+     */
+    public static final DependencyKey<Looper> BG_LOOPER = new DependencyKey<>(BG_LOOPER_NAME);
+    /**
+     * Key for getting a background Handler for background work.
+     */
+    public static final DependencyKey<Handler> BG_HANDLER = new DependencyKey<>(BG_HANDLER_NAME);
+    /**
+     * Key for getting a Handler for receiving time tick broadcasts on.
+     */
+    public static final DependencyKey<Handler> TIME_TICK_HANDLER =
+            new DependencyKey<>(TIME_TICK_HANDLER_NAME);
+    /**
+     * Generic handler on the main thread.
+     */
+    public static final DependencyKey<Handler> MAIN_HANDLER =
+            new DependencyKey<>(MAIN_HANDLER_NAME);
+
+    /**
+     * An email address to send memory leak reports to by default.
+     */
+    public static final DependencyKey<String> LEAK_REPORT_EMAIL =
+            new DependencyKey<>(LEAK_REPORT_EMAIL_NAME);
 
     private final ArrayMap<Object, Object> mDependencies = new ArrayMap<>();
     private final ArrayMap<Object, DependencyProvider> mProviders = new ArrayMap<>();
 
+    @Inject Lazy<ActivityStarter> mActivityStarter;
+    @Inject Lazy<ActivityStarterDelegate> mActivityStarterDelegate;
+    @Inject Lazy<AsyncSensorManager> mAsyncSensorManager;
+    @Inject Lazy<BluetoothController> mBluetoothController;
+    @Inject Lazy<LocationController> mLocationController;
+    @Inject Lazy<RotationLockController> mRotationLockController;
+    @Inject Lazy<NetworkController> mNetworkController;
+    @Inject Lazy<ZenModeController> mZenModeController;
+    @Inject Lazy<HotspotController> mHotspotController;
+    @Inject Lazy<CastController> mCastController;
+    @Inject Lazy<FlashlightController> mFlashlightController;
+    @Inject Lazy<UserSwitcherController> mUserSwitcherController;
+    @Inject Lazy<UserInfoController> mUserInfoController;
+    @Inject Lazy<KeyguardMonitor> mKeyguardMonitor;
+    @Inject Lazy<BatteryController> mBatteryController;
+    @Inject Lazy<ColorDisplayController> mColorDisplayController;
+    @Inject Lazy<ManagedProfileController> mManagedProfileController;
+    @Inject Lazy<NextAlarmController> mNextAlarmController;
+    @Inject Lazy<DataSaverController> mDataSaverController;
+    @Inject Lazy<AccessibilityController> mAccessibilityController;
+    @Inject Lazy<DeviceProvisionedController> mDeviceProvisionedController;
+    @Inject Lazy<PluginManager> mPluginManager;
+    @Inject Lazy<AssistManager> mAssistManager;
+    @Inject Lazy<SecurityController> mSecurityController;
+    @Inject Lazy<LeakDetector> mLeakDetector;
+    @Inject Lazy<LeakReporter> mLeakReporter;
+    @Inject Lazy<GarbageMonitor> mGarbageMonitor;
+    @Inject Lazy<IconLogger> mIconLogger;
+    @Inject Lazy<TunerService> mTunerService;
+    @Inject Lazy<StatusBarWindowController> mStatusBarWindowController;
+    @Inject Lazy<DarkIconDispatcher> mDarkIconDispatcher;
+    @Inject Lazy<ConfigurationController> mConfigurationController;
+    @Inject Lazy<StatusBarIconController> mStatusBarIconController;
+    @Inject Lazy<ScreenLifecycle> mScreenLifecycle;
+    @Inject Lazy<WakefulnessLifecycle> mWakefulnessLifecycle;
+    @Inject Lazy<FragmentService> mFragmentService;
+    @Inject Lazy<ExtensionController> mExtensionController;
+    @Inject Lazy<PluginDependencyProvider> mPluginDependencyProvider;
+    @Nullable
+    @Inject Lazy<LocalBluetoothManager> mLocalBluetoothManager;
+    @Inject Lazy<VolumeDialogController> mVolumeDialogController;
+    @Inject Lazy<MetricsLogger> mMetricsLogger;
+    @Inject Lazy<AccessibilityManagerWrapper> mAccessibilityManagerWrapper;
+    @Inject Lazy<SysuiColorExtractor> mSysuiColorExtractor;
+    @Inject Lazy<TunablePaddingService> mTunablePaddingService;
+    @Inject Lazy<ForegroundServiceController> mForegroundServiceController;
+    @Inject Lazy<UiOffloadThread> mUiOffloadThread;
+    @Inject Lazy<PowerUI.WarningsUI> mWarningsUI;
+    @Inject Lazy<LightBarController> mLightBarController;
+    @Inject Lazy<IWindowManager> mIWindowManager;
+    @Inject Lazy<OverviewProxyService> mOverviewProxyService;
+    @Inject Lazy<EnhancedEstimates> mEnhancedEstimates;
+    @Inject Lazy<VibratorHelper> mVibratorHelper;
+    @Inject Lazy<IStatusBarService> mIStatusBarService;
+    @Inject Lazy<DisplayMetrics> mDisplayMetrics;
+    @Inject Lazy<LockscreenGestureLogger> mLockscreenGestureLogger;
+    @Inject Lazy<KeyguardEnvironment> mKeyguardEnvironment;
+    @Inject Lazy<ShadeController> mShadeController;
+    @Inject Lazy<NotificationRemoteInputManager.Callback> mNotificationRemoteInputManagerCallback;
+    @Inject Lazy<InitController> mInitController;
+    @Inject Lazy<AppOpsController> mAppOpsController;
+    @Inject Lazy<NavigationBarController> mNavigationBarController;
+    @Inject Lazy<StatusBarStateController> mStatusBarStateController;
+    @Inject Lazy<NotificationLockscreenUserManager> mNotificationLockscreenUserManager;
+    @Inject Lazy<NotificationGroupAlertTransferHelper> mNotificationGroupAlertTransferHelper;
+    @Inject Lazy<NotificationGroupManager> mNotificationGroupManager;
+    @Inject Lazy<VisualStabilityManager> mVisualStabilityManager;
+    @Inject Lazy<NotificationGutsManager> mNotificationGutsManager;
+    @Inject Lazy<NotificationMediaManager> mNotificationMediaManager;
+    @Inject Lazy<AmbientPulseManager> mAmbientPulseManager;
+    @Inject Lazy<NotificationBlockingHelperManager> mNotificationBlockingHelperManager;
+    @Inject Lazy<NotificationRemoteInputManager> mNotificationRemoteInputManager;
+    @Inject Lazy<SmartReplyConstants> mSmartReplyConstants;
+    @Inject Lazy<NotificationListener> mNotificationListener;
+    @Inject Lazy<NotificationLogger> mNotificationLogger;
+    @Inject Lazy<NotificationViewHierarchyManager> mNotificationViewHierarchyManager;
+    @Inject Lazy<NotificationRowBinder> mNotificationRowBinder;
+    @Inject Lazy<NotificationFilter> mNotificationFilter;
+    @Inject Lazy<NotificationInterruptionStateProvider> mNotificationInterruptionStateProvider;
+    @Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil;
+    @Inject Lazy<SmartReplyController> mSmartReplyController;
+    @Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler;
+    @Inject Lazy<BubbleController> mBubbleController;
+    @Inject Lazy<NotificationEntryManager> mNotificationEntryManager;
+    @Inject
+    Lazy<NotificationAlertingManager> mNotificationAlertingManager;
+    @Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager;
+    @Inject Lazy<AutoHideController> mAutoHideController;
+    @Inject Lazy<ForegroundServiceNotificationListener> mForegroundServiceNotificationListener;
+    @Inject @Named(BG_LOOPER_NAME) Lazy<Looper> mBgLooper;
+    @Inject @Named(BG_HANDLER_NAME) Lazy<Handler> mBgHandler;
+    @Inject @Named(MAIN_HANDLER_NAME) Lazy<Handler> mMainHandler;
+    @Inject @Named(TIME_TICK_HANDLER_NAME) Lazy<Handler> mTimeTickHandler;
+    @Nullable
+    @Inject @Named(LEAK_REPORT_EMAIL_NAME) Lazy<String> mLeakReportEmail;
+
+    @Inject
+    public Dependency() {
+    }
+
     @Override
     public void start() {
         // TODO: Think about ways to push these creation rules out of Dependency to cut down
         // on imports.
-        mProviders.put(TIME_TICK_HANDLER, () -> {
-            HandlerThread thread = new HandlerThread("TimeTick");
-            thread.start();
-            return new Handler(thread.getLooper());
-        });
-        mProviders.put(BG_LOOPER, () -> {
-            HandlerThread thread = new HandlerThread("SysUiBg",
-                    Process.THREAD_PRIORITY_BACKGROUND);
-            thread.start();
-            return thread.getLooper();
-        });
-        mProviders.put(BG_HANDLER, () -> new Handler(getDependency(BG_LOOPER)));
-        mProviders.put(MAIN_HANDLER, () -> new Handler(Looper.getMainLooper()));
-        mProviders.put(ActivityStarter.class, () -> new ActivityStarterDelegate());
-        mProviders.put(ActivityStarterDelegate.class, () ->
-                getDependency(ActivityStarter.class));
+        mProviders.put(TIME_TICK_HANDLER, mTimeTickHandler::get);
+        mProviders.put(BG_LOOPER, mBgLooper::get);
+        mProviders.put(BG_HANDLER, mBgHandler::get);
+        mProviders.put(MAIN_HANDLER, mMainHandler::get);
+        mProviders.put(ActivityStarter.class, mActivityStarter::get);
+        mProviders.put(ActivityStarterDelegate.class, mActivityStarterDelegate::get);
 
-        mProviders.put(AsyncSensorManager.class, () ->
-                new AsyncSensorManager(mContext.getSystemService(SensorManager.class),
-                        getDependency(PluginManager.class)));
+        mProviders.put(AsyncSensorManager.class, mAsyncSensorManager::get);
 
-        mProviders.put(BluetoothController.class, () ->
-                new BluetoothControllerImpl(mContext, getDependency(BG_LOOPER)));
+        mProviders.put(BluetoothController.class, mBluetoothController::get);
+        mProviders.put(SensorPrivacyManager.class, mSensorPrivacyManager::get);
 
-        mProviders.put(LocationController.class, () ->
-                new LocationControllerImpl(mContext, getDependency(BG_LOOPER)));
+        mProviders.put(LocationController.class, mLocationController::get);
 
-        mProviders.put(RotationLockController.class, () ->
-                new RotationLockControllerImpl(mContext));
+        mProviders.put(RotationLockController.class, mRotationLockController::get);
 
-        mProviders.put(NetworkController.class, () ->
-                new NetworkControllerImpl(mContext, getDependency(BG_LOOPER),
-                        getDependency(DeviceProvisionedController.class)));
+        mProviders.put(NetworkController.class, mNetworkController::get);
 
-        mProviders.put(ZenModeController.class, () ->
-                new ZenModeControllerImpl(mContext, getDependency(MAIN_HANDLER)));
+        mProviders.put(ZenModeController.class, mZenModeController::get);
 
-        mProviders.put(HotspotController.class, () ->
-                new HotspotControllerImpl(mContext));
+        mProviders.put(HotspotController.class, mHotspotController::get);
 
-        mProviders.put(CastController.class, () ->
-                new CastControllerImpl(mContext));
+        mProviders.put(CastController.class, mCastController::get);
 
-        mProviders.put(FlashlightController.class, () ->
-                new FlashlightControllerImpl(mContext));
+        mProviders.put(FlashlightController.class, mFlashlightController::get);
 
-        mProviders.put(KeyguardMonitor.class, () ->
-                new KeyguardMonitorImpl(mContext));
+        mProviders.put(KeyguardMonitor.class, mKeyguardMonitor::get);
 
-        mProviders.put(UserSwitcherController.class, () ->
-                new UserSwitcherController(mContext, getDependency(KeyguardMonitor.class),
-                        getDependency(MAIN_HANDLER), getDependency(ActivityStarter.class)));
+        mProviders.put(UserSwitcherController.class, mUserSwitcherController::get);
 
-        mProviders.put(UserInfoController.class, () ->
-                new UserInfoControllerImpl(mContext));
+        mProviders.put(UserInfoController.class, mUserInfoController::get);
 
-        mProviders.put(BatteryController.class, () ->
-                new BatteryControllerImpl(mContext));
+        mProviders.put(BatteryController.class, mBatteryController::get);
 
-        mProviders.put(ColorDisplayController.class, () ->
-                new ColorDisplayController(mContext));
+        mProviders.put(ColorDisplayController.class, mColorDisplayController::get);
 
-        mProviders.put(ManagedProfileController.class, () ->
-                new ManagedProfileControllerImpl(mContext));
+        mProviders.put(ManagedProfileController.class, mManagedProfileController::get);
 
-        mProviders.put(NextAlarmController.class, () ->
-                new NextAlarmControllerImpl(mContext));
+        mProviders.put(NextAlarmController.class, mNextAlarmController::get);
 
-        mProviders.put(DataSaverController.class, () ->
-                get(NetworkController.class).getDataSaverController());
+        mProviders.put(DataSaverController.class, mDataSaverController::get);
 
-        mProviders.put(AccessibilityController.class, () ->
-                new AccessibilityController(mContext));
+        mProviders.put(AccessibilityController.class, mAccessibilityController::get);
 
-        mProviders.put(DeviceProvisionedController.class, () ->
-                new DeviceProvisionedControllerImpl(mContext));
+        mProviders.put(DeviceProvisionedController.class, mDeviceProvisionedController::get);
 
-        mProviders.put(PluginManager.class, () ->
-                new PluginManagerImpl(mContext, new PluginInitializerImpl()));
+        mProviders.put(PluginManager.class, mPluginManager::get);
 
-        mProviders.put(AssistManager.class, () ->
-                new AssistManager(getDependency(DeviceProvisionedController.class), mContext));
+        mProviders.put(AssistManager.class, mAssistManager::get);
 
-        mProviders.put(SecurityController.class, () ->
-                new SecurityControllerImpl(mContext));
+        mProviders.put(SecurityController.class, mSecurityController::get);
 
-        mProviders.put(LeakDetector.class, LeakDetector::create);
+        mProviders.put(LeakDetector.class, mLeakDetector::get);
 
-        mProviders.put(LEAK_REPORT_EMAIL, () -> null);
+        mProviders.put(LEAK_REPORT_EMAIL, mLeakReportEmail::get);
 
-        mProviders.put(LeakReporter.class, () -> new LeakReporter(
-                mContext,
-                getDependency(LeakDetector.class),
-                getDependency(LEAK_REPORT_EMAIL)));
+        mProviders.put(LeakReporter.class, mLeakReporter::get);
 
-        mProviders.put(
-                GarbageMonitor.class,
-                () ->
-                        new GarbageMonitor(
-                                mContext,
-                                getDependency(BG_LOOPER),
-                                getDependency(LeakDetector.class),
-                                getDependency(LeakReporter.class)));
+        mProviders.put(GarbageMonitor.class, mGarbageMonitor::get);
 
-        mProviders.put(TunerService.class, () ->
-                new TunerServiceImpl(mContext));
+        mProviders.put(TunerService.class, mTunerService::get);
 
-        mProviders.put(StatusBarWindowController.class, () ->
-                new StatusBarWindowController(mContext));
+        mProviders.put(StatusBarWindowController.class, mStatusBarWindowController::get);
 
-        mProviders.put(DarkIconDispatcher.class, () ->
-                new DarkIconDispatcherImpl(mContext));
+        mProviders.put(DarkIconDispatcher.class, mDarkIconDispatcher::get);
 
-        mProviders.put(ConfigurationController.class, () ->
-                new ConfigurationControllerImpl(mContext));
+        mProviders.put(ConfigurationController.class, mConfigurationController::get);
 
-        mProviders.put(StatusBarIconController.class, () ->
-                new StatusBarIconControllerImpl(mContext));
+        mProviders.put(StatusBarIconController.class, mStatusBarIconController::get);
 
-        mProviders.put(ScreenLifecycle.class, () ->
-                new ScreenLifecycle());
+        mProviders.put(ScreenLifecycle.class, mScreenLifecycle::get);
 
-        mProviders.put(WakefulnessLifecycle.class, () ->
-                new WakefulnessLifecycle());
+        mProviders.put(WakefulnessLifecycle.class, mWakefulnessLifecycle::get);
 
-        mProviders.put(FragmentService.class, () ->
-                new FragmentService());
+        mProviders.put(FragmentService.class, mFragmentService::get);
 
-        mProviders.put(ExtensionController.class, () ->
-                new ExtensionControllerImpl(mContext));
+        mProviders.put(ExtensionController.class, mExtensionController::get);
 
-        mProviders.put(PluginDependencyProvider.class, () ->
-                new PluginDependencyProvider(get(PluginManager.class)));
+        mProviders.put(PluginDependencyProvider.class, mPluginDependencyProvider::get);
 
-        mProviders.put(LocalBluetoothManager.class, () ->
-                LocalBluetoothManager.create(mContext, getDependency(BG_HANDLER),
-                        UserHandle.ALL));
+        mProviders.put(LocalBluetoothManager.class, mLocalBluetoothManager::get);
 
-        mProviders.put(VolumeDialogController.class, () ->
-                new VolumeDialogControllerImpl(mContext));
+        mProviders.put(VolumeDialogController.class, mVolumeDialogController::get);
 
-        mProviders.put(MetricsLogger.class, () -> new MetricsLogger());
+        mProviders.put(MetricsLogger.class, mMetricsLogger::get);
 
-        mProviders.put(AccessibilityManagerWrapper.class,
-                () -> new AccessibilityManagerWrapper(mContext));
+        mProviders.put(AccessibilityManagerWrapper.class, mAccessibilityManagerWrapper::get);
 
-        // Creating a new instance will trigger color extraction.
-        // Thankfully this only happens once - during boot - and WallpaperManagerService
-        // loads colors from cache.
-        mProviders.put(SysuiColorExtractor.class, () -> new SysuiColorExtractor(mContext));
+        mProviders.put(SysuiColorExtractor.class, mSysuiColorExtractor::get);
 
-        mProviders.put(TunablePaddingService.class, () -> new TunablePaddingService());
+        mProviders.put(TunablePaddingService.class, mTunablePaddingService::get);
 
-        mProviders.put(ForegroundServiceController.class,
-                () -> new ForegroundServiceControllerImpl(mContext));
+        mProviders.put(ForegroundServiceController.class, mForegroundServiceController::get);
 
-        mProviders.put(UiOffloadThread.class, UiOffloadThread::new);
+        mProviders.put(UiOffloadThread.class, mUiOffloadThread::get);
 
-        mProviders.put(PowerUI.WarningsUI.class, () -> new PowerNotificationWarnings(mContext));
+        mProviders.put(PowerUI.WarningsUI.class, mWarningsUI::get);
 
-        mProviders.put(IconLogger.class, () -> new IconLoggerImpl(mContext,
-                getDependency(BG_LOOPER), getDependency(MetricsLogger.class)));
+        mProviders.put(IconLogger.class, mIconLogger::get);
 
-        mProviders.put(LightBarController.class, () -> new LightBarController(mContext));
+        mProviders.put(LightBarController.class, mLightBarController::get);
 
-        mProviders.put(IWindowManager.class, () -> WindowManagerGlobal.getWindowManagerService());
+        mProviders.put(IWindowManager.class, mIWindowManager::get);
 
-        mProviders.put(OverviewProxyService.class, () -> new OverviewProxyService(mContext));
+        mProviders.put(OverviewProxyService.class, mOverviewProxyService::get);
 
-        mProviders.put(EnhancedEstimates.class, () -> new EnhancedEstimatesImpl());
+        mProviders.put(EnhancedEstimates.class, mEnhancedEstimates::get);
 
-        mProviders.put(VibratorHelper.class, () -> new VibratorHelper(mContext));
+        mProviders.put(VibratorHelper.class, mVibratorHelper::get);
 
-        mProviders.put(IStatusBarService.class, () -> IStatusBarService.Stub.asInterface(
-                ServiceManager.getService(Context.STATUS_BAR_SERVICE)));
+        mProviders.put(IStatusBarService.class, mIStatusBarService::get);
 
-        // Single instance of DisplayMetrics, gets updated by StatusBar, but can be used
-        // anywhere it is needed.
-        mProviders.put(DisplayMetrics.class, () -> new DisplayMetrics());
+        mProviders.put(DisplayMetrics.class, mDisplayMetrics::get);
 
-        mProviders.put(LockscreenGestureLogger.class, () -> new LockscreenGestureLogger());
+        mProviders.put(LockscreenGestureLogger.class, mLockscreenGestureLogger::get);
 
-        mProviders.put(KeyguardEnvironment.class, () -> new KeyguardEnvironmentImpl());
-        mProviders.put(ShadeController.class, () ->
-                SysUiServiceProvider.getComponent(mContext, StatusBar.class));
+        mProviders.put(KeyguardEnvironment.class, mKeyguardEnvironment::get);
+        mProviders.put(ShadeController.class, mShadeController::get);
         mProviders.put(NotificationRemoteInputManager.Callback.class,
-                () -> new StatusBarRemoteInputCallback(mContext));
+                mNotificationRemoteInputManagerCallback::get);
 
-        mProviders.put(InitController.class, InitController::new);
+        mProviders.put(InitController.class, mInitController::get);
 
-        mProviders.put(AppOpsController.class, () ->
-                new AppOpsControllerImpl(mContext, getDependency(BG_LOOPER)));
+        mProviders.put(AppOpsController.class, mAppOpsController::get);
 
-        mProviders.put(DisplayNavigationBarController.class, () ->
-                new DisplayNavigationBarController(mContext, getDependency(MAIN_HANDLER)));
+        mProviders.put(NavigationBarController.class, mNavigationBarController::get);
 
-        // Put all dependencies above here so the factory can override them if it wants.
-        SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
+        mProviders.put(StatusBarStateController.class, mStatusBarStateController::get);
+        mProviders.put(NotificationLockscreenUserManager.class,
+                mNotificationLockscreenUserManager::get);
+        mProviders.put(VisualStabilityManager.class, mVisualStabilityManager::get);
+        mProviders.put(NotificationGroupManager.class, mNotificationGroupManager::get);
+        mProviders.put(NotificationGroupAlertTransferHelper.class,
+                mNotificationGroupAlertTransferHelper::get);
+        mProviders.put(NotificationMediaManager.class, mNotificationMediaManager::get);
+        mProviders.put(NotificationGutsManager.class, mNotificationGutsManager::get);
+        mProviders.put(AmbientPulseManager.class, mAmbientPulseManager::get);
+        mProviders.put(NotificationBlockingHelperManager.class,
+                mNotificationBlockingHelperManager::get);
+        mProviders.put(NotificationRemoteInputManager.class,
+                mNotificationRemoteInputManager::get);
+        mProviders.put(SmartReplyConstants.class, mSmartReplyConstants::get);
+        mProviders.put(NotificationListener.class, mNotificationListener::get);
+        mProviders.put(NotificationLogger.class, mNotificationLogger::get);
+        mProviders.put(NotificationViewHierarchyManager.class,
+                mNotificationViewHierarchyManager::get);
+        mProviders.put(NotificationRowBinder.class, mNotificationRowBinder::get);
+        mProviders.put(NotificationFilter.class, mNotificationFilter::get);
+        mProviders.put(NotificationInterruptionStateProvider.class,
+                mNotificationInterruptionStateProvider::get);
+        mProviders.put(KeyguardDismissUtil.class, mKeyguardDismissUtil::get);
+        mProviders.put(SmartReplyController.class, mSmartReplyController::get);
+        mProviders.put(RemoteInputQuickSettingsDisabler.class,
+                mRemoteInputQuickSettingsDisabler::get);
+        mProviders.put(BubbleController.class, mBubbleController::get);
+        mProviders.put(NotificationEntryManager.class, mNotificationEntryManager::get);
+        mProviders.put(NotificationAlertingManager.class, mNotificationAlertingManager::get);
+        mProviders.put(ForegroundServiceNotificationListener.class,
+                mForegroundServiceNotificationListener::get);
+
+        // TODO(b/118592525): to support multi-display , we start to add something which is
+        //                    per-display, while others may be global. I think it's time to add
+        //                    a new class maybe named DisplayDependency to solve per-display
+        //                    Dependency problem.
+        mProviders.put(AutoHideController.class, mAutoHideController::get);
 
         sDependency = this;
     }
@@ -437,6 +528,9 @@
     public static void initDependencies(Context context) {
         if (sDependency != null) return;
         Dependency d = new Dependency();
+        SystemUIFactory.getInstance().getRootComponent()
+                .createDependency()
+                .createSystemUI(d);
         d.mContext = context;
         d.mComponents = new HashMap<>();
         d.start();
@@ -460,10 +554,18 @@
         sDependency.destroyDependency(cls, destroy);
     }
 
+    /**
+     * @deprecated see docs/dagger.md
+     */
+    @Deprecated
     public static <T> T get(Class<T> cls) {
         return sDependency.getDependency(cls);
     }
 
+    /**
+     * @deprecated see docs/dagger.md
+     */
+    @Deprecated
     public static <T> T get(DependencyKey<T> cls) {
         return sDependency.getDependency(cls);
     }
@@ -480,4 +582,20 @@
             return mDisplayName;
         }
     }
+
+    @Subcomponent
+    public interface DependencyInjector {
+        void createSystemUI(Dependency dependency);
+    }
+
+    public static class DependencyCreator implements Injector {
+        @Override
+        public SystemUI apply(Context context) {
+            Dependency dependency = new Dependency();
+            SystemUIFactory.getInstance().getRootComponent()
+                    .createDependency()
+                    .createSystemUI(dependency);
+            return dependency;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
new file mode 100644
index 0000000..f324a05b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import com.android.systemui.appops.AppOpsController;
+import com.android.systemui.appops.AppOpsControllerImpl;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.VolumeDialogController;
+import com.android.systemui.power.PowerNotificationWarnings;
+import com.android.systemui.power.PowerUI;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
+import com.android.systemui.statusbar.phone.ManagedProfileController;
+import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
+import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BatteryControllerImpl;
+import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.CastControllerImpl;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
+import com.android.systemui.statusbar.policy.ExtensionController;
+import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
+import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
+import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.HotspotControllerImpl;
+import com.android.systemui.statusbar.policy.IconLogger;
+import com.android.systemui.statusbar.policy.IconLoggerImpl;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.LocationControllerImpl;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkControllerImpl;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
+import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.statusbar.policy.SecurityControllerImpl;
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerServiceImpl;
+import com.android.systemui.volume.VolumeDialogControllerImpl;
+
+import dagger.Binds;
+import dagger.Module;
+
+/**
+ * Maps interfaces to implementations for use with Dagger.
+ */
+@Module
+public abstract class DependencyBinder {
+
+    /**
+     */
+    @Binds
+    public abstract ActivityStarter provideActivityStarter(ActivityStarterDelegate delegate);
+
+    /**
+     */
+    @Binds
+    public abstract BluetoothController provideBluetoothController(
+            BluetoothControllerImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract LocationController provideLocationController(
+            LocationControllerImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract RotationLockController provideRotationLockController(
+            RotationLockControllerImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract NetworkController provideNetworkController(
+            NetworkControllerImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract ZenModeController provideZenModeController(
+            ZenModeControllerImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract HotspotController provideHotspotController(
+            HotspotControllerImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract AppOpsController provideAppOpsController(
+            AppOpsControllerImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract NotificationRemoteInputManager.Callback provideNotificationRemoteInputManager(
+            StatusBarRemoteInputCallback callbackImpl);
+
+    /**
+     */
+    @Binds
+    public abstract IconLogger provideIconLogger(IconLoggerImpl loggerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract CastController provideCastController(CastControllerImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract FlashlightController provideFlashlightController(
+            FlashlightControllerImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract KeyguardMonitor provideKeyguardMonitor(KeyguardMonitorImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract UserInfoController provideUserInfoContrller(
+            UserInfoControllerImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract BatteryController provideBatteryController(
+            BatteryControllerImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract ManagedProfileController provideManagedProfileController(
+            ManagedProfileControllerImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract NextAlarmController provideNextAlarmController(
+            NextAlarmControllerImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract DeviceProvisionedController provideDeviceProvisionedController(
+            DeviceProvisionedControllerImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract SecurityController provideSecurityController(
+            SecurityControllerImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract TunerService provideTunerService(TunerServiceImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract DarkIconDispatcher provideDarkIconDispatcher(
+            DarkIconDispatcherImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract StatusBarIconController provideStatusBarIconController(
+            StatusBarIconControllerImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract ExtensionController provideExtensionController(
+            ExtensionControllerImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract VolumeDialogController provideVolumeDialogController(
+            VolumeDialogControllerImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract PowerUI.WarningsUI provideWarningsUi(PowerNotificationWarnings controllerImpl);
+
+    /**
+     */
+    @Binds
+    public abstract QSHost provideQsHost(QSTileHost controllerImpl);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
new file mode 100644
index 0000000..88e32cb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import static com.android.systemui.Dependency.BG_HANDLER_NAME;
+import static com.android.systemui.Dependency.BG_LOOPER_NAME;
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.SensorPrivacyManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Process;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.DisplayMetrics;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+
+import com.android.internal.app.ColorDisplayController;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.systemui.plugins.PluginInitializerImpl;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManagerImpl;
+import com.android.systemui.statusbar.NavigationBarController;
+import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.util.leak.LeakDetector;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Provides dependencies for the root component of sysui injection.
+ * See SystemUI/docs/dagger.md
+ */
+@Module
+public class DependencyProvider {
+
+    @Singleton
+    @Provides
+    @Named(TIME_TICK_HANDLER_NAME)
+    public Handler provideHandler() {
+        HandlerThread thread = new HandlerThread("TimeTick");
+        thread.start();
+        return new Handler(thread.getLooper());
+    }
+
+    @Singleton
+    @Provides
+    @Named(BG_LOOPER_NAME)
+    public Looper provideBgLooper() {
+        HandlerThread thread = new HandlerThread("SysUiBg",
+                Process.THREAD_PRIORITY_BACKGROUND);
+        thread.start();
+        return thread.getLooper();
+    }
+
+    @Singleton
+    @Provides
+    @Named(BG_HANDLER_NAME)
+    public Handler provideBgHandler(@Named(BG_LOOPER_NAME) Looper bgLooper) {
+        return new Handler(bgLooper);
+    }
+
+    @Singleton
+    @Provides
+    @Named(MAIN_HANDLER_NAME)
+    public Handler provideMainHandler() {
+        return new Handler(Looper.getMainLooper());
+    }
+
+    @Singleton
+    @Provides
+    public DataSaverController provideDataSaverController(NetworkController networkController) {
+        return networkController.getDataSaverController();
+    }
+
+    @Singleton
+    @Provides
+    @Nullable
+    public LocalBluetoothManager provideLocalBluetoothController(Context context,
+            @Named(BG_HANDLER_NAME) Handler bgHandler) {
+        return LocalBluetoothManager.create(context, bgHandler,
+                UserHandle.ALL);
+    }
+
+    @Singleton
+    @Provides
+    public MetricsLogger provideMetricsLogger() {
+        return new MetricsLogger();
+    }
+
+    @Singleton
+    @Provides
+    public IWindowManager provideIWindowManager() {
+        return WindowManagerGlobal.getWindowManagerService();
+    }
+
+    @Singleton
+    @Provides
+    public IStatusBarService provideIStatusBarService() {
+        return IStatusBarService.Stub.asInterface(
+                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+    }
+
+    @Singleton
+    @Provides
+    // Single instance of DisplayMetrics, gets updated by StatusBar, but can be used
+    // anywhere it is needed.
+    public DisplayMetrics provideDisplayMetrics() {
+        return new DisplayMetrics();
+    }
+
+    @Singleton
+    @Provides
+    public SensorPrivacyManager provideSensorPrivacyManager(Context context) {
+        return context.getSystemService(SensorPrivacyManager.class);
+    }
+
+    @Singleton
+    @Provides
+    public LeakDetector provideLeakDetector() {
+        return LeakDetector.create();
+
+    }
+
+    @Singleton
+    @Provides
+    public ColorDisplayController provideColorDisplayController(Context context) {
+        return new ColorDisplayController(context);
+    }
+
+    @Singleton
+    @Provides
+    public PluginManager providePluginManager(Context context) {
+        return new PluginManagerImpl(context, new PluginInitializerImpl());
+    }
+
+    @Singleton
+    @Provides
+    public NavigationBarController provideNavigationBarController(Context context,
+            @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+        return new NavigationBarController(context, mainHandler);
+    }
+
+    @Singleton
+    @Provides
+    public ConfigurationController provideConfigurationController(Context context) {
+        return new ConfigurationControllerImpl(context);
+    }
+
+    @Singleton
+    @Provides
+    public AutoHideController provideAutoHideController(Context context,
+            @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+        return new AutoHideController(context, mainHandler);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
index ae6ee2a..df0d787 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
@@ -15,65 +15,158 @@
 package com.android.systemui;
 
 import android.annotation.Nullable;
+import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.util.ArraySet;
+import android.util.SparseArray;
 
-public interface ForegroundServiceController {
-    /**
-     * @param sbn notification that was just posted
-     * @param importance
-     */
-    void addNotification(StatusBarNotification sbn, int importance);
+import com.android.internal.messages.nano.SystemMessageProto;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Tracks state of foreground services and notifications related to foreground services per user.
+ */
+@Singleton
+public class ForegroundServiceController {
+
+    private final SparseArray<ForegroundServicesUserState> mUserServices = new SparseArray<>();
+    private final Object mMutex = new Object();
+
+    @Inject
+    public ForegroundServiceController() {
+    }
 
     /**
-     * @param sbn notification that was just changed in some way
-     * @param newImportance
-     */
-    void updateNotification(StatusBarNotification sbn, int newImportance);
-
-    /**
-     * @param sbn notification that was just canceled
-     */
-    boolean removeNotification(StatusBarNotification sbn);
-
-    /**
-     * @param userId
      * @return true if this user has services missing notifications and therefore needs a
      * disclosure notification.
      */
-    boolean isDungeonNeededForUser(int userId);
-
-    /**
-     * @param sbn
-     * @return true if sbn is the system-provided "dungeon" (list of running foreground services).
-     */
-    boolean isDungeonNotification(StatusBarNotification sbn);
-
-    /**
-     * @return true if sbn is one of the window manager "drawing over other apps" notifications
-     */
-    boolean isSystemAlertNotification(StatusBarNotification sbn);
-
-    /**
-     * Returns the key of the foreground service from this package using the standard template,
-     * if one exists.
-     */
-    @Nullable String getStandardLayoutKey(int userId, String pkg);
+    public boolean isDisclosureNeededForUser(int userId) {
+        synchronized (mMutex) {
+            final ForegroundServicesUserState services = mUserServices.get(userId);
+            if (services == null) return false;
+            return services.isDisclosureNeeded();
+        }
+    }
 
     /**
      * @return true if this user/pkg has a missing or custom layout notification and therefore needs
      * a disclosure notification for system alert windows.
      */
-    boolean isSystemAlertWarningNeeded(int userId, String pkg);
+    public boolean isSystemAlertWarningNeeded(int userId, String pkg) {
+        synchronized (mMutex) {
+            final ForegroundServicesUserState services = mUserServices.get(userId);
+            if (services == null) return false;
+            return services.getStandardLayoutKey(pkg) == null;
+        }
+    }
+
+    /**
+     * Returns the key of the foreground service from this package using the standard template,
+     * if one exists.
+     */
+    @Nullable
+    public String getStandardLayoutKey(int userId, String pkg) {
+        synchronized (mMutex) {
+            final ForegroundServicesUserState services = mUserServices.get(userId);
+            if (services == null) return null;
+            return services.getStandardLayoutKey(pkg);
+        }
+    }
+
+    /**
+     * Gets active app ops for this user and package
+     */
+    @Nullable
+    public ArraySet<Integer> getAppOps(int userId, String pkg) {
+        synchronized (mMutex) {
+            final ForegroundServicesUserState services = mUserServices.get(userId);
+            if (services == null) {
+                return null;
+            }
+            return services.getFeatures(pkg);
+        }
+    }
 
     /**
      * Records active app ops. App Ops are stored in FSC in addition to NotificationData in
      * case they change before we have a notification to tag.
      */
-    void onAppOpChanged(int code, int uid, String packageName, boolean active);
+    public void onAppOpChanged(int code, int uid, String packageName, boolean active) {
+        int userId = UserHandle.getUserId(uid);
+        synchronized (mMutex) {
+            ForegroundServicesUserState userServices = mUserServices.get(userId);
+            if (userServices == null) {
+                userServices = new ForegroundServicesUserState();
+                mUserServices.put(userId, userServices);
+            }
+            if (active) {
+                userServices.addOp(packageName, code);
+            } else {
+                userServices.removeOp(packageName, code);
+            }
+        }
+    }
 
     /**
-     * Gets active app ops for this user and package
+     * Looks up the {@link ForegroundServicesUserState} for the given {@code userId}, then performs
+     * the given {@link UserStateUpdateCallback} on it.  If no state exists for the user ID, creates
+     * a new one if {@code createIfNotFound} is true, then performs the update on the new state.
+     * If {@code createIfNotFound} is false, no update is performed.
+     *
+     * @return false if no user state was found and none was created; true otherwise.
      */
-    @Nullable ArraySet<Integer> getAppOps(int userId, String packageName);
+    boolean updateUserState(int userId,
+            UserStateUpdateCallback updateCallback,
+            boolean createIfNotFound) {
+        synchronized (mMutex) {
+            ForegroundServicesUserState userState = mUserServices.get(userId);
+            if (userState == null) {
+                if (createIfNotFound) {
+                    userState = new ForegroundServicesUserState();
+                    mUserServices.put(userId, userState);
+                } else {
+                    return false;
+                }
+            }
+            return updateCallback.updateUserState(userState);
+        }
+    }
+
+    /**
+     * @return true if {@code sbn} is the system-provided disclosure notification containing the
+     * list of running foreground services.
+     */
+    public boolean isDisclosureNotification(StatusBarNotification sbn) {
+        return sbn.getId() == SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICES
+                && sbn.getTag() == null
+                && sbn.getPackageName().equals("android");
+    }
+
+    /**
+     * @return true if sbn is one of the window manager "drawing over other apps" notifications
+     */
+    public boolean isSystemAlertNotification(StatusBarNotification sbn) {
+        return sbn.getPackageName().equals("android")
+                && sbn.getTag() != null
+                && sbn.getTag().contains("AlertWindowNotification");
+    }
+
+    /**
+     * Callback provided to {@link #updateUserState(int, UserStateUpdateCallback, boolean)}
+     * to perform the update.
+     */
+    interface UserStateUpdateCallback {
+        /**
+         * Perform update operations on the provided {@code userState}.
+         *
+         * @return true if the update succeeded.
+         */
+        boolean updateUserState(ForegroundServicesUserState userState);
+
+        /** Called if the state was not found and was not created. */
+        default void userStateNotFound(int userId) {
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java
deleted file mode 100644
index bab472c..0000000
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Copyright (C) 2017 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;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.internal.messages.nano.SystemMessageProto;
-
-import java.util.Arrays;
-
-/**
- * Foreground service controller, a/k/a Dianne's Dungeon.
- */
-public class ForegroundServiceControllerImpl
-        implements ForegroundServiceController {
-
-    // shelf life of foreground services before they go bad
-    public static final long FG_SERVICE_GRACE_MILLIS = 5000;
-
-    private static final String TAG = "FgServiceController";
-    private static final boolean DBG = false;
-
-    private final Context mContext;
-    private final SparseArray<UserServices> mUserServices = new SparseArray<>();
-    private final Object mMutex = new Object();
-
-    public ForegroundServiceControllerImpl(Context context) {
-        mContext = context;
-    }
-
-    @Override
-    public boolean isDungeonNeededForUser(int userId) {
-        synchronized (mMutex) {
-            final UserServices services = mUserServices.get(userId);
-            if (services == null) return false;
-            return services.isDungeonNeeded();
-        }
-    }
-
-    @Override
-    public boolean isSystemAlertWarningNeeded(int userId, String pkg) {
-        synchronized (mMutex) {
-            final UserServices services = mUserServices.get(userId);
-            if (services == null) return false;
-            return services.getStandardLayoutKey(pkg) == null;
-        }
-    }
-
-    @Override
-    public String getStandardLayoutKey(int userId, String pkg) {
-        synchronized (mMutex) {
-            final UserServices services = mUserServices.get(userId);
-            if (services == null) return null;
-            return services.getStandardLayoutKey(pkg);
-        }
-    }
-
-    @Override
-    public ArraySet<Integer> getAppOps(int userId, String pkg) {
-        synchronized (mMutex) {
-            final UserServices services = mUserServices.get(userId);
-            if (services == null) {
-                return null;
-            }
-            return services.getFeatures(pkg);
-        }
-    }
-
-    @Override
-    public void onAppOpChanged(int code, int uid, String packageName, boolean active) {
-        int userId = UserHandle.getUserId(uid);
-        synchronized (mMutex) {
-            UserServices userServices = mUserServices.get(userId);
-            if (userServices == null) {
-                userServices = new UserServices();
-                mUserServices.put(userId, userServices);
-            }
-            if (active) {
-                userServices.addOp(packageName, code);
-            } else {
-                userServices.removeOp(packageName, code);
-            }
-        }
-    }
-
-    @Override
-    public void addNotification(StatusBarNotification sbn, int importance) {
-        updateNotification(sbn, importance);
-    }
-
-    @Override
-    public boolean removeNotification(StatusBarNotification sbn) {
-        synchronized (mMutex) {
-            final UserServices userServices = mUserServices.get(sbn.getUserId());
-            if (userServices == null) {
-                if (DBG) {
-                    Log.w(TAG, String.format(
-                            "user %d with no known notifications got removeNotification for %s",
-                            sbn.getUserId(), sbn));
-                }
-                return false;
-            }
-            if (isDungeonNotification(sbn)) {
-                // if you remove the dungeon entirely, we take that to mean there are
-                // no running services
-                userServices.setRunningServices(null, 0);
-                return true;
-            } else {
-                // this is safe to call on any notification, not just FLAG_FOREGROUND_SERVICE
-                return userServices.removeNotification(sbn.getPackageName(), sbn.getKey());
-            }
-        }
-    }
-
-    @Override
-    public void updateNotification(StatusBarNotification sbn, int newImportance) {
-        synchronized (mMutex) {
-            UserServices userServices = mUserServices.get(sbn.getUserId());
-            if (userServices == null) {
-                userServices = new UserServices();
-                mUserServices.put(sbn.getUserId(), userServices);
-            }
-
-            if (isDungeonNotification(sbn)) {
-                final Bundle extras = sbn.getNotification().extras;
-                if (extras != null) {
-                    final String[] svcs = extras.getStringArray(Notification.EXTRA_FOREGROUND_APPS);
-                    userServices.setRunningServices(svcs, sbn.getNotification().when);
-                }
-            } else {
-                userServices.removeNotification(sbn.getPackageName(), sbn.getKey());
-                if (0 != (sbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE)) {
-                    if (newImportance > NotificationManager.IMPORTANCE_MIN) {
-                        userServices.addImportantNotification(sbn.getPackageName(), sbn.getKey());
-                    }
-                    final Notification.Builder builder = Notification.Builder.recoverBuilder(
-                            mContext, sbn.getNotification());
-                    if (builder.usesStandardHeader()) {
-                        userServices.addStandardLayoutNotification(
-                                sbn.getPackageName(), sbn.getKey());
-                    }
-                }
-            }
-        }
-    }
-
-    @Override
-    public boolean isDungeonNotification(StatusBarNotification sbn) {
-        return sbn.getId() == SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICES
-                && sbn.getTag() == null
-                && sbn.getPackageName().equals("android");
-    }
-
-    @Override
-    public boolean isSystemAlertNotification(StatusBarNotification sbn) {
-        return sbn.getPackageName().equals("android")
-                && sbn.getTag() != null
-                && sbn.getTag().contains("AlertWindowNotification");
-    }
-
-    /**
-     * Struct to track relevant packages and notifications for a userid's foreground services.
-     */
-    private static class UserServices {
-        private String[] mRunning = null;
-        private long mServiceStartTime = 0;
-        // package -> sufficiently important posted notification keys
-        private ArrayMap<String, ArraySet<String>> mImportantNotifications = new ArrayMap<>(1);
-        // package -> standard layout posted notification keys
-        private ArrayMap<String, ArraySet<String>> mStandardLayoutNotifications = new ArrayMap<>(1);
-
-        // package -> app ops
-        private ArrayMap<String, ArraySet<Integer>> mAppOps = new ArrayMap<>(1);
-
-        public void setRunningServices(String[] pkgs, long serviceStartTime) {
-            mRunning = pkgs != null ? Arrays.copyOf(pkgs, pkgs.length) : null;
-            mServiceStartTime = serviceStartTime;
-        }
-
-        public void addOp(String pkg, int op) {
-            if (mAppOps.get(pkg) == null) {
-                mAppOps.put(pkg, new ArraySet<>(3));
-            }
-            mAppOps.get(pkg).add(op);
-        }
-
-        public boolean removeOp(String pkg, int op) {
-            final boolean found;
-            final ArraySet<Integer> keys = mAppOps.get(pkg);
-            if (keys == null) {
-                found = false;
-            } else {
-                found = keys.remove(op);
-                if (keys.size() == 0) {
-                    mAppOps.remove(pkg);
-                }
-            }
-            return found;
-        }
-
-        public void addImportantNotification(String pkg, String key) {
-            addNotification(mImportantNotifications, pkg, key);
-        }
-
-        public boolean removeImportantNotification(String pkg, String key) {
-            return removeNotification(mImportantNotifications, pkg, key);
-        }
-
-        public void addStandardLayoutNotification(String pkg, String key) {
-            addNotification(mStandardLayoutNotifications, pkg, key);
-        }
-
-        public boolean removeStandardLayoutNotification(String pkg, String key) {
-            return removeNotification(mStandardLayoutNotifications, pkg, key);
-        }
-
-        public boolean removeNotification(String pkg, String key) {
-            boolean removed = false;
-            removed |= removeImportantNotification(pkg, key);
-            removed |= removeStandardLayoutNotification(pkg, key);
-            return removed;
-        }
-
-        public void addNotification(ArrayMap<String, ArraySet<String>> map, String pkg,
-                String key) {
-            if (map.get(pkg) == null) {
-                map.put(pkg, new ArraySet<>());
-            }
-            map.get(pkg).add(key);
-        }
-
-        public boolean removeNotification(ArrayMap<String, ArraySet<String>> map,
-                String pkg, String key) {
-            final boolean found;
-            final ArraySet<String> keys = map.get(pkg);
-            if (keys == null) {
-                found = false;
-            } else {
-                found = keys.remove(key);
-                if (keys.size() == 0) {
-                    map.remove(pkg);
-                }
-            }
-            return found;
-        }
-
-        public boolean isDungeonNeeded() {
-            if (mRunning != null
-                && System.currentTimeMillis() - mServiceStartTime >= FG_SERVICE_GRACE_MILLIS) {
-
-                for (String pkg : mRunning) {
-                    final ArraySet<String> set = mImportantNotifications.get(pkg);
-                    if (set == null || set.size() == 0) {
-                        return true;
-                    }
-                }
-            }
-            return false;
-        }
-
-        public ArraySet<Integer> getFeatures(String pkg) {
-            return mAppOps.get(pkg);
-        }
-
-        public String getStandardLayoutKey(String pkg) {
-            final ArraySet<String> set = mStandardLayoutNotifications.get(pkg);
-            if (set == null || set.size() == 0) {
-                return null;
-            }
-            return set.valueAt(0);
-        }
-
-        @Override
-        public String toString() {
-            return "UserServices{" +
-                    "mRunning=" + Arrays.toString(mRunning) +
-                    ", mServiceStartTime=" + mServiceStartTime +
-                    ", mImportantNotifications=" + mImportantNotifications +
-                    ", mStandardLayoutNotifications=" + mStandardLayoutNotifications +
-                    '}';
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
new file mode 100644
index 0000000..96f216e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Bundle;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** Updates foreground service notification state in response to notification data events. */
+@Singleton
+public class ForegroundServiceNotificationListener {
+
+    private static final String TAG = "FgServiceController";
+    private static final boolean DBG = false;
+
+    private final Context mContext;
+    private final ForegroundServiceController mForegroundServiceController;
+
+    @Inject
+    public ForegroundServiceNotificationListener(Context context,
+            ForegroundServiceController foregroundServiceController,
+            NotificationEntryManager notificationEntryManager) {
+        mContext = context;
+        mForegroundServiceController = foregroundServiceController;
+        notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
+            @Override
+            public void onPendingEntryAdded(NotificationEntry entry) {
+                addNotification(entry.notification, entry.importance);
+            }
+
+            @Override
+            public void onEntryUpdated(NotificationEntry entry) {
+                updateNotification(entry.notification, entry.importance);
+            }
+
+            @Override
+            public void onEntryRemoved(
+                    NotificationEntry entry,
+                    NotificationVisibility visibility,
+                    boolean removedByUser) {
+                removeNotification(entry.notification);
+            }
+        });
+    }
+
+    /**
+     * @param sbn notification that was just posted
+     */
+    private void addNotification(StatusBarNotification sbn, int importance) {
+        updateNotification(sbn, importance);
+    }
+
+    /**
+     * @param sbn notification that was just removed
+     */
+    private void removeNotification(StatusBarNotification sbn) {
+        mForegroundServiceController.updateUserState(
+                sbn.getUserId(),
+                new ForegroundServiceController.UserStateUpdateCallback() {
+                    @Override
+                    public boolean updateUserState(ForegroundServicesUserState userState) {
+                        if (mForegroundServiceController.isDisclosureNotification(sbn)) {
+                            // if you remove the dungeon entirely, we take that to mean there are
+                            // no running services
+                            userState.setRunningServices(null, 0);
+                            return true;
+                        } else {
+                            // this is safe to call on any notification, not just
+                            // FLAG_FOREGROUND_SERVICE
+                            return userState.removeNotification(sbn.getPackageName(), sbn.getKey());
+                        }
+                    }
+
+                    @Override
+                    public void userStateNotFound(int userId) {
+                        if (DBG) {
+                            Log.w(TAG, String.format(
+                                    "user %d with no known notifications got removeNotification "
+                                            + "for %s",
+                                    sbn.getUserId(), sbn));
+                        }
+                    }
+                },
+                false /* don't create */);
+    }
+
+    /**
+     * @param sbn notification that was just changed in some way
+     */
+    private void updateNotification(StatusBarNotification sbn, int newImportance) {
+        mForegroundServiceController.updateUserState(
+                sbn.getUserId(),
+                userState -> {
+                    if (mForegroundServiceController.isDisclosureNotification(sbn)) {
+                        final Bundle extras = sbn.getNotification().extras;
+                        if (extras != null) {
+                            final String[] svcs = extras.getStringArray(
+                                    Notification.EXTRA_FOREGROUND_APPS);
+                            userState.setRunningServices(svcs, sbn.getNotification().when);
+                        }
+                    } else {
+                        userState.removeNotification(sbn.getPackageName(), sbn.getKey());
+                        if (0 != (sbn.getNotification().flags
+                                & Notification.FLAG_FOREGROUND_SERVICE)) {
+                            if (newImportance > NotificationManager.IMPORTANCE_MIN) {
+                                userState.addImportantNotification(sbn.getPackageName(),
+                                        sbn.getKey());
+                            }
+                            final Notification.Builder builder =
+                                    Notification.Builder.recoverBuilder(
+                                            mContext, sbn.getNotification());
+                            if (builder.usesStandardHeader()) {
+                                userState.addStandardLayoutNotification(
+                                        sbn.getPackageName(), sbn.getKey());
+                            }
+                        }
+                    }
+                    return true;
+                },
+                true /* create if not found */);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServicesUserState.java b/packages/SystemUI/src/com/android/systemui/ForegroundServicesUserState.java
new file mode 100644
index 0000000..a8ae654
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServicesUserState.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import java.util.Arrays;
+
+/**
+ * Struct to track relevant packages and notifications for a userid's foreground services.
+ */
+class ForegroundServicesUserState {
+    // shelf life of foreground services before they go bad
+    private static final long FG_SERVICE_GRACE_MILLIS = 5000;
+
+    private String[] mRunning = null;
+    private long mServiceStartTime = 0;
+    // package -> sufficiently important posted notification keys
+    private ArrayMap<String, ArraySet<String>> mImportantNotifications = new ArrayMap<>(1);
+    // package -> standard layout posted notification keys
+    private ArrayMap<String, ArraySet<String>> mStandardLayoutNotifications = new ArrayMap<>(1);
+
+    // package -> app ops
+    private ArrayMap<String, ArraySet<Integer>> mAppOps = new ArrayMap<>(1);
+
+    public void setRunningServices(String[] pkgs, long serviceStartTime) {
+        mRunning = pkgs != null ? Arrays.copyOf(pkgs, pkgs.length) : null;
+        mServiceStartTime = serviceStartTime;
+    }
+
+    public void addOp(String pkg, int op) {
+        if (mAppOps.get(pkg) == null) {
+            mAppOps.put(pkg, new ArraySet<>(3));
+        }
+        mAppOps.get(pkg).add(op);
+    }
+
+    public boolean removeOp(String pkg, int op) {
+        final boolean found;
+        final ArraySet<Integer> keys = mAppOps.get(pkg);
+        if (keys == null) {
+            found = false;
+        } else {
+            found = keys.remove(op);
+            if (keys.size() == 0) {
+                mAppOps.remove(pkg);
+            }
+        }
+        return found;
+    }
+
+    public void addImportantNotification(String pkg, String key) {
+        addNotification(mImportantNotifications, pkg, key);
+    }
+
+    public boolean removeImportantNotification(String pkg, String key) {
+        return removeNotification(mImportantNotifications, pkg, key);
+    }
+
+    public void addStandardLayoutNotification(String pkg, String key) {
+        addNotification(mStandardLayoutNotifications, pkg, key);
+    }
+
+    public boolean removeStandardLayoutNotification(String pkg, String key) {
+        return removeNotification(mStandardLayoutNotifications, pkg, key);
+    }
+
+    public boolean removeNotification(String pkg, String key) {
+        boolean removed = false;
+        removed |= removeImportantNotification(pkg, key);
+        removed |= removeStandardLayoutNotification(pkg, key);
+        return removed;
+    }
+
+    public void addNotification(ArrayMap<String, ArraySet<String>> map, String pkg,
+            String key) {
+        if (map.get(pkg) == null) {
+            map.put(pkg, new ArraySet<>());
+        }
+        map.get(pkg).add(key);
+    }
+
+    public boolean removeNotification(ArrayMap<String, ArraySet<String>> map,
+            String pkg, String key) {
+        final boolean found;
+        final ArraySet<String> keys = map.get(pkg);
+        if (keys == null) {
+            found = false;
+        } else {
+            found = keys.remove(key);
+            if (keys.size() == 0) {
+                map.remove(pkg);
+            }
+        }
+        return found;
+    }
+
+    public boolean isDisclosureNeeded() {
+        if (mRunning != null
+                && System.currentTimeMillis() - mServiceStartTime
+                >= FG_SERVICE_GRACE_MILLIS) {
+
+            for (String pkg : mRunning) {
+                final ArraySet<String> set = mImportantNotifications.get(pkg);
+                if (set == null || set.size() == 0) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public ArraySet<Integer> getFeatures(String pkg) {
+        return mAppOps.get(pkg);
+    }
+
+    public String getStandardLayoutKey(String pkg) {
+        final ArraySet<String> set = mStandardLayoutNotifications.get(pkg);
+        if (set == null || set.size() == 0) {
+            return null;
+        }
+        return set.valueAt(0);
+    }
+
+    @Override
+    public String toString() {
+        return "UserServices{"
+                + "mRunning=" + Arrays.toString(mRunning)
+                + ", mServiceStartTime=" + mServiceStartTime
+                + ", mImportantNotifications=" + mImportantNotifications
+                + ", mStandardLayoutNotifications=" + mStandardLayoutNotifications
+                + '}';
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/InitController.java b/packages/SystemUI/src/com/android/systemui/InitController.java
index 81d3251..a2dd123 100644
--- a/packages/SystemUI/src/com/android/systemui/InitController.java
+++ b/packages/SystemUI/src/com/android/systemui/InitController.java
@@ -16,10 +16,14 @@
 
 import java.util.ArrayList;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Created by {@link Dependency} on SystemUI startup. Add tasks which need to be executed only
  * after all other dependencies have been created.
  */
+@Singleton
 public class InitController {
 
     /**
@@ -29,6 +33,10 @@
 
     private final ArrayList<Runnable> mTasks = new ArrayList<>();
 
+    @Inject
+    public InitController() {
+    }
+
     /**
      * Add a task to be executed after {@link Dependency#start()}
      * @param runnable the task to be executed
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 1dd231c..ab077d6 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -329,11 +329,11 @@
 
     private void updateRoundedCornerRadii() {
         final int newRoundedDefault = mContext.getResources().getDimensionPixelSize(
-                R.dimen.rounded_corner_radius);
+                com.android.internal.R.dimen.rounded_corner_radius);
         final int newRoundedDefaultTop = mContext.getResources().getDimensionPixelSize(
-                R.dimen.rounded_corner_radius_top);
+                com.android.internal.R.dimen.rounded_corner_radius_top);
         final int newRoundedDefaultBottom = mContext.getResources().getDimensionPixelSize(
-                R.dimen.rounded_corner_radius_bottom);
+                com.android.internal.R.dimen.rounded_corner_radius_bottom);
 
         final boolean roundedCornersChanged = mRoundedDefault != newRoundedDefault
                 || mRoundedDefaultBottom != newRoundedDefaultBottom
diff --git a/packages/SystemUI/src/com/android/systemui/SystemBars.java b/packages/SystemUI/src/com/android/systemui/SystemBars.java
index 6edc23b..4285af0 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemBars.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemBars.java
@@ -16,6 +16,8 @@
 
 import android.util.Log;
 
+import com.android.systemui.statusbar.phone.StatusBar;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
@@ -63,6 +65,11 @@
         }
         mStatusBar.mContext = mContext;
         mStatusBar.mComponents = mComponents;
+        if (mStatusBar instanceof StatusBar) {
+            SystemUIFactory.getInstance().getRootComponent()
+                    .getStatusBarInjector()
+                    .createStatusBar((StatusBar) mStatusBar);
+        }
         mStatusBar.start();
         if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
     }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUI.java b/packages/SystemUI/src/com/android/systemui/SystemUI.java
index 30fbef6..78a1246 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUI.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUI.java
@@ -24,6 +24,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.Map;
+import java.util.function.Function;
 
 public abstract class SystemUI implements SysUiServiceProvider {
     public Context mContext;
@@ -61,4 +62,7 @@
 
         n.addExtras(extras);
     }
+
+    public interface Injector extends Function<Context, SystemUI> {
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 92aa652..1d8a21d 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -170,7 +170,11 @@
             Class cls;
             try {
                 cls = Class.forName(clsName);
-                mServices[i] = (SystemUI) cls.newInstance();
+                Object o = cls.newInstance();
+                if (o instanceof SystemUI.Injector) {
+                    o = ((SystemUI.Injector) o).apply(this);
+                }
+                mServices[i] = (SystemUI) o;
             } catch(ClassNotFoundException ex){
                 throw new RuntimeException(ex);
             } catch (IllegalAccessException ex) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 867c917..d0111cb 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -16,9 +16,12 @@
 
 package com.android.systemui;
 
+import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
+import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME;
+
+import android.annotation.Nullable;
 import android.app.AlarmManager;
 import android.content.Context;
-import android.util.ArrayMap;
 import android.util.Log;
 import android.view.ViewGroup;
 
@@ -26,56 +29,57 @@
 import com.android.internal.util.function.TriConsumer;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.Dependency.DependencyProvider;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.assist.AssistManager;
 import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.qs.QSTileHost;
-import com.android.systemui.statusbar.AmbientPulseManager;
+import com.android.systemui.power.EnhancedEstimates;
+import com.android.systemui.power.EnhancedEstimatesImpl;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.SmartReplyController;
-import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
-import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
+import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
 import com.android.systemui.statusbar.phone.LockIcon;
 import com.android.systemui.statusbar.phone.LockscreenWallpaper;
-import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.ScrimState;
+import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.policy.SmartReplyConstants;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.InjectionInflationController;
+import com.android.systemui.util.leak.GarbageMonitor;
 import com.android.systemui.volume.VolumeDialogComponent;
 
 import java.util.function.Consumer;
 
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+
 /**
  * Class factory to provide customizable SystemUI components.
  */
+@Module
 public class SystemUIFactory {
     private static final String TAG = "SystemUIFactory";
 
     static SystemUIFactory mFactory;
+    private SystemUIRootComponent mRootComponent;
 
-    public static SystemUIFactory getInstance() {
-        return mFactory;
+    public static <T extends SystemUIFactory> T getInstance() {
+        return (T) mFactory;
     }
 
     public static void createFromConfig(Context context) {
@@ -88,6 +92,7 @@
             Class<?> cls = null;
             cls = context.getClassLoader().loadClass(clsName);
             mFactory = (SystemUIFactory) cls.newInstance();
+            mFactory.init(context);
         } catch (Throwable t) {
             Log.w(TAG, "Error creating SystemUIFactory component: " + clsName, t);
             throw new RuntimeException(t);
@@ -96,6 +101,18 @@
 
     public SystemUIFactory() {}
 
+    protected void init(Context context) {
+        mRootComponent = DaggerSystemUIFactory_SystemUIRootComponent.builder()
+                .systemUIFactory(this)
+                .dependencyProvider(new com.android.systemui.DependencyProvider())
+                .contextHolder(new ContextHolder(context))
+                .build();
+    }
+
+    public SystemUIRootComponent getRootComponent() {
+        return mRootComponent;
+    }
+
     public StatusBarKeyguardViewManager createStatusBarKeyguardViewManager(Context context,
             ViewMediatorCallback viewMediatorCallback, LockPatternUtils lockPatternUtils) {
         return new StatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
@@ -128,42 +145,112 @@
         return new KeyguardIndicationController(context, indicationArea, lockIcon);
     }
 
-    public QSTileHost createQSTileHost(Context context, StatusBar statusBar,
-            StatusBarIconController iconController) {
-        return new QSTileHost(context, statusBar, iconController);
-    }
-
     public VolumeDialogComponent createVolumeDialogComponent(SystemUI systemUi, Context context) {
         return new VolumeDialogComponent(systemUi, context);
     }
 
-    public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
+    @Singleton
+    @Provides
+    public NotificationData.KeyguardEnvironment provideKeyguardEnvironment(Context context) {
+        return new KeyguardEnvironmentImpl();
+    }
+
+    @Singleton
+    @Provides
+    public NotificationLockscreenUserManager provideNotificationLockscreenUserManager(
             Context context) {
-        providers.put(StatusBarStateController.class, StatusBarStateController::new);
-        providers.put(NotificationLockscreenUserManager.class,
-                () -> new NotificationLockscreenUserManagerImpl(context));
-        providers.put(VisualStabilityManager.class, VisualStabilityManager::new);
-        providers.put(NotificationGroupManager.class, NotificationGroupManager::new);
-        providers.put(NotificationGroupAlertTransferHelper.class,
-                NotificationGroupAlertTransferHelper::new);
-        providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context));
-        providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(context));
-        providers.put(AmbientPulseManager.class, () -> new AmbientPulseManager(context));
-        providers.put(NotificationBlockingHelperManager.class,
-                () -> new NotificationBlockingHelperManager(context));
-        providers.put(NotificationRemoteInputManager.class,
-                () -> new NotificationRemoteInputManager(context));
-        providers.put(SmartReplyConstants.class,
-                () -> new SmartReplyConstants(Dependency.get(Dependency.MAIN_HANDLER), context));
-        providers.put(NotificationListener.class, () -> new NotificationListener(context));
-        providers.put(NotificationLogger.class, NotificationLogger::new);
-        providers.put(NotificationViewHierarchyManager.class,
-                () -> new NotificationViewHierarchyManager(context));
-        providers.put(NotificationEntryManager.class, () -> new NotificationEntryManager(context));
-        providers.put(KeyguardDismissUtil.class, KeyguardDismissUtil::new);
-        providers.put(SmartReplyController.class, () -> new SmartReplyController());
-        providers.put(RemoteInputQuickSettingsDisabler.class,
-                () -> new RemoteInputQuickSettingsDisabler(context));
-        providers.put(BubbleController.class, () -> new BubbleController(context));
+        return new NotificationLockscreenUserManagerImpl(context);
+    }
+
+    @Singleton
+    @Provides
+    public AssistManager provideAssistManager(DeviceProvisionedController controller,
+            Context context) {
+        return new AssistManager(controller, context);
+    }
+
+    @Singleton
+    @Provides
+    public NotificationEntryManager provideNotificationEntryManager(Context context) {
+        return new NotificationEntryManager(context);
+    }
+
+    @Singleton
+    @Provides
+    public EnhancedEstimates provideEnhancedEstimates(Context context) {
+        return new EnhancedEstimatesImpl();
+    }
+
+    @Singleton
+    @Provides
+    @Named(LEAK_REPORT_EMAIL_NAME)
+    @Nullable
+    public String provideLeakReportEmail() {
+        return null;
+    }
+
+    @Singleton
+    @Provides
+    public NotificationListener provideNotificationListener(Context context) {
+        return new NotificationListener(context);
+    }
+
+    @Singleton
+    @Provides
+    public NotificationInterruptionStateProvider provideNotificationInterruptionStateProvider(
+            Context context) {
+        return new NotificationInterruptionStateProvider(context);
+    }
+
+    @Singleton
+    @Provides
+    @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME)
+    public boolean provideAllowNotificationLongPress() {
+        return true;
+    }
+
+    @Singleton
+    @Provides
+    public ShadeController provideShadeController(Context context) {
+        return SysUiServiceProvider.getComponent(context, StatusBar.class);
+    }
+
+    @Module
+    protected static class ContextHolder {
+        private Context mContext;
+
+        public ContextHolder(Context context) {
+            mContext = context;
+        }
+
+        @Provides
+        public Context provideContext() {
+            return mContext;
+        }
+    }
+
+    @Singleton
+    @Component(modules = {SystemUIFactory.class, DependencyProvider.class, DependencyBinder.class,
+            ContextHolder.class})
+    public interface SystemUIRootComponent {
+        @Singleton
+        Dependency.DependencyInjector createDependency();
+
+        @Singleton
+        StatusBar.StatusBarInjector getStatusBarInjector();
+
+        /**
+         * FragmentCreator generates all Fragments that need injection.
+         */
+        @Singleton
+        FragmentService.FragmentCreator createFragmentCreator();
+
+        /**
+         * ViewCreator generates all Views that need injection.
+         */
+        InjectionInflationController.ViewCreator createViewCreator();
+
+        @Singleton
+        GarbageMonitor createGarbageMonitor();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java b/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java
index 82fd9b3..a726b42 100644
--- a/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java
+++ b/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java
@@ -20,14 +20,22 @@
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Thread that offloads work from the UI thread but that is still perceptible to the user, so the
  * priority is the same as the main thread.
  */
+@Singleton
 public class UiOffloadThread {
 
     private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
 
+    @Inject
+    public UiOffloadThread() {
+    }
+
     public Future<?> submit(Runnable runnable) {
         return mExecutorService.submit(runnable);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 906a210..e3bcb37 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.appops;
 
+import static com.android.systemui.Dependency.BG_LOOPER_NAME;
+
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.os.Handler;
@@ -32,34 +34,46 @@
 import java.util.List;
 import java.util.Set;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
 /**
  * Controller to keep track of applications that have requested access to given App Ops
  *
  * It can be subscribed to with callbacks. Additionally, it passes on the information to
  * NotificationPresenter to be displayed to the user.
  */
+@Singleton
 public class AppOpsControllerImpl implements AppOpsController,
-        AppOpsManager.OnOpActiveChangedListener {
+        AppOpsManager.OnOpActiveChangedListener,
+        AppOpsManager.OnOpNotedListener {
 
-    private static final long LOCATION_TIME_DELAY_MS = 5000;
+    private static final long NOTED_OP_TIME_DELAY_MS = 5000;
     private static final String TAG = "AppOpsControllerImpl";
     private static final boolean DEBUG = false;
     private final Context mContext;
 
-    protected final AppOpsManager mAppOps;
-    private final H mBGHandler;
+    private final AppOpsManager mAppOps;
+    private H mBGHandler;
     private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>();
     private final ArrayMap<Integer, Set<Callback>> mCallbacksByCode = new ArrayMap<>();
+
     @GuardedBy("mActiveItems")
     private final List<AppOpItem> mActiveItems = new ArrayList<>();
+    @GuardedBy("mNotedItems")
+    private final List<AppOpItem> mNotedItems = new ArrayList<>();
 
-    protected static final int[] OPS = new int[] {AppOpsManager.OP_CAMERA,
+    protected static final int[] OPS = new int[] {
+            AppOpsManager.OP_CAMERA,
             AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
             AppOpsManager.OP_RECORD_AUDIO,
             AppOpsManager.OP_COARSE_LOCATION,
-            AppOpsManager.OP_FINE_LOCATION};
+            AppOpsManager.OP_FINE_LOCATION
+    };
 
-    public AppOpsControllerImpl(Context context, Looper bgLooper) {
+    @Inject
+    public AppOpsControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper) {
         mContext = context;
         mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
         mBGHandler = new H(bgLooper);
@@ -70,11 +84,18 @@
     }
 
     @VisibleForTesting
+    protected void setBGHandler(H handler) {
+        mBGHandler = handler;
+    }
+
+    @VisibleForTesting
     protected void setListening(boolean listening) {
         if (listening) {
             mAppOps.startWatchingActive(OPS, this);
+            mAppOps.startWatchingNoted(OPS, this);
         } else {
             mAppOps.stopWatchingActive(this);
+            mAppOps.stopWatchingNoted(this);
         }
     }
 
@@ -124,10 +145,11 @@
         if (mCallbacks.isEmpty()) setListening(false);
     }
 
-    private AppOpItem getAppOpItem(int code, int uid, String packageName) {
-        final int itemsQ = mActiveItems.size();
+    private AppOpItem getAppOpItem(List<AppOpItem> appOpList, int code, int uid,
+            String packageName) {
+        final int itemsQ = appOpList.size();
         for (int i = 0; i < itemsQ; i++) {
-            AppOpItem item = mActiveItems.get(i);
+            AppOpItem item = appOpList.get(i);
             if (item.getCode() == code && item.getUid() == uid
                     && item.getPackageName().equals(packageName)) {
                 return item;
@@ -138,39 +160,59 @@
 
     private boolean updateActives(int code, int uid, String packageName, boolean active) {
         synchronized (mActiveItems) {
-            AppOpItem item = getAppOpItem(code, uid, packageName);
+            AppOpItem item = getAppOpItem(mActiveItems, code, uid, packageName);
             if (item == null && active) {
                 item = new AppOpItem(code, uid, packageName, System.currentTimeMillis());
                 mActiveItems.add(item);
-                if (code == AppOpsManager.OP_COARSE_LOCATION
-                        || code == AppOpsManager.OP_FINE_LOCATION) {
-                    mBGHandler.scheduleRemoval(item, LOCATION_TIME_DELAY_MS);
-                }
                 if (DEBUG) Log.w(TAG, "Added item: " + item.toString());
                 return true;
             } else if (item != null && !active) {
                 mActiveItems.remove(item);
                 if (DEBUG) Log.w(TAG, "Removed item: " + item.toString());
                 return true;
-            } else if (item != null && active
-                    && (code == AppOpsManager.OP_COARSE_LOCATION
-                            || code == AppOpsManager.OP_FINE_LOCATION)) {
-                mBGHandler.scheduleRemoval(item, LOCATION_TIME_DELAY_MS);
-                return true;
             }
             return false;
         }
     }
 
+    private void removeNoted(int code, int uid, String packageName) {
+        AppOpItem item;
+        synchronized (mNotedItems) {
+            item = getAppOpItem(mNotedItems, code, uid, packageName);
+            if (item == null) return;
+            mNotedItems.remove(item);
+            if (DEBUG) Log.w(TAG, "Removed item: " + item.toString());
+        }
+        notifySuscribers(code, uid, packageName, false);
+    }
+
+    private void addNoted(int code, int uid, String packageName) {
+        AppOpItem item;
+        synchronized (mNotedItems) {
+            item = getAppOpItem(mNotedItems, code, uid, packageName);
+            if (item == null) {
+                item = new AppOpItem(code, uid, packageName, System.currentTimeMillis());
+                mNotedItems.add(item);
+                if (DEBUG) Log.w(TAG, "Added item: " + item.toString());
+            }
+        }
+        mBGHandler.scheduleRemoval(item, NOTED_OP_TIME_DELAY_MS);
+    }
+
     /**
      * Returns a copy of the list containing all the active AppOps that the controller tracks.
      *
      * @return List of active AppOps information
      */
     public List<AppOpItem> getActiveAppOps() {
+        ArrayList<AppOpItem> active;
         synchronized (mActiveItems) {
-            return new ArrayList<>(mActiveItems);
+            active = new ArrayList<>(mActiveItems);
         }
+        synchronized (mNotedItems) {
+            active.addAll(mNotedItems);
+        }
+        return active;
     }
 
     /**
@@ -192,19 +234,44 @@
                 }
             }
         }
+        synchronized (mNotedItems) {
+            final int numNotedItems = mNotedItems.size();
+            for (int i = 0; i < numNotedItems; i++) {
+                AppOpItem item = mNotedItems.get(i);
+                if (UserHandle.getUserId(item.getUid()) == userId) {
+                    list.add(item);
+                }
+            }
+        }
         return list;
     }
 
     @Override
     public void onOpActiveChanged(int code, int uid, String packageName, boolean active) {
         if (updateActives(code, uid, packageName, active)) {
+            notifySuscribers(code, uid, packageName, active);
+        }
+    }
+
+    @Override
+    public void onOpNoted(int code, int uid, String packageName, int result) {
+        if (DEBUG) {
+            Log.w(TAG, "Op: " + code + " with result " + AppOpsManager.MODE_NAMES[result]);
+        }
+        if (result != AppOpsManager.MODE_ALLOWED) return;
+        addNoted(code, uid, packageName);
+        notifySuscribers(code, uid, packageName, true);
+    }
+
+    private void notifySuscribers(int code, int uid, String packageName, boolean active) {
+        if (mCallbacksByCode.containsKey(code)) {
             for (Callback cb: mCallbacksByCode.get(code)) {
                 cb.onActiveStateChanged(code, uid, packageName, active);
             }
         }
     }
 
-    private final class H extends Handler {
+    protected final class H extends Handler {
         H(Looper looper) {
             super(looper);
         }
@@ -214,8 +281,7 @@
             postDelayed(new Runnable() {
                 @Override
                 public void run() {
-                    onOpActiveChanged(item.getCode(), item.getUid(),
-                            item.getPackageName(), false);
+                    removeNoted(item.getCode(), item.getUid(), item.getPackageName());
                 }
             }, item, timeToRemoval);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
index ba89fe6..3167b9e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -240,7 +240,10 @@
             mConfirmShowing = true;
             mCurrentDialog.showConfirmationButton(true /* show */);
         } else {
-            handleHideDialog(false /* userCanceled */);
+            mCurrentDialog.updateState(BiometricDialogView.STATE_AUTHENTICATED);
+            mHandler.postDelayed(() -> {
+                handleHideDialog(false /* userCanceled */);
+            }, mCurrentDialog.getDelayAfterAuthenticatedDurationMs());
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
index e085f23..9934bfd 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
@@ -21,6 +21,7 @@
 import android.content.res.TypedArray;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
+import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
 import android.hardware.biometrics.BiometricPrompt;
 import android.os.Binder;
@@ -63,7 +64,8 @@
     protected static final int STATE_NONE = 0;
     protected static final int STATE_AUTHENTICATING = 1;
     protected static final int STATE_ERROR = 2;
-    protected static final int STATE_AUTHENTICATED = 3;
+    protected static final int STATE_PENDING_CONFIRMATION = 3;
+    protected static final int STATE_AUTHENTICATED = 4;
 
     private final IBinder mWindowToken = new Binder();
     private final Interpolator mLinearOutSlowIn;
@@ -77,6 +79,8 @@
     private final DialogViewCallback mCallback;
 
     private ViewGroup mLayout;
+    private final Button mPositiveButton;
+    private final Button mNegativeButton;
     private final TextView mErrorText;
     private Bundle mBundle;
     private final LinearLayout mDialog;
@@ -90,10 +94,12 @@
     private boolean mPendingShowTryAgain;
     private boolean mPendingShowConfirm;
 
-    protected abstract void updateIcon(int lastState, int newState);
     protected abstract int getHintStringResourceId();
     protected abstract int getAuthenticatedAccessibilityResourceId();
     protected abstract int getIconDescriptionResourceId();
+    protected abstract Drawable getAnimationForTransition(int oldState, int newState);
+    protected abstract boolean shouldAnimateForTransition(int oldState, int newState);
+    protected abstract int getDelayAfterAuthenticatedDurationMs();
 
     private final Runnable mShowAnimationRunnable = new Runnable() {
         @Override
@@ -178,10 +184,10 @@
         final View space = mLayout.findViewById(R.id.space);
         final View leftSpace = mLayout.findViewById(R.id.left_space);
         final View rightSpace = mLayout.findViewById(R.id.right_space);
-        final Button negative = mLayout.findViewById(R.id.button2);
-        final Button positive = mLayout.findViewById(R.id.button1);
         final ImageView icon = mLayout.findViewById(R.id.biometric_icon);
         final Button tryAgain = mLayout.findViewById(R.id.button_try_again);
+        mNegativeButton = mLayout.findViewById(R.id.button2);
+        mPositiveButton = mLayout.findViewById(R.id.button1);
 
         icon.setContentDescription(getResources().getString(getIconDescriptionResourceId()));
 
@@ -189,12 +195,15 @@
         setDismissesDialog(leftSpace);
         setDismissesDialog(rightSpace);
 
-        negative.setOnClickListener((View v) -> {
+        mNegativeButton.setOnClickListener((View v) -> {
             mCallback.onNegativePressed();
         });
 
-        positive.setOnClickListener((View v) -> {
-            mCallback.onPositivePressed();
+        mPositiveButton.setOnClickListener((View v) -> {
+            updateState(STATE_AUTHENTICATED);
+            mHandler.postDelayed(() -> {
+                mCallback.onPositivePressed();
+            }, getDelayAfterAuthenticatedDurationMs());
         });
 
         tryAgain.setOnClickListener((View v) -> {
@@ -215,7 +224,6 @@
         final TextView title = mLayout.findViewById(R.id.title);
         final TextView subtitle = mLayout.findViewById(R.id.subtitle);
         final TextView description = mLayout.findViewById(R.id.description);
-        final Button negative = mLayout.findViewById(R.id.button2);
         final ImageView backgroundView = mLayout.findViewById(R.id.background);
 
         if (mUserManager.isManagedProfile(mUserId)) {
@@ -229,6 +237,9 @@
             backgroundView.setBackgroundColor(R.color.biometric_dialog_dim_color);
         }
 
+        mNegativeButton.setVisibility(View.VISIBLE);
+        mErrorText.setVisibility(View.VISIBLE);
+
         if (RotationUtils.getRotation(mContext) != RotationUtils.ROTATION_NONE) {
             mDialog.getLayoutParams().width = (int) mDialogWidth;
         }
@@ -258,7 +269,7 @@
             description.setText(descriptionText);
         }
 
-        negative.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
+        mNegativeButton.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
 
         showTryAgainButton(mPendingShowTryAgain);
         showConfirmationButton(mPendingShowConfirm);
@@ -280,10 +291,32 @@
         mSkipIntro = false;
     }
 
+    protected void updateIcon(int lastState, int newState) {
+        final Drawable icon = getAnimationForTransition(lastState, newState);
+        if (icon == null) {
+            Log.e(TAG, "Animation not found");
+            return;
+        }
+
+        final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
+                ? (AnimatedVectorDrawable) icon
+                : null;
+
+        final ImageView imageView = getLayout().findViewById(R.id.biometric_icon);
+        imageView.setImageDrawable(icon);
+
+        if (animation != null && shouldAnimateForTransition(lastState, newState)) {
+            animation.forceAnimationOnUI();
+            animation.start();
+        }
+    }
+
     private void setDismissesDialog(View v) {
         v.setClickable(true);
         v.setOnTouchListener((View view, MotionEvent event) -> {
-            mCallback.onUserCanceled();
+            if (mLastState != STATE_AUTHENTICATED) {
+                mCallback.onUserCanceled();
+            }
             return true;
         });
     }
@@ -302,6 +335,7 @@
                 showTryAgainButton(false /* show */);
                 mPendingShowTryAgain = false;
                 mPendingShowConfirm = false;
+                updateState(STATE_NONE);
             }
         };
 
@@ -362,11 +396,11 @@
     }
 
     public void showConfirmationButton(boolean show) {
-        final Button positive = mLayout.findViewById(R.id.button1);
         if (show) {
-            positive.setVisibility(View.VISIBLE);
+            updateState(STATE_PENDING_CONFIRMATION);
+            mPositiveButton.setVisibility(View.VISIBLE);
         } else {
-            positive.setVisibility(View.GONE);
+            mPositiveButton.setVisibility(View.GONE);
         }
     }
 
@@ -411,7 +445,15 @@
         mCallback.onErrorShown();
     }
 
-    private void updateState(int newState) {
+    public void updateState(int newState) {
+        if (newState == STATE_PENDING_CONFIRMATION) {
+            mErrorText.setVisibility(View.INVISIBLE);
+        } else if (newState == STATE_AUTHENTICATED) {
+            mPositiveButton.setVisibility(View.GONE);
+            mNegativeButton.setVisibility(View.GONE);
+            mErrorText.setVisibility(View.INVISIBLE);
+        }
+
         updateIcon(mLastState, newState);
         mLastState = newState;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
index feef3a6d..de3f947 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
@@ -18,16 +18,18 @@
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
-import android.widget.ImageView;
 
 import com.android.systemui.R;
 
 /**
  * This class loads the view for the system-provided dialog. The view consists of:
- * Application Icon, Title, Subtitle, Description, Fingerprint Icon, Error/Help message area,
+ * Application Icon, Title, Subtitle, Description, Biometric Icon, Error/Help message area,
  * and positive/negative buttons.
  */
 public class FaceDialogView extends BiometricDialogView {
+
+    private static final int HIDE_DIALOG_DELAY = 500; // ms
+
     public FaceDialogView(Context context,
             DialogViewCallback callback) {
         super(context, callback);
@@ -53,10 +55,46 @@
     }
 
     @Override
-    protected void updateIcon(int lastState, int newState) {
-        Drawable icon = mContext.getDrawable(R.drawable.face_dialog_icon);
+    protected boolean shouldAnimateForTransition(int oldState, int newState) {
+        if (oldState == STATE_NONE && newState == STATE_AUTHENTICATING) {
+            return false;
+        } else if (oldState == STATE_AUTHENTICATING && newState == STATE_ERROR) {
+            return true;
+        } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATING) {
+            return true;
+        } else if (oldState == STATE_AUTHENTICATING && newState == STATE_PENDING_CONFIRMATION) {
+            return true;
+        } else if (oldState == STATE_PENDING_CONFIRMATION && newState == STATE_AUTHENTICATED) {
+            return true;
+        } else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) {
+            return true;
+        }
+        return false;
+    }
 
-        final ImageView faceIcon = getLayout().findViewById(R.id.biometric_icon);
-        faceIcon.setImageDrawable(icon);
+    @Override
+    protected int getDelayAfterAuthenticatedDurationMs() {
+        return HIDE_DIALOG_DELAY;
+    }
+
+    @Override
+    protected Drawable getAnimationForTransition(int oldState, int newState) {
+        int iconRes;
+        if (oldState == STATE_NONE && newState == STATE_AUTHENTICATING) {
+            iconRes = R.drawable.face_dialog_face_to_error;
+        } else if (oldState == STATE_AUTHENTICATING && newState == STATE_ERROR) {
+            iconRes = R.drawable.face_dialog_face_to_error;
+        } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATING) {
+            iconRes = R.drawable.face_dialog_error_to_face;
+        } else if (oldState == STATE_AUTHENTICATING && newState == STATE_PENDING_CONFIRMATION) {
+            iconRes = R.drawable.face_dialog_face_gray_to_face_blue;
+        } else if (oldState == STATE_PENDING_CONFIRMATION && newState == STATE_AUTHENTICATED) {
+            iconRes = R.drawable.face_dialog_face_blue_to_checkmark;
+        } else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) {
+            iconRes = R.drawable.face_dialog_face_gray_to_checkmark;
+        } else {
+            return null;
+        }
+        return mContext.getDrawable(iconRes);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
index 38a69a9..1a6cee2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
@@ -17,21 +17,21 @@
 package com.android.systemui.biometrics;
 
 import android.content.Context;
-import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
-import android.util.Log;
-import android.widget.ImageView;
 
 import com.android.systemui.R;
 
 /**
  * This class loads the view for the system-provided dialog. The view consists of:
- * Application Icon, Title, Subtitle, Description, Fingerprint Icon, Error/Help message area,
+ * Application Icon, Title, Subtitle, Description, Biometric Icon, Error/Help message area,
  * and positive/negative buttons.
  */
 public class FingerprintDialogView extends BiometricDialogView {
-    private static final String TAG = "FingerprintDialogView";
 
+    public FingerprintDialogView(Context context,
+            DialogViewCallback callback) {
+        super(context, callback);
+    }
     @Override
     protected int getHintStringResourceId() {
         return R.string.fingerprint_dialog_touch_sensor;
@@ -48,33 +48,7 @@
     }
 
     @Override
-    protected void updateIcon(int lastState, int newState) {
-        Drawable icon = getAnimationForTransition(lastState, newState);
-
-        if (icon == null) {
-            Log.e(TAG, "Animation not found");
-            return;
-        }
-
-        final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
-                ? (AnimatedVectorDrawable) icon
-                : null;
-
-        final ImageView fingerprintIcon = getLayout().findViewById(R.id.biometric_icon);
-        fingerprintIcon.setImageDrawable(icon);
-
-        if (animation != null && shouldAnimateForTransition(lastState, newState)) {
-            animation.forceAnimationOnUI();
-            animation.start();
-        }
-    }
-
-    public FingerprintDialogView(Context context,
-            DialogViewCallback callback) {
-        super(context, callback);
-    }
-
-    private boolean shouldAnimateForTransition(int oldState, int newState) {
+    protected boolean shouldAnimateForTransition(int oldState, int newState) {
         if (oldState == STATE_NONE && newState == STATE_AUTHENTICATING) {
             return false;
         } else if (oldState == STATE_AUTHENTICATING && newState == STATE_ERROR) {
@@ -88,7 +62,13 @@
         return false;
     }
 
-    private Drawable getAnimationForTransition(int oldState, int newState) {
+    @Override
+    protected int getDelayAfterAuthenticatedDurationMs() {
+        return 0;
+    }
+
+    @Override
+    protected Drawable getAnimationForTransition(int oldState, int newState) {
         int iconRes;
         if (oldState == STATE_NONE && newState == STATE_AUTHENTICATING) {
             iconRes = R.drawable.fingerprint_dialog_fp_to_error;
@@ -98,7 +78,7 @@
             iconRes = R.drawable.fingerprint_dialog_error_to_fp;
         } else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) {
             // TODO(b/77328470): add animation when fingerprint is authenticated
-            iconRes = R.drawable.fingerprint_dialog_error_to_fp;
+            iconRes = R.drawable.fingerprint_dialog_fp_to_error;
         } else {
             return null;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 1e91ef3..7577609 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -35,66 +35,56 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
 
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Bubbles are a special type of content that can "float" on top of other apps or System UI.
  * Bubbles can be expanded to show more content.
  *
  * The controller manages addition, removal, and visible state of bubbles on screen.
  */
+@Singleton
 public class BubbleController {
     private static final int MAX_BUBBLES = 5; // TODO: actually enforce this
 
     private static final String TAG = "BubbleController";
 
     // Enables some subset of notifs to automatically become bubbles
-    public static final boolean DEBUG_ENABLE_AUTO_BUBBLE = false;
+    private static final boolean DEBUG_ENABLE_AUTO_BUBBLE = false;
     // When a bubble is dismissed, recreate it as a notification
-    public static final boolean DEBUG_DEMOTE_TO_NOTIF = false;
+    private static final boolean DEBUG_DEMOTE_TO_NOTIF = false;
 
     // Secure settings
     private static final String ENABLE_AUTO_BUBBLE_MESSAGES = "experiment_autobubble_messaging";
     private static final String ENABLE_AUTO_BUBBLE_ONGOING = "experiment_autobubble_ongoing";
     private static final String ENABLE_AUTO_BUBBLE_ALL = "experiment_autobubble_all";
 
-    private Context mContext;
-    private BubbleDismissListener mDismissListener;
+    private final Context mContext;
+    private final NotificationEntryManager mNotificationEntryManager;
     private BubbleStateChangeListener mStateChangeListener;
     private BubbleExpandListener mExpandListener;
 
-    private Map<String, BubbleView> mBubbles = new HashMap<>();
+    private final Map<String, BubbleView> mBubbles = new HashMap<>();
     private BubbleStackView mStackView;
-    private Point mDisplaySize;
+    private final Point mDisplaySize;
 
     // Bubbles get added to the status bar view
-    @VisibleForTesting
-    protected StatusBarWindowController mStatusBarWindowController;
+    private final StatusBarWindowController mStatusBarWindowController;
 
     // Used for determining view rect for touch interaction
     private Rect mTempRect = new Rect();
 
     /**
-     * Listener to find out about bubble / bubble stack dismissal events.
-     */
-    public interface BubbleDismissListener {
-        /**
-         * Called when the entire stack of bubbles is dismissed by the user.
-         */
-        void onStackDismissed();
-
-        /**
-         * Called when a specific bubble is dismissed by the user.
-         */
-        void onBubbleDismissed(String key);
-    }
-
-    /**
      * Listener to be notified when some states of the bubbles change.
      */
     public interface BubbleStateChangeListener {
@@ -117,19 +107,16 @@
         void onBubbleExpandChanged(boolean isExpanding, float amount);
     }
 
-    public BubbleController(Context context) {
+    @Inject
+    public BubbleController(Context context, StatusBarWindowController statusBarWindowController) {
         mContext = context;
+        mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         mDisplaySize = new Point();
         wm.getDefaultDisplay().getSize(mDisplaySize);
-        mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
-    }
+        mStatusBarWindowController = statusBarWindowController;
 
-    /**
-     * Set a listener to be notified of bubble dismissal events.
-     */
-    public void setDismissListener(BubbleDismissListener listener) {
-        mDismissListener = listener;
+        mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
     }
 
     /**
@@ -176,7 +163,7 @@
     /**
      * Tell the stack of bubbles to be dismissed, this will remove all of the bubbles in the stack.
      */
-    public void dismissStack() {
+    void dismissStack() {
         if (mStackView == null) {
             return;
         }
@@ -186,16 +173,14 @@
         for (String key: mBubbles.keySet()) {
             removeBubble(key);
         }
-        if (mDismissListener != null) {
-            mDismissListener.onStackDismissed();
-        }
+        mNotificationEntryManager.updateNotifications();
         updateBubblesShowing();
     }
 
     /**
      * Adds a bubble associated with the provided notification entry or updates it if it exists.
      */
-    public void addBubble(NotificationData.Entry notif) {
+    public void addBubble(NotificationEntry notif) {
         if (mBubbles.containsKey(notif.key)) {
             // It's an update
             BubbleView bubble = mBubbles.get(notif.key);
@@ -234,18 +219,35 @@
     /**
      * Removes the bubble associated with the {@param uri}.
      */
-    public void removeBubble(String key) {
+    void removeBubble(String key) {
         BubbleView bv = mBubbles.get(key);
         if (mStackView != null && bv != null) {
             mStackView.removeBubble(bv);
             bv.getEntry().setBubbleDismissed(true);
         }
-        if (mDismissListener != null) {
-            mDismissListener.onBubbleDismissed(key);
+
+        NotificationEntry entry = mNotificationEntryManager.getNotificationData().get(key);
+        if (entry != null) {
+            entry.setBubbleDismissed(true);
+            if (!DEBUG_DEMOTE_TO_NOTIF) {
+                mNotificationEntryManager.performRemoveNotification(entry.notification);
+            }
         }
+        mNotificationEntryManager.updateNotifications();
+
         updateBubblesShowing();
     }
 
+    @SuppressWarnings("FieldCanBeLocal")
+    private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
+        @Override
+        public void onPendingEntryAdded(NotificationEntry entry) {
+            if (shouldAutoBubble(mContext, entry)) {
+                entry.setIsBubble(true);
+            }
+        }
+    };
+
     private void updateBubblesShowing() {
         boolean hasBubblesShowing = false;
         for (BubbleView bv : mBubbles.values()) {
@@ -273,7 +275,7 @@
         }
         ArrayList<BubbleView> viewsToRemove = new ArrayList<>();
         for (BubbleView bv : mBubbles.values()) {
-            NotificationData.Entry entry = bv.getEntry();
+            NotificationEntry entry = bv.getEntry();
             if (entry != null) {
                 if (entry.isRowRemoved() || entry.isBubbleDismissed() || entry.isRowDismissed()) {
                     viewsToRemove.add(bv);
@@ -305,7 +307,7 @@
     }
 
     @VisibleForTesting
-    public BubbleStackView getStackView() {
+    BubbleStackView getStackView() {
         return mStackView;
     }
 
@@ -313,7 +315,7 @@
     /**
      * Gets an appropriate starting point to position the bubble stack.
      */
-    public static Point getStartPoint(int size, Point displaySize) {
+    private static Point getStartPoint(int size, Point displaySize) {
         final int x = displaySize.x - size + EDGE_OVERLAP;
         final int y = displaySize.y / 4;
         return new Point(x, y);
@@ -322,7 +324,7 @@
     /**
      * Gets an appropriate position for the bubble when the stack is expanded.
      */
-    public static Point getExpandPoint(BubbleStackView view, int size, Point displaySize) {
+    static Point getExpandPoint(BubbleStackView view, int size, Point displaySize) {
         // Same place for now..
         return new Point(EDGE_OVERLAP, size);
     }
@@ -330,7 +332,7 @@
     /**
      * Whether the notification should bubble or not.
      */
-    public static boolean shouldAutoBubble(Context context, NotificationData.Entry entry) {
+    private static boolean shouldAutoBubble(Context context, NotificationEntry entry) {
         if (entry.isBubbleDismissed()) {
             return false;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index dfd18b2..d69e7b3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -40,7 +40,7 @@
 
 import com.android.internal.widget.ViewClippingUtil;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 import com.android.systemui.statusbar.notification.stack.ViewState;
@@ -247,7 +247,7 @@
      * @param bubbleView the view to update in the stack.
      * @param entry the entry to update it with.
      */
-    public void updateBubble(BubbleView bubbleView, NotificationData.Entry entry) {
+    public void updateBubble(BubbleView bubbleView, NotificationEntry entry) {
         // TODO - move to top of bubble stack, make it show its update if it makes sense
         bubbleView.update(entry);
         if (bubbleView.equals(mExpandedBubble)) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index 6c47aac..3307992 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -31,7 +31,7 @@
 
 import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 /**
@@ -43,7 +43,7 @@
     private Context mContext;
     private View mIconView;
 
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
     private int mBubbleSize;
     private int mIconSize;
 
@@ -72,7 +72,7 @@
      *
      * @param entry the notification to display as a bubble.
      */
-    public void setNotif(NotificationData.Entry entry) {
+    public void setNotif(NotificationEntry entry) {
         removeAllViews();
         // TODO: migrate to inflater
         mIconView = new ImageView(mContext);
@@ -89,7 +89,7 @@
     /**
      * Updates the UI based on the entry.
      */
-    public void update(NotificationData.Entry entry) {
+    public void update(NotificationEntry entry) {
         mEntry = entry;
         Notification n = entry.notification.getNotification();
         Icon ic = n.getLargeIcon() != null ? n.getLargeIcon() : n.getSmallIcon();
@@ -112,7 +112,7 @@
     /**
      * @return the notification entry associated with this bubble.
      */
-    public NotificationData.Entry getEntry() {
+    public NotificationEntry getEntry() {
         return mEntry;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarNotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/car/CarNotificationMediaManager.java
deleted file mode 100644
index f34d6b3..0000000
--- a/packages/SystemUI/src/com/android/systemui/car/CarNotificationMediaManager.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.car;
-
-import android.content.Context;
-
-import com.android.systemui.statusbar.NotificationMediaManager;
-
-public class CarNotificationMediaManager extends NotificationMediaManager {
-    public CarNotificationMediaManager(Context context) {
-        super(context);
-    }
-
-    @Override
-    public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
-        // Do nothing, we don't want to display media art in the lock screen for a car.
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
index 3c8a461..fdf18ce 100644
--- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
@@ -39,9 +39,13 @@
 import java.io.PrintWriter;
 import java.util.Arrays;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * ColorExtractor aware of wallpaper visibility
  */
+@Singleton
 public class SysuiColorExtractor extends ColorExtractor implements Dumpable {
     private static final String TAG = "SysuiColorExtractor";
     private boolean mWallpaperVisible;
@@ -49,6 +53,7 @@
     // Colors to return when the wallpaper isn't visible
     private final GradientColors mWpHiddenColors;
 
+    @Inject
     public SysuiColorExtractor(Context context) {
         this(context, new Tonal(context), true);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dock/DockManager.java b/packages/SystemUI/src/com/android/systemui/dock/DockManager.java
new file mode 100644
index 0000000..fa5a114
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dock/DockManager.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dock;
+
+/**
+ * Allows an app to handle dock events
+ */
+public interface DockManager {
+
+    /**
+     * Uninitialized / undocking dock states
+     */
+    int STATE_NONE = 0;
+    /**
+     * The state for docking
+     */
+    int STATE_DOCKED = 1;
+    /**
+     * The state for docking without showing UI
+     */
+    int STATE_DOCKED_HIDE = 2;
+
+    /**
+     * Add a dock event listener into manager
+     *
+     * @param callback A {@link DockEventListener} which want to add
+     */
+    void addListener(DockEventListener callback);
+
+    /**
+     * Remove the added listener from dock manager
+     *
+     * @param callback A {@link DockEventListener} which want to remove
+     */
+    void removeListener(DockEventListener callback);
+
+    /** Callback for receiving dock events */
+    interface DockEventListener {
+        /**
+         * Override to handle dock events
+         */
+        void onEvent(int event);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
new file mode 100644
index 0000000..9fc2234
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.doze;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.internal.hardware.AmbientDisplayConfiguration;
+import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.dock.DockManager;
+import com.android.systemui.doze.DozeMachine.State;
+
+import java.io.PrintWriter;
+
+/**
+ * Handles dock events for ambient state changes.
+ */
+public class DozeDockHandler implements DozeMachine.Part {
+
+    private static final String TAG = "DozeDockHandler";
+    private static final boolean DEBUG = DozeService.DEBUG;
+
+    private final DozeMachine mMachine;
+    private final DozeHost mDozeHost;
+    private final AmbientDisplayConfiguration mConfig;
+    private final Handler mHandler;
+    private final DockEventListener mDockEventListener = new DockEventListener();
+    private final DockManager mDockManager;
+
+    private int mDockState = DockManager.STATE_NONE;
+
+    public DozeDockHandler(Context context, DozeMachine machine, DozeHost dozeHost,
+            AmbientDisplayConfiguration config, Handler handler) {
+        mMachine = machine;
+        mDozeHost = dozeHost;
+        mConfig = config;
+        mHandler = handler;
+        mDockManager = SysUiServiceProvider.getComponent(context, DockManager.class);
+    }
+
+    @Override
+    public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
+        switch (newState) {
+            case INITIALIZED:
+                mDockEventListener.register();
+                break;
+            case DOZE_AOD:
+                if (mDockState == DockManager.STATE_DOCKED_HIDE) {
+                    mMachine.requestState(State.DOZE);
+                    break;
+                }
+                // continue below
+            case DOZE:
+                if (mDockState == DockManager.STATE_DOCKED) {
+                    mHandler.post(() -> requestPulse(newState));
+                }
+                break;
+            case FINISH:
+                mDockEventListener.unregister();
+                break;
+            default:
+                // no-op
+        }
+    }
+
+    private void requestPulse(State dozeState) {
+        if (mDozeHost.isPulsingBlocked() || !dozeState.canPulse()) {
+            return;
+        }
+
+        mMachine.requestPulse(DozeLog.PULSE_REASON_DOCKING);
+    }
+
+    private void requestPulseOutNow(State dozeState) {
+        if (dozeState == State.DOZE_REQUEST_PULSE || dozeState == State.DOZE_PULSING) {
+            final int pulseReason = mMachine.getPulseReason();
+            if (pulseReason == DozeLog.PULSE_REASON_DOCKING) {
+                mDozeHost.stopPulsing();
+            }
+        }
+    }
+
+    private boolean isDocked() {
+        return mDockState == DockManager.STATE_DOCKED
+                || mDockState == DockManager.STATE_DOCKED_HIDE;
+    }
+
+    @Override
+    public void dump(PrintWriter pw) {
+        pw.print(" DozeDockTriggers docking="); pw.println(isDocked());
+    }
+
+    private class DockEventListener implements DockManager.DockEventListener {
+        private boolean mRegistered;
+
+        @Override
+        public void onEvent(int event) {
+            if (DEBUG) Log.d(TAG, "dock event = " + event);
+            final DozeMachine.State dozeState = mMachine.getState();
+            mDockState = event;
+            switch (mDockState) {
+                case DockManager.STATE_DOCKED:
+                    requestPulse(dozeState);
+                    break;
+                case DockManager.STATE_NONE:
+                    if (dozeState == State.DOZE
+                            && mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)) {
+                        mMachine.requestState(State.DOZE_AOD);
+                        break;
+                    }
+                    // continue below
+                case DockManager.STATE_DOCKED_HIDE:
+                    requestPulseOutNow(dozeState);
+                    break;
+                default:
+                    // no-op
+            }
+        }
+
+        void register() {
+            if (mRegistered) {
+                return;
+            }
+            if (mDockManager != null) {
+                mDockManager.addListener(this);
+            }
+            mRegistered = true;
+        }
+
+        void unregister() {
+            if (!mRegistered) {
+                return;
+            }
+            if (mDockManager != null) {
+                mDockManager.removeListener(this);
+            }
+            mRegistered = false;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index d93ed178..58ae555 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -68,7 +68,8 @@
                 new DozeScreenState(wrappedService, handler, params, wakeLock),
                 createDozeScreenBrightness(context, wrappedService, sensorManager, host, params,
                         handler),
-                new DozeWallpaperState(context)
+                new DozeWallpaperState(context),
+                new DozeDockHandler(context, machine, host, config, handler)
         });
 
         return machine;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 1e61a77..4cb1fee 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -17,7 +17,6 @@
 package com.android.systemui.doze;
 
 import android.annotation.NonNull;
-import android.app.PendingIntent;
 
 /**
  * Interface the doze service uses to communicate with the rest of system UI.
@@ -34,7 +33,6 @@
     boolean isProvisioned();
     boolean isBlockingDoze();
 
-    void startPendingIntentDismissingKeyguard(PendingIntent intent);
     void extendPulse();
 
     void setAnimateWakeup(boolean animateWakeup);
@@ -48,10 +46,9 @@
     void onIgnoreTouchWhilePulsing(boolean ignore);
 
     /**
-     * If the device was waken up by a passive interrupt that will show the lock screen without
-     * expanding the notification panel/shade.
+     * Leaves pulsing state, going back to ambient UI.
      */
-    void setPassiveInterrupt(boolean lightInterrupt);
+    void stopPulsing();
 
     interface Callback {
         /**
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index eda3c59..50003e3 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -35,7 +35,7 @@
     private static final int SIZE = Build.IS_DEBUGGABLE ? 400 : 50;
     static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
 
-    private static final int REASONS = 8;
+    private static final int REASONS = 9;
 
     public static final int PULSE_REASON_NONE = -1;
     public static final int PULSE_REASON_INTENT = 0;
@@ -44,8 +44,9 @@
     public static final int PULSE_REASON_SENSOR_PICKUP = 3;
     public static final int PULSE_REASON_SENSOR_DOUBLE_TAP = 4;
     public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5;
-    public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 6;
+    public static final int PULSE_REASON_DOCKING = 6;
     public static final int REASON_SENSOR_WAKE_UP = 7;
+    public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 8;
 
     private static boolean sRegisterKeyguardCallback = true;
 
@@ -178,21 +179,12 @@
     }
 
     /**
-     * Appends lock screen wake up event to the logs.
-     * @param wake if we're waking up or sleeping.
-     */
-    public static void traceLockScreenWakeUp(boolean wake) {
-        if (!ENABLED) return;
-        log("wakeLockScreenWakeUp " + wake);
-    }
-
-    /**
      * Appends wake-display event to the logs.
      * @param wake if we're waking up or sleeping.
      */
     public static void traceWakeDisplay(boolean wake) {
         if (!ENABLED) return;
-        log("wakeLockScreenWakeUp " + wake);
+        log("wakeDisplay " + wake);
     }
 
     public static void traceProximityResult(Context context, boolean near, long millis,
@@ -212,7 +204,8 @@
             case PULSE_REASON_SENSOR_PICKUP: return "pickup";
             case PULSE_REASON_SENSOR_DOUBLE_TAP: return "doubletap";
             case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress";
-            case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakeLockScreen";
+            case PULSE_REASON_DOCKING: return "docking";
+            case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakelockscreen";
             case REASON_SENSOR_WAKE_UP: return "wakeup";
             default: throw new IllegalArgumentException("bad reason: " + pulseReason);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 7e77843..b6fc355 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -26,7 +26,6 @@
 import android.content.Context;
 import android.database.ContentObserver;
 import android.hardware.Sensor;
-import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.hardware.TriggerEvent;
@@ -86,6 +85,7 @@
         mProxCallback = proxCallback;
         mResolver = mContext.getContentResolver();
 
+        boolean alwaysOn = mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT);
         mSensors = new TriggerSensor[] {
                 new TriggerSensor(
                         mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION),
@@ -114,20 +114,20 @@
                         DozeLog.PULSE_REASON_SENSOR_LONG_PRESS,
                         true /* reports touch coordinates */,
                         true /* touchscreen */),
-                new PluginTriggerSensor(
-                        new SensorManagerPlugin.Sensor(TYPE_WAKE_LOCK_SCREEN),
-                        Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
-                        true /* configured */,
-                        DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
-                        false /* reports touch coordinates */,
-                        false /* touchscreen */),
-                new PluginTriggerSensor(
+                new PluginSensor(
                         new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY),
                         Settings.Secure.DOZE_WAKE_SCREEN_GESTURE,
-                        true /* configured */,
+                        mConfig.wakeScreenGestureAvailable() && alwaysOn,
                         DozeLog.REASON_SENSOR_WAKE_UP,
                         false /* reports touch coordinates */,
                         false /* touchscreen */),
+                new PluginSensor(
+                        new SensorManagerPlugin.Sensor(TYPE_WAKE_LOCK_SCREEN),
+                        Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
+                        mConfig.wakeScreenGestureAvailable(),
+                        DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
+                        false /* reports touch coordinates */,
+                        false /* touchscreen */),
         };
 
         mProxSensor = new ProxSensor(policy);
@@ -272,7 +272,7 @@
         }
 
         @Override
-        public void onSensorChanged(SensorEvent event) {
+        public void onSensorChanged(android.hardware.SensorEvent event) {
             if (DEBUG) Log.d(TAG, "onSensorChanged " + event);
 
             mCurrentlyFar = event.values[0] >= event.sensor.getMaximumRange();
@@ -360,7 +360,9 @@
         }
 
         protected boolean enabledBySetting() {
-            if (TextUtils.isEmpty(mSetting)) {
+            if (!mConfig.enabled(UserHandle.USER_CURRENT)) {
+                return false;
+            } else if (TextUtils.isEmpty(mSetting)) {
                 return true;
             }
             return Settings.Secure.getIntForUser(mResolver, mSetting, mSettingDefault ? 1 : 0,
@@ -417,7 +419,7 @@
 
         protected String triggerEventToString(TriggerEvent event) {
             if (event == null) return null;
-            final StringBuilder sb = new StringBuilder("TriggerEvent[")
+            final StringBuilder sb = new StringBuilder("SensorEvent[")
                     .append(event.timestamp).append(',')
                     .append(event.sensor.getName());
             if (event.values != null) {
@@ -432,23 +434,19 @@
     /**
      * A Sensor that is injected via plugin.
      */
-    private class PluginTriggerSensor extends TriggerSensor {
+    private class PluginSensor extends TriggerSensor {
 
         private final SensorManagerPlugin.Sensor mPluginSensor;
-        private final SensorManagerPlugin.TriggerEventListener mTriggerEventListener = (event) -> {
+        private final SensorManagerPlugin.SensorEventListener mTriggerEventListener = (event) -> {
             DozeLog.traceSensor(mContext, mPulseReason);
             mHandler.post(mWakeLock.wrap(() -> {
-                if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event));
-                mRegistered = false;
+                if (DEBUG) Log.d(TAG, "onSensorEvent: " + triggerEventToString(event));
                 mCallback.onSensorPulse(mPulseReason, true /* sensorPerformsProxCheck */, -1, -1,
                         event.getValues());
-                if (!mRegistered) {
-                    updateListener();  // reregister, this sensor only fires once
-                }
             }));
         };
 
-        PluginTriggerSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured,
+        PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured,
                 int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen) {
             super(null, setting, configured, pulseReason, reportsTouchCoordinates,
                     requiresTouchscreen);
@@ -460,13 +458,13 @@
             if (!mConfigured) return;
             AsyncSensorManager asyncSensorManager = (AsyncSensorManager) mSensorManager;
             if (mRequested && !mDisabled && enabledBySetting() && !mRegistered) {
-                asyncSensorManager.requestPluginTriggerSensor(mPluginSensor, mTriggerEventListener);
+                asyncSensorManager.registerPluginListener(mPluginSensor, mTriggerEventListener);
                 mRegistered = true;
-                if (DEBUG) Log.d(TAG, "requestPluginTriggerSensor");
+                if (DEBUG) Log.d(TAG, "registerPluginListener");
             } else if (mRegistered) {
-                asyncSensorManager.cancelPluginTriggerSensor(mPluginSensor, mTriggerEventListener);
+                asyncSensorManager.unregisterPluginListener(mPluginSensor, mTriggerEventListener);
                 mRegistered = false;
-                if (DEBUG) Log.d(TAG, "cancelPluginTriggerSensor");
+                if (DEBUG) Log.d(TAG, "unregisterPluginListener");
             }
         }
 
@@ -479,7 +477,7 @@
                     .append(", mSensor=").append(mPluginSensor).append("}").toString();
         }
 
-        private String triggerEventToString(SensorManagerPlugin.TriggerEvent event) {
+        private String triggerEventToString(SensorManagerPlugin.SensorEvent event) {
             if (event == null) return null;
             final StringBuilder sb = new StringBuilder("PluginTriggerEvent[")
                     .append(event.getSensor()).append(',')
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index afe9a74..6a9b689 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -53,6 +53,12 @@
     /** adb shell am broadcast -a com.android.systemui.doze.pulse com.android.systemui */
     private static final String PULSE_ACTION = "com.android.systemui.doze.pulse";
 
+    /**
+     * Last value sent by the wake-display sensor.
+     * Assuming that the screen should start on.
+     */
+    private static boolean sWakeDisplaySensorState = true;
+
     private final Context mContext;
     private final DozeMachine mMachine;
     private final DozeSensors mDozeSensors;
@@ -128,13 +134,13 @@
         boolean isDoubleTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
         boolean isPickup = pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
         boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
-        boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
         boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
+        boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
         boolean wakeEvent = rawValues != null && rawValues.length > 0 && rawValues[0] != 0;
 
         if (isWakeDisplay) {
             onWakeScreen(wakeEvent);
-        } else if (isLongPress) {
+        } else if (isLongPress || isWakeLockScreen) {
             requestPulse(pulseReason, sensorPerformedProxCheck);
         } else {
             proximityCheckThenCall((result) -> {
@@ -145,22 +151,12 @@
                 if (isDoubleTap) {
                     mDozeHost.onDoubleTap(screenX, screenY);
                     mMachine.wakeUp();
-                } else if (isWakeLockScreen) {
-                    if (wakeEvent) {
-                        mDozeHost.setPassiveInterrupt(true);
-                        mMachine.wakeUp();
-                        DozeLog.traceLockScreenWakeUp(wakeEvent);
-                    } else {
-                        if (DEBUG) Log.d(TAG, "Unpulsing");
-                    }
                 } else if (isPickup) {
-                    mDozeHost.setPassiveInterrupt(true);
                     mMachine.wakeUp();
                 } else {
                     mDozeHost.extendPulse();
                 }
             }, sensorPerformedProxCheck, pulseReason);
-            return;
         }
 
         if (isPickup) {
@@ -199,6 +195,7 @@
         DozeMachine.State state = mMachine.getState();
         boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
         boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
+        sWakeDisplaySensorState = wake;
 
         if (wake) {
             proximityCheckThenCall((result) -> {
@@ -223,7 +220,6 @@
             case INITIALIZED:
                 mBroadcastReceiver.register(mContext);
                 mDozeHost.addCallback(mHostCallback);
-                mDozeHost.setPassiveInterrupt(false);
                 checkTriggersAtInit();
                 break;
             case DOZE:
@@ -233,7 +229,9 @@
                     mDozeSensors.reregisterAllSensors();
                 }
                 mDozeSensors.setListening(true);
-                mDozeHost.setPassiveInterrupt(false);
+                if (newState == DozeMachine.State.DOZE_AOD && !sWakeDisplaySensorState) {
+                    onWakeScreen(false);
+                }
                 break;
             case DOZE_AOD_PAUSED:
             case DOZE_AOD_PAUSING:
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 67aa82d..7656564 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -124,6 +124,7 @@
                 unscheduleTimeTick();
                 break;
             case DOZE_REQUEST_PULSE:
+                scheduleTimeTick();
                 pulseWhileDozing(mMachine.getPulseReason());
                 break;
             case INITIALIZED:
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
index 60e39b2..30f6e1a 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -41,6 +41,8 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.HashMap;
 
@@ -186,6 +188,13 @@
         mFragments.dispatchDestroy();
     }
 
+    /**
+     * Creates a fragment that requires injection.
+     */
+    public <T> T create(Class<T> fragmentCls) {
+        return (T) mPlugins.instantiate(mContext, fragmentCls.getName(), null);
+    }
+
     public interface FragmentListener {
         void onFragmentViewCreated(String tag, Fragment fragment);
 
@@ -294,13 +303,36 @@
         Fragment instantiate(Context context, String className, Bundle arguments) {
             Context extensionContext = mExtensionLookup.get(className);
             if (extensionContext != null) {
-                Fragment f = Fragment.instantiate(extensionContext, className, arguments);
+                Fragment f = instantiateWithInjections(extensionContext, className, arguments);
                 if (f instanceof Plugin) {
                     ((Plugin) f).onCreate(mContext, extensionContext);
                 }
                 return f;
             }
-            return Fragment.instantiate(context, className, arguments);
+            return instantiateWithInjections(context, className, arguments);
+        }
+
+        private Fragment instantiateWithInjections(Context context, String className,
+                Bundle args) {
+            Method method = mManager.getInjectionMap().get(className);
+            if (method != null) {
+                try {
+                    Fragment f = (Fragment) method.invoke(mManager.getFragmentCreator());
+                    // Setup the args, taken from Fragment#instantiate.
+                    if (args != null) {
+                        args.setClassLoader(f.getClass().getClassLoader());
+                        f.setArguments(args);
+                    }
+                    return f;
+                } catch (IllegalAccessException e) {
+                    throw new Fragment.InstantiationException("Unable to instantiate " + className,
+                            e);
+                } catch (InvocationTargetException e) {
+                    throw new Fragment.InstantiationException("Unable to instantiate " + className,
+                            e);
+                }
+            }
+            return Fragment.instantiate(context, className, args);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
index bf7d629..8dbaf0f 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
@@ -14,6 +14,7 @@
 
 package com.android.systemui.fragments;
 
+import android.app.Fragment;
 import android.content.res.Configuration;
 import android.os.Handler;
 import android.util.ArrayMap;
@@ -21,20 +22,56 @@
 
 import com.android.systemui.ConfigurationChangedReceiver;
 import com.android.systemui.Dumpable;
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.qs.QSFragment;
+import com.android.systemui.statusbar.phone.NavigationBarFragment;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Subcomponent;
 
 /**
  * Holds a map of root views to FragmentHostStates and generates them as needed.
  * Also dispatches the configuration changes to all current FragmentHostStates.
  */
+@Singleton
 public class FragmentService implements ConfigurationChangedReceiver, Dumpable {
 
     private static final String TAG = "FragmentService";
 
     private final ArrayMap<View, FragmentHostState> mHosts = new ArrayMap<>();
+    private final ArrayMap<String, Method> mInjectionMap = new ArrayMap<>();
     private final Handler mHandler = new Handler();
+    private final FragmentCreator mFragmentCreator;
+
+    @Inject
+    public FragmentService(SystemUIFactory.SystemUIRootComponent rootComponent) {
+        mFragmentCreator = rootComponent.createFragmentCreator();
+        initInjectionMap();
+    }
+
+    ArrayMap<String, Method> getInjectionMap() {
+        return mInjectionMap;
+    }
+
+    FragmentCreator getFragmentCreator() {
+        return mFragmentCreator;
+    }
+
+    private void initInjectionMap() {
+        for (Method method : FragmentCreator.class.getDeclaredMethods()) {
+            if (Fragment.class.isAssignableFrom(method.getReturnType())
+                    && (method.getModifiers() & Modifier.PUBLIC) != 0) {
+                mInjectionMap.put(method.getReturnType().getName(), method);
+            }
+        }
+    }
 
     public FragmentHostManager getFragmentHostManager(View view) {
         View root = view.getRootView();
@@ -74,6 +111,21 @@
         }
     }
 
+    /**
+     * The subcomponent of dagger that holds all fragments that need injection.
+     */
+    @Subcomponent
+    public interface FragmentCreator {
+        /**
+         * Inject a NavigationBarFragment.
+         */
+        NavigationBarFragment createNavigationBarFragment();
+        /**
+         * Inject a QSFragment.
+         */
+        QSFragment createQSFragment();
+    }
+
     private class FragmentHostState {
         private final View mView;
 
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index dc11b4c..19a7cea 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -132,9 +132,9 @@
     }
 
     @Override
-    public void disable(int state1, int state2, boolean animate) {
+    public void disable(int displayId, int state1, int state2, boolean animate) {
         final boolean disabled = (state2 & DISABLE2_GLOBAL_ACTIONS) != 0;
-        if (disabled == mDisabled) return;
+        if (displayId != mContext.getDisplayId() || disabled == mDisabled) return;
         mDisabled = disabled;
         if (disabled && mGlobalActions != null) {
             mGlobalActions.dismissDialog();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 201c7e6..c4c8bc7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard;
 
+import android.annotation.AnyThread;
 import android.app.ActivityManager;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -24,14 +25,20 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.graphics.Typeface;
+import android.graphics.drawable.Icon;
 import android.icu.text.DateFormat;
 import android.icu.text.DisplayContext;
+import android.media.MediaMetadata;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Trace;
 import android.provider.Settings;
 import android.service.notification.ZenModeConfig;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
+import android.text.style.StyleSpan;
 
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.slice.Slice;
@@ -41,7 +48,11 @@
 import androidx.slice.builders.SliceAction;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.policy.NextAlarmController;
 import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -49,19 +60,24 @@
 
 import java.util.Date;
 import java.util.Locale;
+import java.util.TimeZone;
 import java.util.concurrent.TimeUnit;
 
 /**
  * Simple Slice provider that shows the current date.
  */
 public class KeyguardSliceProvider extends SliceProvider implements
-        NextAlarmController.NextAlarmChangeCallback, ZenModeController.Callback {
+        NextAlarmController.NextAlarmChangeCallback, ZenModeController.Callback,
+        NotificationMediaManager.MediaListener, StatusBarStateController.StateListener {
 
+    private static final StyleSpan BOLD_STYLE = new StyleSpan(Typeface.BOLD);
     public static final String KEYGUARD_SLICE_URI = "content://com.android.systemui.keyguard/main";
     public static final String KEYGUARD_DATE_URI = "content://com.android.systemui.keyguard/date";
     public static final String KEYGUARD_NEXT_ALARM_URI =
             "content://com.android.systemui.keyguard/alarm";
     public static final String KEYGUARD_DND_URI = "content://com.android.systemui.keyguard/dnd";
+    public static final String KEYGUARD_MEDIA_URI =
+            "content://com.android.systemui.keyguard/media";
     public static final String KEYGUARD_ACTION_URI =
             "content://com.android.systemui.keyguard/action";
 
@@ -77,6 +93,7 @@
     protected final Uri mDateUri;
     protected final Uri mAlarmUri;
     protected final Uri mDndUri;
+    protected final Uri mMediaUri;
     private final Date mCurrentTime = new Date();
     private final Handler mHandler;
     private final AlarmManager.OnAlarmListener mUpdateNextAlarm = this::updateNextAlarm;
@@ -91,6 +108,10 @@
     protected ContentResolver mContentResolver;
     private AlarmManager.AlarmClockInfo mNextAlarmInfo;
     private PendingIntent mPendingIntent;
+    protected NotificationMediaManager mMediaManager;
+    private StatusBarStateController mStatusBarStateController;
+    protected MediaMetadata mMediaMetaData;
+    protected boolean mDozing;
 
     /**
      * Receiver responsible for time ticking and updating the date format.
@@ -100,21 +121,36 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
-            if (Intent.ACTION_TIME_TICK.equals(action)
-                    || Intent.ACTION_DATE_CHANGED.equals(action)
-                    || Intent.ACTION_TIME_CHANGED.equals(action)
-                    || Intent.ACTION_TIMEZONE_CHANGED.equals(action)
-                    || Intent.ACTION_LOCALE_CHANGED.equals(action)) {
-                if (Intent.ACTION_LOCALE_CHANGED.equals(action)
-                        || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
-                    // need to get a fresh date format
-                    mHandler.post(KeyguardSliceProvider.this::cleanDateFormat);
+            if (Intent.ACTION_DATE_CHANGED.equals(action)) {
+                synchronized (this) {
+                    updateClockLocked();
                 }
-                mHandler.post(KeyguardSliceProvider.this::updateClock);
+            } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
+                synchronized (this) {
+                    cleanDateFormatLocked();
+                }
             }
         }
     };
 
+    @VisibleForTesting
+    final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
+            new KeyguardUpdateMonitorCallback() {
+                @Override
+                public void onTimeChanged() {
+                    synchronized (this) {
+                        updateClockLocked();
+                    }
+                }
+
+                @Override
+                public void onTimeZoneChanged(TimeZone timeZone) {
+                    synchronized (this) {
+                        cleanDateFormatLocked();
+                    }
+                }
+            };
+
     public KeyguardSliceProvider() {
         this(new Handler());
     }
@@ -130,22 +166,77 @@
         mDateUri = Uri.parse(KEYGUARD_DATE_URI);
         mAlarmUri = Uri.parse(KEYGUARD_NEXT_ALARM_URI);
         mDndUri = Uri.parse(KEYGUARD_DND_URI);
+        mMediaUri = Uri.parse(KEYGUARD_MEDIA_URI);
     }
 
+    /**
+     * Initialize dependencies that don't exist during {@link android.content.ContentProvider}
+     * instantiation.
+     *
+     * @param mediaManager {@link NotificationMediaManager} singleton.
+     * @param statusBarStateController {@link StatusBarStateController} singleton.
+     */
+    public void initDependencies(
+            NotificationMediaManager mediaManager,
+            StatusBarStateController statusBarStateController) {
+        mMediaManager = mediaManager;
+        mMediaManager.addCallback(this);
+        mStatusBarStateController = statusBarStateController;
+        mStatusBarStateController.addCallback(this);
+    }
+
+    @AnyThread
     @Override
     public Slice onBindSlice(Uri sliceUri) {
         Trace.beginSection("KeyguardSliceProvider#onBindSlice");
-        ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY);
-        builder.addRow(new RowBuilder(mDateUri).setTitle(mLastText));
-        addNextAlarm(builder);
-        addZenMode(builder);
-        addPrimaryAction(builder);
-        Slice slice = builder.build();
+        Slice slice;
+        synchronized (this) {
+            ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY);
+            if (needsMediaLocked()) {
+                addMediaLocked(builder);
+            } else {
+                builder.addRow(new RowBuilder(mDateUri).setTitle(mLastText));
+                addNextAlarmLocked(builder);
+                addZenModeLocked(builder);
+            }
+            addPrimaryActionLocked(builder);
+            slice = builder.build();
+        }
         Trace.endSection();
         return slice;
     }
 
-    protected void addPrimaryAction(ListBuilder builder) {
+    protected boolean needsMediaLocked() {
+        return mMediaMetaData != null && mDozing;
+    }
+
+    protected void addMediaLocked(ListBuilder listBuilder) {
+        if (mMediaMetaData != null) {
+            SpannableStringBuilder builder = new SpannableStringBuilder();
+            CharSequence title = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_TITLE);
+            if (TextUtils.isEmpty(title)) {
+                title = getContext().getResources().getString(R.string.music_controls_no_title);
+            }
+            builder.append(title);
+            builder.setSpan(BOLD_STYLE, 0, title.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+
+            CharSequence album = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_ARTIST);
+            if (!TextUtils.isEmpty(album)) {
+                builder.append("  ").append(album);
+            }
+
+            RowBuilder mediaBuilder = new RowBuilder(mMediaUri).setTitle(builder);
+            Icon notificationIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon();
+            if (notificationIcon != null) {
+                IconCompat icon = IconCompat.createFromIcon(notificationIcon);
+                mediaBuilder.addEndItem(icon, ListBuilder.ICON_IMAGE);
+            }
+
+            listBuilder.addRow(mediaBuilder);
+        }
+    }
+
+    protected void addPrimaryActionLocked(ListBuilder builder) {
         // Add simple action because API requires it; Keyguard handles presenting
         // its own slices so this action + icon are actually never used.
         IconCompat icon = IconCompat.createWithResource(getContext(),
@@ -157,7 +248,7 @@
         builder.addRow(primaryActionRow);
     }
 
-    protected void addNextAlarm(ListBuilder builder) {
+    protected void addNextAlarmLocked(ListBuilder builder) {
         if (TextUtils.isEmpty(mNextAlarm)) {
             return;
         }
@@ -173,7 +264,7 @@
      * Add zen mode (DND) icon to slice if it's enabled.
      * @param builder The slice builder.
      */
-    protected void addZenMode(ListBuilder builder) {
+    protected void addZenModeLocked(ListBuilder builder) {
         if (!isDndOn()) {
             return;
         }
@@ -204,33 +295,35 @@
         mPendingIntent = PendingIntent.getActivity(getContext(), 0, new Intent(), 0);
         KeyguardSliceProvider.sInstance = this;
         registerClockUpdate();
-        updateClock();
+        updateClockLocked();
         return true;
     }
 
     @Override
     public void onZenChanged(int zen) {
-        mContentResolver.notifyChange(mSliceUri, null /* observer */);
+        notifyChange();
     }
 
     @Override
     public void onConfigChanged(ZenModeConfig config) {
-        mContentResolver.notifyChange(mSliceUri, null /* observer */);
+        notifyChange();
     }
 
     private void updateNextAlarm() {
-        if (withinNHours(mNextAlarmInfo, ALARM_VISIBILITY_HOURS)) {
-            String pattern = android.text.format.DateFormat.is24HourFormat(getContext(),
-                    ActivityManager.getCurrentUser()) ? "HH:mm" : "h:mm";
-            mNextAlarm = android.text.format.DateFormat.format(pattern,
-                    mNextAlarmInfo.getTriggerTime()).toString();
-        } else {
-            mNextAlarm = "";
+        synchronized (this) {
+            if (withinNHoursLocked(mNextAlarmInfo, ALARM_VISIBILITY_HOURS)) {
+                String pattern = android.text.format.DateFormat.is24HourFormat(getContext(),
+                        ActivityManager.getCurrentUser()) ? "HH:mm" : "h:mm";
+                mNextAlarm = android.text.format.DateFormat.format(pattern,
+                        mNextAlarmInfo.getTriggerTime()).toString();
+            } else {
+                mNextAlarm = "";
+            }
         }
-        mContentResolver.notifyChange(mSliceUri, null /* observer */);
+        notifyChange();
     }
 
-    private boolean withinNHours(AlarmManager.AlarmClockInfo alarmClockInfo, int hours) {
+    private boolean withinNHoursLocked(AlarmManager.AlarmClockInfo alarmClockInfo, int hours) {
         if (alarmClockInfo == null) {
             return false;
         }
@@ -244,34 +337,37 @@
      * changing the date/time via the settings app.
      */
     private void registerClockUpdate() {
-        if (mRegistered) {
-            return;
-        }
+        synchronized (this) {
+            if (mRegistered) {
+                return;
+            }
 
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_DATE_CHANGED);
-        filter.addAction(Intent.ACTION_TIME_CHANGED);
-        filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
-        filter.addAction(Intent.ACTION_LOCALE_CHANGED);
-        getContext().registerReceiver(mIntentReceiver, filter, null /* permission*/,
-                null /* scheduler */);
-        mRegistered = true;
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(Intent.ACTION_DATE_CHANGED);
+            filter.addAction(Intent.ACTION_LOCALE_CHANGED);
+            getContext().registerReceiver(mIntentReceiver, filter, null /* permission*/,
+                    null /* scheduler */);
+            getKeyguardUpdateMonitor().registerCallback(mKeyguardUpdateMonitorCallback);
+            mRegistered = true;
+        }
     }
 
     @VisibleForTesting
     boolean isRegistered() {
-        return mRegistered;
-    }
-
-    protected void updateClock() {
-        final String text = getFormattedDate();
-        if (!text.equals(mLastText)) {
-            mLastText = text;
-            mContentResolver.notifyChange(mSliceUri, null /* observer */);
+        synchronized (this) {
+            return mRegistered;
         }
     }
 
-    protected String getFormattedDate() {
+    protected void updateClockLocked() {
+        final String text = getFormattedDateLocked();
+        if (!text.equals(mLastText)) {
+            mLastText = text;
+            notifyChange();
+        }
+    }
+
+    protected String getFormattedDateLocked() {
         if (mDateFormat == null) {
             final Locale l = Locale.getDefault();
             DateFormat format = DateFormat.getInstanceForSkeleton(mDatePattern, l);
@@ -283,21 +379,62 @@
     }
 
     @VisibleForTesting
-    void cleanDateFormat() {
+    void cleanDateFormatLocked() {
         mDateFormat = null;
     }
 
     @Override
     public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
-        mNextAlarmInfo = nextAlarm;
-        mAlarmManager.cancel(mUpdateNextAlarm);
+        synchronized (this) {
+            mNextAlarmInfo = nextAlarm;
+            mAlarmManager.cancel(mUpdateNextAlarm);
 
-        long triggerAt = mNextAlarmInfo == null ? -1 : mNextAlarmInfo.getTriggerTime()
-                - TimeUnit.HOURS.toMillis(ALARM_VISIBILITY_HOURS);
-        if (triggerAt > 0) {
-            mAlarmManager.setExact(AlarmManager.RTC, triggerAt, "lock_screen_next_alarm",
-                    mUpdateNextAlarm, mHandler);
+            long triggerAt = mNextAlarmInfo == null ? -1 : mNextAlarmInfo.getTriggerTime()
+                    - TimeUnit.HOURS.toMillis(ALARM_VISIBILITY_HOURS);
+            if (triggerAt > 0) {
+                mAlarmManager.setExact(AlarmManager.RTC, triggerAt, "lock_screen_next_alarm",
+                        mUpdateNextAlarm, mHandler);
+            }
         }
         updateNextAlarm();
     }
+
+    @VisibleForTesting
+    protected KeyguardUpdateMonitor getKeyguardUpdateMonitor() {
+        return KeyguardUpdateMonitor.getInstance(getContext());
+    }
+
+    @Override
+    public void onMetadataChanged(MediaMetadata metadata) {
+        final boolean notify;
+        synchronized (this) {
+            boolean neededMedia = needsMediaLocked();
+            mMediaMetaData = metadata;
+            notify = neededMedia != needsMediaLocked();
+        }
+        if (notify) {
+            notifyChange();
+        }
+    }
+
+    protected void notifyChange() {
+        mContentResolver.notifyChange(mSliceUri, null /* observer */);
+    }
+
+    @Override
+    public void onDozingChanged(boolean isDozing) {
+        final boolean notify;
+        synchronized (this) {
+            boolean neededMedia = needsMediaLocked();
+            mDozing = isDozing;
+            notify = neededMedia != needsMediaLocked();
+        }
+        if (notify) {
+            notifyChange();
+        }
+    }
+
+    @Override
+    public void onStateChanged(int newState) {
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 9ccdf79..323cf1f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -406,24 +406,6 @@
         }
 
         @Override
-        public void onPhoneStateChanged(int phoneState) {
-            synchronized (KeyguardViewMediator.this) {
-                if (TelephonyManager.CALL_STATE_IDLE == phoneState  // call ending
-                        && !mDeviceInteractive                           // screen off
-                        && mExternallyEnabled) {                // not disabled by any app
-
-                    // note: this is a way to gracefully reenable the keyguard when the call
-                    // ends and the screen is off without always reenabling the keyguard
-                    // each time the screen turns off while in call (and having an occasional ugly
-                    // flicker while turning back on the screen and disabling the keyguard again).
-                    if (DEBUG) Log.d(TAG, "screen is off and call ended, let's make sure the "
-                            + "keyguard is showing");
-                    doKeyguardLocked(null);
-                }
-            }
-        }
-
-        @Override
         public void onClockVisibilityChanged() {
             adjustStatusBarLocked();
         }
@@ -463,8 +445,7 @@
             boolean simWasLocked;
             synchronized (KeyguardViewMediator.this) {
                 IccCardConstants.State lastState = mLastSimStates.get(slotId);
-                simWasLocked = (lastState == PIN_REQUIRED || lastState == PUK_REQUIRED)
-                    && simState == READY;
+                simWasLocked = (lastState == PIN_REQUIRED || lastState == PUK_REQUIRED);
                 mLastSimStates.append(slotId, simState);
             }
 
@@ -489,6 +470,8 @@
                             // so we should only lock when they are ABSENT.
                             onSimAbsentLocked();
                             if (simWasLocked) {
+                                if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to ABSENT when the "
+                                        + "previous state was locked. Reset the state.");
                                 resetStateLocked();
                             }
                         }
@@ -525,6 +508,8 @@
                     synchronized (KeyguardViewMediator.this) {
                         if (DEBUG_SIM_STATES) Log.d(TAG, "READY, reset state? " + mShowing);
                         if (mShowing && simWasLocked) {
+                            if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to READY when the "
+                                    + "previous state was locked. Reset the state.");
                             resetStateLocked();
                         }
                         mLockWhenSimRemoved = true;
@@ -1316,15 +1301,7 @@
         if (!mExternallyEnabled) {
             if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
 
-            // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes
-            // for an occasional ugly flicker in this situation:
-            // 1) receive a call with the screen on (no keyguard) or make a call
-            // 2) screen times out
-            // 3) user hits key to turn screen back on
-            // instead, we reenable the keyguard when we know the screen is off and the call
-            // ends (see the broadcast receiver below)
-            // TODO: clean this up when we have better support at the window manager level
-            // for apps that wish to be on top of the keyguard
+            mNeedToReshowWhenReenabled = true;
             return;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java
index b6fce44..834bca5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java
@@ -23,9 +23,13 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Tracks the screen lifecycle.
  */
+@Singleton
 public class ScreenLifecycle extends Lifecycle<ScreenLifecycle.Observer> implements Dumpable {
 
     public static final int SCREEN_OFF = 0;
@@ -35,6 +39,10 @@
 
     private int mScreenState = SCREEN_OFF;
 
+    @Inject
+    public ScreenLifecycle() {
+    }
+
     public int getScreenState() {
         return mScreenState;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
index 59c7f23..52a0214 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
@@ -23,9 +23,13 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Tracks the wakefulness lifecycle.
  */
+@Singleton
 public class WakefulnessLifecycle extends Lifecycle<WakefulnessLifecycle.Observer> implements
         Dumpable {
 
@@ -36,6 +40,10 @@
 
     private int mWakefulness = WAKEFULNESS_ASLEEP;
 
+    @Inject
+    public WakefulnessLifecycle() {
+    }
+
     public int getWakefulness() {
         return mWakefulness;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
index 03daa95..6949531 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
@@ -20,11 +20,20 @@
 import com.android.systemui.plugins.PluginDependency.DependencyProvider;
 import com.android.systemui.shared.plugins.PluginManager;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
 public class PluginDependencyProvider extends DependencyProvider {
 
     private final ArrayMap<Class<?>, Object> mDependencies = new ArrayMap<>();
     private final PluginManager mManager;
 
+    /**
+     */
+    @Inject
     public PluginDependencyProvider(PluginManager manager) {
         mManager = manager;
         PluginDependency.sProvider = this;
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java
index e2417f7..6337415 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java
@@ -16,28 +16,44 @@
 
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.shared.plugins.PluginEnabler;
 
 public class PluginEnablerImpl implements PluginEnabler {
+    private static final String CRASH_DISABLED_PLUGINS_PREF_FILE = "auto_disabled_plugins_prefs";
 
-    final private PackageManager mPm;
+    private PackageManager mPm;
+    private final SharedPreferences mAutoDisabledPrefs;
 
     public PluginEnablerImpl(Context context) {
-        this(context.getPackageManager());
+        this(context, context.getPackageManager());
     }
 
-    @VisibleForTesting public PluginEnablerImpl(PackageManager pm) {
+    @VisibleForTesting public PluginEnablerImpl(Context context, PackageManager pm) {
+        mAutoDisabledPrefs = context.getSharedPreferences(
+                CRASH_DISABLED_PLUGINS_PREF_FILE, Context.MODE_PRIVATE);
         mPm = pm;
     }
 
     @Override
-    public void setEnabled(ComponentName component, boolean enabled) {
+    public void setEnabled(ComponentName component) {
+        setDisabled(component, ENABLED);
+    }
+
+    @Override
+    public void setDisabled(ComponentName component, @DisableReason int reason) {
+        boolean enabled = reason == ENABLED;
         final int desiredState = enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
                 : PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
         mPm.setComponentEnabledSetting(component, desiredState, PackageManager.DONT_KILL_APP);
+        if (enabled) {
+            mAutoDisabledPrefs.edit().remove(component.flattenToString()).apply();
+        } else {
+            mAutoDisabledPrefs.edit().putInt(component.flattenToString(), reason).apply();
+        }
     }
 
     @Override
@@ -45,4 +61,12 @@
         return mPm.getComponentEnabledSetting(component)
                 != PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
     }
+
+    @Override
+    public @DisableReason int getDisableReason(ComponentName componentName) {
+        if (isEnabled(componentName)) {
+            return ENABLED;
+        }
+        return mAutoDisabledPrefs.getInt(componentName.flattenToString(), DISABLED_MANUALLY);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index b722f9f..50dda1c 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -58,6 +58,12 @@
 import java.util.Locale;
 import java.util.Objects;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
 public class PowerNotificationWarnings implements PowerUI.WarningsUI {
     private static final String TAG = PowerUI.TAG + ".Notification";
     private static final boolean DEBUG = PowerUI.DEBUG;
@@ -136,6 +142,9 @@
     private SystemUIDialog mHighTempDialog;
     private SystemUIDialog mThermalShutdownDialog;
 
+    /**
+     */
+    @Inject
     public PowerNotificationWarnings(Context context) {
         mContext = context;
         mNoMan = mContext.getSystemService(NotificationManager.class);
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
index bbdae29..01ee5ca 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -50,6 +50,7 @@
                     object : DialogInterface.OnClickListener {
                         val intent = Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE)
 
+                        @Suppress("DEPRECATION")
                         override fun onClick(dialog: DialogInterface?, which: Int) {
                             Dependency.get(ActivityStarter::class.java).startActivity(intent, false)
                         }
@@ -106,12 +107,12 @@
         val appName = item.findViewById(R.id.app_name) as TextView
         val icons = item.findViewById(R.id.icons) as LinearLayout
 
-        var lp = LinearLayout.LayoutParams(iconSize, iconSize).apply {
+        val lp = LinearLayout.LayoutParams(iconSize, iconSize).apply {
             gravity = Gravity.CENTER_VERTICAL
             marginStart = iconMargin
         }
 
-        app.icon?.let {
+        app.icon.let {
             appIcon.setImageDrawable(it)
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
index 519df19..2894621 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
@@ -29,7 +29,9 @@
     init {
         appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType })
                 .toList()
-                .sortedWith(compareBy({ -it.second.size }, { it.first }))
+                .sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps
+                        { it.second.min() }, // Sort by "smallest" AppOpp (Location is largest)
+                        { it.first })) // Sort alphabetically bt App Name
         types = itemsList.map { it.privacyType }.distinct().sorted()
         val singleApp = appsAndTypes.size == 1
         app = if (singleApp) appsAndTypes[0].first else null
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
index 85e99f0..9252167 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
@@ -24,8 +24,8 @@
 
 enum class PrivacyType(val nameId: Int, val iconId: Int) {
     TYPE_CAMERA(R.string.privacy_type_camera, R.drawable.stat_sys_camera),
-    TYPE_LOCATION(R.string.privacy_type_location, R.drawable.stat_sys_location),
-    TYPE_MICROPHONE(R.string.privacy_type_microphone, R.drawable.stat_sys_mic_none);
+    TYPE_MICROPHONE(R.string.privacy_type_microphone, R.drawable.stat_sys_mic_none),
+    TYPE_LOCATION(R.string.privacy_type_location, R.drawable.stat_sys_location);
 
     fun getName(context: Context) = context.resources.getString(nameId)
 
@@ -44,7 +44,7 @@
         return applicationName.compareTo(other.applicationName)
     }
 
-    var icon: Drawable? = null
+    var icon: Drawable = context.getDrawable(android.R.drawable.sym_def_app_icon)
     var applicationName: String
 
     init {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index 268462e..b218e80 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.Dependency
 import com.android.systemui.appops.AppOpItem
 import com.android.systemui.appops.AppOpsController
+import com.android.systemui.R
 
 class PrivacyItemController(val context: Context, val callback: Callback) {
 
@@ -41,15 +42,20 @@
                 Intent.ACTION_MANAGED_PROFILE_ADDED,
                 Intent.ACTION_MANAGED_PROFILE_REMOVED)
         const val TAG = "PrivacyItemController"
+        const val SYSTEM_UID = 1000
     }
-
     private var privacyList = emptyList<PrivacyItem>()
+
+    @Suppress("DEPRECATION")
     private val appOpsController = Dependency.get(AppOpsController::class.java)
     private val userManager = context.getSystemService(UserManager::class.java)
     private var currentUserIds = emptyList<Int>()
+    @Suppress("DEPRECATION")
     private val bgHandler = Handler(Dependency.get(Dependency.BG_LOOPER))
+    @Suppress("DEPRECATION")
     private val uiHandler = Dependency.get(Dependency.MAIN_HANDLER)
     private var listening = false
+    val systemApp = PrivacyApplication(context.getString(R.string.device_services), context)
 
     private val notifyChanges = Runnable {
         callback.privacyChanged(privacyList)
@@ -115,7 +121,7 @@
 
     private fun updatePrivacyList() {
         privacyList = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) }
-                .mapNotNull { toPrivacyItem(it) }
+                .mapNotNull { toPrivacyItem(it) }.distinct()
     }
 
     private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
@@ -126,6 +132,7 @@
             AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
             else -> return null
         }
+        if (appOpItem.uid == SYSTEM_UID) return PrivacyItem(type, systemApp)
         val app = PrivacyApplication(appOpItem.packageName, context)
         return PrivacyItem(type, app)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
index dfd3f73..2365e67 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
@@ -35,6 +35,8 @@
 import java.util.Collection;
 import java.util.Collections;
 
+import javax.inject.Inject;
+
 public class AutoAddTracker {
 
     private static final String[][] CONVERT_PREFS = {
@@ -48,6 +50,7 @@
     private final ArraySet<String> mAutoAdded;
     private final Context mContext;
 
+    @Inject
     public AutoAddTracker(Context context) {
         mContext = context;
         mAutoAdded = new ArraySet<>(getAdded());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 8903a38..e63f88a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -18,6 +18,8 @@
 
 import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
 
+import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
+
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.UserInfo;
@@ -48,7 +50,6 @@
 import com.android.settingslib.Utils;
 import com.android.settingslib.drawable.UserIconDrawable;
 import com.android.settingslib.graph.SignalDrawable;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.R.dimen;
 import com.android.systemui.plugins.ActivityStarter;
@@ -63,11 +64,16 @@
 import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
 import com.android.systemui.tuner.TunerService;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+
 public class QSFooterImpl extends FrameLayout implements QSFooter,
         OnClickListener, OnUserInfoChangedListener, EmergencyListener, SignalCallback {
 
-    private ActivityStarter mActivityStarter;
-    private UserInfoController mUserInfoController;
+    private final ActivityStarter mActivityStarter;
+    private final UserInfoController mUserInfoController;
+    private final NetworkController mNetworkController;
+    private final DeviceProvisionedController mDeviceProvisionedController;
     private SettingsButton mSettingsButton;
     protected View mSettingsContainer;
     private PageIndicator mPageIndicator;
@@ -100,9 +106,17 @@
     private final CellSignalState mInfo = new CellSignalState();
     private OnClickListener mExpandClickListener;
 
-    public QSFooterImpl(Context context, AttributeSet attrs) {
+    @Inject
+    public QSFooterImpl(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
+            ActivityStarter activityStarter, UserInfoController userInfoController,
+            NetworkController networkController,
+            DeviceProvisionedController deviceProvisionedController) {
         super(context, attrs);
         mColorForeground = Utils.getColorAttrDefaultColor(context, android.R.attr.colorForeground);
+        mActivityStarter = activityStarter;
+        mUserInfoController = userInfoController;
+        mNetworkController = networkController;
+        mDeviceProvisionedController = deviceProvisionedController;
     }
 
     @Override
@@ -111,7 +125,7 @@
         mDivider = findViewById(R.id.qs_footer_divider);
         mEdit = findViewById(android.R.id.edit);
         mEdit.setOnClickListener(view ->
-                Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() ->
+                mActivityStarter.postQSRunnableDismissingKeyguard(() ->
                         mQsPanel.showEdit(view)));
 
         mPageIndicator = findViewById(R.id.footer_page_indicator);
@@ -137,8 +151,6 @@
 
         updateResources();
 
-        mUserInfoController = Dependency.get(UserInfoController.class);
-        mActivityStarter = Dependency.get(ActivityStarter.class);
         addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight,
                 oldBottom) -> updateAnimator(right - left));
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
@@ -272,10 +284,17 @@
     public void updateEverything() {
         post(() -> {
             updateVisibilities();
+            updateClickabilities();
             setClickable(false);
         });
     }
 
+    private void updateClickabilities() {
+        mMultiUserSwitch.setClickable(mMultiUserSwitch.getVisibility() == View.VISIBLE);
+        mEdit.setClickable(mEdit.getVisibility() == View.VISIBLE);
+        mSettingsButton.setClickable(mSettingsButton.getVisibility() == View.VISIBLE);
+    }
+
     private void updateVisibilities() {
         mSettingsContainer.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
         mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
@@ -309,14 +328,14 @@
     private void updateListeners() {
         if (mListening) {
             mUserInfoController.addCallback(this);
-            if (Dependency.get(NetworkController.class).hasVoiceCallingFeature()) {
-                Dependency.get(NetworkController.class).addEmergencyListener(this);
-                Dependency.get(NetworkController.class).addCallback(this);
+            if (mNetworkController.hasVoiceCallingFeature()) {
+                mNetworkController.addEmergencyListener(this);
+                mNetworkController.addCallback(this);
             }
         } else {
             mUserInfoController.removeCallback(this);
-            Dependency.get(NetworkController.class).removeEmergencyListener(this);
-            Dependency.get(NetworkController.class).removeCallback(this);
+            mNetworkController.removeEmergencyListener(this);
+            mNetworkController.removeCallback(this);
         }
     }
 
@@ -337,7 +356,7 @@
         }
 
         if (v == mSettingsButton) {
-            if (!Dependency.get(DeviceProvisionedController.class).isCurrentUserSetup()) {
+            if (!mDeviceProvisionedController.isCurrentUserSetup()) {
                 // If user isn't setup just unlock the device and dump them back at SUW.
                 mActivityStarter.postQSRunnableDismissingKeyguard(() -> { });
                 return;
@@ -346,7 +365,7 @@
                     mExpanded ? MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH
                             : MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH);
             if (mSettingsButton.isTunerClick()) {
-                Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() -> {
+                mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
                     if (TunerService.isTunerEnabled(mContext)) {
                         TunerService.showResetRequest(mContext, () -> {
                             // Relaunch settings so that the tuner disappears.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 2acbea4..087a826 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -18,7 +18,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.app.Fragment;
+import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -35,7 +35,6 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.R.id;
@@ -46,8 +45,12 @@
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
+import com.android.systemui.util.InjectionInflationController;
+import com.android.systemui.util.LifecycleFragment;
 
-public class QSFragment extends Fragment implements QS, CommandQueue.Callbacks {
+import javax.inject.Inject;
+
+public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Callbacks {
     private static final String TAG = "QS";
     private static final boolean DEBUG = false;
     private static final String EXTRA_EXPANDED = "expanded";
@@ -74,13 +77,27 @@
     private float mLastQSExpansion = -1;
     private boolean mQsDisabled;
 
-    private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler =
-            Dependency.get(RemoteInputQuickSettingsDisabler.class);
+    private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
+    private final InjectionInflationController mInjectionInflater;
+    private final QSTileHost mHost;
+
+    @Inject
+    public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
+            InjectionInflationController injectionInflater,
+            Context context,
+            QSTileHost qsTileHost) {
+        mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
+        mInjectionInflater = injectionInflater;
+        SysUiServiceProvider.getComponent(context, CommandQueue.class)
+                .observe(getLifecycle(), this);
+        mHost = qsTileHost;
+    }
 
     @Override
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
             Bundle savedInstanceState) {
-        inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(), R.style.qs_theme));
+        inflater = mInjectionInflater.injectable(
+                inflater.cloneInContext(new ContextThemeWrapper(getContext(), R.style.qs_theme)));
         return inflater.inflate(R.layout.qs_panel, container, false);
     }
 
@@ -108,13 +125,7 @@
                 mQSPanel.getTileLayout().restoreInstanceState(savedInstanceState);
             }
         }
-        SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallback(this);
-    }
-
-    @Override
-    public void onDestroyView() {
-        SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallback(this);
-        super.onDestroyView();
+        setHost(mHost);
     }
 
     @Override
@@ -204,7 +215,10 @@
     }
 
     @Override
-    public void disable(int state1, int state2, boolean animate) {
+    public void disable(int displayId, int state1, int state2, boolean animate) {
+        if (displayId != getContext().getDisplayId()) {
+            return;
+        }
         state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
 
         final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index d7d3981..7c937a9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -17,6 +17,7 @@
 
 import android.app.ActivityManager;
 import android.app.AlertDialog;
+import android.app.admin.DevicePolicyEventLogger;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
@@ -30,6 +31,7 @@
 import android.text.method.LinkMovementMethod;
 import android.text.style.ClickableSpan;
 import android.util.Log;
+import android.util.StatsLog;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -119,6 +121,9 @@
 
     private void handleClick() {
         showDeviceMonitoringDialog();
+        DevicePolicyEventLogger
+                .createEvent(StatsLog.DEVICE_POLICY_EVENT__EVENT_ID__DO_USER_INFO_CLICKED)
+                .write();
     }
 
     public void showDeviceMonitoringDialog() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index ca8e824..dfc3e66 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -20,6 +20,7 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -30,6 +31,7 @@
 
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.qs.QSTile;
@@ -52,7 +54,13 @@
 import java.util.List;
 import java.util.function.Predicate;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
 /** Platform implementation of the quick settings tile host **/
+@Singleton
 public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory> {
     private static final String TAG = "QSTileHost";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -60,31 +68,47 @@
     public static final String TILES_SETTING = Secure.QS_TILES;
 
     private final Context mContext;
-    private final StatusBar mStatusBar;
     private final LinkedHashMap<String, QSTile> mTiles = new LinkedHashMap<>();
     protected final ArrayList<String> mTileSpecs = new ArrayList<>();
     private final TileServices mServices;
+    private final TunerService mTunerService;
+    private final PluginManager mPluginManager;
 
     private final List<Callback> mCallbacks = new ArrayList<>();
-    private final AutoTileManager mAutoTiles;
+    private AutoTileManager mAutoTiles;
     private final StatusBarIconController mIconController;
     private final ArrayList<QSFactory> mQsFactories = new ArrayList<>();
     private int mCurrentUser;
+    private StatusBar mStatusBar;
 
-    public QSTileHost(Context context, StatusBar statusBar,
-            StatusBarIconController iconController) {
+    @Inject
+    public QSTileHost(Context context,
+            StatusBarIconController iconController,
+            QSFactoryImpl defaultFactory,
+            @Named(Dependency.MAIN_HANDLER_NAME) Handler mainHandler,
+            @Named(Dependency.BG_LOOPER_NAME) Looper bgLooper,
+            PluginManager pluginManager,
+            TunerService tunerService,
+            Provider<AutoTileManager> autoTiles) {
         mIconController = iconController;
         mContext = context;
-        mStatusBar = statusBar;
+        mTunerService = tunerService;
+        mPluginManager = pluginManager;
 
-        mServices = new TileServices(this, Dependency.get(Dependency.BG_LOOPER));
+        mServices = new TileServices(this, bgLooper);
 
-        mQsFactories.add(new QSFactoryImpl(this));
-        Dependency.get(PluginManager.class).addPluginListener(this, QSFactory.class, true);
+        defaultFactory.setHost(this);
+        mQsFactories.add(defaultFactory);
+        pluginManager.addPluginListener(this, QSFactory.class, true);
 
-        Dependency.get(TunerService.class).addTunable(this, TILES_SETTING);
-        // AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
-        mAutoTiles = new AutoTileManager(context, this);
+        mainHandler.post(() -> {
+            // This is technically a hack to avoid circular dependency of
+            // QSTileHost -> XXXTile -> QSTileHost. Posting ensures creation
+            // finishes before creating any tiles.
+            tunerService.addTunable(this, TILES_SETTING);
+            // AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
+            mAutoTiles = autoTiles.get();
+        });
     }
 
     public StatusBarIconController getIconController() {
@@ -94,16 +118,16 @@
     public void destroy() {
         mTiles.values().forEach(tile -> tile.destroy());
         mAutoTiles.destroy();
-        Dependency.get(TunerService.class).removeTunable(this);
+        mTunerService.removeTunable(this);
         mServices.destroy();
-        Dependency.get(PluginManager.class).removePluginListener(this);
+        mPluginManager.removePluginListener(this);
     }
 
     @Override
     public void onPluginConnected(QSFactory plugin, Context pluginContext) {
         // Give plugins priority over creation so they can override if they wish.
         mQsFactories.add(0, plugin);
-        String value = Dependency.get(TunerService.class).getValue(TILES_SETTING);
+        String value = mTunerService.getValue(TILES_SETTING);
         // Force remove and recreate of all tiles.
         onTuningChanged(TILES_SETTING, "");
         onTuningChanged(TILES_SETTING, value);
@@ -113,7 +137,7 @@
     public void onPluginDisconnected(QSFactory plugin) {
         mQsFactories.remove(plugin);
         // Force remove and recreate of all tiles.
-        String value = Dependency.get(TunerService.class).getValue(TILES_SETTING);
+        String value = mTunerService.getValue(TILES_SETTING);
         onTuningChanged(TILES_SETTING, "");
         onTuningChanged(TILES_SETTING, value);
     }
@@ -140,16 +164,25 @@
 
     @Override
     public void collapsePanels() {
+        if (mStatusBar == null) {
+            mStatusBar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
+        }
         mStatusBar.postAnimateCollapsePanels();
     }
 
     @Override
     public void forceCollapsePanels() {
+        if (mStatusBar == null) {
+            mStatusBar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
+        }
         mStatusBar.postAnimateForceCollapsePanels();
     }
 
     @Override
     public void openPanels() {
+        if (mStatusBar == null) {
+            mStatusBar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
+        }
         mStatusBar.postAnimateOpenPanels();
     }
 
@@ -233,7 +266,7 @@
 
     @Override
     public void unmarkTileAsAutoAdded(String spec) {
-        mAutoTiles.unmarkTileAsAutoAdded(spec);
+        if (mAutoTiles != null) mAutoTiles.unmarkTileAsAutoAdded(spec);
     }
 
     public void addTile(String spec) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 3cecff0..7224599 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -17,6 +17,8 @@
 import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
 import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
 
+import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.annotation.ColorInt;
@@ -58,10 +60,11 @@
 
 import com.android.settingslib.Utils;
 import com.android.systemui.BatteryMeterView;
-import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.privacy.OngoingPrivacyChip;
 import com.android.systemui.privacy.OngoingPrivacyDialog;
 import com.android.systemui.privacy.PrivacyItem;
@@ -74,8 +77,6 @@
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.Clock;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.policy.DateView;
 import com.android.systemui.statusbar.policy.NextAlarmController;
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -84,6 +85,9 @@
 import java.util.Locale;
 import java.util.Objects;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+
 /**
  * View that contains the top-most bits of the screen (primarily the status bar with date, time, and
  * battery) and also contains the {@link QuickQSPanel} along with some of the panel's inner
@@ -102,6 +106,11 @@
     public static final int MAX_TOOLTIP_SHOWN_COUNT = 2;
 
     private final Handler mHandler = new Handler();
+    private final BatteryController mBatteryController;
+    private final NextAlarmController mAlarmController;
+    private final ZenModeController mZenController;
+    private final StatusBarIconController mStatusBarIconController;
+    private final ActivityStarter mActivityStarter;
 
     private QSPanel mQsPanel;
 
@@ -141,8 +150,6 @@
     private TextView mBatteryRemainingText;
     private boolean mShowBatteryPercentAndEstimate;
 
-    private NextAlarmController mAlarmController;
-    private ZenModeController mZenController;
     private PrivacyItemController mPrivacyItemController;
     /** Counts how many times the long press tooltip has been shown to the user. */
     private int mShownCount;
@@ -172,10 +179,17 @@
         }
     };
 
-    public QuickStatusBarHeader(Context context, AttributeSet attrs) {
+    @Inject
+    public QuickStatusBarHeader(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
+            NextAlarmController nextAlarmController, ZenModeController zenModeController,
+            BatteryController batteryController, StatusBarIconController statusBarIconController,
+            ActivityStarter activityStarter) {
         super(context, attrs);
-        mAlarmController = Dependency.get(NextAlarmController.class);
-        mZenController = Dependency.get(ZenModeController.class);
+        mAlarmController = nextAlarmController;
+        mZenController = zenModeController;
+        mBatteryController = batteryController;
+        mStatusBarIconController = statusBarIconController;
+        mActivityStarter = activityStarter;
         mPrivacyItemController = new PrivacyItemController(context, mPICCallback);
         mShownCount = getStoredShownCount();
     }
@@ -405,15 +419,13 @@
         if (!mShowBatteryPercentAndEstimate) {
             return;
         }
-        mBatteryRemainingText.setText(
-                Dependency.get(BatteryController.class).getEstimatedTimeRemainingString());
+        mBatteryRemainingText.setText(mBatteryController.getEstimatedTimeRemainingString());
     }
 
     public void setExpanded(boolean expanded) {
         if (mExpanded == expanded) return;
         mExpanded = expanded;
         mHeaderQsPanel.setExpanded(expanded);
-        updateEverything();
     }
 
     /**
@@ -472,7 +484,7 @@
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
-        Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager);
+        mStatusBarIconController.addIconGroup(mIconManager);
         requestApplyInsets();
         mContext.getContentResolver().registerContentObserver(
                 Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mPercentSettingObserver,
@@ -515,7 +527,7 @@
     @VisibleForTesting
     public void onDetachedFromWindow() {
         setListening(false);
-        Dependency.get(StatusBarIconController.class).removeIconGroup(mIconManager);
+        mStatusBarIconController.removeIconGroup(mIconManager);
         mContext.getContentResolver().unregisterContentObserver(mPercentSettingObserver);
         super.onDetachedFromWindow();
     }
@@ -544,10 +556,10 @@
     @Override
     public void onClick(View v) {
         if (v == mClockView) {
-            Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
+            mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
                     AlarmClock.ACTION_SHOW_ALARMS),0);
         } else if (v == mBatteryMeterView) {
-            Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
+            mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
                     Intent.ACTION_POWER_USAGE_SUMMARY),0);
         } else if (v == mPrivacyChip) {
             Handler mUiHandler = new Handler(Looper.getMainLooper());
@@ -685,10 +697,6 @@
                 .start();
     }
 
-    public void updateEverything() {
-        post(() -> setClickable(false));
-    }
-
     public void setQSPanel(final QSPanel qsPanel) {
         mQsPanel = qsPanel;
         setupHost(qsPanel.getHost());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 9d2be39..4dcacd4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -87,6 +87,8 @@
         if (current != null) {
             // The setting QS_TILES is not populated immediately upon Factory Reset
             possibleTiles.addAll(Arrays.asList(current.split(",")));
+        } else {
+            current = "";
         }
         String[] stockSplit =  stock.split(",");
         for (String spec : stockSplit) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 8906665..466c808 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -183,6 +183,7 @@
     public void updateState(Tile tile) {
         mTile.setIcon(tile.getIcon());
         mTile.setLabel(tile.getLabel());
+        mTile.setSubtitle(tile.getSubtitle());
         mTile.setContentDescription(tile.getContentDescription());
         mTile.setState(tile.getState());
     }
@@ -322,6 +323,14 @@
             return null;
         };
         state.label = mTile.getLabel();
+
+        CharSequence subtitle = mTile.getSubtitle();
+        if (subtitle != null && subtitle.length() > 0) {
+            state.secondaryLabel = subtitle;
+        } else {
+            state.secondaryLabel = null;
+        }
+
         if (mTile.getContentDescription() != null) {
             state.contentDescription = mTile.getContentDescription();
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 7d52f0b..d26cee9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -41,17 +41,85 @@
 import com.android.systemui.qs.tiles.NfcTile;
 import com.android.systemui.qs.tiles.NightDisplayTile;
 import com.android.systemui.qs.tiles.RotationLockTile;
+import com.android.systemui.qs.tiles.SensorPrivacyTile;
 import com.android.systemui.qs.tiles.UserTile;
 import com.android.systemui.qs.tiles.WifiTile;
 import com.android.systemui.qs.tiles.WorkModeTile;
 import com.android.systemui.util.leak.GarbageMonitor;
 
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+@Singleton
 public class QSFactoryImpl implements QSFactory {
 
     private static final String TAG = "QSFactory";
-    private final QSTileHost mHost;
 
-    public QSFactoryImpl(QSTileHost host) {
+    private final Provider<WifiTile> mWifiTileProvider;
+    private final Provider<BluetoothTile> mBluetoothTileProvider;
+    private final Provider<CellularTile> mCellularTileProvider;
+    private final Provider<DndTile> mDndTileProvider;
+    private final Provider<ColorInversionTile> mColorInversionTileProvider;
+    private final Provider<AirplaneModeTile> mAirplaneModeTileProvider;
+    private final Provider<WorkModeTile> mWorkModeTileProvider;
+    private final Provider<RotationLockTile> mRotationLockTileProvider;
+    private final Provider<FlashlightTile> mFlashlightTileProvider;
+    private final Provider<LocationTile> mLocationTileProvider;
+    private final Provider<CastTile> mCastTileProvider;
+    private final Provider<HotspotTile> mHotspotTileProvider;
+    private final Provider<UserTile> mUserTileProvider;
+    private final Provider<BatterySaverTile> mBatterySaverTileProvider;
+    private final Provider<DataSaverTile> mDataSaverTileProvider;
+    private final Provider<NightDisplayTile> mNightDisplayTileProvider;
+    private final Provider<NfcTile> mNfcTileProvider;
+    private final Provider<SensorPrivacyTile> mSensorPrivacyTileProvider;
+    private final Provider<GarbageMonitor.MemoryTile> mMemoryTileProvider;
+
+    private QSTileHost mHost;
+
+    @Inject
+    public QSFactoryImpl(Provider<WifiTile> wifiTileProvider,
+            Provider<BluetoothTile> bluetoothTileProvider,
+            Provider<CellularTile> cellularTileProvider,
+            Provider<DndTile> dndTileProvider,
+            Provider<ColorInversionTile> colorInversionTileProvider,
+            Provider<AirplaneModeTile> airplaneModeTileProvider,
+            Provider<WorkModeTile> workModeTileProvider,
+            Provider<RotationLockTile> rotationLockTileProvider,
+            Provider<FlashlightTile> flashlightTileProvider,
+            Provider<LocationTile> locationTileProvider,
+            Provider<CastTile> castTileProvider,
+            Provider<HotspotTile> hotspotTileProvider,
+            Provider<UserTile> userTileProvider,
+            Provider<BatterySaverTile> batterySaverTileProvider,
+            Provider<DataSaverTile> dataSaverTileProvider,
+            Provider<NightDisplayTile> nightDisplayTileProvider,
+            Provider<NfcTile> nfcTileProvider,
+            Provider<SensorPrivacyTile> sensorPrivacyTileProvider,
+            Provider<GarbageMonitor.MemoryTile> memoryTileProvider) {
+        mWifiTileProvider = wifiTileProvider;
+        mBluetoothTileProvider = bluetoothTileProvider;
+        mCellularTileProvider = cellularTileProvider;
+        mDndTileProvider = dndTileProvider;
+        mColorInversionTileProvider = colorInversionTileProvider;
+        mAirplaneModeTileProvider = airplaneModeTileProvider;
+        mWorkModeTileProvider = workModeTileProvider;
+        mRotationLockTileProvider = rotationLockTileProvider;
+        mFlashlightTileProvider = flashlightTileProvider;
+        mLocationTileProvider = locationTileProvider;
+        mCastTileProvider = castTileProvider;
+        mHotspotTileProvider = hotspotTileProvider;
+        mUserTileProvider = userTileProvider;
+        mBatterySaverTileProvider = batterySaverTileProvider;
+        mDataSaverTileProvider = dataSaverTileProvider;
+        mNightDisplayTileProvider = nightDisplayTileProvider;
+        mNfcTileProvider = nfcTileProvider;
+        mSensorPrivacyTileProvider = sensorPrivacyTileProvider;
+        mMemoryTileProvider = memoryTileProvider;
+    }
+
+    public void setHost(QSTileHost host) {
         mHost = host;
     }
 
@@ -67,39 +135,41 @@
         // Stock tiles.
         switch (tileSpec) {
             case "wifi":
-                return new WifiTile(mHost);
+                return mWifiTileProvider.get();
             case "bt":
-                return new BluetoothTile(mHost);
+                return mBluetoothTileProvider.get();
             case "cell":
-                return new CellularTile(mHost);
+                return mCellularTileProvider.get();
             case "dnd":
-                return new DndTile(mHost);
+                return mDndTileProvider.get();
             case "inversion":
-                return new ColorInversionTile(mHost);
+                return mColorInversionTileProvider.get();
             case "airplane":
-                return new AirplaneModeTile(mHost);
+                return mAirplaneModeTileProvider.get();
             case "work":
-                return new WorkModeTile(mHost);
+                return mWorkModeTileProvider.get();
             case "rotation":
-                return new RotationLockTile(mHost);
+                return mRotationLockTileProvider.get();
             case "flashlight":
-                return new FlashlightTile(mHost);
+                return mFlashlightTileProvider.get();
             case "location":
-                return new LocationTile(mHost);
+                return mLocationTileProvider.get();
             case "cast":
-                return new CastTile(mHost);
+                return mCastTileProvider.get();
             case "hotspot":
-                return new HotspotTile(mHost);
+                return mHotspotTileProvider.get();
             case "user":
-                return new UserTile(mHost);
+                return mUserTileProvider.get();
             case "battery":
-                return new BatterySaverTile(mHost);
+                return mBatterySaverTileProvider.get();
             case "saver":
-                return new DataSaverTile(mHost);
+                return mDataSaverTileProvider.get();
             case "night":
-                return new NightDisplayTile(mHost);
+                return mNightDisplayTileProvider.get();
             case "nfc":
-                return new NfcTile(mHost);
+                return mNfcTileProvider.get();
+            case "sensorprivacy":
+                return mSensorPrivacyTileProvider.get();
         }
 
         // Intent tiles.
@@ -109,7 +179,7 @@
         // Debug tiles.
         if (Build.IS_DEBUGGABLE) {
             if (tileSpec.equals(GarbageMonitor.MemoryTile.TILE_SPEC)) {
-                return new GarbageMonitor.MemoryTile(mHost);
+                return mMemoryTileProvider.get();
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index cc27135..e1a4378 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -14,6 +14,9 @@
 
 package com.android.systemui.qs.tileimpl;
 
+import static androidx.lifecycle.Lifecycle.State.DESTROYED;
+import static androidx.lifecycle.Lifecycle.State.RESUMED;
+
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_CLICK;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_LONG_PRESS;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_SECONDARY_CLICK;
@@ -38,6 +41,11 @@
 import android.util.Log;
 import android.util.SparseArray;
 
+import androidx.annotation.NonNull;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.settingslib.RestrictedLockUtils;
@@ -66,7 +74,7 @@
  *
  * @param <TState> see above
  */
-public abstract class QSTileImpl<TState extends State> implements QSTile {
+public abstract class QSTileImpl<TState extends State> implements QSTile, LifecycleOwner {
     protected final String TAG = "Tile." + getClass().getSimpleName();
     protected static final boolean DEBUG = Log.isLoggable("Tile", Log.DEBUG);
 
@@ -94,6 +102,8 @@
     private boolean mShowingDetail;
     private int mIsFullQs;
 
+    private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
+
     public abstract TState newTileState();
 
     abstract protected void handleClick();
@@ -113,6 +123,12 @@
         mContext = host.getContext();
     }
 
+    @NonNull
+    @Override
+    public Lifecycle getLifecycle() {
+        return mLifecycle;
+    }
+
     /**
      * Adds or removes a listening client for the tile. If the tile has one or more
      * listening client it will go into the listening state.
@@ -341,12 +357,14 @@
         if (listening) {
             if (mListeners.add(listener) && mListeners.size() == 1) {
                 if (DEBUG) Log.d(TAG, "handleSetListening true");
+                mLifecycle.markState(RESUMED);
                 handleSetListening(listening);
                 refreshState(); // Ensure we get at least one refresh after listening.
             }
         } else {
             if (mListeners.remove(listener) && mListeners.size() == 0) {
                 if (DEBUG) Log.d(TAG, "handleSetListening false");
+                mLifecycle.markState(DESTROYED);
                 handleSetListening(listening);
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index c8c2158..387de71 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -32,7 +32,6 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.TelephonyProperties;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
@@ -40,16 +39,21 @@
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 
+import javax.inject.Inject;
+
 /** Quick settings tile: Airplane mode **/
 public class AirplaneModeTile extends QSTileImpl<BooleanState> {
     private final Icon mIcon =
             ResourceIcon.get(R.drawable.ic_signal_airplane);
     private final GlobalSetting mSetting;
+    private final ActivityStarter mActivityStarter;
 
     private boolean mListening;
 
-    public AirplaneModeTile(QSHost host) {
+    @Inject
+    public AirplaneModeTile(QSHost host, ActivityStarter activityStarter) {
         super(host);
+        mActivityStarter = activityStarter;
 
         mSetting = new GlobalSetting(mContext, mHandler, Global.AIRPLANE_MODE_ON) {
             @Override
@@ -70,7 +74,7 @@
         MetricsLogger.action(mContext, getMetricsCategory(), !airplaneModeEnabled);
         if (!airplaneModeEnabled && Boolean.parseBoolean(
                 SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
-            Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(
+            mActivityStarter.postStartActivityDismissingKeyguard(
                     new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS), 0);
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index da2828e..7f76900 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -23,13 +23,14 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.graph.BatteryMeterDrawableBase;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.statusbar.policy.BatteryController;
 
+import javax.inject.Inject;
+
 public class BatterySaverTile extends QSTileImpl<BooleanState> implements
         BatteryController.BatteryStateChangeCallback {
 
@@ -40,9 +41,11 @@
     private boolean mCharging;
     private boolean mPluggedIn;
 
-    public BatterySaverTile(QSHost host) {
+    @Inject
+    public BatterySaverTile(QSHost host, BatteryController batteryController) {
         super(host);
-        mBatteryController = Dependency.get(BatteryController.class);
+        mBatteryController = batteryController;
+        mBatteryController.observe(getLifecycle(), this);
     }
 
     @Override
@@ -57,11 +60,6 @@
 
     @Override
     public void handleSetListening(boolean listening) {
-        if (listening) {
-            mBatteryController.addCallback(this);
-        } else {
-            mBatteryController.removeCallback(this);
-        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 3ab1c21..5b85498 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -36,7 +36,6 @@
 import com.android.settingslib.Utils;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.DetailAdapter;
@@ -51,6 +50,8 @@
 import java.util.Collection;
 import java.util.List;
 
+import javax.inject.Inject;
+
 /** Quick settings tile: Bluetooth **/
 public class BluetoothTile extends QSTileImpl<BooleanState> {
     private static final Intent BLUETOOTH_SETTINGS = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
@@ -59,11 +60,15 @@
     private final BluetoothDetailAdapter mDetailAdapter;
     private final ActivityStarter mActivityStarter;
 
-    public BluetoothTile(QSHost host) {
+    @Inject
+    public BluetoothTile(QSHost host,
+            BluetoothController bluetoothController,
+            ActivityStarter activityStarter) {
         super(host);
-        mController = Dependency.get(BluetoothController.class);
-        mActivityStarter = Dependency.get(ActivityStarter.class);
+        mController = bluetoothController;
+        mActivityStarter = activityStarter;
         mDetailAdapter = (BluetoothDetailAdapter) createDetailAdapter();
+        mController.observe(getLifecycle(), mCallback);
     }
 
     @Override
@@ -78,11 +83,6 @@
 
     @Override
     public void handleSetListening(boolean listening) {
-        if (listening) {
-            mController.addCallback(mCallback);
-        } else {
-            mController.removeCallback(mCallback);
-        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 921db69..f05ac4c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -33,7 +33,6 @@
 import com.android.internal.app.MediaRouteDialogPresenter;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.DetailAdapter;
@@ -51,6 +50,8 @@
 import java.util.LinkedHashMap;
 import java.util.Set;
 
+import javax.inject.Inject;
+
 /** Quick settings tile: Cast **/
 public class CastTile extends QSTileImpl<BooleanState> {
     private static final Intent CAST_SETTINGS =
@@ -65,13 +66,18 @@
     private Dialog mDialog;
     private boolean mWifiConnected;
 
-    public CastTile(QSHost host) {
+    @Inject
+    public CastTile(QSHost host, CastController castController, KeyguardMonitor keyguardMonitor,
+            NetworkController networkController, ActivityStarter activityStarter) {
         super(host);
-        mController = Dependency.get(CastController.class);
+        mController = castController;
         mDetailAdapter = new CastDetailAdapter();
-        mKeyguard = Dependency.get(KeyguardMonitor.class);
-        mNetworkController = Dependency.get(NetworkController.class);
-        mActivityStarter = Dependency.get(ActivityStarter.class);
+        mKeyguard = keyguardMonitor;
+        mNetworkController = networkController;
+        mActivityStarter = activityStarter;
+        mController.observe(this, mCallback);
+        mKeyguard.observe(this, mCallback);
+        mNetworkController.observe(this, mSignalCallback);
     }
 
     @Override
@@ -87,15 +93,8 @@
     @Override
     public void handleSetListening(boolean listening) {
         if (DEBUG) Log.d(TAG, "handleSetListening " + listening);
-        if (listening) {
-            mController.addCallback(mCallback);
-            mKeyguard.addCallback(mCallback);
-            mNetworkController.addCallback(mSignalCallback);
-        } else {
+        if (!listening) {
             mController.setDiscovering(false);
-            mController.removeCallback(mCallback);
-            mKeyguard.removeCallback(mCallback);
-            mNetworkController.removeCallback(mSignalCallback);
         }
     }
 
@@ -135,7 +134,7 @@
             mDialog = MediaRouteDialogPresenter.createDialog(mContext, ROUTE_TYPE_REMOTE_DISPLAY,
                     v -> {
                         mDialog.dismiss();
-                        Dependency.get(ActivityStarter.class)
+                        mActivityStarter
                                 .postStartActivityDismissingKeyguard(getLongClickIntent(), 0);
                     });
             mDialog.getWindow().setType(LayoutParams.TYPE_KEYGUARD_DIALOG);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 9e341e2..1155a41 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -35,7 +35,6 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.net.DataUsageController;
-import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
@@ -51,6 +50,8 @@
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
 
+import javax.inject.Inject;
+
 /** Quick settings tile: Cellular **/
 public class CellularTile extends QSTileImpl<SignalState> {
     private static final String ENABLE_SETTINGS_DATA_PLAN = "enable.settings.data.plan";
@@ -63,13 +64,16 @@
     private final ActivityStarter mActivityStarter;
     private final KeyguardMonitor mKeyguardMonitor;
 
-    public CellularTile(QSHost host) {
+    @Inject
+    public CellularTile(QSHost host, NetworkController networkController,
+            ActivityStarter activityStarter, KeyguardMonitor keyguardMonitor) {
         super(host);
-        mController = Dependency.get(NetworkController.class);
-        mActivityStarter = Dependency.get(ActivityStarter.class);
-        mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+        mController = networkController;
+        mActivityStarter = activityStarter;
+        mKeyguardMonitor = keyguardMonitor;
         mDataController = mController.getMobileDataController();
         mDetailAdapter = new CellularDetailAdapter();
+        mController.observe(getLifecycle(), mSignalCallback);
     }
 
     @Override
@@ -84,11 +88,6 @@
 
     @Override
     public void handleSetListening(boolean listening) {
-        if (listening) {
-            mController.addCallback(mSignalCallback);
-        } else {
-            mController.removeCallback(mSignalCallback);
-        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index c13a07f..ecb4048 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -30,6 +30,8 @@
 import com.android.systemui.qs.SecureSetting;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 
+import javax.inject.Inject;
+
 /** Quick settings tile: Invert colors **/
 public class ColorInversionTile extends QSTileImpl<BooleanState> {
 
@@ -38,6 +40,7 @@
 
     private boolean mListening;
 
+    @Inject
     public ColorInversionTile(QSHost host) {
         super(host);
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index fd8b9c9..c6c6f87 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -20,7 +20,6 @@
 import android.widget.Switch;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
@@ -30,14 +29,18 @@
 import com.android.systemui.statusbar.policy.DataSaverController;
 import com.android.systemui.statusbar.policy.NetworkController;
 
+import javax.inject.Inject;
+
 public class DataSaverTile extends QSTileImpl<BooleanState> implements
         DataSaverController.Listener{
 
     private final DataSaverController mDataSaverController;
 
-    public DataSaverTile(QSHost host) {
+    @Inject
+    public DataSaverTile(QSHost host, NetworkController networkController) {
         super(host);
-        mDataSaverController = Dependency.get(NetworkController.class).getDataSaverController();
+        mDataSaverController = networkController.getDataSaverController();
+        mDataSaverController.observe(getLifecycle(), this);
     }
 
     @Override
@@ -47,11 +50,6 @@
 
     @Override
     public void handleSetListening(boolean listening) {
-        if (listening) {
-            mDataSaverController.addCallback(this);
-        } else {
-            mDataSaverController.removeCallback(this);
-        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 50c8698..5f04e56 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -49,7 +49,6 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.notification.EnableZenModeDialog;
-import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.SysUIToast;
@@ -62,6 +61,8 @@
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.volume.ZenModePanel;
 
+import javax.inject.Inject;
+
 /** Quick settings tile: Do not disturb **/
 public class DndTile extends QSTileImpl<BooleanState> {
 
@@ -76,17 +77,22 @@
 
     private final ZenModeController mController;
     private final DndDetailAdapter mDetailAdapter;
+    private final ActivityStarter mActivityStarter;
 
     private boolean mListening;
     private boolean mShowingDetail;
     private boolean mReceiverRegistered;
 
-    public DndTile(QSHost host) {
+    @Inject
+    public DndTile(QSHost host, ZenModeController zenModeController,
+            ActivityStarter activityStarter) {
         super(host);
-        mController = Dependency.get(ZenModeController.class);
+        mController = zenModeController;
+        mActivityStarter = activityStarter;
         mDetailAdapter = new DndDetailAdapter();
         mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_SET_VISIBLE));
         mReceiverRegistered = true;
+        mController.observe(getLifecycle(), mZenCallback);
     }
 
     @Override
@@ -157,7 +163,7 @@
             // show on-boarding screen
             Intent intent = new Intent(Settings.ZEN_MODE_ONBOARDING);
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-            Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(intent, 0);
+            mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
         } else {
             switch (zenDuration) {
                 case Settings.Secure.ZEN_DURATION_PROMPT:
@@ -281,10 +287,8 @@
         if (mListening == listening) return;
         mListening = listening;
         if (mListening) {
-            mController.addCallback(mZenCallback);
             Prefs.registerListener(mContext, mPrefListener);
         } else {
-            mController.removeCallback(mZenCallback);
             Prefs.unregisterListener(mContext, mPrefListener);
         }
     }
@@ -445,7 +449,7 @@
     private final ZenModePanel.Callback mZenModePanelCallback = new ZenModePanel.Callback() {
         @Override
         public void onPrioritySettings() {
-            Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(
+            mActivityStarter.postStartActivityDismissingKeyguard(
                     ZEN_PRIORITY_SETTINGS, 0);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index d7ac253..dfa3fb9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -23,13 +23,14 @@
 import android.widget.Switch;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.statusbar.policy.FlashlightController;
 
+import javax.inject.Inject;
+
 /** Quick settings tile: Control flashlight **/
 public class FlashlightTile extends QSTileImpl<BooleanState> implements
         FlashlightController.FlashlightListener {
@@ -37,9 +38,11 @@
     private final Icon mIcon = ResourceIcon.get(R.drawable.ic_signal_flashlight);
     private final FlashlightController mFlashlightController;
 
-    public FlashlightTile(QSHost host) {
+    @Inject
+    public FlashlightTile(QSHost host, FlashlightController flashlightController) {
         super(host);
-        mFlashlightController = Dependency.get(FlashlightController.class);
+        mFlashlightController = flashlightController;
+        mFlashlightController.observe(getLifecycle(), this);
     }
 
     @Override
@@ -56,11 +59,6 @@
 
     @Override
     public void handleSetListening(boolean listening) {
-        if (listening) {
-            mFlashlightController.addCallback(this);
-        } else {
-            mFlashlightController.removeCallback(this);
-        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 6bf0793..b7e07f8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -25,7 +25,6 @@
 import android.widget.Switch;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QSTile.AirplaneBooleanState;
 import com.android.systemui.qs.GlobalSetting;
@@ -34,6 +33,8 @@
 import com.android.systemui.statusbar.policy.DataSaverController;
 import com.android.systemui.statusbar.policy.HotspotController;
 
+import javax.inject.Inject;
+
 /** Quick settings tile: Hotspot **/
 public class HotspotTile extends QSTileImpl<AirplaneBooleanState> {
     private static final Intent TETHER_SETTINGS = new Intent().setComponent(new ComponentName(
@@ -48,16 +49,20 @@
     private final GlobalSetting mAirplaneMode;
     private boolean mListening;
 
-    public HotspotTile(QSHost host) {
+    @Inject
+    public HotspotTile(QSHost host, HotspotController hotspotController,
+            DataSaverController dataSaverController) {
         super(host);
-        mHotspotController = Dependency.get(HotspotController.class);
-        mDataSaverController = Dependency.get(DataSaverController.class);
+        mHotspotController = hotspotController;
+        mDataSaverController = dataSaverController;
         mAirplaneMode = new GlobalSetting(mContext, mHandler, Global.AIRPLANE_MODE_ON) {
             @Override
             protected void handleValueChanged(int value) {
                 refreshState();
             }
         };
+        mHotspotController.observe(this, mCallbacks);
+        mDataSaverController.observe(this, mCallbacks);
     }
 
     @Override
@@ -80,12 +85,7 @@
         if (mListening == listening) return;
         mListening = listening;
         if (listening) {
-            mHotspotController.addCallback(mCallbacks);
-            mDataSaverController.addCallback(mCallbacks);
             refreshState();
-        } else {
-            mHotspotController.removeCallback(mCallbacks);
-            mDataSaverController.removeCallback(mCallbacks);
         }
         mAirplaneMode.setListening(listening);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index b5f2d00..d740033 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -23,7 +23,6 @@
 import android.widget.Switch;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.R.drawable;
 import com.android.systemui.plugins.ActivityStarter;
@@ -34,6 +33,8 @@
 import com.android.systemui.statusbar.policy.LocationController;
 import com.android.systemui.statusbar.policy.LocationController.LocationChangeCallback;
 
+import javax.inject.Inject;
+
 /** Quick settings tile: Location **/
 public class LocationTile extends QSTileImpl<BooleanState> {
 
@@ -41,12 +42,18 @@
 
     private final LocationController mController;
     private final KeyguardMonitor mKeyguard;
+    private final ActivityStarter mActivityStarter;
     private final Callback mCallback = new Callback();
 
-    public LocationTile(QSHost host) {
+    @Inject
+    public LocationTile(QSHost host, LocationController locationController,
+            KeyguardMonitor keyguardMonitor, ActivityStarter activityStarter) {
         super(host);
-        mController = Dependency.get(LocationController.class);
-        mKeyguard = Dependency.get(KeyguardMonitor.class);
+        mController = locationController;
+        mKeyguard = keyguardMonitor;
+        mActivityStarter = activityStarter;
+        mController.observe(this, mCallback);
+        mKeyguard.observe(this, mCallback);
     }
 
     @Override
@@ -56,13 +63,6 @@
 
     @Override
     public void handleSetListening(boolean listening) {
-        if (listening) {
-            mController.addCallback(mCallback);
-            mKeyguard.addCallback(mCallback);
-        } else {
-            mController.removeCallback(mCallback);
-            mKeyguard.removeCallback(mCallback);
-        }
     }
 
     @Override
@@ -73,7 +73,7 @@
     @Override
     protected void handleClick() {
         if (mKeyguard.isSecure() && mKeyguard.isShowing()) {
-            Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() -> {
+            mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
                 final boolean wasEnabled = mState.value;
                 mHost.openPanels();
                 mController.setLocationEnabled(!wasEnabled);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index a365e4c..476a239 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -33,6 +33,8 @@
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 
+import javax.inject.Inject;
+
 /** Quick settings tile: Enable/Disable NFC **/
 public class NfcTile extends QSTileImpl<BooleanState> {
 
@@ -40,6 +42,7 @@
 
     private boolean mListening;
 
+    @Inject
     public NfcTile(QSHost host) {
         super(host);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index 90890c0..b04132d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -44,6 +44,8 @@
 import java.util.Calendar;
 import java.util.TimeZone;
 
+import javax.inject.Inject;
+
 public class NightDisplayTile extends QSTileImpl<BooleanState>
         implements ColorDisplayController.Callback {
 
@@ -58,6 +60,7 @@
     private ColorDisplayController mController;
     private boolean mIsListening;
 
+    @Inject
     public NightDisplayTile(QSHost host) {
         super(host);
         mController = new ColorDisplayController(mContext, ActivityManager.getCurrentUser());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 6345816..21f3d6e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -24,7 +24,6 @@
 import android.widget.Switch;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.qs.QSHost;
@@ -32,15 +31,19 @@
 import com.android.systemui.statusbar.policy.RotationLockController;
 import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
 
+import javax.inject.Inject;
+
 /** Quick settings tile: Rotation **/
 public class RotationLockTile extends QSTileImpl<BooleanState> {
 
     private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_auto_rotate);
     private final RotationLockController mController;
 
-    public RotationLockTile(QSHost host) {
+    @Inject
+    public RotationLockTile(QSHost host, RotationLockController rotationLockController) {
         super(host);
-        mController = Dependency.get(RotationLockController.class);
+        mController = rotationLockController;
+        mController.observe(this, mCallback);
     }
 
     @Override
@@ -49,11 +52,6 @@
     }
 
     public void handleSetListening(boolean listening) {
-        if (listening) {
-            mController.addCallback(mCallback);
-        } else {
-            mController.removeCallback(mCallback);
-        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyTile.java
new file mode 100644
index 0000000..5230cea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyTile.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import android.content.Intent;
+import android.hardware.SensorPrivacyManager;
+import android.service.quicksettings.Tile;
+import android.widget.Switch;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+
+import javax.inject.Inject;
+
+/** Quick settings tile: SensorPrivacy mode **/
+public class SensorPrivacyTile extends QSTileImpl<BooleanState> implements
+        SensorPrivacyManager.OnSensorPrivacyChangedListener {
+    private static final String TAG = "SensorPrivacy";
+    private final Icon mIcon =
+            ResourceIcon.get(R.drawable.ic_signal_sensors);
+    private final KeyguardMonitor mKeyguard;
+    private final SensorPrivacyManager mSensorPrivacyManager;
+    private final ActivityStarter mActivityStarter;
+
+    @Inject
+    public SensorPrivacyTile(QSHost host, SensorPrivacyManager sensorPrivacyManager,
+            KeyguardMonitor keyguardMonitor, ActivityStarter activityStarter) {
+        super(host);
+
+        mSensorPrivacyManager = sensorPrivacyManager;
+        mKeyguard = keyguardMonitor;
+        mActivityStarter = activityStarter;
+    }
+
+    @Override
+    public BooleanState newTileState() {
+        return new BooleanState();
+    }
+
+    @Override
+    public void handleClick() {
+        final boolean wasEnabled = mState.value;
+        // Don't allow disabling from the lockscreen.
+        if (wasEnabled && mKeyguard.isSecure() && mKeyguard.isShowing()) {
+            mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
+                MetricsLogger.action(mContext, getMetricsCategory(), !wasEnabled);
+                setEnabled(!wasEnabled);
+            });
+            return;
+        }
+
+        MetricsLogger.action(mContext, getMetricsCategory(), !wasEnabled);
+        setEnabled(!wasEnabled);
+    }
+
+    private void setEnabled(boolean enabled) {
+        mSensorPrivacyManager.setSensorPrivacy(enabled);
+    }
+
+    @Override
+    public CharSequence getTileLabel() {
+        return mContext.getString(R.string.sensor_privacy_mode);
+    }
+
+    @Override
+    public Intent getLongClickIntent() {
+        return null;
+    }
+
+    @Override
+    protected void handleUpdateState(BooleanState state, Object arg) {
+        final boolean enabled = arg instanceof Boolean ? (Boolean) arg
+                : mSensorPrivacyManager.isSensorPrivacyEnabled();
+        state.value = enabled;
+        state.label = mContext.getString(R.string.sensor_privacy_mode);
+        state.icon = mIcon;
+        state.state = enabled ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
+        state.contentDescription = state.label;
+        state.expandedAccessibilityClassName = Switch.class.getName();
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.QS_SENSOR_PRIVACY;
+    }
+
+    @Override
+    protected String composeChangeAnnouncement() {
+        if (mState.value) {
+            return mContext
+                    .getString(R.string.accessibility_quick_settings_sensor_privacy_changed_on);
+        } else {
+            return mContext
+                    .getString(R.string.accessibility_quick_settings_sensor_privacy_changed_off);
+        }
+    }
+
+    @Override
+    protected void handleSetListening(boolean listening) {
+        if (listening) {
+            mSensorPrivacyManager.addSensorPrivacyListener(this);
+        } else {
+            mSensorPrivacyManager.removeSensorPrivacyListener(this);
+        }
+    }
+
+    @Override
+    public void onSensorPrivacyChanged(boolean enabled) {
+        refreshState(enabled);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
index e5c51a6..7c1ffde 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
@@ -22,7 +22,6 @@
 import android.util.Pair;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Dependency;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTile.State;
@@ -31,16 +30,21 @@
 import com.android.systemui.statusbar.policy.UserInfoController;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 
+import javax.inject.Inject;
+
 public class UserTile extends QSTileImpl<State> implements UserInfoController.OnUserInfoChangedListener {
 
     private final UserSwitcherController mUserSwitcherController;
     private final UserInfoController mUserInfoController;
     private Pair<String, Drawable> mLastUpdate;
 
-    public UserTile(QSHost host) {
+    @Inject
+    public UserTile(QSHost host, UserSwitcherController userSwitcherController,
+            UserInfoController userInfoController) {
         super(host);
-        mUserSwitcherController = Dependency.get(UserSwitcherController.class);
-        mUserInfoController = Dependency.get(UserInfoController.class);
+        mUserSwitcherController = userSwitcherController;
+        mUserInfoController = userInfoController;
+        mUserInfoController.observe(getLifecycle(), this);
     }
 
     @Override
@@ -70,11 +74,6 @@
 
     @Override
     public void handleSetListening(boolean listening) {
-        if (listening) {
-            mUserInfoController.addCallback(this);
-        } else {
-            mUserInfoController.removeCallback(this);
-        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index d8f7b71..52a8814 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -30,7 +30,6 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.wifi.AccessPoint;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.DetailAdapter;
@@ -47,9 +46,12 @@
 import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.policy.WifiIcons;
 
 import java.util.List;
 
+import javax.inject.Inject;
+
 /** Quick settings tile: Wifi **/
 public class WifiTile extends QSTileImpl<SignalState> {
     private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS);
@@ -63,12 +65,15 @@
     private final ActivityStarter mActivityStarter;
     private boolean mExpectDisabled;
 
-    public WifiTile(QSHost host) {
+    @Inject
+    public WifiTile(QSHost host, NetworkController networkController,
+            ActivityStarter activityStarter) {
         super(host);
-        mController = Dependency.get(NetworkController.class);
+        mController = networkController;
         mWifiController = mController.getAccessPointController();
         mDetailAdapter = (WifiDetailAdapter) createDetailAdapter();
-        mActivityStarter = Dependency.get(ActivityStarter.class);
+        mActivityStarter = activityStarter;
+        mController.observe(getLifecycle(), mSignalCallback);
     }
 
     @Override
@@ -78,11 +83,6 @@
 
     @Override
     public void handleSetListening(boolean listening) {
-        if (listening) {
-            mController.addCallback(mSignalCallback);
-        } else {
-            mController.removeCallback(mSignalCallback);
-        }
     }
 
     @Override
@@ -190,7 +190,7 @@
         } else if (!state.value) {
             state.slash.isSlashed = true;
             state.state = Tile.STATE_INACTIVE;
-            state.icon = ResourceIcon.get(R.drawable.ic_qs_wifi_disabled);
+            state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_DISABLED);
             state.label = r.getString(R.string.quick_settings_wifi_label);
         } else if (wifiConnected) {
             state.icon = ResourceIcon.get(cb.wifiSignalIconId);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 3109dea..f921eb9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -22,13 +22,14 @@
 import android.widget.Switch;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.statusbar.phone.ManagedProfileController;
 
+import javax.inject.Inject;
+
 /** Quick settings tile: Work profile on/off */
 public class WorkModeTile extends QSTileImpl<BooleanState> implements
         ManagedProfileController.Callback {
@@ -36,9 +37,11 @@
 
     private final ManagedProfileController mProfileController;
 
-    public WorkModeTile(QSHost host) {
+    @Inject
+    public WorkModeTile(QSHost host, ManagedProfileController managedProfileController) {
         super(host);
-        mProfileController = Dependency.get(ManagedProfileController.class);
+        mProfileController = managedProfileController;
+        mProfileController.observe(getLifecycle(), this);
     }
 
     @Override
@@ -48,11 +51,6 @@
 
     @Override
     public void handleSetListening(boolean listening) {
-        if (listening) {
-            mProfileController.addCallback(this);
-        } else {
-            mProfileController.removeCallback(this);
-        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 12b6f67..81757d0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -44,7 +44,6 @@
 import android.view.MotionEvent;
 
 import com.android.internal.policy.ScreenDecorationsUtils;
-import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
 import com.android.systemui.Prefs;
 import com.android.systemui.SysUiServiceProvider;
@@ -63,9 +62,13 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Class to send information from overview to launcher with a binder.
  */
+@Singleton
 public class OverviewProxyService implements CallbackController<OverviewProxyListener>, Dumpable {
 
     private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
@@ -86,8 +89,7 @@
     private final Handler mHandler;
     private final Runnable mConnectionRunnable = this::internalConnectToCurrentUser;
     private final ComponentName mRecentsComponentName;
-    private final DeviceProvisionedController mDeviceProvisionedController
-            = Dependency.get(DeviceProvisionedController.class);
+    private final DeviceProvisionedController mDeviceProvisionedController;
     private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>();
     private final Intent mQuickStepIntent;
 
@@ -338,9 +340,11 @@
     private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
             = this::cleanupAfterDeath;
 
-    public OverviewProxyService(Context context) {
+    @Inject
+    public OverviewProxyService(Context context, DeviceProvisionedController provisionController) {
         mContext = context;
         mHandler = new Handler();
+        mDeviceProvisionedController = provisionController;
         mConnectionBackoffAttempts = 0;
         mRecentsComponentName = ComponentName.unflattenFromString(context.getString(
                 com.android.internal.R.string.config_recentsComponentName));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index f13b565..0fc4fe7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -54,8 +54,10 @@
     }
 
     @Override
-    public void appTransitionFinished() {
-        mImpl.onAppTransitionFinished();
+    public void appTransitionFinished(int displayId) {
+        if (mContext.getDisplayId() == displayId) {
+            mImpl.onAppTransitionFinished();
+        }
     }
 
     public void growRecents() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index bc38169..a776d0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar;
 
-import static com.android.systemui.statusbar.notification.NotificationData.Entry;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Handler;
@@ -29,6 +27,7 @@
 import android.view.accessibility.AccessibilityEvent;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 
 import java.util.stream.Stream;
@@ -48,7 +47,7 @@
      * NotificationManagerService side, but we keep it to prevent the UI from looking weird and
      * will remove when possible. See {@link NotificationLifetimeExtender}
      */
-    protected final ArraySet<Entry> mExtendedLifetimeAlertEntries = new ArraySet<>();
+    protected final ArraySet<NotificationEntry> mExtendedLifetimeAlertEntries = new ArraySet<>();
 
     protected NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
     protected int mMinimumDisplayTime;
@@ -61,7 +60,7 @@
      * Adds the notification to be managed.
      * @param entry entry to show
      */
-    public void showNotification(@NonNull Entry entry) {
+    public void showNotification(@NonNull NotificationEntry entry) {
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "showNotification");
         }
@@ -139,7 +138,7 @@
      * @return the entry
      */
     @Nullable
-    public Entry getEntry(@NonNull String key) {
+    public NotificationEntry getEntry(@NonNull String key) {
         AlertEntry entry = mAlertEntries.get(key);
         return entry != null ? entry.mEntry : null;
     }
@@ -149,7 +148,7 @@
      * @return all entries
      */
     @NonNull
-    public Stream<Entry> getAllEntries() {
+    public Stream<NotificationEntry> getAllEntries() {
         return mAlertEntries.values().stream().map(headsUpEntry -> headsUpEntry.mEntry);
     }
 
@@ -180,7 +179,7 @@
      * Add a new entry and begin managing it.
      * @param entry the entry to add
      */
-    protected final void addAlertEntry(@NonNull Entry entry) {
+    protected final void addAlertEntry(@NonNull NotificationEntry entry) {
         AlertEntry alertEntry = createAlertEntry();
         alertEntry.setEntry(entry);
         mAlertEntries.put(entry.key, alertEntry);
@@ -203,7 +202,7 @@
         if (alertEntry == null) {
             return;
         }
-        Entry entry = alertEntry.mEntry;
+        NotificationEntry entry = alertEntry.mEntry;
         mAlertEntries.remove(key);
         onAlertEntryRemoved(alertEntry);
         entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
@@ -250,12 +249,12 @@
     }
 
     @Override
-    public boolean shouldExtendLifetime(Entry entry) {
+    public boolean shouldExtendLifetime(NotificationEntry entry) {
         return !canRemoveImmediately(entry.key);
     }
 
     @Override
-    public void setShouldManageLifetime(Entry entry, boolean shouldExtend) {
+    public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) {
         if (shouldExtend) {
             mExtendedLifetimeAlertEntries.add(entry);
         } else {
@@ -265,17 +264,17 @@
     ///////////////////////////////////////////////////////////////////////////////////////////////
 
     protected class AlertEntry implements Comparable<AlertEntry> {
-        @Nullable public Entry mEntry;
+        @Nullable public NotificationEntry mEntry;
         public long mPostTime;
         public long mEarliestRemovaltime;
 
         @Nullable protected Runnable mRemoveAlertRunnable;
 
-        public void setEntry(@NonNull final Entry entry) {
+        public void setEntry(@NonNull final NotificationEntry entry) {
             setEntry(entry, () -> removeAlertEntry(entry.key));
         }
 
-        public void setEntry(@NonNull final Entry entry,
+        public void setEntry(@NonNull final NotificationEntry entry,
                 @Nullable Runnable removeAlertRunnable) {
             mEntry = entry;
             mRemoveAlertRunnable = removeAlertRunnable;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index 8821679..a3beb96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -25,20 +25,25 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Manager which handles high priority notifications that should "pulse" in when the device is
  * dozing and/or in AOD.  The pulse uses the notification's ambient view and pops in briefly
  * before automatically dismissing the alert.
  */
+@Singleton
 public class AmbientPulseManager extends AlertingNotificationManager {
 
     protected final ArraySet<OnAmbientChangedListener> mListeners = new ArraySet<>();
     @VisibleForTesting
     protected long mExtensionTime;
 
+    @Inject
     public AmbientPulseManager(@NonNull final Context context) {
         Resources resources = context.getResources();
         mAutoDismissNotificationDecay = resources.getInteger(R.integer.ambient_notification_decay);
@@ -78,7 +83,7 @@
 
     @Override
     protected void onAlertEntryAdded(AlertEntry alertEntry) {
-        NotificationData.Entry entry = alertEntry.mEntry;
+        NotificationEntry entry = alertEntry.mEntry;
         entry.setAmbientPulsing(true);
         for (OnAmbientChangedListener listener : mListeners) {
             listener.onAmbientStateChanged(entry, true);
@@ -87,7 +92,7 @@
 
     @Override
     protected void onAlertEntryRemoved(AlertEntry alertEntry) {
-        NotificationData.Entry entry = alertEntry.mEntry;
+        NotificationEntry entry = alertEntry.mEntry;
         entry.setAmbientPulsing(false);
         for (OnAmbientChangedListener listener : mListeners) {
             listener.onAmbientStateChanged(entry, false);
@@ -126,7 +131,7 @@
          * @param entry the entry that changed
          * @param isPulsing true if the entry is now pulsing, false otherwise
          */
-        void onAmbientStateChanged(NotificationData.Entry entry, boolean isPulsing);
+        void onAmbientStateChanged(NotificationEntry entry, boolean isPulsing);
     }
 
     private final class AmbientEntry extends AlertEntry {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 95019ee..6a01563 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -16,18 +16,30 @@
 
 package com.android.systemui.statusbar;
 
+import static android.app.StatusBarManager.DISABLE2_NONE;
+import static android.app.StatusBarManager.DISABLE_NONE;
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static com.android.systemui.statusbar.phone.StatusBar.ONLY_CORE_APPS;
 
 import android.app.StatusBarManager;
+import android.app.StatusBarManager.Disable2Flags;
+import android.app.StatusBarManager.DisableFlags;
+import android.app.StatusBarManager.WindowType;
+import android.app.StatusBarManager.WindowVisibleState;
 import android.content.ComponentName;
+import android.content.Context;
 import android.graphics.Rect;
 import android.hardware.biometrics.IBiometricServiceReceiverInternal;
+import android.hardware.display.DisplayManager;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.util.Pair;
+import android.util.SparseArray;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -47,7 +59,8 @@
  * coalescing these calls so they don't stack up.  For the calls
  * are coalesced, note that they are all idempotent.
  */
-public class CommandQueue extends IStatusBar.Stub implements CallbackController<Callbacks> {
+public class CommandQueue extends IStatusBar.Stub implements CallbackController<Callbacks>,
+        DisplayManager.DisplayListener {
     private static final int INDEX_MASK = 0xffff;
     private static final int MSG_SHIFT  = 16;
     private static final int MSG_MASK   = 0xffff << MSG_SHIFT;
@@ -112,8 +125,8 @@
     private final Object mLock = new Object();
     private ArrayList<Callbacks> mCallbacks = new ArrayList<>();
     private Handler mHandler = new H(Looper.getMainLooper());
-    private int mDisable1;
-    private int mDisable2;
+    /** A map of display id - disable flag pair */
+    private SparseArray<Pair<Integer, Integer>> mDisplayDisabled = new SparseArray<>();
 
     /**
      * These methods are called back on the main thread.
@@ -121,17 +134,63 @@
     public interface Callbacks {
         default void setIcon(String slot, StatusBarIcon icon) { }
         default void removeIcon(String slot) { }
-        default void disable(int state1, int state2, boolean animate) { }
+
+        /**
+         * Called to notify that disable flags are updated.
+         * @see IStatusBar#disable(int, int, int).
+         *
+         * @param displayId The id of the display to notify.
+         * @param state1 The combination of following DISABLE_* flags:
+         * @param state2 The combination of following DISABLE2_* flags:
+         * @param animate {@code true} to show animations.
+         */
+        default void disable(int displayId, @DisableFlags int state1, @Disable2Flags int state2,
+                boolean animate) { }
         default void animateExpandNotificationsPanel() { }
         default void animateCollapsePanels(int flags, boolean force) { }
         default void togglePanel() { }
         default void animateExpandSettingsPanel(String obj) { }
-        default void setSystemUiVisibility(int vis, int fullscreenStackVis,
+
+        /**
+         * Called to notify visibility flag changes.
+         * @see IStatusBar#setSystemUiVisibility(int, int, int, int, int, Rect, Rect).
+         *
+         * @param displayId The id of the display to notify.
+         * @param vis The visibility flags except SYSTEM_UI_FLAG_LIGHT_STATUS_BAR which will
+         *            be reported separately in fullscreenStackVis and dockedStackVis.
+         * @param fullscreenStackVis The flags which only apply in the region of the fullscreen
+         *                           stack, which is currently only SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
+         * @param dockedStackVis The flags that only apply in the region of the docked stack, which
+         *                       is currently only SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
+         * @param mask Which flags to change.
+         * @param fullscreenStackBounds The current bounds of the fullscreen stack, in screen
+         *                              coordinates.
+         * @param dockedStackBounds The current bounds of the docked stack, in screen coordinates.
+         */
+        default void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
                 int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
         }
-        default void topAppWindowChanged(boolean visible) { }
-        default void setImeWindowStatus(IBinder token, int vis, int backDisposition,
-                boolean showImeSwitcher) { }
+
+        /**
+         * Called to notify top app window changes.
+         * @see IStatusBar#topAppWindowChanged(int, boolean)
+         *
+         * @param displayId The id of the display to notify.
+         * @param visible {@code true} to show menu button.
+         */
+        default void topAppWindowChanged(int displayId, boolean visible) { }
+
+        /**
+         * Called to notify IME window status changes.
+         *
+         * @param displayId The id of the display to notify.
+         * @param token IME token.
+         * @param vis IME visibility.
+         * @param backDisposition Disposition mode of back button. It should be one of below flags:
+         * @param showImeSwitcher {@code true} to show IME switch button.
+         */
+        default void setImeWindowStatus(int displayId, IBinder token,  int vis,
+                @BackDispositionMode int backDisposition, boolean showImeSwitcher) { }
         default void showRecentApps(boolean triggeredFromAltTab) { }
         default void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { }
         default void toggleRecentApps() { }
@@ -140,12 +199,56 @@
         default void dismissKeyboardShortcutsMenu() { }
         default void toggleKeyboardShortcutsMenu(int deviceId) { }
         default void cancelPreloadRecentApps() { }
-        default void setWindowState(int window, int state) { }
+
+        /**
+         * Called to notify window state changes.
+         * @see IStatusBar#setWindowState(int, int, int)
+         *
+         * @param displayId The id of the display to notify.
+         * @param window Window type. It should be one of {@link StatusBarManager#WINDOW_STATUS_BAR}
+         *               or {@link StatusBarManager#WINDOW_NAVIGATION_BAR}
+         * @param state Window visible state.
+         */
+        default void setWindowState(int displayId, @WindowType int window,
+                @WindowVisibleState int state) { }
         default void showScreenPinningRequest(int taskId) { }
-        default void appTransitionPending(boolean forced) { }
-        default void appTransitionCancelled() { }
-        default void appTransitionStarting(long startTime, long duration, boolean forced) { }
-        default void appTransitionFinished() { }
+
+        /**
+         * Called to notify System UI that an application transition is pending.
+         * @see IStatusBar#appTransitionPending(int).
+         *
+         * @param displayId The id of the display to notify.
+         * @param forced {@code true} to force transition pending.
+         */
+        default void appTransitionPending(int displayId, boolean forced) { }
+
+        /**
+         * Called to notify System UI that an application transition is canceled.
+         * @see IStatusBar#appTransitionCancelled(int).
+         *
+         * @param displayId The id of the display to notify.
+         */
+        default void appTransitionCancelled(int displayId) { }
+
+        /**
+         * Called to notify System UI that an application transition is starting.
+         * @see IStatusBar#appTransitionStarting(int, long, long).
+         *
+         * @param displayId The id of the display to notify.
+         * @param startTime Transition start time.
+         * @param duration Transition duration.
+         * @param forced {@code true} to force transition pending.
+         */
+        default void appTransitionStarting(
+                int displayId, long startTime, long duration, boolean forced) { }
+
+        /**
+         * Called to notify System UI that an application transition is finished.
+         * @see IStatusBar#appTransitionFinished(int)
+         *
+         * @param displayId The id of the display to notify.
+         */
+        default void appTransitionFinished(int displayId) { }
         default void showAssistDisclosure() { }
         default void startAssist(Bundle args) { }
         default void onCameraLaunchGestureDetected(int source) { }
@@ -176,18 +279,43 @@
     }
 
     @VisibleForTesting
-    public CommandQueue() {
+    public CommandQueue(Context context) {
+        context.getSystemService(DisplayManager.class).registerDisplayListener(this, mHandler);
+        // We always have default display.
+        setDisabled(DEFAULT_DISPLAY, DISABLE_NONE, DISABLE2_NONE);
     }
 
+    @Override
+    public void onDisplayAdded(int displayId) { }
+
+    @Override
+    public void onDisplayRemoved(int displayId) {
+        synchronized (mLock) {
+            mDisplayDisabled.remove(displayId);
+        }
+    }
+
+    @Override
+    public void onDisplayChanged(int displayId) { }
+
+    // TODO(b/118592525): add multi-display support if needed.
     public boolean panelsEnabled() {
-        return (mDisable1 & StatusBarManager.DISABLE_EXPAND) == 0
-                && (mDisable2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0
+        final int disabled1 = getDisabled1(DEFAULT_DISPLAY);
+        final int disabled2 = getDisabled2(DEFAULT_DISPLAY);
+        return (disabled1 & StatusBarManager.DISABLE_EXPAND) == 0
+                && (disabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0
                 && !ONLY_CORE_APPS;
     }
 
     public void addCallback(Callbacks callbacks) {
         mCallbacks.add(callbacks);
-        callbacks.disable(mDisable1, mDisable2, false /* animate */);
+        // TODO(b/117478341): find a better way to pass disable flags by display.
+        for (int i = 0; i < mDisplayDisabled.size(); i++) {
+            int displayId = mDisplayDisabled.keyAt(i);
+            int disabled1 = getDisabled1(displayId);
+            int disabled2 = getDisabled2(displayId);
+            callbacks.disable(displayId, disabled1, disabled2, false /* animate */);
+        }
     }
 
     public void removeCallback(Callbacks callbacks) {
@@ -209,12 +337,21 @@
         }
     }
 
-    public void disable(int state1, int state2, boolean animate) {
+    /**
+     * Called to notify that disable flags are updated.
+     * @see Callbacks#disable(int, int, int, boolean).
+     */
+    public void disable(int displayId, @DisableFlags int state1, @Disable2Flags int state2,
+            boolean animate) {
         synchronized (mLock) {
-            mDisable1 = state1;
-            mDisable2 = state2;
+            setDisabled(displayId, state1, state2);
             mHandler.removeMessages(MSG_DISABLE);
-            Message msg = mHandler.obtainMessage(MSG_DISABLE, state1, state2, animate);
+            final SomeArgs args = SomeArgs.obtain();
+            args.argi1 = displayId;
+            args.argi2 = state1;
+            args.argi3 = state2;
+            args.argi4 = animate ? 1 : 0;
+            Message msg = mHandler.obtainMessage(MSG_DISABLE, args);
             if (Looper.myLooper() == mHandler.getLooper()) {
                 // If its the right looper execute immediately so hides can be handled quickly.
                 mHandler.handleMessage(msg);
@@ -225,14 +362,42 @@
         }
     }
 
-    // TODO(b/117478341): Add multi-display support.
     @Override
-    public void disable(int displayId, int state1, int state2) {
-        disable(state1, state2, true);
+    public void disable(int displayId, @DisableFlags int state1, @Disable2Flags int state2) {
+        disable(displayId, state1, state2, true);
     }
 
-    public void recomputeDisableFlags(boolean animate) {
-        disable(mDisable1, mDisable2, animate);
+    /**
+     * Apply current disable flags by {@link CommandQueue#disable(int, int, int, boolean)}.
+     *
+     * @param displayId The id of the display to notify.
+     * @param animate {@code true} to show animations.
+     */
+    public void recomputeDisableFlags(int displayId, boolean animate) {
+        int disabled1 = getDisabled1(displayId);
+        int disabled2 = getDisabled2(displayId);
+        disable(displayId, disabled1, disabled2, animate);
+    }
+
+    private void setDisabled(int displayId, int disabled1, int disabled2) {
+        mDisplayDisabled.put(displayId, new Pair<>(disabled1, disabled2));
+    }
+
+    private int getDisabled1(int displayId) {
+        return getDisabled(displayId).first;
+    }
+
+    private int getDisabled2(int displayId) {
+        return getDisabled(displayId).second;
+    }
+
+    private Pair<Integer, Integer> getDisabled(int displayId) {
+        Pair<Integer, Integer> disablePair = mDisplayDisabled.get(displayId);
+        if (disablePair == null) {
+            disablePair = new Pair<>(DISABLE_NONE, DISABLE2_NONE);
+            mDisplayDisabled.put(displayId, disablePair);
+        }
+        return disablePair;
     }
 
     public void animateExpandNotificationsPanel() {
@@ -270,7 +435,6 @@
         }
     }
 
-    // TODO(b/117478341): Add multi-display support.
     @Override
     public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
             int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
@@ -278,34 +442,38 @@
             // Don't coalesce these, since it might have one time flags set such as
             // STATUS_BAR_UNHIDE which might get lost.
             SomeArgs args = SomeArgs.obtain();
-            args.argi1 = vis;
-            args.argi2 = fullscreenStackVis;
-            args.argi3 = dockedStackVis;
-            args.argi4 = mask;
+            args.argi1 = displayId;
+            args.argi2 = vis;
+            args.argi3 = fullscreenStackVis;
+            args.argi4 = dockedStackVis;
+            args.argi5 = mask;
             args.arg1 = fullscreenStackBounds;
             args.arg2 = dockedStackBounds;
             mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, args).sendToTarget();
         }
     }
 
-    // TODO(b/117478341): Add multi-display support.
     @Override
     public void topAppWindowChanged(int displayId, boolean menuVisible) {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_TOP_APP_WINDOW_CHANGED);
-            mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED, menuVisible ? 1 : 0, 0,
-                    null).sendToTarget();
+            mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED,
+                    displayId, menuVisible ? 1 : 0, null).sendToTarget();
         }
     }
 
-    // TODO(b/117478341): Add multi-display support.
     @Override
     public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
             boolean showImeSwitcher) {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_SHOW_IME_BUTTON);
-            Message m = mHandler.obtainMessage(MSG_SHOW_IME_BUTTON, vis, backDisposition, token);
-            m.getData().putBoolean(SHOW_IME_SWITCHER_KEY, showImeSwitcher);
+            SomeArgs args = SomeArgs.obtain();
+            args.argi1 = displayId;
+            args.argi2 = vis;
+            args.argi3 = backDisposition;
+            args.argi4 = showImeSwitcher ? 1 : 0;
+            args.arg1 = token;
+            Message m = mHandler.obtainMessage(MSG_SHOW_IME_BUTTON, args);
             m.sendToTarget();
         }
     }
@@ -381,12 +549,11 @@
         }
     }
 
-    // TODO(b/117478341): Add multi-display support.
     @Override
     public void setWindowState(int displayId, int window, int state) {
         synchronized (mLock) {
             // don't coalesce these
-            mHandler.obtainMessage(MSG_SET_WINDOW_STATE, window, state, null).sendToTarget();
+            mHandler.obtainMessage(MSG_SET_WINDOW_STATE, displayId, window, state).sendToTarget();
         }
     }
 
@@ -397,44 +564,54 @@
         }
     }
 
-    // TODO(b/117478341): Add multi-display support.
     @Override
     public void appTransitionPending(int displayId) {
-        appTransitionPending(false /* forced */);
+        appTransitionPending(displayId, false /* forced */);
     }
 
-    public void appTransitionPending(boolean forced) {
+    /**
+     * Called to notify System UI that an application transition is pending.
+     * @see Callbacks#appTransitionPending(int, boolean)
+     */
+    public void appTransitionPending(int displayId, boolean forced) {
         synchronized (mLock) {
-            mHandler.obtainMessage(MSG_APP_TRANSITION_PENDING, forced ? 1 : 0, 0).sendToTarget();
+            mHandler.obtainMessage(MSG_APP_TRANSITION_PENDING, displayId, forced ? 1 : 0)
+                    .sendToTarget();
         }
     }
 
-    // TODO(b/117478341): Add multi-display support.
     @Override
     public void appTransitionCancelled(int displayId) {
         synchronized (mLock) {
-            mHandler.sendEmptyMessage(MSG_APP_TRANSITION_CANCELLED);
+            mHandler.obtainMessage(MSG_APP_TRANSITION_CANCELLED, displayId).sendToTarget();
         }
     }
 
-    // TODO(b/117478341): Add multi-display support.
     @Override
     public void appTransitionStarting(int displayId, long startTime, long duration) {
-        appTransitionStarting(startTime, duration, false /* forced */);
+        appTransitionStarting(displayId, startTime, duration, false /* forced */);
     }
 
-    public void appTransitionStarting(long startTime, long duration, boolean forced) {
+    /**
+     * Called to notify System UI that an application transition is starting.
+     * @see Callbacks#appTransitionStarting(int, long, long, boolean).
+     */
+    public void appTransitionStarting(int displayId, long startTime, long duration,
+            boolean forced) {
         synchronized (mLock) {
-            mHandler.obtainMessage(MSG_APP_TRANSITION_STARTING, forced ? 1 : 0, 0,
-                    Pair.create(startTime, duration)).sendToTarget();
+            final SomeArgs args = SomeArgs.obtain();
+            args.argi1 = displayId;
+            args.argi2 = forced ? 1 : 0;
+            args.arg1 = startTime;
+            args.arg2 = duration;
+            mHandler.obtainMessage(MSG_APP_TRANSITION_STARTING, args).sendToTarget();
         }
     }
 
-    // TODO(b/117478341): Add multi-display support.
     @Override
     public void appTransitionFinished(int displayId) {
         synchronized (mLock) {
-            mHandler.sendEmptyMessage(MSG_APP_TRANSITION_FINISHED);
+            mHandler.obtainMessage(MSG_APP_TRANSITION_FINISHED, displayId).sendToTarget();
         }
     }
 
@@ -619,8 +796,10 @@
                     break;
                 }
                 case MSG_DISABLE:
+                    SomeArgs args = (SomeArgs) msg.obj;
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).disable(msg.arg1, msg.arg2, (Boolean) msg.obj);
+                        mCallbacks.get(i).disable(args.argi1, args.argi2, args.argi3,
+                                args.argi4 != 0 /* animate */);
                     }
                     break;
                 case MSG_EXPAND_NOTIFICATIONS:
@@ -644,22 +823,23 @@
                     }
                     break;
                 case MSG_SET_SYSTEMUI_VISIBILITY:
-                    SomeArgs args = (SomeArgs) msg.obj;
+                    args = (SomeArgs) msg.obj;
                     for (int i = 0; i < mCallbacks.size(); i++) {
                         mCallbacks.get(i).setSystemUiVisibility(args.argi1, args.argi2, args.argi3,
-                                args.argi4, (Rect) args.arg1, (Rect) args.arg2);
+                                args.argi4, args.argi5, (Rect) args.arg1, (Rect) args.arg2);
                     }
                     args.recycle();
                     break;
                 case MSG_TOP_APP_WINDOW_CHANGED:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).topAppWindowChanged(msg.arg1 != 0);
+                        mCallbacks.get(i).topAppWindowChanged(msg.arg1, msg.arg2 != 0);
                     }
                     break;
                 case MSG_SHOW_IME_BUTTON:
+                    args = (SomeArgs) msg.obj;
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).setImeWindowStatus((IBinder) msg.obj, msg.arg1, msg.arg2,
-                                msg.getData().getBoolean(SHOW_IME_SWITCHER_KEY, false));
+                        mCallbacks.get(i).setImeWindowStatus(args.argi1, (IBinder) args.arg1,
+                                args.argi2, args.argi3, args.argi4 != 0 /* showImeSwitcher */);
                     }
                     break;
                 case MSG_SHOW_RECENT_APPS:
@@ -699,7 +879,7 @@
                     break;
                 case MSG_SET_WINDOW_STATE:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).setWindowState(msg.arg1, msg.arg2);
+                        mCallbacks.get(i).setWindowState(msg.arg1, msg.arg2, (int) msg.obj);
                     }
                     break;
                 case MSG_SHOW_SCREEN_PIN_REQUEST:
@@ -709,24 +889,24 @@
                     break;
                 case MSG_APP_TRANSITION_PENDING:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).appTransitionPending(msg.arg1 != 0);
+                        mCallbacks.get(i).appTransitionPending(msg.arg1, msg.arg2 != 0);
                     }
                     break;
                 case MSG_APP_TRANSITION_CANCELLED:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).appTransitionCancelled();
+                        mCallbacks.get(i).appTransitionCancelled(msg.arg1);
                     }
                     break;
                 case MSG_APP_TRANSITION_STARTING:
+                    args = (SomeArgs) msg.obj;
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        Pair<Long, Long> data = (Pair<Long, Long>) msg.obj;
-                        mCallbacks.get(i).appTransitionStarting(data.first, data.second,
-                                msg.arg1 != 0);
+                        mCallbacks.get(i).appTransitionStarting(args.argi1, (long) args.arg1,
+                                (long) args.arg2, args.argi2 != 0 /* forced */);
                     }
                     break;
                 case MSG_APP_TRANSITION_FINISHED:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).appTransitionFinished();
+                        mCallbacks.get(i).appTransitionFinished(msg.arg1);
                     }
                     break;
                 case MSG_ASSIST_DISCLOSURE:
@@ -858,7 +1038,7 @@
     public static class CommandQueueStart extends SystemUI {
         @Override
         public void start() {
-            putComponent(CommandQueue.class, new CommandQueue());
+            putComponent(CommandQueue.class, new CommandQueue(mContext));
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java
deleted file mode 100644
index 2f26109..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import android.content.Context;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManager.DisplayListener;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.Display;
-import android.view.IWindowManager;
-import android.view.View;
-import android.view.WindowManagerGlobal;
-
-import com.android.systemui.statusbar.phone.NavigationBarFragment;
-
-/**
- * A controller to handle external navigation bars
- */
-public class DisplayNavigationBarController implements DisplayListener {
-
-    private static final String TAG = DisplayNavigationBarController.class.getName();
-
-    private final Context mContext;
-    private final Handler mHandler;
-    private final DisplayManager mDisplayManager;
-
-    /** A displayId - nav bar mapping */
-    private SparseArray<NavigationBarFragment> mExternalNavigationBarMap = new SparseArray<>();
-
-    public DisplayNavigationBarController(Context context, Handler handler) {
-        mContext = context;
-        mHandler = handler;
-        mDisplayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
-
-        registerListener();
-    }
-
-    @Override
-    public void onDisplayAdded(int displayId) {
-        final Display display = mDisplayManager.getDisplay(displayId);
-        addExternalNavigationBar(display);
-    }
-
-    @Override
-    public void onDisplayRemoved(int displayId) {
-        final NavigationBarFragment navBar = mExternalNavigationBarMap.get(displayId);
-        if (navBar != null) {
-            final View navigationView = navBar.getView().getRootView();
-            WindowManagerGlobal.getInstance().removeView(navigationView, true);
-            mExternalNavigationBarMap.remove(displayId);
-        }
-    }
-
-    @Override
-    public void onDisplayChanged(int displayId) {
-    }
-
-    /** Create external navigation bars when car/status bar initializes */
-    public void createNavigationBars() {
-        // Add external navigation bars if more than one displays exist.
-        final Display[] displays = mDisplayManager.getDisplays();
-        for (Display display : displays) {
-            addExternalNavigationBar(display);
-        }
-    }
-
-    /** remove external navigation bars and unset everything related to external navigation bars */
-    public void destroy() {
-        unregisterListener();
-        if (mExternalNavigationBarMap.size() > 0) {
-            for (int i = 0; i < mExternalNavigationBarMap.size(); i++) {
-                final View navigationWindow = mExternalNavigationBarMap.valueAt(i)
-                        .getView().getRootView();
-                WindowManagerGlobal.getInstance()
-                        .removeView(navigationWindow, true /* immediate */);
-            }
-            mExternalNavigationBarMap.clear();
-        }
-    }
-
-    private void registerListener() {
-        mDisplayManager.registerDisplayListener(this, mHandler);
-    }
-
-    private void unregisterListener() {
-        mDisplayManager.unregisterDisplayListener(this);
-    }
-
-    /**
-     * Add a phone navigation bar on an external display if the display supports system decorations.
-     *
-     * @param display the display to add navigation bar on
-     */
-    private void addExternalNavigationBar(Display display) {
-        if (display == null || display.getDisplayId() == DEFAULT_DISPLAY
-                || !display.supportsSystemDecorations()) {
-            return;
-        }
-
-        final int displayId = display.getDisplayId();
-        final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
-
-        try {
-            if (!wms.hasNavigationBar(displayId)) {
-                return;
-            }
-        } catch (RemoteException e) {
-            // Cannot get wms, just return with warning message.
-            Log.w(TAG, "Cannot get WindowManager.");
-            return;
-        }
-        final Context externalDisplayContext = mContext.createDisplayContext(display);
-        NavigationBarFragment.create(externalDisplayContext, (tag, fragment) -> {
-            final NavigationBarFragment navBar = (NavigationBarFragment) fragment;
-            // TODO(b/115978725): handle external nav bars sysuiVisibility
-            navBar.setCurrentSysuiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
-            mExternalNavigationBarMap.append(displayId, navBar);
-        });
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
index b39a96d..3f1ff33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
@@ -31,8 +31,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.AlphaOptimizedLinearLayout;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.util.List;
 
@@ -50,7 +50,7 @@
     private int mEndMargin;
     private View mIconPlaceholder;
     private TextView mTextView;
-    private NotificationData.Entry mShowingEntry;
+    private NotificationEntry mShowingEntry;
     private Rect mLayoutedIconRect = new Rect();
     private int[] mTmpPosition = new int[2];
     private boolean mFirstLayout = true;
@@ -162,7 +162,7 @@
         mTextView = findViewById(R.id.text);
     }
 
-    public void setEntry(NotificationData.Entry entry) {
+    public void setEntry(NotificationEntry entry) {
         if (entry != null) {
             mShowingEntry = entry;
             CharSequence text = entry.headsUpStatusBarText;
@@ -261,7 +261,7 @@
         return super.fitSystemWindows(insets);
     }
 
-    public NotificationData.Entry getShowingEntry() {
+    public NotificationEntry getShowingEntry() {
         return mShowingEntry;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 7d1b640..158430f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -19,10 +19,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.Color;
@@ -35,7 +32,6 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.UserHandle;
 import android.os.UserManager;
 import android.text.TextUtils;
 import android.text.format.Formatter;
@@ -152,10 +148,7 @@
     private void registerCallbacks(KeyguardUpdateMonitor monitor) {
         monitor.registerCallback(getKeyguardCallback());
 
-        mContext.registerReceiverAsUser(mTickReceiver, UserHandle.SYSTEM,
-                new IntentFilter(Intent.ACTION_TIME_TICK), null,
-                Dependency.get(Dependency.TIME_TICK_HANDLER));
-
+        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mTickReceiver);
         Dependency.get(StatusBarStateController.class).addCallback(this);
     }
 
@@ -166,7 +159,7 @@
      * //TODO: This can probably be converted to a fragment and not have to be manually recreated
      */
     public void destroy() {
-        mContext.unregisterReceiver(mTickReceiver);
+        KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mTickReceiver);
         Dependency.get(StatusBarStateController.class).removeCallback(this);
     }
 
@@ -477,16 +470,15 @@
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
     }
 
-    private final BroadcastReceiver mTickReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            mHandler.post(() -> {
-                if (mVisible) {
-                    updateIndication(false);
+    private final KeyguardUpdateMonitorCallback mTickReceiver =
+            new KeyguardUpdateMonitorCallback() {
+                @Override
+                public void onTimeChanged() {
+                    if (mVisible) {
+                        updateIndication(false /* animate */);
+                    }
                 }
-            });
-        }
-    };
+            };
 
     private final Handler mHandler = new Handler() {
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
new file mode 100644
index 0000000..9740d1d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.View;
+import android.view.WindowManagerGlobal;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
+import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.NavigationBarFragment;
+import com.android.systemui.statusbar.phone.NavigationBarView;
+import com.android.systemui.statusbar.policy.BatteryController;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+
+/** A controller to handle navigation bars. */
+@Singleton
+public class NavigationBarController implements DisplayListener {
+
+    private static final String TAG = NavigationBarController.class.getName();
+
+    private final Context mContext;
+    private final Handler mHandler;
+    private final DisplayManager mDisplayManager;
+
+    /** A displayId - nav bar maps. */
+    private SparseArray<NavigationBarFragment> mNavigationBars = new SparseArray<>();
+
+    @Inject
+    public NavigationBarController(Context context, @Named(MAIN_HANDLER_NAME) Handler handler) {
+        mContext = context;
+        mHandler = handler;
+        mDisplayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+        mDisplayManager.registerDisplayListener(this, mHandler);
+    }
+
+    @Override
+    public void onDisplayAdded(int displayId) {
+        Display display = mDisplayManager.getDisplay(displayId);
+        createNavigationBar(display);
+    }
+
+    @Override
+    public void onDisplayRemoved(int displayId) {
+        removeNavigationBar(displayId);
+    }
+
+    @Override
+    public void onDisplayChanged(int displayId) {
+    }
+
+    // TODO(b/117478341): I use {@code includeDefaultDisplay} to make this method compatible to
+    // CarStatusBar because they have their own nav bar. Think about a better way for it.
+    /**
+     * Creates navigation bars when car/status bar initializes.
+     *
+     * @param includeDefaultDisplay {@code true} to create navigation bar on default display.
+     */
+    public void createNavigationBars(final boolean includeDefaultDisplay) {
+        Display[] displays = mDisplayManager.getDisplays();
+        for (Display display : displays) {
+            if (includeDefaultDisplay || display.getDisplayId() != DEFAULT_DISPLAY) {
+                createNavigationBar(display);
+            }
+        }
+    }
+
+    /**
+     * Adds a navigation bar on default display or an external display if the display supports
+     * system decorations.
+     *
+     * @param display the display to add navigation bar on.
+     */
+    private void createNavigationBar(Display display) {
+        if (display == null
+                || (display.getDisplayId() != DEFAULT_DISPLAY
+                        && !display.supportsSystemDecorations())) {
+            return;
+        }
+
+        final int displayId = display.getDisplayId();
+        final boolean isOnDefaultDisplay = displayId == DEFAULT_DISPLAY;
+        final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
+
+        try {
+            if (!wms.hasNavigationBar(displayId)) {
+                return;
+            }
+        } catch (RemoteException e) {
+            // Cannot get wms, just return with warning message.
+            Log.w(TAG, "Cannot get WindowManager.");
+            return;
+        }
+        final Context context = isOnDefaultDisplay
+                ? mContext
+                : mContext.createDisplayContext(display);
+        NavigationBarFragment.create(context, (tag, fragment) -> {
+            NavigationBarFragment navBar = (NavigationBarFragment) fragment;
+
+            // Unfortunately, we still need it because status bar needs LightBarController
+            // before notifications creation. We cannot directly use getLightBarController()
+            // from NavigationBarFragment directly.
+            LightBarController lightBarController = isOnDefaultDisplay
+                    ? Dependency.get(LightBarController.class)
+                    : new LightBarController(context,
+                            Dependency.get(DarkIconDispatcher.class),
+                            Dependency.get(BatteryController.class));
+            navBar.setLightBarController(lightBarController);
+
+            // TODO(b/118592525): to support multi-display, we start to add something which is
+            //                    per-display, while others may be global. I think it's time to add
+            //                    a new class maybe named DisplayDependency to solve per-display
+            //                    Dependency problem.
+            AutoHideController autoHideController = isOnDefaultDisplay
+                    ? Dependency.get(AutoHideController.class)
+                    : new AutoHideController(context, mHandler);
+            navBar.setAutoHideController(autoHideController);
+            navBar.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
+            mNavigationBars.append(displayId, navBar);
+        });
+    }
+
+    /** Removes navigation bars. */
+    public void destroy() {
+        mDisplayManager.unregisterDisplayListener(this);
+        if (mNavigationBars.size() > 0) {
+            for (int i = 0; i < mNavigationBars.size(); i++) {
+                int displayId = mNavigationBars.keyAt(i);
+                removeNavigationBar(displayId);
+            }
+            mNavigationBars.clear();
+        }
+    }
+
+    private void removeNavigationBar(int displayId) {
+        NavigationBarFragment navBar = mNavigationBars.get(displayId);
+        if (navBar != null) {
+            View navigationWindow = navBar.getView().getRootView();
+            WindowManagerGlobal.getInstance()
+                    .removeView(navigationWindow, true /* immediate */);
+            mNavigationBars.remove(displayId);
+        }
+    }
+
+    /** @see NavigationBarFragment#checkNavBarModes() */
+    public void checkNavBarModes(int displayId) {
+        NavigationBarFragment navBar = mNavigationBars.get(displayId);
+        if (navBar != null) {
+            navBar.checkNavBarModes();
+        }
+    }
+
+    /** @see NavigationBarFragment#finishBarAnimations() */
+    public void finishBarAnimations(int displayId) {
+        NavigationBarFragment navBar = mNavigationBars.get(displayId);
+        if (navBar != null) {
+            navBar.finishBarAnimations();
+        }
+    }
+
+    /** @see NavigationBarFragment#touchAutoDim() */
+    public void touchAutoDim(int displayId) {
+        NavigationBarFragment navBar = mNavigationBars.get(displayId);
+        if (navBar != null) {
+            navBar.touchAutoDim();
+        }
+    }
+
+    /** @see NavigationBarFragment#transitionTo(int, boolean) */
+    public void transitionTo(int displayId, @TransitionMode int barMode, boolean animate) {
+        NavigationBarFragment navBar = mNavigationBars.get(displayId);
+        if (navBar != null) {
+            navBar.transitionTo(barMode, animate);
+        }
+    }
+
+    /** @see NavigationBarFragment#disableAnimationsDuringHide(long) */
+    public void disableAnimationsDuringHide(int displayId, long delay) {
+        NavigationBarFragment navBar = mNavigationBars.get(displayId);
+        if (navBar != null) {
+            navBar.disableAnimationsDuringHide(delay);
+        }
+    }
+
+    /** @return {@link NavigationBarView} on the default display. */
+    public NavigationBarView getDefaultNavigationBarView() {
+        NavigationBarFragment navBar = mNavigationBars.get(DEFAULT_DISPLAY);
+        return (navBar == null) ? null : (NavigationBarView) navBar.getView();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
index ecd9814..0f295ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
@@ -2,7 +2,7 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 /**
  * Interface for anything that may need to keep notifications managed even after
@@ -24,7 +24,7 @@
      * @param entry the entry containing the notification to check
      * @return true if the notification lifetime should be extended
      */
-    boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry);
+    boolean shouldExtendLifetime(@NonNull NotificationEntry entry);
 
     /**
      * Sets whether or not the lifetime should be managed by the extender.  In practice, if
@@ -37,7 +37,7 @@
      * @param entry the entry that needs an extended lifetime
      * @param shouldManage true if the extender should manage the entry now, false otherwise
      */
-    void setShouldManageLifetime(@NonNull NotificationData.Entry entry, boolean shouldManage);
+    void setShouldManageLifetime(@NonNull NotificationEntry entry, boolean shouldManage);
 
     /**
      * The callback for when the notification is now safe to remove (i.e. its lifetime has ended).
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index bc662e3..f46ded4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -18,7 +18,7 @@
 import android.service.notification.StatusBarNotification;
 import android.util.SparseArray;
 
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 public interface NotificationLockscreenUserManager {
     String PERMISSION_SELF = "com.android.systemui.permission.SELF";
@@ -55,7 +55,7 @@
 
     void updatePublicMode();
 
-    boolean needsRedaction(Entry entry);
+    boolean needsRedaction(NotificationEntry entry);
 
     boolean userAllowsPrivateNotificationsInPublic(int currentUserId);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index bba4369..d5f4d04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -46,9 +46,9 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 
@@ -407,7 +407,7 @@
     }
 
     /** @return true if the entry needs redaction when on the lockscreen. */
-    public boolean needsRedaction(NotificationData.Entry ent) {
+    public boolean needsRedaction(NotificationEntry ent) {
         int userId = ent.notification.getUserId();
 
         boolean currentUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index f045548..7412702 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -25,11 +25,10 @@
 import android.app.Notification;
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.media.MediaMetadata;
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
@@ -42,12 +41,14 @@
 import android.view.View;
 import android.widget.ImageView;
 
+import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
 import com.android.systemui.Interpolators;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.LockscreenWallpaper;
 import com.android.systemui.statusbar.phone.ScrimController;
@@ -61,10 +62,16 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+
 /**
  * Handles tasks and state related to media notifications. For example, there is a 'current' media
  * notification, which this class keeps track of.
  */
+@Singleton
 public class NotificationMediaManager implements Dumpable {
     private static final String TAG = "NotificationMediaManager";
     public static final boolean DEBUG_MEDIA = false;
@@ -79,9 +86,9 @@
 
     // Late binding, also @Nullable due to being in com.android.systemui.statusbar.phone package
     @Nullable
-    private ShadeController mShadeController;
+    private Lazy<ShadeController> mShadeController;
     @Nullable
-    private StatusBarWindowController mStatusBarWindowController;
+    private Lazy<StatusBarWindowController> mStatusBarWindowController;
 
     @Nullable
     private BiometricUnlockController mBiometricUnlockController;
@@ -90,14 +97,11 @@
     @Nullable
     private LockscreenWallpaper mLockscreenWallpaper;
 
-    protected final PorterDuffXfermode mSrcXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
-    protected final PorterDuffXfermode mSrcOverXferMode =
-            new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER);
-
     private final Handler mHandler = Dependency.get(MAIN_HANDLER);
 
     private final Context mContext;
     private final MediaSessionManager mMediaSessionManager;
+    private final ArrayList<MediaListener> mMediaListeners;
 
     protected NotificationPresenter mPresenter;
     private MediaController mMediaController;
@@ -118,7 +122,7 @@
             if (state != null) {
                 if (!isPlaybackActive(state.getState())) {
                     clearCurrentMediaNotification();
-                    mPresenter.updateMediaMetaData(true, true);
+                    dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */);
                 }
             }
         }
@@ -130,39 +134,34 @@
                 Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
             }
             mMediaMetadata = metadata;
-            mPresenter.updateMediaMetaData(true, true);
+            dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */);
         }
     };
 
-    @Nullable
-    private ShadeController getShadeController() {
-        if (mShadeController == null) {
-            mShadeController = Dependency.get(ShadeController.class);
-        }
-        return mShadeController;
-    }
-
-    @Nullable
-    private StatusBarWindowController getWindowController() {
-        if (mStatusBarWindowController == null) {
-            mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
-        }
-        return mStatusBarWindowController;
-    }
-
-    private NotificationEntryManager getEntryManager() {
-        if (mEntryManager == null) {
-            mEntryManager = Dependency.get(NotificationEntryManager.class);
-        }
-        return mEntryManager;
-    }
-
-    public NotificationMediaManager(Context context) {
+    @Inject
+    public NotificationMediaManager(
+            Context context,
+            Lazy<ShadeController> shadeController,
+            Lazy<StatusBarWindowController> statusBarWindowController,
+            NotificationEntryManager notificationEntryManager) {
         mContext = context;
+        mMediaListeners = new ArrayList<>();
         mMediaSessionManager
                 = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
         // TODO: use MediaSessionManager.SessionListener to hook us up to future updates
         // in session state
+        mShadeController = shadeController;
+        mStatusBarWindowController = statusBarWindowController;
+        mEntryManager = notificationEntryManager;
+        notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
+            @Override
+            public void onEntryRemoved(
+                    NotificationEntry entry,
+                    NotificationVisibility visibility,
+                    boolean removedByUser) {
+                onNotificationRemoved(entry.key);
+            }
+        });
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter) {
@@ -172,7 +171,7 @@
     public void onNotificationRemoved(String key) {
         if (key.equals(mMediaNotificationKey)) {
             clearCurrentMediaNotification();
-            mPresenter.updateMediaMetaData(true, true);
+            dispatchUpdateMediaMetaData(true /* changed */, true /* allowEnterAnimation */);
         }
     }
 
@@ -184,20 +183,42 @@
         return mMediaMetadata;
     }
 
+    public Icon getMediaIcon() {
+        if (mMediaNotificationKey == null) {
+            return null;
+        }
+        synchronized (mEntryManager.getNotificationData()) {
+            NotificationEntry entry = mEntryManager.getNotificationData().get(mMediaNotificationKey);
+            if (entry == null || entry.expandedIcon == null) {
+                return null;
+            }
+
+            return entry.expandedIcon.getSourceIcon();
+        }
+    }
+
+    public void addCallback(MediaListener callback) {
+        mMediaListeners.add(callback);
+        callback.onMetadataChanged(mMediaMetadata);
+    }
+
+    public void removeCallback(MediaListener callback) {
+        mMediaListeners.remove(callback);
+    }
+
     public void findAndUpdateMediaNotifications() {
         boolean metaDataChanged = false;
 
-        NotificationEntryManager manager = getEntryManager();
-        synchronized (manager.getNotificationData()) {
-            ArrayList<NotificationData.Entry> activeNotifications = manager
-                    .getNotificationData().getActiveNotifications();
+        synchronized (mEntryManager.getNotificationData()) {
+            ArrayList<NotificationEntry> activeNotifications =
+                    mEntryManager.getNotificationData().getActiveNotifications();
             final int N = activeNotifications.size();
 
             // Promote the media notification with a controller in 'playing' state, if any.
-            NotificationData.Entry mediaNotification = null;
+            NotificationEntry mediaNotification = null;
             MediaController controller = null;
             for (int i = 0; i < N; i++) {
-                final NotificationData.Entry entry = activeNotifications.get(i);
+                final NotificationEntry entry = activeNotifications.get(i);
 
                 if (entry.isMediaNotification()) {
                     final MediaSession.Token token =
@@ -237,7 +258,7 @@
                             final String pkg = aController.getPackageName();
 
                             for (int i = 0; i < N; i++) {
-                                final NotificationData.Entry entry = activeNotifications.get(i);
+                                final NotificationEntry entry = activeNotifications.get(i);
                                 if (entry.notification.getPackageName().equals(pkg)) {
                                     if (DEBUG_MEDIA) {
                                         Log.v(TAG, "DEBUG_MEDIA: found controller matching "
@@ -278,12 +299,10 @@
         }
 
         if (metaDataChanged) {
-            getEntryManager().updateNotifications();
+            mEntryManager.updateNotifications();
         }
 
-        if (mPresenter != null) {
-            mPresenter.updateMediaMetaData(metaDataChanged, true);
-        }
+        dispatchUpdateMediaMetaData(metaDataChanged, true /* allowEnterAnimation */);
     }
 
     public void clearCurrentMediaNotification() {
@@ -291,6 +310,16 @@
         clearCurrentMediaNotificationSession();
     }
 
+    private void dispatchUpdateMediaMetaData(boolean changed, boolean allowEnterAnimation) {
+        if (mPresenter != null) {
+            mPresenter.updateMediaMetaData(changed, allowEnterAnimation);
+        }
+        ArrayList<MediaListener> callbacks = new ArrayList<>(mMediaListeners);
+        for (int i = 0; i < callbacks.size(); i++) {
+            callbacks.get(i).onMetadataChanged(mMediaMetadata);
+        }
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.print("    mMediaSessionManager=");
@@ -405,8 +434,9 @@
             }
         }
 
-        boolean hideBecauseOccluded = getShadeController() != null
-                && getShadeController().isOccluded();
+        ShadeController shadeController = mShadeController.get();
+        StatusBarWindowController windowController = mStatusBarWindowController.get();
+        boolean hideBecauseOccluded = shadeController != null && shadeController.isOccluded();
 
         final boolean hasArtwork = artworkDrawable != null;
         mColorExtractor.setHasBackdrop(hasArtwork);
@@ -429,8 +459,8 @@
                     mBackdrop.animate().cancel();
                     mBackdrop.setAlpha(1f);
                 }
-                if (getWindowController() != null) {
-                    getWindowController().setBackdropShowing(true);
+                if (windowController != null) {
+                    windowController.setBackdropShowing(true);
                 }
                 metaDataChanged = true;
                 if (DEBUG_MEDIA) {
@@ -477,8 +507,8 @@
                 if (DEBUG_MEDIA) {
                     Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
                 }
-                boolean cannotAnimateDoze = getShadeController() != null
-                        && getShadeController().isDozing()
+                boolean cannotAnimateDoze = shadeController != null
+                        && shadeController.isDozing()
                         && !ScrimState.AOD.getAnimateChange();
                 if (mBiometricUnlockController != null && mBiometricUnlockController.getMode()
                         == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
@@ -487,12 +517,12 @@
                     // We are unlocking directly - no animation!
                     mBackdrop.setVisibility(View.GONE);
                     mBackdropBack.setImageDrawable(null);
-                    if (getWindowController() != null) {
-                        getWindowController().setBackdropShowing(false);
+                    if (windowController != null) {
+                        windowController.setBackdropShowing(false);
                     }
                 } else {
-                    if (getWindowController() != null) {
-                        getWindowController().setBackdropShowing(false);
+                    if (windowController != null) {
+                        windowController.setBackdropShowing(false);
                     }
                     mBackdrop.animate()
                             .alpha(0)
@@ -547,4 +577,8 @@
             mBackdropFront.setImageDrawable(null);
         }
     };
+
+    public interface MediaListener {
+        void onMetadataChanged(MediaMetadata metadata);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index a5c0a2d..c945afd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -15,7 +15,7 @@
  */
 package com.android.systemui.statusbar;
 
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
@@ -28,7 +28,7 @@
  */
 public interface NotificationPresenter extends ExpandableNotificationRow.OnExpandClickListener,
         ActivatableNotificationView.OnActivatedListener,
-        NotificationEntryManager.Callback {
+        NotificationRowBinder.BindRowCallback {
     /**
      * Returns true if the presenter is not visible. For example, it may not be necessary to do
      * animations if this returns true.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 9391737..7d6231f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -17,7 +17,10 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
 
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.KeyguardManager;
@@ -26,6 +29,7 @@
 import android.app.RemoteInput;
 import android.content.Context;
 import android.content.Intent;
+import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -46,10 +50,10 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.policy.RemoteInputView;
@@ -59,12 +63,19 @@
 import java.util.ArrayList;
 import java.util.Set;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+
 /**
  * Class for handling remote input state over a set of notifications. This class handles things
  * like keeping notifications temporarily that were cancelled as a response to a remote input
  * interaction, keeping track of notifications to remove when NotificationPresenter is collapsed,
  * and handling clicks on remote views.
  */
+@Singleton
 public class NotificationRemoteInputManager implements Dumpable {
     public static final boolean ENABLE_REMOTE_INPUT =
             SystemProperties.getBoolean("debug.enable_remote_input", true);
@@ -92,19 +103,16 @@
      * Notifications that are already removed but are kept around because the remote input is
      * actively being used (i.e. user is typing in it).  See {@link RemoteInputActiveExtender}.
      */
-    protected final ArraySet<NotificationData.Entry> mEntriesKeptForRemoteInputActive =
+    protected final ArraySet<NotificationEntry> mEntriesKeptForRemoteInputActive =
             new ArraySet<>();
 
     // Dependencies:
-    protected final NotificationLockscreenUserManager mLockscreenUserManager =
-            Dependency.get(NotificationLockscreenUserManager.class);
-    protected final SmartReplyController mSmartReplyController =
-            Dependency.get(SmartReplyController.class);
-    private final NotificationEntryManager mEntryManager
-            = Dependency.get(NotificationEntryManager.class);
+    private final NotificationLockscreenUserManager mLockscreenUserManager;
+    private final SmartReplyController mSmartReplyController;
+    private final NotificationEntryManager mEntryManager;
+    private final Handler mMainHandler;
 
-    // Lazy
-    private ShadeController mShadeController;
+    private final Lazy<ShadeController> mShadeController;
 
     protected final Context mContext;
     private final UserManager mUserManager;
@@ -122,7 +130,7 @@
         @Override
         public boolean onClickHandler(
                 View view, PendingIntent pendingIntent, RemoteViews.RemoteResponse response) {
-            getShadeController().wakeUpIfDozing(SystemClock.uptimeMillis(), view);
+            mShadeController.get().wakeUpIfDozing(SystemClock.uptimeMillis(), view);
 
             if (handleRemoteInput(view, pendingIntent)) {
                 return true;
@@ -222,20 +230,37 @@
         }
     };
 
-    private ShadeController getShadeController() {
-        if (mShadeController == null) {
-            mShadeController = Dependency.get(ShadeController.class);
-        }
-        return mShadeController;
-    }
-
-    public NotificationRemoteInputManager(Context context) {
+    @Inject
+    public NotificationRemoteInputManager(
+            Context context,
+            NotificationLockscreenUserManager lockscreenUserManager,
+            SmartReplyController smartReplyController,
+            NotificationEntryManager notificationEntryManager,
+            Lazy<ShadeController> shadeController,
+            @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
         mContext = context;
+        mLockscreenUserManager = lockscreenUserManager;
+        mSmartReplyController = smartReplyController;
+        mEntryManager = notificationEntryManager;
+        mShadeController = shadeController;
+        mMainHandler = mainHandler;
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         addLifetimeExtenders();
         mKeyguardManager = context.getSystemService(KeyguardManager.class);
+
+        notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
+            @Override
+            public void onEntryRemoved(
+                    @Nullable NotificationEntry entry,
+                    NotificationVisibility visibility,
+                    boolean removedByUser) {
+                if (removedByUser && entry != null) {
+                    onPerformRemoveNotification(entry, entry.key);
+                }
+            }
+        });
     }
 
     /** Initializes this component with the provided dependencies. */
@@ -244,7 +269,7 @@
         mRemoteInputController = new RemoteInputController(delegate);
         mRemoteInputController.addCallback(new RemoteInputController.Callback() {
             @Override
-            public void onRemoteInputSent(NotificationData.Entry entry) {
+            public void onRemoteInputSent(NotificationEntry entry) {
                 if (FORCE_REMOTE_INPUT_HISTORY
                         && isNotificationKeptForRemoteInputHistory(entry.key)) {
                     mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key);
@@ -253,7 +278,7 @@
                     // view it is already canceled, so we'll need to cancel it on the apps behalf
                     // after sending - unless the app posts an update in the mean time, so wait a
                     // bit.
-                    Dependency.get(Dependency.MAIN_HANDLER).postDelayed(() -> {
+                    mMainHandler.postDelayed(() -> {
                         if (mEntriesKeptForRemoteInputActive.remove(entry)) {
                             mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key);
                         }
@@ -387,10 +412,10 @@
         return mRemoteInputController;
     }
 
-    public void onPerformRemoveNotification(StatusBarNotification n,
-            NotificationData.Entry entry) {
-        if (mKeysKeptForRemoteInputHistory.contains(n.getKey())) {
-            mKeysKeptForRemoteInputHistory.remove(n.getKey());
+    @VisibleForTesting
+    void onPerformRemoveNotification(NotificationEntry entry, final String key) {
+        if (mKeysKeptForRemoteInputHistory.contains(key)) {
+            mKeysKeptForRemoteInputHistory.remove(key);
         }
         if (mRemoteInputController.isRemoteInputActive(entry)) {
             mRemoteInputController.removeRemoteInput(entry, null);
@@ -399,7 +424,7 @@
 
     public void onPanelCollapsed() {
         for (int i = 0; i < mEntriesKeptForRemoteInputActive.size(); i++) {
-            NotificationData.Entry entry = mEntriesKeptForRemoteInputActive.valueAt(i);
+            NotificationEntry entry = mEntriesKeptForRemoteInputActive.valueAt(i);
             mRemoteInputController.removeRemoteInput(entry, null);
             if (mNotificationLifetimeFinishedCallback != null) {
                 mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key);
@@ -412,7 +437,7 @@
         return mKeysKeptForRemoteInputHistory.contains(key);
     }
 
-    public boolean shouldKeepForRemoteInputHistory(NotificationData.Entry entry) {
+    public boolean shouldKeepForRemoteInputHistory(NotificationEntry entry) {
         if (entry.isDismissed()) {
             return false;
         }
@@ -422,7 +447,7 @@
         return (mRemoteInputController.isSpinning(entry.key) || entry.hasJustSentRemoteInput());
     }
 
-    public boolean shouldKeepForSmartReplyHistory(NotificationData.Entry entry) {
+    public boolean shouldKeepForSmartReplyHistory(NotificationEntry entry) {
         if (entry.isDismissed()) {
             return false;
         }
@@ -442,13 +467,13 @@
 
     @VisibleForTesting
     StatusBarNotification rebuildNotificationForCanceledSmartReplies(
-            NotificationData.Entry entry) {
+            NotificationEntry entry) {
         return rebuildNotificationWithRemoteInput(entry, null /* remoteInputTest */,
                 false /* showSpinner */);
     }
 
     @VisibleForTesting
-    StatusBarNotification rebuildNotificationWithRemoteInput(NotificationData.Entry entry,
+    StatusBarNotification rebuildNotificationWithRemoteInput(NotificationEntry entry,
             CharSequence remoteInputText, boolean showSpinner) {
         StatusBarNotification sbn = entry.notification;
 
@@ -505,7 +530,7 @@
     }
 
     @VisibleForTesting
-    public Set<NotificationData.Entry> getEntriesKeptForRemoteInputActive() {
+    public Set<NotificationEntry> getEntriesKeptForRemoteInputActive() {
         return mEntriesKeptForRemoteInputActive;
     }
 
@@ -528,12 +553,12 @@
      */
     protected class RemoteInputHistoryExtender extends RemoteInputExtender {
         @Override
-        public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) {
+        public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
             return shouldKeepForRemoteInputHistory(entry);
         }
 
         @Override
-        public void setShouldManageLifetime(NotificationData.Entry entry,
+        public void setShouldManageLifetime(NotificationEntry entry,
                 boolean shouldExtend) {
             if (shouldExtend) {
                 CharSequence remoteInputText = entry.remoteInputText;
@@ -574,12 +599,12 @@
      */
     protected class SmartReplyHistoryExtender extends RemoteInputExtender {
         @Override
-        public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) {
+        public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
             return shouldKeepForSmartReplyHistory(entry);
         }
 
         @Override
-        public void setShouldManageLifetime(NotificationData.Entry entry,
+        public void setShouldManageLifetime(NotificationEntry entry,
                 boolean shouldExtend) {
             if (shouldExtend) {
                 StatusBarNotification newSbn = rebuildNotificationForCanceledSmartReplies(entry);
@@ -612,7 +637,7 @@
      */
     protected class RemoteInputActiveExtender extends RemoteInputExtender {
         @Override
-        public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) {
+        public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
             if (entry.isDismissed()) {
                 return false;
             }
@@ -620,7 +645,7 @@
         }
 
         @Override
-        public void setShouldManageLifetime(NotificationData.Entry entry,
+        public void setShouldManageLifetime(NotificationEntry entry,
                 boolean shouldExtend) {
             if (shouldExtend) {
                 if (Log.isLoggable(TAG, Log.DEBUG)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
index f23ae3f..f0d804d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
@@ -24,7 +24,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -52,7 +52,7 @@
     }
 
     public static NotificationUiAdjustment extractFromNotificationEntry(
-            NotificationData.Entry entry) {
+            NotificationEntry entry) {
         return new NotificationUiAdjustment(
                 entry.key, entry.systemGeneratedSmartActions, entry.smartReplies);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index dc3a607..bf6caa0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -25,12 +25,11 @@
 import android.view.View;
 import android.view.ViewGroup;
 
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -41,6 +40,11 @@
 import java.util.List;
 import java.util.Stack;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+
 /**
  * NotificationViewHierarchyManager manages updating the view hierarchy of notification views based
  * on their group structure. For example, if a notification becomes bundled with another,
@@ -48,6 +52,7 @@
  * tell NotificationListContainer which notifications to display, and inform it of changes to those
  * notifications that might affect their display.
  */
+@Singleton
 public class NotificationViewHierarchyManager {
     private static final String TAG = "NotificationViewHierarchyManager";
 
@@ -56,20 +61,15 @@
             mTmpChildOrderMap = new HashMap<>();
 
     // Dependencies:
-    protected final NotificationLockscreenUserManager mLockscreenUserManager =
-            Dependency.get(NotificationLockscreenUserManager.class);
-    protected final NotificationGroupManager mGroupManager =
-            Dependency.get(NotificationGroupManager.class);
-    protected final VisualStabilityManager mVisualStabilityManager =
-            Dependency.get(VisualStabilityManager.class);
-    private final StatusBarStateController mStatusBarStateController =
-            Dependency.get(StatusBarStateController.class);
-    private final NotificationEntryManager mEntryManager =
-            Dependency.get(NotificationEntryManager.class);
-    private final BubbleController mBubbleController = Dependency.get(BubbleController.class);
+    protected final NotificationLockscreenUserManager mLockscreenUserManager;
+    protected final NotificationGroupManager mGroupManager;
+    protected final VisualStabilityManager mVisualStabilityManager;
+    private final StatusBarStateController mStatusBarStateController;
+    private final NotificationEntryManager mEntryManager;
+    private final BubbleController mBubbleController;
 
     // Lazy
-    private ShadeController mShadeController;
+    private final Lazy<ShadeController> mShadeController;
 
     /**
      * {@code true} if notifications not part of a group should by default be rendered in their
@@ -116,20 +116,27 @@
         }
     }
 
-    private ShadeController getShadeController() {
-        if (mShadeController == null) {
-            mShadeController = Dependency.get(ShadeController.class);
-        }
-        return mShadeController;
-    }
-
-    public NotificationViewHierarchyManager(Context context) {
+    @Inject
+    public NotificationViewHierarchyManager(Context context,
+            NotificationLockscreenUserManager notificationLockscreenUserManager,
+            NotificationGroupManager groupManager,
+            VisualStabilityManager visualStabilityManager,
+            StatusBarStateController statusBarStateController,
+            NotificationEntryManager notificationEntryManager,
+            BubbleController bubbleController,
+            Lazy<ShadeController> shadeController) {
+        mLockscreenUserManager = notificationLockscreenUserManager;
+        mGroupManager = groupManager;
+        mVisualStabilityManager = visualStabilityManager;
+        mStatusBarStateController = statusBarStateController;
+        mEntryManager = notificationEntryManager;
+        mBubbleController = bubbleController;
+        mShadeController = shadeController;
         Resources res = context.getResources();
         mAlwaysExpandNonGroupedNotification =
                 res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
         mStatusBarStateListener = new StatusBarStateListener(mBubbleController);
-        mEntryManager.setStatusBarStateListener(mStatusBarStateListener);
-        Dependency.get(StatusBarStateController.class).addCallback(mStatusBarStateListener);
+        mStatusBarStateController.addCallback(mStatusBarStateListener);
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter,
@@ -143,13 +150,13 @@
      */
     //TODO: Rewrite this to focus on Entries, or some other data object instead of views
     public void updateNotificationViews() {
-        ArrayList<NotificationData.Entry> activeNotifications = mEntryManager.getNotificationData()
+        ArrayList<NotificationEntry> activeNotifications = mEntryManager.getNotificationData()
                 .getActiveNotifications();
         ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
-        ArrayList<NotificationData.Entry> toBubble = new ArrayList<>();
+        ArrayList<NotificationEntry> toBubble = new ArrayList<>();
         final int N = activeNotifications.size();
         for (int i = 0; i < N; i++) {
-            NotificationData.Entry ent = activeNotifications.get(i);
+            NotificationEntry ent = activeNotifications.get(i);
             if (ent.isRowDismissed() || ent.isRowRemoved()) {
                 // we don't want to update removed notifications because they could
                 // temporarily become children if they were isolated before.
@@ -180,7 +187,7 @@
             ent.getRow().setSensitive(sensitive, deviceSensitive);
             ent.getRow().setNeedsRedaction(needsRedaction);
             if (mGroupManager.isChildInGroupWithSummary(ent.notification)) {
-                NotificationData.Entry summary = mGroupManager.getGroupSummary(ent.notification);
+                NotificationEntry summary = mGroupManager.getGroupSummary(ent.notification);
                 List<ExpandableNotificationRow> orderedChildren =
                         mTmpChildOrderMap.get(summary.getRow());
                 if (orderedChildren == null) {
@@ -264,7 +271,7 @@
 
         for (int i = 0; i < toBubble.size(); i++) {
             // TODO: might make sense to leave them in the shade and just reposition them
-            NotificationData.Entry ent = toBubble.get(i);
+            NotificationEntry ent = toBubble.get(i);
             mBubbleController.addBubble(ent);
         }
 
@@ -378,7 +385,7 @@
         }
         while(!stack.isEmpty()) {
             ExpandableNotificationRow row = stack.pop();
-            NotificationData.Entry entry = row.getEntry();
+            NotificationEntry entry = row.getEntry();
             boolean isChildNotification =
                     mGroupManager.isChildInGroupWithSummary(entry.notification);
 
@@ -392,7 +399,7 @@
                         && !row.isLowPriority()));
             }
 
-            entry.getRow().setOnAmbient(getShadeController().isDozing());
+            entry.getRow().setOnAmbient(mShadeController.get().isDozing());
             int userId = entry.notification.getUserId();
             boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
                     entry.notification) && !entry.isRowRemoved();
@@ -401,7 +408,7 @@
             if (!showOnKeyguard) {
                 // min priority notifications should show if their summary is showing
                 if (mGroupManager.isChildInGroupWithSummary(entry.notification)) {
-                    NotificationData.Entry summary = mGroupManager.getLogicalGroupSummary(
+                    NotificationEntry summary = mGroupManager.getLogicalGroupSummary(
                             entry.notification);
                     if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(
                             summary.notification))         {
@@ -437,7 +444,7 @@
             }
 
             row.showAppOpsIcons(entry.mActiveAppOps);
-            row.setAudiblyAlerted(entry.audiblyAlerted);
+            row.setLastAudiblyAlertedMs(entry.lastAudiblyAlertedMs);
         }
 
         Trace.beginSection("NotificationPresenter#onUpdateRowStates");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
index f1a891b..d1b3c3c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
@@ -30,8 +30,8 @@
 import com.android.settingslib.WirelessUtils;
 import com.android.systemui.DemoMode;
 import com.android.systemui.Dependency;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index e8abcc2..998cf52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -24,7 +24,7 @@
 import android.util.Pair;
 
 import com.android.internal.util.Preconditions;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.policy.RemoteInputView;
 
 import java.lang.ref.WeakReference;
@@ -38,7 +38,7 @@
     private static final boolean ENABLE_REMOTE_INPUT =
             SystemProperties.getBoolean("debug.enable_remote_input", true);
 
-    private final ArrayList<Pair<WeakReference<NotificationData.Entry>, Object>> mOpen
+    private final ArrayList<Pair<WeakReference<NotificationEntry>, Object>> mOpen
             = new ArrayList<>();
     private final ArrayMap<String, Object> mSpinning = new ArrayMap<>();
     private final ArrayList<Callback> mCallbacks = new ArrayList<>(3);
@@ -101,7 +101,7 @@
      * @param entry the entry for which a remote input is now active.
      * @param token a token identifying the view that is managing the remote input
      */
-    public void addRemoteInput(NotificationData.Entry entry, Object token) {
+    public void addRemoteInput(NotificationEntry entry, Object token) {
         Preconditions.checkNotNull(entry);
         Preconditions.checkNotNull(token);
 
@@ -122,7 +122,7 @@
      *              the entry is only removed if the token matches the last added token for this
      *              entry. If null, the entry is removed regardless.
      */
-    public void removeRemoteInput(NotificationData.Entry entry, Object token) {
+    public void removeRemoteInput(NotificationEntry entry, Object token) {
         Preconditions.checkNotNull(entry);
 
         pruneWeakThenRemoveAndContains(null /* contains */, entry /* remove */, token);
@@ -173,7 +173,7 @@
         return mSpinning.get(key) == token;
     }
 
-    private void apply(NotificationData.Entry entry) {
+    private void apply(NotificationEntry entry) {
         mDelegate.setRemoteInputActive(entry, isRemoteInputActive(entry));
         boolean remoteInputActive = isRemoteInputActive();
         int N = mCallbacks.size();
@@ -185,7 +185,7 @@
     /**
      * @return true if {@param entry} has an active RemoteInput
      */
-    public boolean isRemoteInputActive(NotificationData.Entry entry) {
+    public boolean isRemoteInputActive(NotificationEntry entry) {
         return pruneWeakThenRemoveAndContains(entry /* contains */, null /* remove */,
                 null /* removeToken */);
     }
@@ -208,10 +208,10 @@
      * @return true if {@param contains} is in the set of active remote inputs
      */
     private boolean pruneWeakThenRemoveAndContains(
-            NotificationData.Entry contains, NotificationData.Entry remove, Object removeToken) {
+            NotificationEntry contains, NotificationEntry remove, Object removeToken) {
         boolean found = false;
         for (int i = mOpen.size() - 1; i >= 0; i--) {
-            NotificationData.Entry item = mOpen.get(i).first.get();
+            NotificationEntry item = mOpen.get(i).first.get();
             Object itemToken = mOpen.get(i).second;
             boolean removeTokenMatches = (removeToken == null || itemToken == removeToken);
 
@@ -235,7 +235,7 @@
         mCallbacks.add(callback);
     }
 
-    public void remoteInputSent(NotificationData.Entry entry) {
+    public void remoteInputSent(NotificationEntry entry) {
         int N = mCallbacks.size();
         for (int i = 0; i < N; i++) {
             mCallbacks.get(i).onRemoteInputSent(entry);
@@ -248,16 +248,16 @@
         }
 
         // Make a copy because closing the remote inputs will modify mOpen.
-        ArrayList<NotificationData.Entry> list = new ArrayList<>(mOpen.size());
+        ArrayList<NotificationEntry> list = new ArrayList<>(mOpen.size());
         for (int i = mOpen.size() - 1; i >= 0; i--) {
-            NotificationData.Entry entry = mOpen.get(i).first.get();
+            NotificationEntry entry = mOpen.get(i).first.get();
             if (entry != null && entry.rowExists()) {
                 list.add(entry);
             }
         }
 
         for (int i = list.size() - 1; i >= 0; i--) {
-            NotificationData.Entry entry = list.get(i);
+            NotificationEntry entry = list.get(i);
             if (entry.rowExists()) {
                 entry.closeRemoteInput();
             }
@@ -268,31 +268,31 @@
         mDelegate.requestDisallowLongPressAndDismiss();
     }
 
-    public void lockScrollTo(NotificationData.Entry entry) {
+    public void lockScrollTo(NotificationEntry entry) {
         mDelegate.lockScrollTo(entry);
     }
 
     public interface Callback {
         default void onRemoteInputActive(boolean active) {}
 
-        default void onRemoteInputSent(NotificationData.Entry entry) {}
+        default void onRemoteInputSent(NotificationEntry entry) {}
     }
 
     public interface Delegate {
         /**
          * Activate remote input if necessary.
          */
-        void setRemoteInputActive(NotificationData.Entry entry, boolean remoteInputActive);
+        void setRemoteInputActive(NotificationEntry entry, boolean remoteInputActive);
 
-       /**
-        * Request that the view does not dismiss nor perform long press for the current touch.
-        */
-       void requestDisallowLongPressAndDismiss();
+        /**
+         * Request that the view does not dismiss nor perform long press for the current touch.
+         */
+        void requestDisallowLongPressAndDismiss();
 
-      /**
-       * Request that the view is made visible by scrolling to it, and keep the scroll locked until
-       * the user scrolls, or {@param v} loses focus or is detached.
-       */
-       void lockScrollTo(NotificationData.Entry entry);
+        /**
+         * Request that the view is made visible by scrolling to it, and keep the scroll locked until
+         * the user scrolls, or {@param entry} loses focus or is detached.
+         */
+        void lockScrollTo(NotificationEntry entry);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index e7b768f..6d2c001 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -41,9 +41,9 @@
 import com.android.settingslib.graph.SignalDrawable;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.policy.IconLogger;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index f5d6904..573c1f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -21,26 +21,30 @@
 
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.Dependency;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.util.Set;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Handles when smart replies are added to a notification
  * and clicked upon.
  */
+@Singleton
 public class SmartReplyController {
-    private IStatusBarService mBarService;
+    private final IStatusBarService mBarService;
+    private final NotificationEntryManager mEntryManager;
     private Set<String> mSendingKeys = new ArraySet<>();
     private Callback mCallback;
-    private final NotificationEntryManager mEntryManager =
-            Dependency.get(NotificationEntryManager.class);
 
-
-    public SmartReplyController() {
-        mBarService = Dependency.get(IStatusBarService.class);
+    @Inject
+    public SmartReplyController(NotificationEntryManager entryManager,
+            IStatusBarService statusBarService) {
+        mBarService = statusBarService;
+        mEntryManager = entryManager;
     }
 
     public void setCallback(Callback callback) {
@@ -50,7 +54,7 @@
     /**
      * Notifies StatusBarService a smart reply is sent.
      */
-    public void smartReplySent(NotificationData.Entry entry, int replyIndex, CharSequence reply,
+    public void smartReplySent(NotificationEntry entry, int replyIndex, CharSequence reply,
             boolean generatedByAssistant) {
         mCallback.onSmartReplySent(entry, reply);
         mSendingKeys.add(entry.key);
@@ -66,7 +70,7 @@
      * Notifies StatusBarService a smart action is clicked.
      */
     public void smartActionClicked(
-            NotificationData.Entry entry, int actionIndex, Notification.Action action,
+            NotificationEntry entry, int actionIndex, Notification.Action action,
             boolean generatedByAssistant) {
         final int count = mEntryManager.getNotificationData().getActiveNotifications().size();
         final int rank = mEntryManager.getNotificationData().getRank(entry.key);
@@ -88,16 +92,20 @@
         return mSendingKeys.contains(key);
     }
 
-    public void smartRepliesAdded(final NotificationData.Entry entry, int replyCount) {
+    /**
+     * Smart Replies and Actions have been added to the UI.
+     */
+    public void smartSuggestionsAdded(final NotificationEntry entry, int replyCount,
+            int actionCount, boolean generatedByAssistant) {
         try {
-            mBarService.onNotificationSmartRepliesAdded(entry.notification.getKey(),
-                    replyCount);
+            mBarService.onNotificationSmartSuggestionsAdded(
+                    entry.notification.getKey(), replyCount, actionCount, generatedByAssistant);
         } catch (RemoteException e) {
             // Nothing to do, system going down
         }
     }
 
-    public void stopSending(final NotificationData.Entry entry) {
+    public void stopSending(final NotificationEntry entry) {
         if (entry != null) {
             mSendingKeys.remove(entry.notification.getKey());
         }
@@ -113,6 +121,6 @@
          * @param entry the entry for the notification
          * @param reply the reply that was sent
          */
-        void onSmartReplySent(NotificationData.Entry entry, CharSequence reply);
+        void onSmartReplySent(NotificationEntry entry, CharSequence reply);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 8b61a5b..3c13354 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar;
 
-import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint;
+import static com.android.systemui.plugins.DarkIconDispatcher.getTint;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index bc89889..4db981d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -16,11 +16,11 @@
 
 package com.android.systemui.statusbar;
 
+import static com.android.systemui.plugins.DarkIconDispatcher.getTint;
+import static com.android.systemui.plugins.DarkIconDispatcher.isInArea;
 import static com.android.systemui.statusbar.StatusBarIconView.STATE_DOT;
 import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN;
 import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON;
-import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint;
-import static com.android.systemui.statusbar.policy.DarkIconDispatcher.isInArea;
 
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -37,8 +37,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.graph.SignalDrawable;
 import com.android.systemui.R;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
 
 public class StatusBarMobileView extends FrameLayout implements DarkReceiver,
         StatusIconDisplayable {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
index 3f84416..087b655 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
@@ -35,9 +35,13 @@
 import java.util.ArrayList;
 import java.util.Comparator;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Tracks and reports on {@link StatusBarState}.
  */
+@Singleton
 public class StatusBarStateController implements CallbackController<StateListener> {
     private static final String TAG = "SbStateController";
 
@@ -101,6 +105,10 @@
     public static final int RANK_STACK_SCROLLER = 2;
     public static final int RANK_SHELF = 3;
 
+    @Inject
+    public StatusBarStateController() {
+    }
+
     public int getState() {
         return mState;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
index 045221f..c5751c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
@@ -16,11 +16,11 @@
 
 package com.android.systemui.statusbar;
 
+import static com.android.systemui.plugins.DarkIconDispatcher.getTint;
+import static com.android.systemui.plugins.DarkIconDispatcher.isInArea;
 import static com.android.systemui.statusbar.StatusBarIconView.STATE_DOT;
 import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN;
 import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON;
-import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint;
-import static com.android.systemui.statusbar.policy.DarkIconDispatcher.isInArea;
 
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -37,8 +37,8 @@
 
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
 
 /**
  * Start small: StatusBarWifiView will be able to layout from a WifiIconState
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
index beb90b8..d541fae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar;
 
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 
 public interface StatusIconDisplayable extends DarkReceiver {
     String getSlot();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
index 6560f8f..442416f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
@@ -26,6 +26,12 @@
 import android.os.Vibrator;
 import android.provider.Settings;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
 public class VibratorHelper {
 
     private final Vibrator mVibrator;
@@ -44,6 +50,9 @@
         }
     };
 
+    /**
+     */
+    @Inject
     public VibratorHelper(Context context) {
         mContext = context;
         mVibrator = context.getSystemService(Vibrator.class);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index f899863..e1b231b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -28,12 +28,12 @@
 import android.view.IRemoteAnimationRunner;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationTarget;
+import android.view.SyncRtSurfaceTransactionApplier;
+import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
 
 import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.systemui.Interpolators;
 import com.android.systemui.shared.system.SurfaceControlCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier.SurfaceParams;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
@@ -267,8 +267,8 @@
             Matrix m = new Matrix();
             m.postTranslate(0, (float) (mParams.top - app.position.y));
             mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight());
-            SurfaceParams params = new SurfaceParams(new SurfaceControlCompat(app.leash),
-                    1f /* alpha */, m, mWindowCrop, app.prefixOrderIndex, mCornerRadius);
+            SurfaceParams params = new SurfaceParams(app.leash, 1f /* alpha */, m, mWindowCrop,
+                    app.prefixOrderIndex, mCornerRadius);
             mSyncRtTransactionApplier.scheduleApply(params);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
new file mode 100644
index 0000000..c24698d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+
+import android.app.Notification;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.statusbar.AlertingNotificationManager;
+import com.android.systemui.statusbar.AmbientPulseManager;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.NotificationInflater;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+
+/** Handles heads-up and pulsing behavior driven by notification changes. */
+@Singleton
+public class NotificationAlertingManager {
+
+    private static final String TAG = "NotifAlertManager";
+
+    private final AmbientPulseManager mAmbientPulseManager;
+    private final NotificationRemoteInputManager mRemoteInputManager;
+    private final VisualStabilityManager mVisualStabilityManager;
+    private final Lazy<ShadeController> mShadeController;
+    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+    private final NotificationListener mNotificationListener;
+
+    private HeadsUpManager mHeadsUpManager;
+
+    @Inject
+    public NotificationAlertingManager(
+            NotificationEntryManager notificationEntryManager,
+            AmbientPulseManager ambientPulseManager,
+            NotificationRemoteInputManager remoteInputManager,
+            VisualStabilityManager visualStabilityManager,
+            Lazy<ShadeController> shadeController,
+            NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+            NotificationListener notificationListener) {
+        mAmbientPulseManager = ambientPulseManager;
+        mRemoteInputManager = remoteInputManager;
+        mVisualStabilityManager = visualStabilityManager;
+        mShadeController = shadeController;
+        mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
+        mNotificationListener = notificationListener;
+
+        notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
+            @Override
+            public void onEntryInflated(NotificationEntry entry, int inflatedFlags) {
+                showAlertingView(entry, inflatedFlags);
+            }
+
+            @Override
+            public void onEntryUpdated(NotificationEntry entry) {
+                updateAlertState(entry);
+            }
+
+            @Override
+            public void onEntryRemoved(
+                    NotificationEntry entry,
+                    NotificationVisibility visibility,
+                    boolean removedByUser) {
+                stopAlerting(entry.key);
+            }
+        });
+    }
+
+    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+        mHeadsUpManager = headsUpManager;
+    }
+
+    /**
+     * Adds the entry to the respective alerting manager if the content view was inflated and
+     * the entry should still alert.
+     *
+     * @param entry         entry to add
+     * @param inflatedFlags flags representing content views that were inflated
+     */
+    private void showAlertingView(NotificationEntry entry,
+            @NotificationInflater.InflationFlag int inflatedFlags) {
+        if ((inflatedFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
+            // Possible for shouldHeadsUp to change between the inflation starting and ending.
+            // If it does and we no longer need to heads up, we should free the view.
+            if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
+                mHeadsUpManager.showNotification(entry);
+                // Mark as seen immediately
+                setNotificationShown(entry.notification);
+            } else {
+                entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
+            }
+        }
+        if ((inflatedFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
+            if (mNotificationInterruptionStateProvider.shouldPulse(entry)) {
+                mAmbientPulseManager.showNotification(entry);
+            } else {
+                entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
+            }
+        }
+    }
+
+    private void updateAlertState(NotificationEntry entry) {
+        boolean alertAgain = alertAgain(entry, entry.notification.getNotification());
+        AlertingNotificationManager alertManager;
+        boolean shouldAlert;
+        if (mShadeController.get().isDozing()) {
+            alertManager = mAmbientPulseManager;
+            shouldAlert = mNotificationInterruptionStateProvider.shouldPulse(entry);
+        } else {
+            alertManager = mHeadsUpManager;
+            shouldAlert = mNotificationInterruptionStateProvider.shouldHeadsUp(entry);
+        }
+        final boolean wasAlerting = alertManager.isAlerting(entry.key);
+        if (wasAlerting) {
+            if (!shouldAlert) {
+                // We don't want this to be interrupting anymore, let's remove it
+                alertManager.removeNotification(entry.key,
+                        false /* ignoreEarliestRemovalTime */);
+            } else {
+                alertManager.updateNotification(entry.key, alertAgain);
+            }
+        } else if (shouldAlert && alertAgain) {
+            // This notification was updated to be alerting, show it!
+            alertManager.showNotification(entry);
+        }
+    }
+
+    private static boolean alertAgain(
+            NotificationEntry oldEntry, Notification newNotification) {
+        return oldEntry == null || !oldEntry.hasInterrupted()
+                || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
+    }
+
+    private void setNotificationShown(StatusBarNotification n) {
+        try {
+            mNotificationListener.setNotificationsShown(new String[]{n.getKey()});
+        } catch (RuntimeException e) {
+            Log.d(TAG, "failed setNotificationsShown: ", e);
+        }
+    }
+
+    private void stopAlerting(final String key) {
+        // Attempt to remove notifications from their alert managers (heads up, ambient pulse).
+        // Though the remove itself may fail, it lets the manager know to remove as soon as
+        // possible.
+        if (mHeadsUpManager.isAlerting(key)) {
+            // A cancel() in response to a remote input shouldn't be delayed, as it makes the
+            // sending look longer than it takes.
+            // Also we should not defer the removal if reordering isn't allowed since otherwise
+            // some notifications can't disappear before the panel is closed.
+            boolean ignoreEarliestRemovalTime =
+                    mRemoteInputManager.getController().isSpinning(key)
+                            && !FORCE_REMOTE_INPUT_HISTORY
+                            || !mVisualStabilityManager.isReorderingAllowed();
+            mHeadsUpManager.removeNotification(key, ignoreEarliestRemovalTime);
+        }
+        if (mAmbientPulseManager.isAlerting(key)) {
+            mAmbientPulseManager.removeNotification(key, false /* ignoreEarliestRemovalTime */);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
new file mode 100644
index 0000000..49f1a8d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification;
+
+import android.app.Notification;
+import android.os.SystemClock;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.view.View;
+
+import com.android.systemui.DejankUtils;
+import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.phone.ShadeController;
+
+/**
+ * Click handler for generic clicks on notifications. Clicks on specific areas (expansion caret,
+ * app ops icon, etc) are handled elsewhere.
+ */
+public final class NotificationClicker implements View.OnClickListener {
+    private static final String TAG = "NotificationClicker";
+
+    private final ShadeController mShadeController;
+    private final BubbleController mBubbleController;
+    private final NotificationActivityStarter mNotificationActivityStarter;
+
+    public NotificationClicker(ShadeController shadeController,
+            BubbleController bubbleController,
+            NotificationActivityStarter notificationActivityStarter) {
+        mShadeController = shadeController;
+        mBubbleController = bubbleController;
+        mNotificationActivityStarter = notificationActivityStarter;
+    }
+
+    @Override
+    public void onClick(final View v) {
+        if (!(v instanceof ExpandableNotificationRow)) {
+            Log.e(TAG, "NotificationClicker called on a view that is not a notification row.");
+            return;
+        }
+
+        mShadeController.wakeUpIfDozing(SystemClock.uptimeMillis(), v);
+
+        final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+        final StatusBarNotification sbn = row.getStatusBarNotification();
+        if (sbn == null) {
+            Log.e(TAG, "NotificationClicker called on an unclickable notification,");
+            return;
+        }
+
+        // Check if the notification is displaying the menu, if so slide notification back
+        if (isMenuVisible(row)) {
+            row.animateTranslateNotification(0);
+            return;
+        } else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) {
+            row.getNotificationParent().animateTranslateNotification(0);
+            return;
+        } else if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
+            // We never want to open the app directly if the user clicks in between
+            // the notifications.
+            return;
+        }
+
+        // Mark notification for one frame.
+        row.setJustClicked(true);
+        DejankUtils.postAfterTraversal(() -> row.setJustClicked(false));
+
+        // If it was a bubble we should close it
+        if (row.getEntry().isBubble()) {
+            mBubbleController.collapseStack();
+        }
+
+        mNotificationActivityStarter.onNotificationClicked(sbn, row);
+    }
+
+    private boolean isMenuVisible(ExpandableNotificationRow row) {
+        return row.getProvider() != null && row.getProvider().isMenuVisible();
+    }
+
+    /**
+     * Attaches the click listener to the row if appropriate.
+     */
+    public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
+        Notification notification = sbn.getNotification();
+        if (notification.contentIntent != null || notification.fullScreenIntent != null) {
+            row.setOnClickListener(this);
+        } else {
+            row.setOnClickListener(null);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
deleted file mode 100644
index f543b46..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ /dev/null
@@ -1,1154 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification;
-
-import static android.app.Notification.CATEGORY_ALARM;
-import static android.app.Notification.CATEGORY_CALL;
-import static android.app.Notification.CATEGORY_EVENT;
-import static android.app.Notification.CATEGORY_MESSAGE;
-import static android.app.Notification.CATEGORY_REMINDER;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.app.AppGlobals;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.Person;
-import android.content.Context;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Icon;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.service.notification.NotificationListenerService.Ranking;
-import android.service.notification.NotificationListenerService.RankingMap;
-import android.service.notification.SnoozeCriterion;
-import android.service.notification.StatusBarNotification;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.view.View;
-import android.widget.ImageView;
-
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.ContrastColorUtil;
-import com.android.systemui.Dependency;
-import com.android.systemui.ForegroundServiceController;
-import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotificationGuts;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * The list of currently displaying notifications.
- */
-public class NotificationData {
-
-    /**
-     * These dependencies are late init-ed
-     */
-    private KeyguardEnvironment mEnvironment;
-    private ShadeController mShadeController;
-    private NotificationMediaManager mMediaManager;
-    private ForegroundServiceController mFsc;
-    private NotificationLockscreenUserManager mUserManager;
-
-    private HeadsUpManager mHeadsUpManager;
-
-    public static final class Entry {
-        private static final long LAUNCH_COOLDOWN = 2000;
-        private static final long REMOTE_INPUT_COOLDOWN = 500;
-        private static final long INITIALIZATION_DELAY = 400;
-        private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN;
-        private static final int COLOR_INVALID = 1;
-        public String key;
-        public StatusBarNotification notification;
-        public NotificationChannel channel;
-        public boolean audiblyAlerted;
-        public boolean noisy;
-        public int importance;
-        public StatusBarIconView icon;
-        public StatusBarIconView expandedIcon;
-        private boolean interruption;
-        public boolean autoRedacted; // whether the redacted notification was generated by us
-        public int targetSdk;
-        private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
-        public CharSequence remoteInputText;
-        public List<SnoozeCriterion> snoozeCriteria;
-        public int userSentiment = Ranking.USER_SENTIMENT_NEUTRAL;
-        /** Smart Actions provided by the NotificationAssistantService. */
-        @NonNull
-        public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
-        public CharSequence[] smartReplies = new CharSequence[0];
-
-        private Entry parent; // our parent (if we're in a group)
-        private ArrayList<Entry> children = new ArrayList<Entry>();
-        private ExpandableNotificationRow row; // the outer expanded view
-
-        private int mCachedContrastColor = COLOR_INVALID;
-        private int mCachedContrastColorIsFor = COLOR_INVALID;
-        private InflationTask mRunningTask = null;
-        private Throwable mDebugThrowable;
-        public CharSequence remoteInputTextWhenReset;
-        public long lastRemoteInputSent = NOT_LAUNCHED_YET;
-        public ArraySet<Integer> mActiveAppOps = new ArraySet<>(3);
-        public CharSequence headsUpStatusBarText;
-        public CharSequence headsUpStatusBarTextPublic;
-
-        private long initializationTime = -1;
-
-        /**
-         * Whether or not this row represents a system notification. Note that if this is
-         * {@code null}, that means we were either unable to retrieve the info or have yet to
-         * retrieve the info.
-         */
-        public Boolean mIsSystemNotification;
-
-        /**
-         * Has the user sent a reply through this Notification.
-         */
-        private boolean hasSentReply;
-
-        /**
-         * Whether this notification should be displayed as a bubble.
-         */
-        private boolean mIsBubble;
-
-        /**
-         * Whether the user has dismissed this notification when it was in bubble form.
-         */
-        private boolean mUserDismissedBubble;
-
-        public Entry(StatusBarNotification n) {
-            this(n, null);
-        }
-
-        public Entry(StatusBarNotification n, @Nullable Ranking ranking) {
-            this.key = n.getKey();
-            this.notification = n;
-            if (ranking != null) {
-                populateFromRanking(ranking);
-            }
-        }
-
-        public void populateFromRanking(@NonNull Ranking ranking) {
-            channel = ranking.getChannel();
-            audiblyAlerted = ranking.audiblyAlerted();
-            importance = ranking.getImportance();
-            snoozeCriteria = ranking.getSnoozeCriteria();
-            userSentiment = ranking.getUserSentiment();
-            systemGeneratedSmartActions = ranking.getSmartActions() == null
-                    ? Collections.emptyList() : ranking.getSmartActions();
-            smartReplies = ranking.getSmartReplies() == null
-                    ? new CharSequence[0]
-                    : ranking.getSmartReplies().toArray(new CharSequence[0]);
-        }
-
-        public void setInterruption() {
-            interruption = true;
-        }
-
-        public boolean hasInterrupted() {
-            return interruption;
-        }
-
-        public void setIsBubble(boolean bubbleable) {
-            mIsBubble = bubbleable;
-        }
-
-        public boolean isBubble() {
-            return mIsBubble;
-        }
-
-        public void setBubbleDismissed(boolean userDismissed) {
-            mUserDismissedBubble = userDismissed;
-        }
-
-        public boolean isBubbleDismissed() {
-            return mUserDismissedBubble;
-        }
-
-        /**
-         * Resets the notification entry to be re-used.
-         */
-        public void reset() {
-            if (row != null) {
-                row.reset();
-            }
-        }
-
-        public ExpandableNotificationRow getRow() {
-            return row;
-        }
-
-        //TODO: This will go away when we have a way to bind an entry to a row
-        public void setRow(ExpandableNotificationRow row) {
-            this.row = row;
-        }
-
-        @Nullable
-        public List<Entry> getChildren() {
-            if (children.size() <= 0) {
-                return null;
-            }
-
-            return children;
-        }
-
-        public void notifyFullScreenIntentLaunched() {
-            setInterruption();
-            lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime();
-        }
-
-        public boolean hasJustLaunchedFullScreenIntent() {
-            return SystemClock.elapsedRealtime() < lastFullScreenIntentLaunchTime + LAUNCH_COOLDOWN;
-        }
-
-        public boolean hasJustSentRemoteInput() {
-            return SystemClock.elapsedRealtime() < lastRemoteInputSent + REMOTE_INPUT_COOLDOWN;
-        }
-
-        public boolean hasFinishedInitialization() {
-            return initializationTime == -1 ||
-                    SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY;
-        }
-
-        /**
-         * Create the icons for a notification
-         * @param context the context to create the icons with
-         * @param sbn the notification
-         * @throws InflationException
-         */
-        public void createIcons(Context context, StatusBarNotification sbn)
-                throws InflationException {
-            Notification n = sbn.getNotification();
-            final Icon smallIcon = n.getSmallIcon();
-            if (smallIcon == null) {
-                throw new InflationException("No small icon in notification from "
-                        + sbn.getPackageName());
-            }
-
-            // Construct the icon.
-            icon = new StatusBarIconView(context,
-                    sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
-            icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-
-            // Construct the expanded icon.
-            expandedIcon = new StatusBarIconView(context,
-                    sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
-            expandedIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-            final StatusBarIcon ic = new StatusBarIcon(
-                    sbn.getUser(),
-                    sbn.getPackageName(),
-                    smallIcon,
-                    n.iconLevel,
-                    n.number,
-                    StatusBarIconView.contentDescForNotification(context, n));
-            if (!icon.set(ic) || !expandedIcon.set(ic)) {
-                icon = null;
-                expandedIcon = null;
-                throw new InflationException("Couldn't create icon: " + ic);
-            }
-            expandedIcon.setVisibility(View.INVISIBLE);
-            expandedIcon.setOnVisibilityChangedListener(
-                    newVisibility -> {
-                        if (row != null) {
-                            row.setIconsVisible(newVisibility != View.VISIBLE);
-                        }
-                    });
-        }
-
-        public void setIconTag(int key, Object tag) {
-            if (icon != null) {
-                icon.setTag(key, tag);
-                expandedIcon.setTag(key, tag);
-            }
-        }
-
-        /**
-         * Update the notification icons.
-         *
-         * @param context the context to create the icons with.
-         * @param sbn the notification to read the icon from.
-         * @throws InflationException
-         */
-        public void updateIcons(Context context, StatusBarNotification sbn)
-                throws InflationException {
-            if (icon != null) {
-                // Update the icon
-                Notification n = sbn.getNotification();
-                final StatusBarIcon ic = new StatusBarIcon(
-                        notification.getUser(),
-                        notification.getPackageName(),
-                        n.getSmallIcon(),
-                        n.iconLevel,
-                        n.number,
-                        StatusBarIconView.contentDescForNotification(context, n));
-                icon.setNotification(sbn);
-                expandedIcon.setNotification(sbn);
-                if (!icon.set(ic) || !expandedIcon.set(ic)) {
-                    throw new InflationException("Couldn't update icon: " + ic);
-                }
-            }
-        }
-
-        public int getContrastedColor(Context context, boolean isLowPriority,
-                int backgroundColor) {
-            int rawColor = isLowPriority ? Notification.COLOR_DEFAULT :
-                    notification.getNotification().color;
-            if (mCachedContrastColorIsFor == rawColor && mCachedContrastColor != COLOR_INVALID) {
-                return mCachedContrastColor;
-            }
-            final int contrasted = ContrastColorUtil.resolveContrastColor(context, rawColor,
-                    backgroundColor);
-            mCachedContrastColorIsFor = rawColor;
-            mCachedContrastColor = contrasted;
-            return mCachedContrastColor;
-        }
-
-        /**
-         * Abort all existing inflation tasks
-         */
-        public void abortTask() {
-            if (mRunningTask != null) {
-                mRunningTask.abort();
-                mRunningTask = null;
-            }
-        }
-
-        public void setInflationTask(InflationTask abortableTask) {
-            // abort any existing inflation
-            InflationTask existing = mRunningTask;
-            abortTask();
-            mRunningTask = abortableTask;
-            if (existing != null && mRunningTask != null) {
-                mRunningTask.supersedeTask(existing);
-            }
-        }
-
-        public void onInflationTaskFinished() {
-            mRunningTask = null;
-        }
-
-        @VisibleForTesting
-        public InflationTask getRunningTask() {
-            return mRunningTask;
-        }
-
-        /**
-         * Set a throwable that is used for debugging
-         *
-         * @param debugThrowable the throwable to save
-         */
-        public void setDebugThrowable(Throwable debugThrowable) {
-            mDebugThrowable = debugThrowable;
-        }
-
-        public Throwable getDebugThrowable() {
-            return mDebugThrowable;
-        }
-
-        public void onRemoteInputInserted() {
-            lastRemoteInputSent = NOT_LAUNCHED_YET;
-            remoteInputTextWhenReset = null;
-        }
-
-        public void setHasSentReply() {
-            hasSentReply = true;
-        }
-
-        public boolean isLastMessageFromReply() {
-            if (!hasSentReply) {
-                return false;
-            }
-            Bundle extras = notification.getNotification().extras;
-            CharSequence[] replyTexts = extras.getCharSequenceArray(
-                    Notification.EXTRA_REMOTE_INPUT_HISTORY);
-            if (!ArrayUtils.isEmpty(replyTexts)) {
-                return true;
-            }
-            Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
-            if (messages != null && messages.length > 0) {
-                Parcelable message = messages[messages.length - 1];
-                if (message instanceof Bundle) {
-                    Notification.MessagingStyle.Message lastMessage =
-                            Notification.MessagingStyle.Message.getMessageFromBundle(
-                                    (Bundle) message);
-                    if (lastMessage != null) {
-                        Person senderPerson = lastMessage.getSenderPerson();
-                        if (senderPerson == null) {
-                            return true;
-                        }
-                        Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON);
-                        return Objects.equals(user, senderPerson);
-                    }
-                }
-            }
-            return false;
-        }
-
-        public void setInitializationTime(long time) {
-            if (initializationTime == -1) {
-                initializationTime = time;
-            }
-        }
-
-        public void sendAccessibilityEvent(int eventType) {
-            if (row != null) {
-                row.sendAccessibilityEvent(eventType);
-            }
-        }
-
-        /**
-         * Used by NotificationMediaManager to determine... things
-         * @return {@code true} if we are a media notification
-         */
-        public boolean isMediaNotification() {
-            if (row == null) return false;
-
-            return row.isMediaRow();
-        }
-
-        /**
-         * We are a top level child if our parent is the list of notifications duh
-         * @return {@code true} if we're a top level notification
-         */
-        public boolean isTopLevelChild() {
-            return row != null && row.isTopLevelChild();
-        }
-
-        public void resetUserExpansion() {
-            if (row != null) row.resetUserExpansion();
-        }
-
-        public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
-            if (row != null) row.freeContentViewWhenSafe(inflationFlag);
-        }
-
-        public void setAmbientPulsing(boolean pulsing) {
-            if (row != null) row.setAmbientPulsing(pulsing);
-        }
-
-        public boolean rowExists() {
-            return row != null;
-        }
-
-        public boolean isRowDismissed() {
-            return row != null && row.isDismissed();
-        }
-
-        public boolean isRowRemoved() {
-            return row != null && row.isRemoved();
-        }
-
-        /**
-         * @return {@code true} if the row is null or removed
-         */
-        public boolean isRemoved() {
-            //TODO: recycling invalidates this
-            return row == null || row.isRemoved();
-        }
-
-        /**
-         * @return {@code true} if the row is null or dismissed
-         */
-        public boolean isDismissed() {
-            //TODO: recycling
-            return row == null || row.isDismissed();
-        }
-
-        public boolean isRowPinned() {
-            return row != null && row.isPinned();
-        }
-
-        public void setRowPinned(boolean pinned) {
-            if (row != null) row.setPinned(pinned);
-        }
-
-        public boolean isRowAnimatingAway() {
-            return row != null && row.isHeadsUpAnimatingAway();
-        }
-
-        public boolean isRowHeadsUp() {
-            return row != null && row.isHeadsUp();
-        }
-
-        public void setHeadsUp(boolean shouldHeadsUp) {
-            if (row != null) row.setHeadsUp(shouldHeadsUp);
-        }
-
-        public boolean mustStayOnScreen() {
-            return row != null && row.mustStayOnScreen();
-        }
-
-        public void setHeadsUpIsVisible() {
-            if (row != null) row.setHeadsUpIsVisible();
-        }
-
-        //TODO: i'm imagining a world where this isn't just the row, but I could be rwong
-        public ExpandableNotificationRow getHeadsUpAnimationView() {
-            return row;
-        }
-
-        public void setUserLocked(boolean userLocked) {
-            if (row != null) row.setUserLocked(userLocked);
-        }
-
-        public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
-            if (row != null) row.setUserExpanded(userExpanded, allowChildExpansion);
-        }
-
-        public void setGroupExpansionChanging(boolean changing) {
-            if (row != null) row.setGroupExpansionChanging(changing);
-        }
-
-        public void notifyHeightChanged(boolean needsAnimation) {
-            if (row != null) row.notifyHeightChanged(needsAnimation);
-        }
-
-        public void closeRemoteInput() {
-            if (row != null) row.closeRemoteInput();
-        }
-
-        public boolean areChildrenExpanded() {
-            return row != null && row.areChildrenExpanded();
-        }
-
-        public boolean keepInParent() {
-            return row != null && row.keepInParent();
-        }
-
-        //TODO: probably less confusing to say "is group fully visible"
-        public boolean isGroupNotFullyVisible() {
-            return row == null || row.isGroupNotFullyVisible();
-        }
-
-        public NotificationGuts getGuts() {
-            if (row != null) return row.getGuts();
-            return null;
-        }
-
-        public boolean hasLowPriorityStateUpdated() {
-            return row != null && row.hasLowPriorityStateUpdated();
-        }
-
-        public void removeRow() {
-            if (row != null) row.setRemoved();
-        }
-
-        public boolean isSummaryWithChildren() {
-            return row != null && row.isSummaryWithChildren();
-        }
-
-        public void setKeepInParent(boolean keep) {
-            if (row != null) row.setKeepInParent(keep);
-        }
-
-        public void onDensityOrFontScaleChanged() {
-            if (row != null) row.onDensityOrFontScaleChanged();
-        }
-
-        public boolean areGutsExposed() {
-            return row != null && row.getGuts() != null && row.getGuts().isExposed();
-        }
-
-        public boolean isChildInGroup() {
-            return parent == null;
-        }
-
-        public void setLowPriorityStateUpdated(boolean updated) {
-            if (row != null) row.setLowPriorityStateUpdated(updated);
-        }
-
-        /**
-         * @return Can the underlying notification be cleared? This can be different from whether the
-         *         notification can be dismissed in case notifications are sensitive on the lockscreen.
-         * @see #canViewBeDismissed()
-         */
-        public boolean isClearable() {
-            if (notification == null || !notification.isClearable()) {
-                return false;
-            }
-            if (children.size() > 0) {
-                for (int i = 0; i < children.size(); i++) {
-                    Entry child =  children.get(i);
-                    if (!child.isClearable()) {
-                        return false;
-                    }
-                }
-            }
-            return true;
-        }
-
-        public boolean canViewBeDismissed() {
-            if (row == null) return true;
-            return row.canViewBeDismissed();
-        }
-    }
-
-    private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
-    private final ArrayList<Entry> mSortedAndFiltered = new ArrayList<>();
-    private final ArrayList<Entry> mFilteredForUser = new ArrayList<>();
-
-    private final NotificationGroupManager mGroupManager
-            = Dependency.get(NotificationGroupManager.class);
-
-    private RankingMap mRankingMap;
-    private final Ranking mTmpRanking = new Ranking();
-
-    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
-        mHeadsUpManager = headsUpManager;
-    }
-
-    private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() {
-        private final Ranking mRankingA = new Ranking();
-        private final Ranking mRankingB = new Ranking();
-
-        @Override
-        public int compare(Entry a, Entry b) {
-            final StatusBarNotification na = a.notification;
-            final StatusBarNotification nb = b.notification;
-            int aImportance = NotificationManager.IMPORTANCE_DEFAULT;
-            int bImportance = NotificationManager.IMPORTANCE_DEFAULT;
-            int aRank = 0;
-            int bRank = 0;
-
-            if (mRankingMap != null) {
-                // RankingMap as received from NoMan
-                getRanking(a.key, mRankingA);
-                getRanking(b.key, mRankingB);
-                aImportance = mRankingA.getImportance();
-                bImportance = mRankingB.getImportance();
-                aRank = mRankingA.getRank();
-                bRank = mRankingB.getRank();
-            }
-
-            String mediaNotification = getMediaManager().getMediaNotificationKey();
-
-            // IMPORTANCE_MIN media streams are allowed to drift to the bottom
-            final boolean aMedia = a.key.equals(mediaNotification)
-                    && aImportance > NotificationManager.IMPORTANCE_MIN;
-            final boolean bMedia = b.key.equals(mediaNotification)
-                    && bImportance > NotificationManager.IMPORTANCE_MIN;
-
-            boolean aSystemMax = aImportance >= NotificationManager.IMPORTANCE_HIGH &&
-                    isSystemNotification(na);
-            boolean bSystemMax = bImportance >= NotificationManager.IMPORTANCE_HIGH &&
-                    isSystemNotification(nb);
-
-            boolean isHeadsUp = a.row.isHeadsUp();
-            if (isHeadsUp != b.row.isHeadsUp()) {
-                return isHeadsUp ? -1 : 1;
-            } else if (isHeadsUp) {
-                // Provide consistent ranking with headsUpManager
-                return mHeadsUpManager.compare(a, b);
-            } else if (a.row.isAmbientPulsing() != b.row.isAmbientPulsing()) {
-                return a.row.isAmbientPulsing() ? -1 : 1;
-            } else if (aMedia != bMedia) {
-                // Upsort current media notification.
-                return aMedia ? -1 : 1;
-            } else if (aSystemMax != bSystemMax) {
-                // Upsort PRIORITY_MAX system notifications
-                return aSystemMax ? -1 : 1;
-            } else if (aRank != bRank) {
-                return aRank - bRank;
-            } else {
-                return Long.compare(nb.getNotification().when, na.getNotification().when);
-            }
-        }
-    };
-
-    private KeyguardEnvironment getEnvironment() {
-        if (mEnvironment == null) {
-            mEnvironment = Dependency.get(KeyguardEnvironment.class);
-        }
-        return mEnvironment;
-    }
-
-    private ShadeController getShadeController() {
-        if (mShadeController == null) {
-            mShadeController = Dependency.get(ShadeController.class);
-        }
-        return mShadeController;
-    }
-
-    private NotificationMediaManager getMediaManager() {
-        if (mMediaManager == null) {
-            mMediaManager = Dependency.get(NotificationMediaManager.class);
-        }
-        return mMediaManager;
-    }
-
-    private ForegroundServiceController getFsc() {
-        if (mFsc == null) {
-            mFsc = Dependency.get(ForegroundServiceController.class);
-        }
-        return mFsc;
-    }
-
-    private NotificationLockscreenUserManager getUserManager() {
-        if (mUserManager == null) {
-            mUserManager = Dependency.get(NotificationLockscreenUserManager.class);
-        }
-        return mUserManager;
-    }
-
-    /**
-     * Returns the sorted list of active notifications (depending on {@link KeyguardEnvironment}
-     *
-     * <p>
-     * This call doesn't update the list of active notifications. Call {@link #filterAndSort()}
-     * when the environment changes.
-     * <p>
-     * Don't hold on to or modify the returned list.
-     */
-    public ArrayList<Entry> getActiveNotifications() {
-        return mSortedAndFiltered;
-    }
-
-    public ArrayList<Entry> getNotificationsForCurrentUser() {
-        mFilteredForUser.clear();
-
-        synchronized (mEntries) {
-            final int N = mEntries.size();
-            for (int i = 0; i < N; i++) {
-                Entry entry = mEntries.valueAt(i);
-                final StatusBarNotification sbn = entry.notification;
-                if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
-                    continue;
-                }
-                mFilteredForUser.add(entry);
-            }
-        }
-        return mFilteredForUser;
-    }
-
-    public Entry get(String key) {
-        return mEntries.get(key);
-    }
-
-    public void add(Entry entry) {
-        synchronized (mEntries) {
-            mEntries.put(entry.notification.getKey(), entry);
-        }
-        mGroupManager.onEntryAdded(entry);
-
-        updateRankingAndSort(mRankingMap);
-    }
-
-    public Entry remove(String key, RankingMap ranking) {
-        Entry removed = null;
-        synchronized (mEntries) {
-            removed = mEntries.remove(key);
-        }
-        if (removed == null) return null;
-        mGroupManager.onEntryRemoved(removed);
-        updateRankingAndSort(ranking);
-        return removed;
-    }
-
-    public void updateRanking(RankingMap ranking) {
-        updateRankingAndSort(ranking);
-    }
-
-    public void updateAppOp(int appOp, int uid, String pkg, String key, boolean showIcon) {
-        synchronized (mEntries) {
-            final int N = mEntries.size();
-            for (int i = 0; i < N; i++) {
-                Entry entry = mEntries.valueAt(i);
-                if (uid == entry.notification.getUid()
-                        && pkg.equals(entry.notification.getPackageName())
-                        && key.equals(entry.key)) {
-                    if (showIcon) {
-                        entry.mActiveAppOps.add(appOp);
-                    } else {
-                        entry.mActiveAppOps.remove(appOp);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Returns true if this notification should be displayed in the high-priority notifications
-     * section (and on the lockscreen and status bar).
-     */
-    public boolean isHighPriority(StatusBarNotification statusBarNotification) {
-        if (mRankingMap != null) {
-            getRanking(statusBarNotification.getKey(), mTmpRanking);
-            if (mTmpRanking.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT
-                    || statusBarNotification.getNotification().isForegroundService()
-                    || statusBarNotification.getNotification().hasMediaSession()) {
-                return true;
-            }
-            if (mGroupManager.isSummaryOfGroup(statusBarNotification)) {
-                for (Entry child : mGroupManager.getLogicalChildren(statusBarNotification)) {
-                    if (isHighPriority(child.notification)) {
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    public boolean isAmbient(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.isAmbient();
-        }
-        return false;
-    }
-
-    public int getVisibilityOverride(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.getVisibilityOverride();
-        }
-        return Ranking.VISIBILITY_NO_OVERRIDE;
-    }
-
-    public boolean shouldSuppressFullScreenIntent(Entry entry) {
-        return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
-    }
-
-    public boolean shouldSuppressPeek(Entry entry) {
-        return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_PEEK);
-    }
-
-    public boolean shouldSuppressStatusBar(Entry entry) {
-        return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_STATUS_BAR);
-    }
-
-    public boolean shouldSuppressAmbient(Entry entry) {
-        return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_AMBIENT);
-    }
-
-    public boolean shouldSuppressNotificationList(Entry entry) {
-        return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_NOTIFICATION_LIST);
-    }
-
-    private boolean shouldSuppressVisualEffect(Entry entry, int effect) {
-        if (isExemptFromDndVisualSuppression(entry)) {
-            return false;
-        }
-        String key = entry.key;
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return (mTmpRanking.getSuppressedVisualEffects() & effect) != 0;
-        }
-        return false;
-    }
-
-    protected boolean isExemptFromDndVisualSuppression(Entry entry) {
-        if (isNotificationBlockedByPolicy(entry.notification.getNotification())) {
-            return false;
-        }
-
-        if ((entry.notification.getNotification().flags
-                & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
-            return true;
-        }
-        if (entry.notification.getNotification().isMediaNotification()) {
-            return true;
-        }
-        if (entry.mIsSystemNotification != null && entry.mIsSystemNotification) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Categories that are explicitly called out on DND settings screens are always blocked, if
-     * DND has flagged them, even if they are foreground or system notifications that might
-     * otherwise visually bypass DND.
-     */
-    protected boolean isNotificationBlockedByPolicy(Notification n) {
-        if (isCategory(CATEGORY_CALL, n)
-                || isCategory(CATEGORY_MESSAGE, n)
-                || isCategory(CATEGORY_ALARM, n)
-                || isCategory(CATEGORY_EVENT, n)
-                || isCategory(CATEGORY_REMINDER, n)) {
-            return true;
-        }
-        return false;
-    }
-
-    private boolean isCategory(String category, Notification n) {
-        return Objects.equals(n.category, category);
-    }
-
-    public int getImportance(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.getImportance();
-        }
-        return NotificationManager.IMPORTANCE_UNSPECIFIED;
-    }
-
-    public String getOverrideGroupKey(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.getOverrideGroupKey();
-        }
-        return null;
-    }
-
-    public List<SnoozeCriterion> getSnoozeCriteria(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.getSnoozeCriteria();
-        }
-        return null;
-    }
-
-    public NotificationChannel getChannel(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.getChannel();
-        }
-        return null;
-    }
-
-    public int getRank(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.getRank();
-        }
-        return 0;
-    }
-
-    public boolean shouldHide(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.isSuspended();
-        }
-        return false;
-    }
-
-    private void updateRankingAndSort(RankingMap ranking) {
-        if (ranking != null) {
-            mRankingMap = ranking;
-            synchronized (mEntries) {
-                final int N = mEntries.size();
-                for (int i = 0; i < N; i++) {
-                    Entry entry = mEntries.valueAt(i);
-                    if (!getRanking(entry.key, mTmpRanking)) {
-                        continue;
-                    }
-                    final StatusBarNotification oldSbn = entry.notification.cloneLight();
-                    final String overrideGroupKey = getOverrideGroupKey(entry.key);
-                    if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) {
-                        entry.notification.setOverrideGroupKey(overrideGroupKey);
-                        mGroupManager.onEntryUpdated(entry, oldSbn);
-                    }
-                    entry.populateFromRanking(mTmpRanking);
-                }
-            }
-        }
-        filterAndSort();
-    }
-
-    /**
-     * Get the ranking from the current ranking map.
-     *
-     * @param key the key to look up
-     * @param outRanking the ranking to populate
-     *
-     * @return {@code true} if the ranking was properly obtained.
-     */
-    @VisibleForTesting
-    protected boolean getRanking(String key, Ranking outRanking) {
-        return mRankingMap.getRanking(key, outRanking);
-    }
-
-    // TODO: This should not be public. Instead the Environment should notify this class when
-    // anything changed, and this class should call back the UI so it updates itself.
-    public void filterAndSort() {
-        mSortedAndFiltered.clear();
-
-        synchronized (mEntries) {
-            final int N = mEntries.size();
-            for (int i = 0; i < N; i++) {
-                Entry entry = mEntries.valueAt(i);
-
-                if (shouldFilterOut(entry)) {
-                    continue;
-                }
-
-                mSortedAndFiltered.add(entry);
-            }
-        }
-
-        Collections.sort(mSortedAndFiltered, mRankingComparator);
-    }
-
-    /**
-     * @return true if this notification should NOT be shown right now
-     */
-    public boolean shouldFilterOut(Entry entry) {
-        final StatusBarNotification sbn = entry.notification;
-        if (!(getEnvironment().isDeviceProvisioned() ||
-                showNotificationEvenIfUnprovisioned(sbn))) {
-            return true;
-        }
-
-        if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
-            return true;
-        }
-
-        if (getUserManager().isLockscreenPublicMode(sbn.getUserId()) &&
-                (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
-                        || getUserManager().shouldHideNotifications(sbn.getUserId())
-                        || getUserManager().shouldHideNotifications(sbn.getKey()))) {
-            return true;
-        }
-
-        if (getShadeController().isDozing() && shouldSuppressAmbient(entry)) {
-            return true;
-        }
-
-        if (!getShadeController().isDozing() && shouldSuppressNotificationList(entry)) {
-            return true;
-        }
-
-        if (shouldHide(sbn.getKey())) {
-            return true;
-        }
-
-        if (!StatusBar.ENABLE_CHILD_NOTIFICATIONS
-                && mGroupManager.isChildInGroupWithSummary(sbn)) {
-            return true;
-        }
-
-        if (getFsc().isDungeonNotification(sbn)
-                && !getFsc().isDungeonNeededForUser(sbn.getUserId())) {
-            // this is a foreground-service disclosure for a user that does not need to show one
-            return true;
-        }
-        if (getFsc().isSystemAlertNotification(sbn)) {
-            final String[] apps = sbn.getNotification().extras.getStringArray(
-                    Notification.EXTRA_FOREGROUND_APPS);
-            if (apps != null && apps.length >= 1) {
-                if (!getFsc().isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    // Q: What kinds of notifications should show during setup?
-    // A: Almost none! Only things coming from packages with permission
-    // android.permission.NOTIFICATION_DURING_SETUP that also have special "kind" tags marking them
-    // as relevant for setup (see below).
-    public static boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
-        return showNotificationEvenIfUnprovisioned(AppGlobals.getPackageManager(), sbn);
-    }
-
-    @VisibleForTesting
-    static boolean showNotificationEvenIfUnprovisioned(IPackageManager packageManager,
-            StatusBarNotification sbn) {
-        return checkUidPermission(packageManager, Manifest.permission.NOTIFICATION_DURING_SETUP,
-                sbn.getUid()) == PackageManager.PERMISSION_GRANTED
-                && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
-    }
-
-    private static int checkUidPermission(IPackageManager packageManager, String permission,
-            int uid) {
-        try {
-            return packageManager.checkUidPermission(permission, uid);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    public void dump(PrintWriter pw, String indent) {
-        int N = mSortedAndFiltered.size();
-        pw.print(indent);
-        pw.println("active notifications: " + N);
-        int active;
-        for (active = 0; active < N; active++) {
-            NotificationData.Entry e = mSortedAndFiltered.get(active);
-            dumpEntry(pw, indent, active, e);
-        }
-        synchronized (mEntries) {
-            int M = mEntries.size();
-            pw.print(indent);
-            pw.println("inactive notifications: " + (M - active));
-            int inactiveCount = 0;
-            for (int i = 0; i < M; i++) {
-                Entry entry = mEntries.valueAt(i);
-                if (!mSortedAndFiltered.contains(entry)) {
-                    dumpEntry(pw, indent, inactiveCount, entry);
-                    inactiveCount++;
-                }
-            }
-        }
-    }
-
-    private void dumpEntry(PrintWriter pw, String indent, int i, Entry e) {
-        getRanking(e.key, mTmpRanking);
-        pw.print(indent);
-        pw.println("  [" + i + "] key=" + e.key + " icon=" + e.icon);
-        StatusBarNotification n = e.notification;
-        pw.print(indent);
-        pw.println("      pkg=" + n.getPackageName() + " id=" + n.getId() + " importance=" +
-                mTmpRanking.getImportance());
-        pw.print(indent);
-        pw.println("      notification=" + n.getNotification());
-    }
-
-    private static boolean isSystemNotification(StatusBarNotification sbn) {
-        String sbnPackage = sbn.getPackageName();
-        return "android".equals(sbnPackage) || "com.android.systemui".equals(sbnPackage);
-    }
-
-    /**
-     * Provides access to keyguard state and user settings dependent data.
-     */
-    public interface KeyguardEnvironment {
-        boolean isDeviceProvisioned();
-        boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
new file mode 100644
index 0000000..c640760
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification;
+
+import android.annotation.Nullable;
+import android.service.notification.StatusBarNotification;
+
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.NotificationInflater;
+
+/**
+ * Listener interface for changes sent by NotificationEntryManager.
+ */
+public interface NotificationEntryListener {
+    /**
+     * Called when a new notification is posted. At this point, the notification is "pending": its
+     * views haven't been inflated yet and most of the system pretends like it doesn't exist yet.
+     */
+    default void onPendingEntryAdded(NotificationEntry entry) {
+    }
+
+    /**
+     * Called when a new entry is created.
+     */
+    default void onNotificationAdded(NotificationEntry entry) {
+    }
+
+    /**
+     * Called when a notification was updated.
+     */
+    default void onEntryUpdated(NotificationEntry entry) {
+    }
+
+    /**
+     * Called when a notification's views are inflated for the first time.
+     */
+    default void onEntryInflated(NotificationEntry entry,
+            @NotificationInflater.InflationFlag int inflatedFlags) {
+    }
+
+    /**
+     * Called when an existing notification's views are reinflated (usually due to an update being
+     * posted to that notification).
+     *
+     * @param entry notification data entry that was reinflated.
+     */
+    default void onEntryReinflated(NotificationEntry entry) {
+    }
+
+    /**
+     * Called when an error occurred inflating the views for a notification.
+     */
+    default void onInflationError(StatusBarNotification notification, Exception exception) {
+    }
+
+    /**
+     * Called when a notification has been removed (either because the user swiped it away or
+     * because the developer retracted it).
+     * @param entry notification data entry that was removed.  Null if no entry existed for the
+     *              removed key at the time of removal.
+     * @param visibility logging data related to the visibility of the notification at the time of
+     *                   removal, if it was removed by a user action.  Null if it was not removed by
+     *                   a user action.
+     * @param removedByUser true if the notification was removed by a user action
+     */
+    default void onEntryRemoved(
+            NotificationEntry entry,
+            @Nullable NotificationVisibility visibility,
+            boolean removedByUser) {
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 16a3849..a4e3f33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -15,78 +15,34 @@
  */
 package com.android.systemui.statusbar.notification;
 
-import static com.android.systemui.bubbles.BubbleController.DEBUG_DEMOTE_TO_NOTIF;
-import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
-import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
-import static com.android.systemui.statusbar.StatusBarState.SHADE;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
-
 import android.annotation.Nullable;
 import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.database.ContentObserver;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.PowerManager;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
+import android.os.Handler;
 import android.os.UserHandle;
-import android.provider.Settings;
-import android.service.dreams.DreamService;
-import android.service.dreams.IDreamManager;
 import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationStats;
 import android.service.notification.StatusBarNotification;
-import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.EventLog;
 import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
-import com.android.internal.util.NotificationMessagingUtil;
-import com.android.systemui.DejankUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
-import com.android.systemui.EventLogTags;
 import com.android.systemui.ForegroundServiceController;
-import com.android.systemui.InitController;
-import com.android.systemui.R;
-import com.android.systemui.UiOffloadThread;
-import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.statusbar.AlertingNotificationManager;
-import com.android.systemui.statusbar.AmbientPulseManager;
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
-import com.android.systemui.statusbar.NotificationListener;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationUiAdjustment;
 import com.android.systemui.statusbar.NotificationUpdateHandler;
-import com.android.systemui.statusbar.NotificationViewHierarchyManager;
-import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.NotificationInflater;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
-import com.android.systemui.statusbar.notification.row.RowInflaterTask;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.leak.LeakDetector;
@@ -96,124 +52,48 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /**
  * NotificationEntryManager is responsible for the adding, removing, and updating of notifications.
  * It also handles tasks such as their inflation and their interaction with other
  * Notification.*Manager objects.
  */
-public class NotificationEntryManager implements Dumpable, NotificationInflater.InflationCallback,
-        ExpandableNotificationRow.ExpansionLogger, NotificationUpdateHandler,
-        VisualStabilityManager.Callback, BubbleController.BubbleDismissListener {
+public class NotificationEntryManager implements
+        Dumpable,
+        NotificationInflater.InflationCallback,
+        NotificationUpdateHandler,
+        VisualStabilityManager.Callback {
     private static final String TAG = "NotificationEntryMgr";
     protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-    private static final boolean ENABLE_HEADS_UP = true;
-    private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
 
-    private final NotificationMessagingUtil mMessagingUtil;
+    public static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
+
     protected final Context mContext;
-    protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>();
-    private final NotificationClicker mNotificationClicker = new NotificationClicker();
+    protected final HashMap<String, NotificationEntry> mPendingNotifications = new HashMap<>();
 
-    private final NotificationGroupManager mGroupManager =
-            Dependency.get(NotificationGroupManager.class);
-    private final NotificationGroupAlertTransferHelper mGroupAlertTransferHelper =
-            Dependency.get(NotificationGroupAlertTransferHelper.class);
     private final NotificationGutsManager mGutsManager =
             Dependency.get(NotificationGutsManager.class);
-    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
     private final DeviceProvisionedController mDeviceProvisionedController =
             Dependency.get(DeviceProvisionedController.class);
-    private final VisualStabilityManager mVisualStabilityManager =
-            Dependency.get(VisualStabilityManager.class);
-    private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
     private final ForegroundServiceController mForegroundServiceController =
             Dependency.get(ForegroundServiceController.class);
-    private final AmbientPulseManager mAmbientPulseManager =
-            Dependency.get(AmbientPulseManager.class);
-    private final BubbleController mBubbleController = Dependency.get(BubbleController.class);
 
     // Lazily retrieved dependencies
     private NotificationRemoteInputManager mRemoteInputManager;
-    private NotificationMediaManager mMediaManager;
-    private NotificationListener mNotificationListener;
-    private ShadeController mShadeController;
+    private NotificationRowBinder mNotificationRowBinder;
 
-    protected IDreamManager mDreamManager;
-    protected IStatusBarService mBarService;
+    private final Handler mDeferredNotificationViewUpdateHandler;
+    private Runnable mUpdateNotificationViewsCallback;
+
     private NotificationPresenter mPresenter;
-    private Callback mCallback;
-    private NotificationActivityStarter mNotificationActivityStarter;
-    protected PowerManager mPowerManager;
     private NotificationListenerService.RankingMap mLatestRankingMap;
-    protected HeadsUpManager mHeadsUpManager;
     protected NotificationData mNotificationData;
-    private ContentObserver mHeadsUpObserver;
-    protected boolean mUseHeadsUp = false;
-    private boolean mDisableNotificationAlerts;
     protected NotificationListContainer mListContainer;
     @VisibleForTesting
     final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
             = new ArrayList<>();
-    private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
-    private NotificationViewHierarchyManager.StatusBarStateListener mStatusBarStateListener;
-
-    private final class NotificationClicker implements View.OnClickListener {
-
-        @Override
-        public void onClick(final View v) {
-            if (!(v instanceof ExpandableNotificationRow)) {
-                Log.e(TAG, "NotificationClicker called on a view that is not a notification row.");
-                return;
-            }
-
-            getShadeController().wakeUpIfDozing(SystemClock.uptimeMillis(), v);
-
-            final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
-            final StatusBarNotification sbn = row.getStatusBarNotification();
-            if (sbn == null) {
-                Log.e(TAG, "NotificationClicker called on an unclickable notification,");
-                return;
-            }
-
-            // Check if the notification is displaying the menu, if so slide notification back
-            if (isMenuVisible(row)) {
-                row.animateTranslateNotification(0);
-                return;
-            } else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) {
-                row.getNotificationParent().animateTranslateNotification(0);
-                return;
-            } else if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
-                // We never want to open the app directly if the user clicks in between
-                // the notifications.
-                return;
-            }
-
-            // Mark notification for one frame.
-            row.setJustClicked(true);
-            DejankUtils.postAfterTraversal(() -> row.setJustClicked(false));
-
-            // If it was a bubble we should close it
-            if (row.getEntry().isBubble()) {
-                mBubbleController.collapseStack();
-            }
-
-            mNotificationActivityStarter.onNotificationClicked(sbn, row);
-        }
-
-        private boolean isMenuVisible(ExpandableNotificationRow row) {
-            return row.getProvider() != null && row.getProvider().isMenuVisible();
-        }
-
-        public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
-            Notification notification = sbn.getNotification();
-            if (notification.contentIntent != null || notification.fullScreenIntent != null) {
-                row.setOnClickListener(this);
-            } else {
-                row.setOnClickListener(null);
-            }
-        }
-    }
+    private final List<NotificationEntryListener> mNotificationEntryListeners = new ArrayList<>();
 
     private final DeviceProvisionedController.DeviceProvisionedListener
             mDeviceProvisionedListener =
@@ -224,11 +104,6 @@
                 }
             };
 
-    public void setDisableNotificationAlerts(boolean disableNotificationAlerts) {
-        mDisableNotificationAlerts = disableNotificationAlerts;
-        mHeadsUpObserver.onChange(true);
-    }
-
     public void destroy() {
         mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener);
     }
@@ -240,30 +115,21 @@
         if (mPendingNotifications.size() == 0) {
             pw.println("null");
         } else {
-            for (NotificationData.Entry entry : mPendingNotifications.values()) {
+            for (NotificationEntry entry : mPendingNotifications.values()) {
                 pw.println(entry.notification);
             }
         }
-        pw.print("  mUseHeadsUp=");
-        pw.println(mUseHeadsUp);
     }
 
     public NotificationEntryManager(Context context) {
         mContext = context;
-        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-        mBarService = IStatusBarService.Stub.asInterface(
-                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
-        mDreamManager = IDreamManager.Stub.asInterface(
-                ServiceManager.checkService(DreamService.DREAM_SERVICE));
-        mMessagingUtil = new NotificationMessagingUtil(context);
-        mBubbleController.setDismissListener(this /* bubbleEventListener */);
         mNotificationData = new NotificationData();
-        Dependency.get(InitController.class).addPostInitTask(this::onPostInit);
+        mDeferredNotificationViewUpdateHandler = new Handler();
     }
 
-    private void onPostInit() {
-        mGroupAlertTransferHelper.setPendingEntries(mPendingNotifications);
-        mGroupManager.addOnGroupChangeListener(mGroupAlertTransferHelper);
+    /** Adds a {@link NotificationEntryListener}. */
+    public void addNotificationEntryListener(NotificationEntryListener listener) {
+        mNotificationEntryListeners.add(listener);
     }
 
     /**
@@ -276,84 +142,41 @@
         return mRemoteInputManager;
     }
 
-    private NotificationMediaManager getMediaManager() {
-        if (mMediaManager == null) {
-            mMediaManager = Dependency.get(NotificationMediaManager.class);
+    private NotificationRowBinder getRowBinder() {
+        if (mNotificationRowBinder == null) {
+            mNotificationRowBinder = Dependency.get(NotificationRowBinder.class);
         }
-        return mMediaManager;
+        return mNotificationRowBinder;
     }
 
-    private NotificationListener getNotificationListener() {
-        if (mNotificationListener == null) {
-            mNotificationListener = Dependency.get(NotificationListener.class);
-        }
-        return mNotificationListener;
-    }
-
-    private ShadeController getShadeController() {
-        if (mShadeController == null) {
-            mShadeController = Dependency.get(ShadeController.class);
-        }
-        return mShadeController;
+    // TODO: Remove this once we can always use a mocked row binder in our tests
+    @VisibleForTesting
+    void setRowBinder(NotificationRowBinder notificationRowBinder) {
+        mNotificationRowBinder = notificationRowBinder;
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter,
-            NotificationListContainer listContainer, Callback callback,
+            NotificationListContainer listContainer,
             HeadsUpManager headsUpManager) {
         mPresenter = presenter;
-        mCallback = callback;
-        mHeadsUpManager = headsUpManager;
-        mNotificationData.setHeadsUpManager(mHeadsUpManager);
+        mUpdateNotificationViewsCallback = mPresenter::updateNotificationViews;
+        mNotificationData.setHeadsUpManager(headsUpManager);
         mListContainer = listContainer;
 
-        mHeadsUpObserver = new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) {
-            @Override
-            public void onChange(boolean selfChange) {
-                boolean wasUsing = mUseHeadsUp;
-                mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts
-                        && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
-                        mContext.getContentResolver(),
-                        Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
-                        Settings.Global.HEADS_UP_OFF);
-                Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
-                if (wasUsing != mUseHeadsUp) {
-                    if (!mUseHeadsUp) {
-                        Log.d(TAG,
-                                "dismissing any existing heads up notification on disable event");
-                        mHeadsUpManager.releaseAllImmediately();
-                    }
-                }
-            }
-        };
-
-        if (ENABLE_HEADS_UP) {
-            mContext.getContentResolver().registerContentObserver(
-                    Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED),
-                    true,
-                    mHeadsUpObserver);
-            mContext.getContentResolver().registerContentObserver(
-                    Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
-                    mHeadsUpObserver);
-        }
-
-        mNotificationLifetimeExtenders.add(mHeadsUpManager);
-        mNotificationLifetimeExtenders.add(mAmbientPulseManager);
-        mNotificationLifetimeExtenders.add(mGutsManager);
-        mNotificationLifetimeExtenders.addAll(getRemoteInputManager().getLifetimeExtenders());
-
-        for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
-            extender.setCallback(key -> removeNotification(key, mLatestRankingMap));
-        }
-
         mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
-
-        mHeadsUpObserver.onChange(true); // set up
-        mOnAppOpsClickListener = mGutsManager::openGuts;
     }
 
-    public void setNotificationActivityStarter(
-            NotificationActivityStarter notificationActivityStarter) {
-        mNotificationActivityStarter = notificationActivityStarter;
+    /** Adds multiple {@link NotificationLifetimeExtender}s. */
+    public void addNotificationLifetimeExtenders(List<NotificationLifetimeExtender> extenders) {
+        for (NotificationLifetimeExtender extender : extenders) {
+            addNotificationLifetimeExtender(extender);
+        }
+    }
+
+    /** Adds a {@link NotificationLifetimeExtender}. */
+    public void addNotificationLifetimeExtender(NotificationLifetimeExtender extender) {
+        mNotificationLifetimeExtenders.add(extender);
+        extender.setCallback(key -> removeNotification(key, mLatestRankingMap));
     }
 
     public NotificationData getNotificationData() {
@@ -368,147 +191,27 @@
         return mPresenter;
     }
 
-    public ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
-        return mGutsManager::openGuts;
-    }
-
-    @Override
-    public void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
-        mUiOffloadThread.submit(() -> {
-            try {
-                mBarService.onNotificationExpansionChanged(key, userAction, expanded);
-            } catch (RemoteException e) {
-                // Ignore.
-            }
-        });
-    }
-
     @Override
     public void onReorderingAllowed() {
         updateNotifications();
     }
 
-    private boolean shouldSuppressFullScreenIntent(NotificationData.Entry entry) {
-        if (mPresenter.isDeviceInVrMode()) {
-            return true;
-        }
-
-        return mNotificationData.shouldSuppressFullScreenIntent(entry);
-    }
-
-    private void inflateViews(NotificationData.Entry entry, ViewGroup parent) {
-        PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
-                entry.notification.getUser().getIdentifier());
-
-        final StatusBarNotification sbn = entry.notification;
-        if (entry.rowExists()) {
-            entry.reset();
-            updateNotification(entry, pmUser, sbn, entry.getRow());
-        } else {
-            new RowInflaterTask().inflate(mContext, parent, entry,
-                    row -> {
-                        bindRow(entry, pmUser, sbn, row);
-                        updateNotification(entry, pmUser, sbn, row);
-                    });
-        }
-    }
-
-    private void bindRow(NotificationData.Entry entry, PackageManager pmUser,
-            StatusBarNotification sbn, ExpandableNotificationRow row) {
-        row.setExpansionLogger(this, entry.notification.getKey());
-        row.setGroupManager(mGroupManager);
-        row.setHeadsUpManager(mHeadsUpManager);
-        row.setOnExpandClickListener(mPresenter);
-        row.setInflationCallback(this);
-        row.setLongPressListener(getNotificationLongClicker());
-        mListContainer.bindRow(row);
-        getRemoteInputManager().bindRow(row);
-
-        // Get the app name.
-        // Note that Notification.Builder#bindHeaderAppName has similar logic
-        // but since this field is used in the guts, it must be accurate.
-        // Therefore we will only show the application label, or, failing that, the
-        // package name. No substitutions.
-        final String pkg = sbn.getPackageName();
-        String appname = pkg;
-        try {
-            final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
-                    PackageManager.MATCH_UNINSTALLED_PACKAGES
-                            | PackageManager.MATCH_DISABLED_COMPONENTS);
-            if (info != null) {
-                appname = String.valueOf(pmUser.getApplicationLabel(info));
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            // Do nothing
-        }
-        row.setAppName(appname);
-        row.setOnDismissRunnable(() ->
-                performRemoveNotification(row.getStatusBarNotification()));
-        row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
-        if (ENABLE_REMOTE_INPUT) {
-            row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
-        }
-
-        row.setAppOpsOnClickListener(mOnAppOpsClickListener);
-
-        mCallback.onBindRow(entry, pmUser, sbn, row);
-    }
-
     public void performRemoveNotification(StatusBarNotification n) {
         final int rank = mNotificationData.getRank(n.getKey());
         final int count = mNotificationData.getActiveNotifications().size();
         final NotificationVisibility nv = NotificationVisibility.obtain(n.getKey(), rank, count,
                 true);
-        NotificationData.Entry entry = mNotificationData.get(n.getKey());
-
-        getRemoteInputManager().onPerformRemoveNotification(n, entry);
-        final String pkg = n.getPackageName();
-        final String tag = n.getTag();
-        final int id = n.getId();
-        final int userId = n.getUserId();
-        try {
-            int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
-            if (mHeadsUpManager.isAlerting(n.getKey())) {
-                dismissalSurface = NotificationStats.DISMISSAL_PEEK;
-            } else if (mListContainer.hasPulsingNotifications()) {
-                dismissalSurface = NotificationStats.DISMISSAL_AOD;
-            }
-            int dismissalSentiment = NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
-            mBarService.onNotificationClear(pkg, tag, id, userId, n.getKey(), dismissalSurface,
-                    dismissalSentiment, nv);
-            removeNotification(n.getKey(), null);
-
-        } catch (RemoteException ex) {
-            // system process is dead if we're here.
-        }
-
-        mCallback.onPerformRemoveNotification(n);
-    }
-
-    @Override
-    public void onStackDismissed() {
-        updateNotifications();
-    }
-
-    @Override
-    public void onBubbleDismissed(String key) {
-        NotificationData.Entry entry = mNotificationData.get(key);
-        if (entry != null) {
-            entry.setBubbleDismissed(true);
-            if (!DEBUG_DEMOTE_TO_NOTIF) {
-                performRemoveNotification(entry.notification);
-            }
-        }
-        updateNotifications();
+        removeNotificationInternal(
+                n.getKey(), null, nv, false /* forceRemove */, true /* removedByUser */);
     }
 
     private void abortExistingInflation(String key) {
         if (mPendingNotifications.containsKey(key)) {
-            NotificationData.Entry entry = mPendingNotifications.get(key);
+            NotificationEntry entry = mPendingNotifications.get(key);
             entry.abortTask();
             mPendingNotifications.remove(key);
         }
-        NotificationData.Entry addedEntry = mNotificationData.get(key);
+        NotificationEntry addedEntry = mNotificationData.get(key);
         if (addedEntry != null) {
             addedEntry.abortTask();
         }
@@ -522,57 +225,24 @@
      */
     @Override
     public void handleInflationException(StatusBarNotification n, Exception e) {
-        removeNotificationInternal(n.getKey(), null, true /* forceRemove */);
-        try {
-            mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
-                    n.getInitialPid(), e.getMessage(), n.getUserId());
-        } catch (RemoteException ex) {
-            // The end is nigh.
+        removeNotificationInternal(
+                n.getKey(), null, null, true /* forceRemove */, false /* removedByUser */);
+        for (NotificationEntryListener listener : mNotificationEntryListeners) {
+            listener.onInflationError(n, e);
         }
     }
 
-    private void addEntry(NotificationData.Entry shadeEntry) {
-        if (shadeEntry == null) {
-            return;
-        }
-        // Add the expanded view and icon.
-        mNotificationData.add(shadeEntry);
-        tagForeground(shadeEntry.notification);
-        updateNotifications();
-        mCallback.onNotificationAdded(shadeEntry);
-    }
-
-    /**
-     * Adds the entry to the respective alerting manager if the content view was inflated and
-     * the entry should still alert.
-     *
-     * @param entry entry to add
-     * @param inflatedFlags flags representing content views that were inflated
-     */
-    private void showAlertingView(NotificationData.Entry entry,
-            @InflationFlag int inflatedFlags) {
-        if ((inflatedFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
-            // Possible for shouldHeadsUp to change between the inflation starting and ending.
-            // If it does and we no longer need to heads up, we should free the view.
-            if (shouldHeadsUp(entry)) {
-                mHeadsUpManager.showNotification(entry);
-                // Mark as seen immediately
-                setNotificationShown(entry.notification);
-            } else {
-                entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
-            }
-        }
-        if ((inflatedFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
-            if (shouldPulse(entry)) {
-                mAmbientPulseManager.showNotification(entry);
-            } else {
-                entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
-            }
+    private void maybeScheduleUpdateNotificationViews(NotificationEntry entry) {
+        long audibleAlertTimeout = RECENTLY_ALERTED_THRESHOLD_MS
+                - (System.currentTimeMillis() - entry.lastAudiblyAlertedMs);
+        if (audibleAlertTimeout > 0) {
+            mDeferredNotificationViewUpdateHandler.postDelayed(
+                    mUpdateNotificationViewsCallback, audibleAlertTimeout);
         }
     }
 
     @Override
-    public void onAsyncInflationFinished(NotificationData.Entry entry,
+    public void onAsyncInflationFinished(NotificationEntry entry,
             @InflationFlag int inflatedFlags) {
         mPendingNotifications.remove(entry.key);
         // If there was an async task started after the removal, we don't want to add it back to
@@ -580,14 +250,20 @@
         if (!entry.isRowRemoved()) {
             boolean isNew = mNotificationData.get(entry.key) == null;
             if (isNew) {
-                showAlertingView(entry, inflatedFlags);
-                addEntry(entry);
-            } else {
-                if (entry.getRow().hasLowPriorityStateUpdated()) {
-                    mVisualStabilityManager.onLowPriorityUpdated(entry);
-                    mPresenter.updateNotificationViews();
+                for (NotificationEntryListener listener : mNotificationEntryListeners) {
+                    listener.onEntryInflated(entry, inflatedFlags);
                 }
-                mGroupAlertTransferHelper.onInflationFinished(entry);
+                mNotificationData.add(entry);
+                tagForeground(entry.notification);
+                updateNotifications();
+                for (NotificationEntryListener listener : mNotificationEntryListeners) {
+                    listener.onNotificationAdded(entry);
+                }
+                maybeScheduleUpdateNotificationViews(entry);
+            } else {
+                for (NotificationEntryListener listener : mNotificationEntryListeners) {
+                    listener.onEntryReinflated(entry);
+                }
             }
         }
         entry.setLowPriorityStateUpdated(false);
@@ -595,83 +271,61 @@
 
     @Override
     public void removeNotification(String key, NotificationListenerService.RankingMap ranking) {
-        removeNotificationInternal(key, ranking, false /* forceRemove */);
+        removeNotificationInternal(
+                key, ranking, null, false /* forceRemove */, false /* removedByUser */);
     }
 
-    private void removeNotificationInternal(String key,
-            @Nullable NotificationListenerService.RankingMap ranking, boolean forceRemove) {
+    private void removeNotificationInternal(
+            String key,
+            @Nullable NotificationListenerService.RankingMap ranking,
+            @Nullable NotificationVisibility visibility,
+            boolean forceRemove,
+            boolean removedByUser) {
+        final NotificationEntry entry = mNotificationData.get(key);
+
         abortExistingInflation(key);
-        mGroupAlertTransferHelper.cleanUpPendingAlertInfo(key);
 
-        // Attempt to remove notifications from their alert managers (heads up, ambient pulse).
-        // Though the remove itself may fail, it lets the manager know to remove as soon as
-        // possible.
-        if (mHeadsUpManager.isAlerting(key)) {
-            // A cancel() in response to a remote input shouldn't be delayed, as it makes the
-            // sending look longer than it takes.
-            // Also we should not defer the removal if reordering isn't allowed since otherwise
-            // some notifications can't disappear before the panel is closed.
-            boolean ignoreEarliestRemovalTime = getRemoteInputManager().getController().isSpinning(key)
-                    && !FORCE_REMOTE_INPUT_HISTORY
-                    || !mVisualStabilityManager.isReorderingAllowed();
-            mHeadsUpManager.removeNotification(key, ignoreEarliestRemovalTime);
-        }
-        if (mAmbientPulseManager.isAlerting(key)) {
-            mAmbientPulseManager.removeNotification(key, false /* ignoreEarliestRemovalTime */);
-        }
+        boolean lifetimeExtended = false;
 
-        NotificationData.Entry entry = mNotificationData.get(key);
+        if (entry != null) {
+            // If a manager needs to keep the notification around for whatever reason, we
+            // keep the notification
+            if (!forceRemove) {
+                for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
+                    if (extender.shouldExtendLifetime(entry)) {
+                        mLatestRankingMap = ranking;
+                        extender.setShouldManageLifetime(entry, true /* shouldManage */);
+                        lifetimeExtended = true;
+                        break;
+                    }
+                }
+            }
 
-        if (entry == null) {
-            mCallback.onNotificationRemoved(key, null /* old */);
-            return;
-        }
+            if (!lifetimeExtended) {
+                // At this point, we are guaranteed the notification will be removed
 
-        // If a manager needs to keep the notification around for whatever reason, we return early
-        // and keep the notification
-        if (!forceRemove) {
-            for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
-                if (extender.shouldExtendLifetime(entry)) {
-                    mLatestRankingMap = ranking;
-                    extender.setShouldManageLifetime(entry, true /* shouldManage */);
-                    return;
+                // Ensure any managers keeping the lifetime extended stop managing the entry
+                for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
+                    extender.setShouldManageLifetime(entry, false /* shouldManage */);
+                }
+
+                if (entry.rowExists()) {
+                    entry.removeRow();
+                    mListContainer.cleanUpViewStateForEntry(entry);
+                }
+
+                // Let's remove the children if this was a summary
+                handleGroupSummaryRemoved(key);
+
+                mNotificationData.remove(key, ranking);
+                updateNotifications();
+                Dependency.get(LeakDetector.class).trackGarbage(entry);
+
+                for (NotificationEntryListener listener : mNotificationEntryListeners) {
+                    listener.onEntryRemoved(entry, visibility, removedByUser);
                 }
             }
         }
-
-        // At this point, we are guaranteed the notification will be removed
-
-        // Ensure any managers keeping the lifetime extended stop managing the entry
-        for (NotificationLifetimeExtender extender: mNotificationLifetimeExtenders) {
-            extender.setShouldManageLifetime(entry, false /* shouldManage */);
-        }
-
-        getMediaManager().onNotificationRemoved(key);
-        mForegroundServiceController.removeNotification(entry.notification);
-
-        if (entry.rowExists()) {
-            entry.removeRow();
-            mListContainer.cleanUpViewStateForEntry(entry);
-        }
-
-        // Let's remove the children if this was a summary
-        handleGroupSummaryRemoved(key);
-
-        StatusBarNotification old = removeNotificationViews(key, ranking);
-
-        mCallback.onNotificationRemoved(key, old);
-    }
-
-    private StatusBarNotification removeNotificationViews(String key,
-            NotificationListenerService.RankingMap ranking) {
-        NotificationData.Entry entry = mNotificationData.remove(key, ranking);
-        if (entry == null) {
-            Log.w(TAG, "removeNotification for unknown key: " + key);
-            return null;
-        }
-        updateNotifications();
-        Dependency.get(LeakDetector.class).trackGarbage(entry);
-        return entry.notification;
     }
 
     /**
@@ -685,19 +339,19 @@
      *
      */
     private void handleGroupSummaryRemoved(String key) {
-        NotificationData.Entry entry = mNotificationData.get(key);
+        NotificationEntry entry = mNotificationData.get(key);
         if (entry != null && entry.rowExists() && entry.isSummaryWithChildren()) {
             if (entry.notification.getOverrideGroupKey() != null && !entry.isRowDismissed()) {
                 // We don't want to remove children for autobundled notifications as they are not
                 // always cancelled. We only remove them if they were dismissed by the user.
                 return;
             }
-            List<NotificationData.Entry> childEntries = entry.getChildren();
+            List<NotificationEntry> childEntries = entry.getChildren();
             if (childEntries == null) {
                 return;
             }
             for (int i = 0; i < childEntries.size(); i++) {
-                NotificationData.Entry childEntry = childEntries.get(i);
+                NotificationEntry childEntry = childEntries.get(i);
                 boolean isForeground = (entry.notification.getNotification().flags
                         & Notification.FLAG_FOREGROUND_SERVICE) != 0;
                 boolean keepForReply =
@@ -717,10 +371,10 @@
     }
 
     public void updateNotificationsOnDensityOrFontScaleChanged() {
-        ArrayList<NotificationData.Entry> userNotifications =
+        ArrayList<NotificationEntry> userNotifications =
                 mNotificationData.getNotificationsForCurrentUser();
         for (int i = 0; i < userNotifications.size(); i++) {
-            NotificationData.Entry entry = userNotifications.get(i);
+            NotificationEntry entry = userNotifications.get(i);
             entry.onDensityOrFontScaleChanged();
             boolean exposedGuts = entry.areGutsExposed();
             if (exposedGuts) {
@@ -729,70 +383,6 @@
         }
     }
 
-    //TODO: This method associates a row with an entry, but eventually needs to not do that
-    protected void updateNotification(NotificationData.Entry entry, PackageManager pmUser,
-            StatusBarNotification sbn, ExpandableNotificationRow row) {
-        boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
-        boolean isUpdate = mNotificationData.get(entry.key) != null;
-        boolean wasLowPriority = row.isLowPriority();
-        row.setIsLowPriority(isLowPriority);
-        row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority));
-        // bind the click event to the content area
-        mNotificationClicker.register(row, sbn);
-
-        // Extract target SDK version.
-        try {
-            ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
-            entry.targetSdk = info.targetSdkVersion;
-        } catch (PackageManager.NameNotFoundException ex) {
-            Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
-        }
-        row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
-                && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
-        entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
-        entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
-
-        entry.setRow(row);
-        row.setOnActivatedListener(mPresenter);
-
-        boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
-                mNotificationData.getImportance(sbn.getKey()));
-        boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight
-                && !mPresenter.isPresenterFullyCollapsed();
-        row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
-        row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
-        row.setEntry(entry);
-
-        if (shouldHeadsUp(entry)) {
-            row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, true /* shouldInflate */);
-        }
-        if (shouldPulse(entry)) {
-            row.updateInflationFlag(FLAG_CONTENT_VIEW_AMBIENT, true /* shouldInflate */);
-        }
-        row.setNeedsRedaction(
-                Dependency.get(NotificationLockscreenUserManager.class).needsRedaction(entry));
-        row.inflateViews();
-    }
-
-    private NotificationData.Entry createNotificationViews(
-            StatusBarNotification sbn, NotificationListenerService.Ranking ranking)
-            throws InflationException {
-        if (DEBUG) {
-            Log.d(TAG, "createNotificationViews(notification=" + sbn + " " + ranking);
-        }
-
-        NotificationData.Entry entry = new NotificationData.Entry(sbn, ranking);
-        if (BubbleController.shouldAutoBubble(getContext(), entry)) {
-            entry.setIsBubble(true);
-        }
-
-        Dependency.get(LeakDetector.class).trackInstance(entry);
-        entry.createIcons(mContext, sbn);
-        // Construct the expanded view.
-        inflateViews(entry, mListContainer.getViewParentForNotification(entry));
-        return entry;
-    }
-
     private void addNotificationInternal(StatusBarNotification notification,
             NotificationListenerService.RankingMap rankingMap) throws InflationException {
         String key = notification.getKey();
@@ -803,50 +393,20 @@
         mNotificationData.updateRanking(rankingMap);
         NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
         rankingMap.getRanking(key, ranking);
-        NotificationData.Entry shadeEntry = createNotificationViews(notification, ranking);
-        boolean isHeadsUped = shouldHeadsUp(shadeEntry);
-        if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
-            if (shouldSuppressFullScreenIntent(shadeEntry)) {
-                if (DEBUG) {
-                    Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + key);
-                }
-            } else if (mNotificationData.getImportance(key)
-                    < NotificationManager.IMPORTANCE_HIGH) {
-                if (DEBUG) {
-                    Log.d(TAG, "No Fullscreen intent: not important enough: "
-                            + key);
-                }
-            } else {
-                // Stop screensaver if the notification has a fullscreen intent.
-                // (like an incoming phone call)
-                Dependency.get(UiOffloadThread.class).submit(() -> {
-                    try {
-                        mDreamManager.awaken();
-                    } catch (RemoteException e) {
-                        e.printStackTrace();
-                    }
-                });
 
-                // not immersive & a fullscreen alert should be shown
-                if (DEBUG)
-                    Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
-                try {
-                    EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
-                            key);
-                    notification.getNotification().fullScreenIntent.send();
-                    shadeEntry.notifyFullScreenIntentLaunched();
-                    mMetricsLogger.count("note_fullscreen", 1);
-                } catch (PendingIntent.CanceledException e) {
-                }
-            }
-        }
+        NotificationEntry entry = new NotificationEntry(notification, ranking);
+
+        Dependency.get(LeakDetector.class).trackInstance(entry);
+        // Construct the expanded view.
+        getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification),
+                mNotificationData.get(entry.key) != null);
+
         abortExistingInflation(key);
 
-        mForegroundServiceController.addNotification(notification,
-                mNotificationData.getImportance(key));
-
-        mPendingNotifications.put(key, shadeEntry);
-        mGroupAlertTransferHelper.onPendingEntryAdded(shadeEntry);
+        mPendingNotifications.put(key, entry);
+        for (NotificationEntryListener listener : mNotificationEntryListeners) {
+            listener.onPendingEntryAdded(entry);
+        }
     }
 
     @VisibleForTesting
@@ -881,18 +441,13 @@
         }
     }
 
-    private boolean alertAgain(NotificationData.Entry oldEntry, Notification newNotification) {
-        return oldEntry == null || !oldEntry.hasInterrupted()
-                || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
-    }
-
     private void updateNotificationInternal(StatusBarNotification notification,
             NotificationListenerService.RankingMap ranking) throws InflationException {
         if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
 
         final String key = notification.getKey();
         abortExistingInflation(key);
-        NotificationData.Entry entry = mNotificationData.get(key);
+        NotificationEntry entry = mNotificationData.get(key);
         if (entry == null) {
             return;
         }
@@ -903,32 +458,13 @@
             extender.setShouldManageLifetime(entry, false /* shouldManage */);
         }
 
-        mNotificationData.updateRanking(ranking);
+        mNotificationData.update(entry, ranking, notification);
 
-        final StatusBarNotification oldNotification = entry.notification;
-        entry.notification = notification;
-        mGroupManager.onEntryUpdated(entry, oldNotification);
+        getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification),
+                mNotificationData.get(entry.key) != null);
 
-        entry.updateIcons(mContext, notification);
-        inflateViews(entry, mListContainer.getViewParentForNotification(entry));
-
-        mForegroundServiceController.updateNotification(notification,
-                mNotificationData.getImportance(key));
-
-        boolean alertAgain = alertAgain(entry, entry.notification.getNotification());
-        if (getShadeController().isDozing()) {
-            updateAlertState(entry, shouldPulse(entry), alertAgain, mAmbientPulseManager);
-        } else {
-            updateAlertState(entry, shouldHeadsUp(entry), alertAgain, mHeadsUpManager);
-        }
         updateNotifications();
 
-        if (!notification.isClearable()) {
-            // The user may have performed a dismiss action on the notification, since it's
-            // not clearable we should snap it back.
-            mListContainer.snapViewIfNeeded(entry);
-        }
-
         if (DEBUG) {
             // Is this for you?
             boolean isForCurrentUser = Dependency.get(KeyguardEnvironment.class)
@@ -936,7 +472,11 @@
             Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
         }
 
-        mCallback.onNotificationUpdated(notification);
+        for (NotificationEntryListener listener : mNotificationEntryListeners) {
+            listener.onEntryUpdated(entry);
+        }
+
+        maybeScheduleUpdateNotificationViews(entry);
     }
 
     @Override
@@ -956,14 +496,14 @@
     }
 
     public void updateNotificationRanking(NotificationListenerService.RankingMap rankingMap) {
-        List<NotificationData.Entry> entries = new ArrayList<>();
+        List<NotificationEntry> entries = new ArrayList<>();
         entries.addAll(mNotificationData.getActiveNotifications());
         entries.addAll(mPendingNotifications.values());
 
         // Has a copy of the current UI adjustments.
         ArrayMap<String, NotificationUiAdjustment> oldAdjustments = new ArrayMap<>();
         ArrayMap<String, Integer> oldImportances = new ArrayMap<>();
-        for (NotificationData.Entry entry : entries) {
+        for (NotificationEntry entry : entries) {
             NotificationUiAdjustment adjustment =
                     NotificationUiAdjustment.extractFromNotificationEntry(entry);
             oldAdjustments.put(entry.key, adjustment);
@@ -975,27 +515,13 @@
         updateRankingOfPendingNotifications(rankingMap);
 
         // By comparing the old and new UI adjustments, reinflate the view accordingly.
-        for (NotificationData.Entry entry : entries) {
-            NotificationUiAdjustment newAdjustment =
-                    NotificationUiAdjustment.extractFromNotificationEntry(entry);
-
-            if (NotificationUiAdjustment.needReinflate(
-                    oldAdjustments.get(entry.key), newAdjustment)) {
-                if (entry.rowExists()) {
-                    entry.reset();
-                    PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
-                            entry.notification.getUser().getIdentifier());
-                    updateNotification(entry, pmUser, entry.notification, entry.getRow());
-                } else {
-                    // Once the RowInflaterTask is done, it will pick up the updated entry, so
-                    // no-op here.
-                }
-            } else if (oldImportances.containsKey(entry.key)
-                    && entry.importance != oldImportances.get(entry.key)) {
-                if (entry.rowExists()) {
-                    entry.getRow().onNotificationRankingUpdated();
-                }
-            }
+        for (NotificationEntry entry : entries) {
+            getRowBinder().onNotificationRankingUpdated(
+                    entry,
+                    oldImportances.get(entry.key),
+                    oldAdjustments.get(entry.key),
+                    NotificationUiAdjustment.extractFromNotificationEntry(entry),
+                    mNotificationData.get(entry.key) != null);
         }
 
         updateNotifications();
@@ -1007,281 +533,18 @@
             return;
         }
         NotificationListenerService.Ranking tmpRanking = new NotificationListenerService.Ranking();
-        for (NotificationData.Entry pendingNotification : mPendingNotifications.values()) {
+        for (NotificationEntry pendingNotification : mPendingNotifications.values()) {
             rankingMap.getRanking(pendingNotification.key, tmpRanking);
             pendingNotification.populateFromRanking(tmpRanking);
         }
     }
 
-    public void setStatusBarStateListener(
-            NotificationViewHierarchyManager.StatusBarStateListener listener) {
-        mStatusBarStateListener  = listener;
-    }
-
     /**
-     * Whether the notification should peek in from the top and alert the user.
-     *
-     * @param entry the entry to check
-     * @return true if the entry should heads up, false otherwise
+     * @return An iterator for all "pending" notifications. Pending notifications are newly-posted
+     * notifications whose views have not yet been inflated. In general, the system pretends like
+     * these don't exist, although there are a couple exceptions.
      */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
-    public boolean shouldHeadsUp(NotificationData.Entry entry) {
-        StatusBarNotification sbn = entry.notification;
-
-        if (getShadeController().isDozing()) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: device is dozing: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        // TODO: need to changes this, e.g. should still heads up in expanded shade, might want
-        // message bubble from the bubble to go through heads up path
-        boolean inShade = mStatusBarStateListener != null
-                && mStatusBarStateListener.getCurrentState() == SHADE;
-        if (entry.isBubble() && !entry.isBubbleDismissed() && inShade) {
-            return false;
-        }
-
-        if (!canAlertCommon(entry)) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: notification shouldn't alert: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: no huns or vr mode");
-            }
-            return false;
-        }
-
-        boolean isDreaming = false;
-        try {
-            isDreaming = mDreamManager.isDreaming();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to query dream manager.", e);
-        }
-        boolean inUse = mPowerManager.isScreenOn() && !isDreaming;
-
-        if (!inUse) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: not in use: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (mNotificationData.shouldSuppressPeek(entry)) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: suppressed by DND: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (isSnoozedPackage(sbn)) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: snoozed package: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (entry.hasJustLaunchedFullScreenIntent()) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: recent fullscreen: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (mNotificationData.getImportance(sbn.getKey()) < NotificationManager.IMPORTANCE_HIGH) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: unimportant notification: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (!mCallback.canHeadsUp(entry, sbn)) {
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Whether or not the notification should "pulse" on the user's display when the phone is
-     * dozing.  This displays the ambient view of the notification.
-     *
-     * @param entry the entry to check
-     * @return true if the entry should ambient pulse, false otherwise
-     */
-    private boolean shouldPulse(NotificationData.Entry entry) {
-        StatusBarNotification sbn = entry.notification;
-
-        if (!getShadeController().isDozing()) {
-            if (DEBUG) {
-                Log.d(TAG, "No pulsing: not dozing: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (!canAlertCommon(entry)) {
-            if (DEBUG) {
-                Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (mNotificationData.shouldSuppressAmbient(entry)) {
-            if (DEBUG) {
-                Log.d(TAG, "No pulsing: ambient effect suppressed: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (mNotificationData.getImportance(sbn.getKey())
-                < NotificationManager.IMPORTANCE_DEFAULT) {
-            if (DEBUG) {
-                Log.d(TAG, "No pulsing: not important enough: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        Bundle extras = sbn.getNotification().extras;
-        CharSequence title = extras.getCharSequence(Notification.EXTRA_TITLE);
-        CharSequence text = extras.getCharSequence(Notification.EXTRA_TEXT);
-        if (TextUtils.isEmpty(title) && TextUtils.isEmpty(text)) {
-            if (DEBUG) {
-                Log.d(TAG, "No pulsing: title and text are empty: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Common checks between heads up alerting and ambient pulse alerting.  See
-     * {@link NotificationEntryManager#shouldHeadsUp(NotificationData.Entry)} and
-     * {@link NotificationEntryManager#shouldPulse(NotificationData.Entry)}.  Notifications that
-     * fail any of these checks should not alert at all.
-     *
-     * @param entry the entry to check
-     * @return true if these checks pass, false if the notification should not alert
-     */
-    protected boolean canAlertCommon(NotificationData.Entry entry) {
-        StatusBarNotification sbn = entry.notification;
-
-        if (mNotificationData.shouldFilterOut(entry)) {
-            if (DEBUG) {
-                Log.d(TAG, "No alerting: filtered notification: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        // Don't alert notifications that are suppressed due to group alert behavior
-        if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
-            if (DEBUG) {
-                Log.d(TAG, "No alerting: suppressed due to group alert behavior");
-            }
-            return false;
-        }
-
-        return true;
-    }
-
-    private void setNotificationShown(StatusBarNotification n) {
-        setNotificationsShown(new String[]{n.getKey()});
-    }
-
-    protected void setNotificationsShown(String[] keys) {
-        try {
-            getNotificationListener().setNotificationsShown(keys);
-        } catch (RuntimeException e) {
-            Log.d(TAG, "failed setNotificationsShown: ", e);
-        }
-    }
-
-    private boolean isSnoozedPackage(StatusBarNotification sbn) {
-        return mHeadsUpManager.isSnoozed(sbn.getPackageName());
-    }
-
-    /**
-     * Update the entry's alert state and call the appropriate {@link AlertingNotificationManager}
-     * method.
-     * @param entry entry to update
-     * @param shouldAlert whether or not it should be alerting
-     * @param alertAgain whether or not an alert should actually come in as if it were new
-     * @param alertManager the alerting notification manager that manages the alert state
-     */
-    private void updateAlertState(NotificationData.Entry entry, boolean shouldAlert,
-            boolean alertAgain, AlertingNotificationManager alertManager) {
-        final boolean wasAlerting = alertManager.isAlerting(entry.key);
-        if (wasAlerting) {
-            if (!shouldAlert) {
-                // We don't want this to be interrupting anymore, lets remove it
-                alertManager.removeNotification(entry.key,
-                        false /* ignoreEarliestRemovalTime */);
-            } else {
-                alertManager.updateNotification(entry.key, alertAgain);
-            }
-        } else if (shouldAlert && alertAgain) {
-            // This notification was updated to be alerting, show it!
-            alertManager.showNotification(entry);
-        }
-    }
-
-    /**
-     * Callback for NotificationEntryManager.
-     */
-    public interface Callback {
-
-        /**
-         * Called when a new entry is created.
-         *
-         * @param shadeEntry entry that was created
-         */
-        void onNotificationAdded(NotificationData.Entry shadeEntry);
-
-        /**
-         * Called when a notification was updated.
-         *
-         * @param notification notification that was updated
-         */
-        void onNotificationUpdated(StatusBarNotification notification);
-
-        /**
-         * Called when a notification was removed.
-         *
-         * @param key key of notification that was removed
-         * @param old StatusBarNotification of the notification before it was removed
-         */
-        void onNotificationRemoved(String key, StatusBarNotification old);
-
-        /**
-         * Called when a new notification and row is created.
-         *
-         * @param entry entry for the notification
-         * @param pmUser package manager for user
-         * @param sbn notification
-         * @param row row for the notification
-         */
-        void onBindRow(NotificationData.Entry entry, PackageManager pmUser,
-                StatusBarNotification sbn, ExpandableNotificationRow row);
-
-        /**
-         * Removes a notification immediately.
-         *
-         * @param statusBarNotification notification that is being removed
-         */
-        void onPerformRemoveNotification(StatusBarNotification statusBarNotification);
-
-        /**
-         * Returns true if NotificationEntryManager can heads up this notification.
-         *
-         * @param entry entry of the notification that might be heads upped
-         * @param sbn notification that might be heads upped
-         * @return true if the notification can be heads upped
-         */
-        boolean canHeadsUp(NotificationData.Entry entry, StatusBarNotification sbn);
+    public Iterable<NotificationEntry> getPendingNotificationsIterator() {
+        return mPendingNotifications.values();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
new file mode 100644
index 0000000..e199ead
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import android.Manifest;
+import android.app.AppGlobals;
+import android.app.Notification;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.service.notification.StatusBarNotification;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** Component which manages the various reasons a notification might be filtered out. */
+@Singleton
+public class NotificationFilter {
+
+    private final NotificationGroupManager mGroupManager = Dependency.get(
+            NotificationGroupManager.class);
+
+    private NotificationData.KeyguardEnvironment mEnvironment;
+    private ShadeController mShadeController;
+    private ForegroundServiceController mFsc;
+    private NotificationLockscreenUserManager mUserManager;
+
+    @Inject
+    public NotificationFilter() {}
+
+    private NotificationData.KeyguardEnvironment getEnvironment() {
+        if (mEnvironment == null) {
+            mEnvironment = Dependency.get(NotificationData.KeyguardEnvironment.class);
+        }
+        return mEnvironment;
+    }
+
+    private ShadeController getShadeController() {
+        if (mShadeController == null) {
+            mShadeController = Dependency.get(ShadeController.class);
+        }
+        return mShadeController;
+    }
+
+    private ForegroundServiceController getFsc() {
+        if (mFsc == null) {
+            mFsc = Dependency.get(ForegroundServiceController.class);
+        }
+        return mFsc;
+    }
+
+    private NotificationLockscreenUserManager getUserManager() {
+        if (mUserManager == null) {
+            mUserManager = Dependency.get(NotificationLockscreenUserManager.class);
+        }
+        return mUserManager;
+    }
+
+
+    /**
+     * @return true if the provided notification should NOT be shown right now.
+     */
+    public boolean shouldFilterOut(NotificationEntry entry) {
+        final StatusBarNotification sbn = entry.notification;
+        if (!(getEnvironment().isDeviceProvisioned()
+                || showNotificationEvenIfUnprovisioned(sbn))) {
+            return true;
+        }
+
+        if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
+            return true;
+        }
+
+        if (getUserManager().isLockscreenPublicMode(sbn.getUserId())
+                && (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
+                        || getUserManager().shouldHideNotifications(sbn.getUserId())
+                        || getUserManager().shouldHideNotifications(sbn.getKey()))) {
+            return true;
+        }
+
+        if (getShadeController().isDozing() && entry.shouldSuppressAmbient()) {
+            return true;
+        }
+
+        if (!getShadeController().isDozing() && entry.shouldSuppressNotificationList()) {
+            return true;
+        }
+
+        if (entry.suspended) {
+            return true;
+        }
+
+        if (!StatusBar.ENABLE_CHILD_NOTIFICATIONS
+                && mGroupManager.isChildInGroupWithSummary(sbn)) {
+            return true;
+        }
+
+        if (getFsc().isDisclosureNotification(sbn)
+                && !getFsc().isDisclosureNeededForUser(sbn.getUserId())) {
+            // this is a foreground-service disclosure for a user that does not need to show one
+            return true;
+        }
+        if (getFsc().isSystemAlertNotification(sbn)) {
+            final String[] apps = sbn.getNotification().extras.getStringArray(
+                    Notification.EXTRA_FOREGROUND_APPS);
+            if (apps != null && apps.length >= 1) {
+                if (!getFsc().isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    // Q: What kinds of notifications should show during setup?
+    // A: Almost none! Only things coming from packages with permission
+    // android.permission.NOTIFICATION_DURING_SETUP that also have special "kind" tags marking them
+    // as relevant for setup (see below).
+    private static boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
+        return showNotificationEvenIfUnprovisioned(AppGlobals.getPackageManager(), sbn);
+    }
+
+    @VisibleForTesting
+    static boolean showNotificationEvenIfUnprovisioned(IPackageManager packageManager,
+            StatusBarNotification sbn) {
+        return checkUidPermission(packageManager, Manifest.permission.NOTIFICATION_DURING_SETUP,
+                sbn.getUid()) == PackageManager.PERMISSION_GRANTED
+                && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
+    }
+
+    private static int checkUidPermission(IPackageManager packageManager, String permission,
+            int uid) {
+        try {
+            return packageManager.checkUidPermission(permission, uid);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
new file mode 100644
index 0000000..fc7a2b3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import static com.android.systemui.statusbar.StatusBarState.SHADE;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
+import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+/**
+ * Provides heads-up and pulsing state for notification entries.
+ */
+public class NotificationInterruptionStateProvider {
+
+    private static final String TAG = "InterruptionStateProvider";
+    private static final boolean DEBUG = false;
+    private static final boolean ENABLE_HEADS_UP = true;
+    private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
+
+    private final StatusBarStateController mStatusBarStateController =
+            Dependency.get(StatusBarStateController.class);
+    private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class);
+
+    private final Context mContext;
+    private final PowerManager mPowerManager;
+    private final IDreamManager mDreamManager;
+
+    private NotificationPresenter mPresenter;
+    private ShadeController mShadeController;
+    private HeadsUpManager mHeadsUpManager;
+    private HeadsUpSuppressor mHeadsUpSuppressor;
+
+    private ContentObserver mHeadsUpObserver;
+    @VisibleForTesting
+    protected boolean mUseHeadsUp = false;
+    private boolean mDisableNotificationAlerts;
+
+    public NotificationInterruptionStateProvider(Context context) {
+        this(context,
+                (PowerManager) context.getSystemService(Context.POWER_SERVICE),
+                IDreamManager.Stub.asInterface(
+                        ServiceManager.checkService(DreamService.DREAM_SERVICE)));
+    }
+
+    @VisibleForTesting
+    protected NotificationInterruptionStateProvider(
+            Context context,
+            PowerManager powerManager,
+            IDreamManager dreamManager) {
+        mContext = context;
+        mPowerManager = powerManager;
+        mDreamManager = dreamManager;
+    }
+
+    /** Sets up late-binding dependencies for this component. */
+    public void setUpWithPresenter(
+            NotificationPresenter notificationPresenter,
+            HeadsUpManager headsUpManager,
+            HeadsUpSuppressor headsUpSuppressor) {
+        mPresenter = notificationPresenter;
+        mHeadsUpManager = headsUpManager;
+        mHeadsUpSuppressor = headsUpSuppressor;
+
+        mHeadsUpObserver = new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) {
+            @Override
+            public void onChange(boolean selfChange) {
+                boolean wasUsing = mUseHeadsUp;
+                mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts
+                        && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
+                        mContext.getContentResolver(),
+                        Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
+                        Settings.Global.HEADS_UP_OFF);
+                Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
+                if (wasUsing != mUseHeadsUp) {
+                    if (!mUseHeadsUp) {
+                        Log.d(TAG,
+                                "dismissing any existing heads up notification on disable event");
+                        mHeadsUpManager.releaseAllImmediately();
+                    }
+                }
+            }
+        };
+
+        if (ENABLE_HEADS_UP) {
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED),
+                    true,
+                    mHeadsUpObserver);
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
+                    mHeadsUpObserver);
+        }
+        mHeadsUpObserver.onChange(true); // set up
+    }
+
+    private ShadeController getShadeController() {
+        if (mShadeController == null) {
+            mShadeController = Dependency.get(ShadeController.class);
+        }
+        return mShadeController;
+    }
+
+    /**
+     * Whether the notification should peek in from the top and alert the user.
+     *
+     * @param entry the entry to check
+     * @return true if the entry should heads up, false otherwise
+     */
+    public boolean shouldHeadsUp(NotificationEntry entry) {
+        StatusBarNotification sbn = entry.notification;
+
+        if (getShadeController().isDozing()) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: device is dozing: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        // TODO: need to changes this, e.g. should still heads up in expanded shade, might want
+        // message bubble from the bubble to go through heads up path
+        boolean inShade = mStatusBarStateController.getState() == SHADE;
+        if (entry.isBubble() && !entry.isBubbleDismissed() && inShade) {
+            return false;
+        }
+
+        if (!canAlertCommon(entry)) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: notification shouldn't alert: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: no huns or vr mode");
+            }
+            return false;
+        }
+
+        boolean isDreaming = false;
+        try {
+            isDreaming = mDreamManager.isDreaming();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to query dream manager.", e);
+        }
+        boolean inUse = mPowerManager.isScreenOn() && !isDreaming;
+
+        if (!inUse) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: not in use: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (entry.shouldSuppressPeek()) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: suppressed by DND: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (isSnoozedPackage(sbn)) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: snoozed package: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (entry.hasJustLaunchedFullScreenIntent()) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: recent fullscreen: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (entry.importance < NotificationManager.IMPORTANCE_HIGH) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: unimportant notification: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (!mHeadsUpSuppressor.canHeadsUp(entry, sbn)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Whether or not the notification should "pulse" on the user's display when the phone is
+     * dozing.  This displays the ambient view of the notification.
+     *
+     * @param entry the entry to check
+     * @return true if the entry should ambient pulse, false otherwise
+     */
+    public boolean shouldPulse(NotificationEntry entry) {
+        StatusBarNotification sbn = entry.notification;
+
+        if (!getShadeController().isDozing()) {
+            if (DEBUG) {
+                Log.d(TAG, "No pulsing: not dozing: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (!canAlertCommon(entry)) {
+            if (DEBUG) {
+                Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (entry.shouldSuppressAmbient()) {
+            if (DEBUG) {
+                Log.d(TAG, "No pulsing: ambient effect suppressed: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (entry.importance < NotificationManager.IMPORTANCE_DEFAULT) {
+            if (DEBUG) {
+                Log.d(TAG, "No pulsing: not important enough: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        Bundle extras = sbn.getNotification().extras;
+        CharSequence title = extras.getCharSequence(Notification.EXTRA_TITLE);
+        CharSequence text = extras.getCharSequence(Notification.EXTRA_TEXT);
+        if (TextUtils.isEmpty(title) && TextUtils.isEmpty(text)) {
+            if (DEBUG) {
+                Log.d(TAG, "No pulsing: title and text are empty: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Common checks between heads up alerting and ambient pulse alerting.  See
+     * {@link #shouldHeadsUp(NotificationEntry)} and
+     * {@link #shouldPulse(NotificationEntry)}.  Notifications that fail any of these checks
+     * should not alert at all.
+     *
+     * @param entry the entry to check
+     * @return true if these checks pass, false if the notification should not alert
+     */
+    protected boolean canAlertCommon(NotificationEntry entry) {
+        StatusBarNotification sbn = entry.notification;
+
+        if (mNotificationFilter.shouldFilterOut(entry)) {
+            if (DEBUG) {
+                Log.d(TAG, "No alerting: filtered notification: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        // Don't alert notifications that are suppressed due to group alert behavior
+        if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
+            if (DEBUG) {
+                Log.d(TAG, "No alerting: suppressed due to group alert behavior");
+            }
+            return false;
+        }
+
+        return true;
+    }
+
+    private boolean isSnoozedPackage(StatusBarNotification sbn) {
+        return mHeadsUpManager.isSnoozed(sbn.getPackageName());
+    }
+
+    /** Sets whether to disable all alerts. */
+    public void setDisableNotificationAlerts(boolean disableNotificationAlerts) {
+        mDisableNotificationAlerts = disableNotificationAlerts;
+        mHeadsUpObserver.onChange(true);
+    }
+
+    protected NotificationPresenter getPresenter() {
+        return mPresenter;
+    }
+
+    /** A component which can suppress heads-up notifications due to the overall state of the UI. */
+    public interface HeadsUpSuppressor {
+        /**
+         * Returns false if the provided notification is ineligible for heads-up according to this
+         * component.
+         *
+         * @param entry entry of the notification that might be heads upped
+         * @param sbn   notification that might be heads upped
+         * @return false if the notification can not be heads upped
+         */
+        boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn);
+
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
new file mode 100644
index 0000000..cc302b1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
+import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.view.ViewGroup;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.util.NotificationMessagingUtil;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.UiOffloadThread;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationUiAdjustment;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationInflater;
+import com.android.systemui.statusbar.notification.row.RowInflaterTask;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+/** Handles inflating and updating views for notifications. */
+@Singleton
+public class NotificationRowBinder {
+
+    private static final String TAG = "NotificationViewManager";
+
+    private final NotificationGroupManager mGroupManager =
+            Dependency.get(NotificationGroupManager.class);
+    private final NotificationGutsManager mGutsManager =
+            Dependency.get(NotificationGutsManager.class);
+    private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
+    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+            Dependency.get(NotificationInterruptionStateProvider.class);
+
+    private final Context mContext;
+    private final IStatusBarService mBarService;
+    private final NotificationMessagingUtil mMessagingUtil;
+    private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
+            this::logNotificationExpansion;
+    private final boolean mAllowLongPress;
+
+    private NotificationRemoteInputManager mRemoteInputManager;
+    private NotificationPresenter mPresenter;
+    private NotificationListContainer mListContainer;
+    private HeadsUpManager mHeadsUpManager;
+    private NotificationInflater.InflationCallback mInflationCallback;
+    private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
+    private BindRowCallback mBindRowCallback;
+    private NotificationClicker mNotificationClicker;
+
+    @Inject
+    public NotificationRowBinder(Context context,
+            @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress) {
+        mContext = context;
+        mMessagingUtil = new NotificationMessagingUtil(context);
+        mAllowLongPress = allowLongPress;
+        mBarService = IStatusBarService.Stub.asInterface(
+                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+    }
+
+    private NotificationRemoteInputManager getRemoteInputManager() {
+        if (mRemoteInputManager == null) {
+            mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
+        }
+        return mRemoteInputManager;
+    }
+
+    /**
+     * Sets up late-bound dependencies for this component.
+     */
+    public void setUpWithPresenter(NotificationPresenter presenter,
+            NotificationListContainer listContainer,
+            HeadsUpManager headsUpManager,
+            NotificationInflater.InflationCallback inflationCallback,
+            BindRowCallback bindRowCallback) {
+        mPresenter = presenter;
+        mListContainer = listContainer;
+        mHeadsUpManager = headsUpManager;
+        mInflationCallback = inflationCallback;
+        mBindRowCallback = bindRowCallback;
+        mOnAppOpsClickListener = mGutsManager::openGuts;
+    }
+
+    public void setNotificationClicker(NotificationClicker clicker) {
+        mNotificationClicker = clicker;
+    }
+
+    /**
+     * Inflates the views for the given entry (possibly asynchronously).
+     */
+    public void inflateViews(
+            NotificationEntry entry,
+            Runnable onDismissRunnable,
+            boolean isUpdate)
+            throws InflationException {
+        ViewGroup parent = mListContainer.getViewParentForNotification(entry);
+        PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
+                entry.notification.getUser().getIdentifier());
+
+        final StatusBarNotification sbn = entry.notification;
+        if (entry.rowExists()) {
+            entry.updateIcons(mContext, sbn);
+            entry.reset();
+            updateNotification(entry, pmUser, sbn, entry.getRow(), isUpdate);
+        } else {
+            entry.createIcons(mContext, sbn);
+            new RowInflaterTask().inflate(mContext, parent, entry,
+                    row -> {
+                        bindRow(entry, pmUser, sbn, row, onDismissRunnable);
+                        updateNotification(entry, pmUser, sbn, row, isUpdate);
+                    });
+        }
+    }
+
+    private void bindRow(NotificationEntry entry, PackageManager pmUser,
+            StatusBarNotification sbn, ExpandableNotificationRow row,
+            Runnable onDismissRunnable) {
+        row.setExpansionLogger(mExpansionLogger, entry.notification.getKey());
+        row.setGroupManager(mGroupManager);
+        row.setHeadsUpManager(mHeadsUpManager);
+        row.setOnExpandClickListener(mPresenter);
+        row.setInflationCallback(mInflationCallback);
+        if (mAllowLongPress) {
+            row.setLongPressListener(mGutsManager::openGuts);
+        }
+        mListContainer.bindRow(row);
+        getRemoteInputManager().bindRow(row);
+
+        // Get the app name.
+        // Note that Notification.Builder#bindHeaderAppName has similar logic
+        // but since this field is used in the guts, it must be accurate.
+        // Therefore we will only show the application label, or, failing that, the
+        // package name. No substitutions.
+        final String pkg = sbn.getPackageName();
+        String appname = pkg;
+        try {
+            final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES
+                            | PackageManager.MATCH_DISABLED_COMPONENTS);
+            if (info != null) {
+                appname = String.valueOf(pmUser.getApplicationLabel(info));
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            // Do nothing
+        }
+        row.setAppName(appname);
+        row.setOnDismissRunnable(onDismissRunnable);
+        row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+        if (ENABLE_REMOTE_INPUT) {
+            row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
+        }
+
+        row.setAppOpsOnClickListener(mOnAppOpsClickListener);
+
+        mBindRowCallback.onBindRow(entry, pmUser, sbn, row);
+    }
+
+    /**
+     * Updates the views bound to an entry when the entry's ranking changes, either in-place or by
+     * reinflating them.
+     */
+    public void onNotificationRankingUpdated(
+            NotificationEntry entry,
+            @Nullable Integer oldImportance,
+            NotificationUiAdjustment oldAdjustment,
+            NotificationUiAdjustment newAdjustment,
+            boolean isUpdate) {
+        if (NotificationUiAdjustment.needReinflate(oldAdjustment, newAdjustment)) {
+            if (entry.rowExists()) {
+                entry.reset();
+                PackageManager pmUser = StatusBar.getPackageManagerForUser(
+                        mContext,
+                        entry.notification.getUser().getIdentifier());
+                updateNotification(entry, pmUser, entry.notification, entry.getRow(), isUpdate);
+            } else {
+                // Once the RowInflaterTask is done, it will pick up the updated entry, so
+                // no-op here.
+            }
+        } else {
+            if (oldImportance != null && entry.importance != oldImportance) {
+                if (entry.rowExists()) {
+                    entry.getRow().onNotificationRankingUpdated();
+                }
+            }
+        }
+    }
+
+    //TODO: This method associates a row with an entry, but eventually needs to not do that
+    private void updateNotification(
+            NotificationEntry entry,
+            PackageManager pmUser,
+            StatusBarNotification sbn,
+            ExpandableNotificationRow row,
+            boolean isUpdate) {
+        boolean isLowPriority = entry.ambient;
+        boolean wasLowPriority = row.isLowPriority();
+        row.setIsLowPriority(isLowPriority);
+        row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority));
+        // bind the click event to the content area
+        checkNotNull(mNotificationClicker).register(row, sbn);
+
+        // Extract target SDK version.
+        try {
+            ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
+            entry.targetSdk = info.targetSdkVersion;
+        } catch (PackageManager.NameNotFoundException ex) {
+            Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
+        }
+        row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
+                && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
+
+        // TODO: should updates to the entry be happening somewhere else?
+        entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
+        entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
+
+        entry.setRow(row);
+        row.setOnActivatedListener(mPresenter);
+
+        boolean useIncreasedCollapsedHeight =
+                mMessagingUtil.isImportantMessaging(sbn, entry.importance);
+        boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight
+                && !mPresenter.isPresenterFullyCollapsed();
+        row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
+        row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
+        row.setEntry(entry);
+
+        if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
+            row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, true /* shouldInflate */);
+        }
+        if (mNotificationInterruptionStateProvider.shouldPulse(entry)) {
+            row.updateInflationFlag(FLAG_CONTENT_VIEW_AMBIENT, true /* shouldInflate */);
+        }
+        row.setNeedsRedaction(
+                Dependency.get(NotificationLockscreenUserManager.class).needsRedaction(entry));
+        row.inflateViews();
+    }
+
+    private void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
+        mUiOffloadThread.submit(() -> {
+            try {
+                mBarService.onNotificationExpansionChanged(key, userAction, expanded);
+            } catch (RemoteException e) {
+                // Ignore.
+            }
+        });
+    }
+
+    /** Callback for when a row is bound to an entry. */
+    public interface BindRowCallback {
+        /**
+         * Called when a new notification and row is created.
+         *
+         * @param entry  entry for the notification
+         * @param pmUser package manager for user
+         * @param sbn    notification
+         * @param row    row for the notification
+         */
+        void onBindRow(NotificationEntry entry, PackageManager pmUser,
+                StatusBarNotification sbn, ExpandableNotificationRow row);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
index a194eef..f09c57d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification;
 
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
 /**
  * An object that can determine the visibility of a Notification.
  */
@@ -27,5 +29,5 @@
      * @param entry
      * @return true if row is in a visible location
      */
-    boolean isInVisibleLocation(NotificationData.Entry entry);
+    boolean isInVisibleLocation(NotificationEntry entry);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index fce7980..c886685 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -20,19 +20,26 @@
 
 import androidx.collection.ArraySet;
 
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 
 import java.util.ArrayList;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * A manager that ensures that notifications are visually stable. It will suppress reorderings
  * and reorder at the right time when they are out of view.
  */
+@Singleton
 public class VisualStabilityManager implements OnHeadsUpChangedListener {
 
     private final ArrayList<Callback> mCallbacks =  new ArrayList<>();
 
+    private NotificationPresenter mPresenter;
     private boolean mPanelExpanded;
     private boolean mScreenOn;
     private boolean mReorderingAllowed;
@@ -42,6 +49,25 @@
     private ArraySet<View> mAddedChildren = new ArraySet<>();
     private boolean mPulsing;
 
+    @Inject
+    public VisualStabilityManager(NotificationEntryManager notificationEntryManager) {
+        notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
+            @Override
+            public void onEntryReinflated(NotificationEntry entry) {
+                if (entry.hasLowPriorityStateUpdated()) {
+                    onLowPriorityUpdated(entry);
+                    if (mPresenter != null) {
+                        mPresenter.updateNotificationViews();
+                    }
+                }
+            }
+        });
+    }
+
+    public void setUpWithPresenter(NotificationPresenter presenter) {
+        mPresenter = presenter;
+    }
+
     /**
      * Add a callback to invoke when reordering is allowed again.
      * @param callback
@@ -138,7 +164,7 @@
     }
 
     @Override
-    public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+    public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
         if (isHeadsUp) {
             // Heads up notifications should in general be allowed to reorder if they are out of
             // view and stay at the current location if they aren't.
@@ -146,7 +172,7 @@
         }
     }
 
-    public void onLowPriorityUpdated(NotificationData.Entry entry) {
+    private void onLowPriorityUpdated(NotificationEntry entry) {
         mLowPriorityReorderingViews.add(entry.getRow());
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
new file mode 100644
index 0000000..8c29fb5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.SnoozeCriterion;
+import android.service.notification.StatusBarNotification;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The list of currently displaying notifications.
+ */
+public class NotificationData {
+
+    private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class);
+
+    /**
+     * These dependencies are late init-ed
+     */
+    private KeyguardEnvironment mEnvironment;
+    private NotificationMediaManager mMediaManager;
+
+    private HeadsUpManager mHeadsUpManager;
+
+    private final ArrayMap<String, NotificationEntry> mEntries = new ArrayMap<>();
+    private final ArrayList<NotificationEntry> mSortedAndFiltered = new ArrayList<>();
+    private final ArrayList<NotificationEntry> mFilteredForUser = new ArrayList<>();
+
+    private final NotificationGroupManager mGroupManager =
+            Dependency.get(NotificationGroupManager.class);
+
+    private RankingMap mRankingMap;
+    private final Ranking mTmpRanking = new Ranking();
+
+    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+        mHeadsUpManager = headsUpManager;
+    }
+
+    private final Comparator<NotificationEntry> mRankingComparator =
+            new Comparator<NotificationEntry>() {
+        private final Ranking mRankingA = new Ranking();
+        private final Ranking mRankingB = new Ranking();
+
+        @Override
+        public int compare(NotificationEntry a, NotificationEntry b) {
+            final StatusBarNotification na = a.notification;
+            final StatusBarNotification nb = b.notification;
+            int aImportance = NotificationManager.IMPORTANCE_DEFAULT;
+            int bImportance = NotificationManager.IMPORTANCE_DEFAULT;
+            int aRank = 0;
+            int bRank = 0;
+
+            if (mRankingMap != null) {
+                // RankingMap as received from NoMan
+                getRanking(a.key, mRankingA);
+                getRanking(b.key, mRankingB);
+                aImportance = mRankingA.getImportance();
+                bImportance = mRankingB.getImportance();
+                aRank = mRankingA.getRank();
+                bRank = mRankingB.getRank();
+            }
+
+            String mediaNotification = getMediaManager().getMediaNotificationKey();
+
+            // IMPORTANCE_MIN media streams are allowed to drift to the bottom
+            final boolean aMedia = a.key.equals(mediaNotification)
+                    && aImportance > NotificationManager.IMPORTANCE_MIN;
+            final boolean bMedia = b.key.equals(mediaNotification)
+                    && bImportance > NotificationManager.IMPORTANCE_MIN;
+
+            boolean aSystemMax = aImportance >= NotificationManager.IMPORTANCE_HIGH
+                    && isSystemNotification(na);
+            boolean bSystemMax = bImportance >= NotificationManager.IMPORTANCE_HIGH
+                    && isSystemNotification(nb);
+
+            boolean isHeadsUp = a.getRow().isHeadsUp();
+            if (isHeadsUp != b.getRow().isHeadsUp()) {
+                return isHeadsUp ? -1 : 1;
+            } else if (isHeadsUp) {
+                // Provide consistent ranking with headsUpManager
+                return mHeadsUpManager.compare(a, b);
+            } else if (a.getRow().isAmbientPulsing() != b.getRow().isAmbientPulsing()) {
+                return a.getRow().isAmbientPulsing() ? -1 : 1;
+            } else if (aMedia != bMedia) {
+                // Upsort current media notification.
+                return aMedia ? -1 : 1;
+            } else if (aSystemMax != bSystemMax) {
+                // Upsort PRIORITY_MAX system notifications
+                return aSystemMax ? -1 : 1;
+            } else if (aRank != bRank) {
+                return aRank - bRank;
+            } else {
+                return Long.compare(nb.getNotification().when, na.getNotification().when);
+            }
+        }
+    };
+
+    private KeyguardEnvironment getEnvironment() {
+        if (mEnvironment == null) {
+            mEnvironment = Dependency.get(KeyguardEnvironment.class);
+        }
+        return mEnvironment;
+    }
+
+    private NotificationMediaManager getMediaManager() {
+        if (mMediaManager == null) {
+            mMediaManager = Dependency.get(NotificationMediaManager.class);
+        }
+        return mMediaManager;
+    }
+
+    /**
+     * Returns the sorted list of active notifications (depending on {@link KeyguardEnvironment}
+     *
+     * <p>
+     * This call doesn't update the list of active notifications. Call {@link #filterAndSort()}
+     * when the environment changes.
+     * <p>
+     * Don't hold on to or modify the returned list.
+     */
+    public ArrayList<NotificationEntry> getActiveNotifications() {
+        return mSortedAndFiltered;
+    }
+
+    public ArrayList<NotificationEntry> getNotificationsForCurrentUser() {
+        mFilteredForUser.clear();
+
+        synchronized (mEntries) {
+            final int len = mEntries.size();
+            for (int i = 0; i < len; i++) {
+                NotificationEntry entry = mEntries.valueAt(i);
+                final StatusBarNotification sbn = entry.notification;
+                if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
+                    continue;
+                }
+                mFilteredForUser.add(entry);
+            }
+        }
+        return mFilteredForUser;
+    }
+
+    public NotificationEntry get(String key) {
+        return mEntries.get(key);
+    }
+
+    public void add(NotificationEntry entry) {
+        synchronized (mEntries) {
+            mEntries.put(entry.notification.getKey(), entry);
+        }
+        mGroupManager.onEntryAdded(entry);
+
+        updateRankingAndSort(mRankingMap);
+    }
+
+    public NotificationEntry remove(String key, RankingMap ranking) {
+        NotificationEntry removed;
+        synchronized (mEntries) {
+            removed = mEntries.remove(key);
+        }
+        if (removed == null) return null;
+        mGroupManager.onEntryRemoved(removed);
+        updateRankingAndSort(ranking);
+        return removed;
+    }
+
+    /** Updates the given notification entry with the provided ranking. */
+    public void update(
+            NotificationEntry entry,
+            RankingMap ranking,
+            StatusBarNotification notification) {
+        updateRanking(ranking);
+        final StatusBarNotification oldNotification = entry.notification;
+        entry.notification = notification;
+        mGroupManager.onEntryUpdated(entry, oldNotification);
+    }
+
+    public void updateRanking(RankingMap ranking) {
+        updateRankingAndSort(ranking);
+    }
+
+    public void updateAppOp(int appOp, int uid, String pkg, String key, boolean showIcon) {
+        synchronized (mEntries) {
+            final int len = mEntries.size();
+            for (int i = 0; i < len; i++) {
+                NotificationEntry entry = mEntries.valueAt(i);
+                if (uid == entry.notification.getUid()
+                        && pkg.equals(entry.notification.getPackageName())
+                        && key.equals(entry.key)) {
+                    if (showIcon) {
+                        entry.mActiveAppOps.add(appOp);
+                    } else {
+                        entry.mActiveAppOps.remove(appOp);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns true if this notification should be displayed in the high-priority notifications
+     * section (and on the lockscreen and status bar).
+     */
+    public boolean isHighPriority(StatusBarNotification statusBarNotification) {
+        if (mRankingMap != null) {
+            getRanking(statusBarNotification.getKey(), mTmpRanking);
+            if (mTmpRanking.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT
+                    || statusBarNotification.getNotification().isForegroundService()
+                    || statusBarNotification.getNotification().hasMediaSession()) {
+                return true;
+            }
+            if (mGroupManager.isSummaryOfGroup(statusBarNotification)) {
+                final ArrayList<NotificationEntry> logicalChildren =
+                        mGroupManager.getLogicalChildren(statusBarNotification);
+                for (NotificationEntry child : logicalChildren) {
+                    if (isHighPriority(child.notification)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    public boolean isAmbient(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.isAmbient();
+        }
+        return false;
+    }
+
+    public int getVisibilityOverride(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.getVisibilityOverride();
+        }
+        return Ranking.VISIBILITY_NO_OVERRIDE;
+    }
+
+    public int getImportance(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.getImportance();
+        }
+        return NotificationManager.IMPORTANCE_UNSPECIFIED;
+    }
+
+    public String getOverrideGroupKey(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.getOverrideGroupKey();
+        }
+        return null;
+    }
+
+    public List<SnoozeCriterion> getSnoozeCriteria(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.getSnoozeCriteria();
+        }
+        return null;
+    }
+
+    public NotificationChannel getChannel(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.getChannel();
+        }
+        return null;
+    }
+
+    public int getRank(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.getRank();
+        }
+        return 0;
+    }
+
+    public boolean shouldHide(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.isSuspended();
+        }
+        return false;
+    }
+
+    private void updateRankingAndSort(RankingMap ranking) {
+        if (ranking != null) {
+            mRankingMap = ranking;
+            synchronized (mEntries) {
+                final int len = mEntries.size();
+                for (int i = 0; i < len; i++) {
+                    NotificationEntry entry = mEntries.valueAt(i);
+                    if (!getRanking(entry.key, mTmpRanking)) {
+                        continue;
+                    }
+                    final StatusBarNotification oldSbn = entry.notification.cloneLight();
+                    final String overrideGroupKey = getOverrideGroupKey(entry.key);
+                    if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) {
+                        entry.notification.setOverrideGroupKey(overrideGroupKey);
+                        mGroupManager.onEntryUpdated(entry, oldSbn);
+                    }
+                    entry.populateFromRanking(mTmpRanking);
+                }
+            }
+        }
+        filterAndSort();
+    }
+
+    /**
+     * Get the ranking from the current ranking map.
+     *
+     * @param key the key to look up
+     * @param outRanking the ranking to populate
+     *
+     * @return {@code true} if the ranking was properly obtained.
+     */
+    @VisibleForTesting
+    protected boolean getRanking(String key, Ranking outRanking) {
+        return mRankingMap.getRanking(key, outRanking);
+    }
+
+    // TODO: This should not be public. Instead the Environment should notify this class when
+    // anything changed, and this class should call back the UI so it updates itself.
+    public void filterAndSort() {
+        mSortedAndFiltered.clear();
+
+        synchronized (mEntries) {
+            final int len = mEntries.size();
+            for (int i = 0; i < len; i++) {
+                NotificationEntry entry = mEntries.valueAt(i);
+
+                if (mNotificationFilter.shouldFilterOut(entry)) {
+                    continue;
+                }
+
+                mSortedAndFiltered.add(entry);
+            }
+        }
+
+        Collections.sort(mSortedAndFiltered, mRankingComparator);
+    }
+
+    public void dump(PrintWriter pw, String indent) {
+        int filteredLen = mSortedAndFiltered.size();
+        pw.print(indent);
+        pw.println("active notifications: " + filteredLen);
+        int active;
+        for (active = 0; active < filteredLen; active++) {
+            NotificationEntry e = mSortedAndFiltered.get(active);
+            dumpEntry(pw, indent, active, e);
+        }
+        synchronized (mEntries) {
+            int totalLen = mEntries.size();
+            pw.print(indent);
+            pw.println("inactive notifications: " + (totalLen - active));
+            int inactiveCount = 0;
+            for (int i = 0; i < totalLen; i++) {
+                NotificationEntry entry = mEntries.valueAt(i);
+                if (!mSortedAndFiltered.contains(entry)) {
+                    dumpEntry(pw, indent, inactiveCount, entry);
+                    inactiveCount++;
+                }
+            }
+        }
+    }
+
+    private void dumpEntry(PrintWriter pw, String indent, int i, NotificationEntry e) {
+        getRanking(e.key, mTmpRanking);
+        pw.print(indent);
+        pw.println("  [" + i + "] key=" + e.key + " icon=" + e.icon);
+        StatusBarNotification n = e.notification;
+        pw.print(indent);
+        pw.println("      pkg=" + n.getPackageName() + " id=" + n.getId() + " importance="
+                + mTmpRanking.getImportance());
+        pw.print(indent);
+        pw.println("      notification=" + n.getNotification());
+    }
+
+    private static boolean isSystemNotification(StatusBarNotification sbn) {
+        String sbnPackage = sbn.getPackageName();
+        return "android".equals(sbnPackage) || "com.android.systemui".equals(sbnPackage);
+    }
+
+    /**
+     * Provides access to keyguard state and user settings dependent data.
+     */
+    public interface KeyguardEnvironment {
+        boolean isDeviceProvisioned();
+        boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
new file mode 100644
index 0000000..58aa02c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -0,0 +1,701 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import static android.app.Notification.CATEGORY_ALARM;
+import static android.app.Notification.CATEGORY_CALL;
+import static android.app.Notification.CATEGORY_EVENT;
+import static android.app.Notification.CATEGORY_MESSAGE;
+import static android.app.Notification.CATEGORY_REMINDER;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+
+import android.annotation.NonNull;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager.Policy;
+import android.app.Person;
+import android.content.Context;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.SnoozeCriterion;
+import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.ContrastColorUtil;
+import com.android.systemui.statusbar.InflationTask;
+import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationGuts;
+import com.android.systemui.statusbar.notification.row.NotificationInflater;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents a notification that the system UI knows about
+ *
+ * Whenever the NotificationManager tells us about the existence of a new notification, we wrap it
+ * in a NotificationEntry. Thus, every notification has an associated NotificationEntry, even if
+ * that notification is never displayed to the user (for example, if it's filtered out for some
+ * reason).
+ *
+ * Entries store information about the current state of the notification. Essentially:
+ * anything that needs to persist or be modifiable even when the notification's views don't
+ * exist. Any other state should be stored on the views/view controllers themselves.
+ *
+ * At the moment, there are many things here that shouldn't be and vice-versa. Hopefully we can
+ * clean this up in the future.
+ */
+public final class NotificationEntry {
+    private static final long LAUNCH_COOLDOWN = 2000;
+    private static final long REMOTE_INPUT_COOLDOWN = 500;
+    private static final long INITIALIZATION_DELAY = 400;
+    private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN;
+    private static final int COLOR_INVALID = 1;
+    public final String key;
+    public StatusBarNotification notification;
+    public NotificationChannel channel;
+    public long lastAudiblyAlertedMs;
+    public boolean noisy;
+    public boolean ambient;
+    public int importance;
+    public StatusBarIconView icon;
+    public StatusBarIconView expandedIcon;
+    private boolean interruption;
+    public boolean autoRedacted; // whether the redacted notification was generated by us
+    public int targetSdk;
+    private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
+    public CharSequence remoteInputText;
+    public List<SnoozeCriterion> snoozeCriteria;
+    public int userSentiment = NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
+    /** Smart Actions provided by the NotificationAssistantService. */
+    @NonNull
+    public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
+    public CharSequence[] smartReplies = new CharSequence[0];
+    @VisibleForTesting
+    public int suppressedVisualEffects;
+    public boolean suspended;
+
+    private NotificationEntry parent; // our parent (if we're in a group)
+    private ArrayList<NotificationEntry> children = new ArrayList<NotificationEntry>();
+    private ExpandableNotificationRow row; // the outer expanded view
+
+    private int mCachedContrastColor = COLOR_INVALID;
+    private int mCachedContrastColorIsFor = COLOR_INVALID;
+    private InflationTask mRunningTask = null;
+    private Throwable mDebugThrowable;
+    public CharSequence remoteInputTextWhenReset;
+    public long lastRemoteInputSent = NOT_LAUNCHED_YET;
+    public ArraySet<Integer> mActiveAppOps = new ArraySet<>(3);
+    public CharSequence headsUpStatusBarText;
+    public CharSequence headsUpStatusBarTextPublic;
+
+    private long initializationTime = -1;
+
+    /**
+     * Whether or not this row represents a system notification. Note that if this is
+     * {@code null}, that means we were either unable to retrieve the info or have yet to
+     * retrieve the info.
+     */
+    public Boolean mIsSystemNotification;
+
+    /**
+     * Has the user sent a reply through this Notification.
+     */
+    private boolean hasSentReply;
+
+    /**
+     * Whether this notification should be displayed as a bubble.
+     */
+    private boolean mIsBubble;
+
+    /**
+     * Whether the user has dismissed this notification when it was in bubble form.
+     */
+    private boolean mUserDismissedBubble;
+
+    public NotificationEntry(StatusBarNotification n) {
+        this(n, null);
+    }
+
+    public NotificationEntry(
+            StatusBarNotification n,
+            @Nullable NotificationListenerService.Ranking ranking) {
+        this.key = n.getKey();
+        this.notification = n;
+        if (ranking != null) {
+            populateFromRanking(ranking);
+        }
+    }
+
+    public void populateFromRanking(@NonNull NotificationListenerService.Ranking ranking) {
+        channel = ranking.getChannel();
+        lastAudiblyAlertedMs = ranking.getLastAudiblyAlertedMillis();
+        importance = ranking.getImportance();
+        ambient = ranking.isAmbient();
+        snoozeCriteria = ranking.getSnoozeCriteria();
+        userSentiment = ranking.getUserSentiment();
+        systemGeneratedSmartActions = ranking.getSmartActions() == null
+                ? Collections.emptyList() : ranking.getSmartActions();
+        smartReplies = ranking.getSmartReplies() == null
+                ? new CharSequence[0]
+                : ranking.getSmartReplies().toArray(new CharSequence[0]);
+        suppressedVisualEffects = ranking.getSuppressedVisualEffects();
+        suspended = ranking.isSuspended();
+    }
+
+    public void setInterruption() {
+        interruption = true;
+    }
+
+    public boolean hasInterrupted() {
+        return interruption;
+    }
+
+    public void setIsBubble(boolean bubbleable) {
+        mIsBubble = bubbleable;
+    }
+
+    public boolean isBubble() {
+        return mIsBubble;
+    }
+
+    public void setBubbleDismissed(boolean userDismissed) {
+        mUserDismissedBubble = userDismissed;
+    }
+
+    public boolean isBubbleDismissed() {
+        return mUserDismissedBubble;
+    }
+
+    /**
+     * Resets the notification entry to be re-used.
+     */
+    public void reset() {
+        if (row != null) {
+            row.reset();
+        }
+    }
+
+    public ExpandableNotificationRow getRow() {
+        return row;
+    }
+
+    //TODO: This will go away when we have a way to bind an entry to a row
+    public void setRow(ExpandableNotificationRow row) {
+        this.row = row;
+    }
+
+    @Nullable
+    public List<NotificationEntry> getChildren() {
+        if (children.size() <= 0) {
+            return null;
+        }
+
+        return children;
+    }
+
+    public void notifyFullScreenIntentLaunched() {
+        setInterruption();
+        lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime();
+    }
+
+    public boolean hasJustLaunchedFullScreenIntent() {
+        return SystemClock.elapsedRealtime() < lastFullScreenIntentLaunchTime + LAUNCH_COOLDOWN;
+    }
+
+    public boolean hasJustSentRemoteInput() {
+        return SystemClock.elapsedRealtime() < lastRemoteInputSent + REMOTE_INPUT_COOLDOWN;
+    }
+
+    public boolean hasFinishedInitialization() {
+        return initializationTime == -1
+                || SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY;
+    }
+
+    /**
+     * Create the icons for a notification
+     * @param context the context to create the icons with
+     * @param sbn the notification
+     * @throws InflationException Exception if required icons are not valid or specified
+     */
+    public void createIcons(Context context, StatusBarNotification sbn)
+            throws InflationException {
+        Notification n = sbn.getNotification();
+        final Icon smallIcon = n.getSmallIcon();
+        if (smallIcon == null) {
+            throw new InflationException("No small icon in notification from "
+                    + sbn.getPackageName());
+        }
+
+        // Construct the icon.
+        icon = new StatusBarIconView(context,
+                sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
+        icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+
+        // Construct the expanded icon.
+        expandedIcon = new StatusBarIconView(context,
+                sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
+        expandedIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+        final StatusBarIcon ic = new StatusBarIcon(
+                sbn.getUser(),
+                sbn.getPackageName(),
+                smallIcon,
+                n.iconLevel,
+                n.number,
+                StatusBarIconView.contentDescForNotification(context, n));
+        if (!icon.set(ic) || !expandedIcon.set(ic)) {
+            icon = null;
+            expandedIcon = null;
+            throw new InflationException("Couldn't create icon: " + ic);
+        }
+        expandedIcon.setVisibility(View.INVISIBLE);
+        expandedIcon.setOnVisibilityChangedListener(
+                newVisibility -> {
+                    if (row != null) {
+                        row.setIconsVisible(newVisibility != View.VISIBLE);
+                    }
+                });
+    }
+
+    public void setIconTag(int key, Object tag) {
+        if (icon != null) {
+            icon.setTag(key, tag);
+            expandedIcon.setTag(key, tag);
+        }
+    }
+
+    /**
+     * Update the notification icons.
+     *
+     * @param context the context to create the icons with.
+     * @param sbn the notification to read the icon from.
+     * @throws InflationException Exception if required icons are not valid or specified
+     */
+    public void updateIcons(Context context, StatusBarNotification sbn)
+            throws InflationException {
+        if (icon != null) {
+            // Update the icon
+            Notification n = sbn.getNotification();
+            final StatusBarIcon ic = new StatusBarIcon(
+                    notification.getUser(),
+                    notification.getPackageName(),
+                    n.getSmallIcon(),
+                    n.iconLevel,
+                    n.number,
+                    StatusBarIconView.contentDescForNotification(context, n));
+            icon.setNotification(sbn);
+            expandedIcon.setNotification(sbn);
+            if (!icon.set(ic) || !expandedIcon.set(ic)) {
+                throw new InflationException("Couldn't update icon: " + ic);
+            }
+        }
+    }
+
+    public int getContrastedColor(Context context, boolean isLowPriority,
+            int backgroundColor) {
+        int rawColor = isLowPriority ? Notification.COLOR_DEFAULT :
+                notification.getNotification().color;
+        if (mCachedContrastColorIsFor == rawColor && mCachedContrastColor != COLOR_INVALID) {
+            return mCachedContrastColor;
+        }
+        final int contrasted = ContrastColorUtil.resolveContrastColor(context, rawColor,
+                backgroundColor);
+        mCachedContrastColorIsFor = rawColor;
+        mCachedContrastColor = contrasted;
+        return mCachedContrastColor;
+    }
+
+    /**
+     * Abort all existing inflation tasks
+     */
+    public void abortTask() {
+        if (mRunningTask != null) {
+            mRunningTask.abort();
+            mRunningTask = null;
+        }
+    }
+
+    public void setInflationTask(InflationTask abortableTask) {
+        // abort any existing inflation
+        InflationTask existing = mRunningTask;
+        abortTask();
+        mRunningTask = abortableTask;
+        if (existing != null && mRunningTask != null) {
+            mRunningTask.supersedeTask(existing);
+        }
+    }
+
+    public void onInflationTaskFinished() {
+        mRunningTask = null;
+    }
+
+    @VisibleForTesting
+    public InflationTask getRunningTask() {
+        return mRunningTask;
+    }
+
+    /**
+     * Set a throwable that is used for debugging
+     *
+     * @param debugThrowable the throwable to save
+     */
+    public void setDebugThrowable(Throwable debugThrowable) {
+        mDebugThrowable = debugThrowable;
+    }
+
+    public Throwable getDebugThrowable() {
+        return mDebugThrowable;
+    }
+
+    public void onRemoteInputInserted() {
+        lastRemoteInputSent = NOT_LAUNCHED_YET;
+        remoteInputTextWhenReset = null;
+    }
+
+    public void setHasSentReply() {
+        hasSentReply = true;
+    }
+
+    public boolean isLastMessageFromReply() {
+        if (!hasSentReply) {
+            return false;
+        }
+        Bundle extras = notification.getNotification().extras;
+        CharSequence[] replyTexts = extras.getCharSequenceArray(
+                Notification.EXTRA_REMOTE_INPUT_HISTORY);
+        if (!ArrayUtils.isEmpty(replyTexts)) {
+            return true;
+        }
+        Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
+        if (messages != null && messages.length > 0) {
+            Parcelable message = messages[messages.length - 1];
+            if (message instanceof Bundle) {
+                Notification.MessagingStyle.Message lastMessage =
+                        Notification.MessagingStyle.Message.getMessageFromBundle(
+                                (Bundle) message);
+                if (lastMessage != null) {
+                    Person senderPerson = lastMessage.getSenderPerson();
+                    if (senderPerson == null) {
+                        return true;
+                    }
+                    Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON);
+                    return Objects.equals(user, senderPerson);
+                }
+            }
+        }
+        return false;
+    }
+
+    public void setInitializationTime(long time) {
+        if (initializationTime == -1) {
+            initializationTime = time;
+        }
+    }
+
+    public void sendAccessibilityEvent(int eventType) {
+        if (row != null) {
+            row.sendAccessibilityEvent(eventType);
+        }
+    }
+
+    /**
+     * Used by NotificationMediaManager to determine... things
+     * @return {@code true} if we are a media notification
+     */
+    public boolean isMediaNotification() {
+        if (row == null) return false;
+
+        return row.isMediaRow();
+    }
+
+    /**
+     * We are a top level child if our parent is the list of notifications duh
+     * @return {@code true} if we're a top level notification
+     */
+    public boolean isTopLevelChild() {
+        return row != null && row.isTopLevelChild();
+    }
+
+    public void resetUserExpansion() {
+        if (row != null) row.resetUserExpansion();
+    }
+
+    public void freeContentViewWhenSafe(@NotificationInflater.InflationFlag int inflationFlag) {
+        if (row != null) row.freeContentViewWhenSafe(inflationFlag);
+    }
+
+    public void setAmbientPulsing(boolean pulsing) {
+        if (row != null) row.setAmbientPulsing(pulsing);
+    }
+
+    public boolean rowExists() {
+        return row != null;
+    }
+
+    public boolean isRowDismissed() {
+        return row != null && row.isDismissed();
+    }
+
+    public boolean isRowRemoved() {
+        return row != null && row.isRemoved();
+    }
+
+    /**
+     * @return {@code true} if the row is null or removed
+     */
+    public boolean isRemoved() {
+        //TODO: recycling invalidates this
+        return row == null || row.isRemoved();
+    }
+
+    /**
+     * @return {@code true} if the row is null or dismissed
+     */
+    public boolean isDismissed() {
+        //TODO: recycling
+        return row == null || row.isDismissed();
+    }
+
+    public boolean isRowPinned() {
+        return row != null && row.isPinned();
+    }
+
+    public void setRowPinned(boolean pinned) {
+        if (row != null) row.setPinned(pinned);
+    }
+
+    public boolean isRowAnimatingAway() {
+        return row != null && row.isHeadsUpAnimatingAway();
+    }
+
+    public boolean isRowHeadsUp() {
+        return row != null && row.isHeadsUp();
+    }
+
+    public void setHeadsUp(boolean shouldHeadsUp) {
+        if (row != null) row.setHeadsUp(shouldHeadsUp);
+    }
+
+    public boolean mustStayOnScreen() {
+        return row != null && row.mustStayOnScreen();
+    }
+
+    public void setHeadsUpIsVisible() {
+        if (row != null) row.setHeadsUpIsVisible();
+    }
+
+    //TODO: i'm imagining a world where this isn't just the row, but I could be rwong
+    public ExpandableNotificationRow getHeadsUpAnimationView() {
+        return row;
+    }
+
+    public void setUserLocked(boolean userLocked) {
+        if (row != null) row.setUserLocked(userLocked);
+    }
+
+    public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
+        if (row != null) row.setUserExpanded(userExpanded, allowChildExpansion);
+    }
+
+    public void setGroupExpansionChanging(boolean changing) {
+        if (row != null) row.setGroupExpansionChanging(changing);
+    }
+
+    public void notifyHeightChanged(boolean needsAnimation) {
+        if (row != null) row.notifyHeightChanged(needsAnimation);
+    }
+
+    public void closeRemoteInput() {
+        if (row != null) row.closeRemoteInput();
+    }
+
+    public boolean areChildrenExpanded() {
+        return row != null && row.areChildrenExpanded();
+    }
+
+    public boolean keepInParent() {
+        return row != null && row.keepInParent();
+    }
+
+    //TODO: probably less confusing to say "is group fully visible"
+    public boolean isGroupNotFullyVisible() {
+        return row == null || row.isGroupNotFullyVisible();
+    }
+
+    public NotificationGuts getGuts() {
+        if (row != null) return row.getGuts();
+        return null;
+    }
+
+    public boolean hasLowPriorityStateUpdated() {
+        return row != null && row.hasLowPriorityStateUpdated();
+    }
+
+    public void removeRow() {
+        if (row != null) row.setRemoved();
+    }
+
+    public boolean isSummaryWithChildren() {
+        return row != null && row.isSummaryWithChildren();
+    }
+
+    public void setKeepInParent(boolean keep) {
+        if (row != null) row.setKeepInParent(keep);
+    }
+
+    public void onDensityOrFontScaleChanged() {
+        if (row != null) row.onDensityOrFontScaleChanged();
+    }
+
+    public boolean areGutsExposed() {
+        return row != null && row.getGuts() != null && row.getGuts().isExposed();
+    }
+
+    public boolean isChildInGroup() {
+        return parent == null;
+    }
+
+    public void setLowPriorityStateUpdated(boolean updated) {
+        if (row != null) row.setLowPriorityStateUpdated(updated);
+    }
+
+    /**
+     * @return Can the underlying notification be cleared? This can be different from whether the
+     *         notification can be dismissed in case notifications are sensitive on the lockscreen.
+     * @see #canViewBeDismissed()
+     */
+    public boolean isClearable() {
+        if (notification == null || !notification.isClearable()) {
+            return false;
+        }
+        if (children.size() > 0) {
+            for (int i = 0; i < children.size(); i++) {
+                NotificationEntry child =  children.get(i);
+                if (!child.isClearable()) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    public boolean canViewBeDismissed() {
+        if (row == null) return true;
+        return row.canViewBeDismissed();
+    }
+
+    @VisibleForTesting
+    boolean isExemptFromDndVisualSuppression() {
+        if (isNotificationBlockedByPolicy(notification.getNotification())) {
+            return false;
+        }
+
+        if ((notification.getNotification().flags
+                & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+            return true;
+        }
+        if (notification.getNotification().isMediaNotification()) {
+            return true;
+        }
+        if (mIsSystemNotification != null && mIsSystemNotification) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean shouldSuppressVisualEffect(int effect) {
+        if (isExemptFromDndVisualSuppression()) {
+            return false;
+        }
+        return (suppressedVisualEffects & effect) != 0;
+    }
+
+    /**
+     * Returns whether {@link Policy#SUPPRESSED_EFFECT_FULL_SCREEN_INTENT}
+     * is set for this entry.
+     */
+    public boolean shouldSuppressFullScreenIntent() {
+        return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
+    }
+
+    /**
+     * Returns whether {@link Policy#SUPPRESSED_EFFECT_PEEK}
+     * is set for this entry.
+     */
+    public boolean shouldSuppressPeek() {
+        return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_PEEK);
+    }
+
+    /**
+     * Returns whether {@link Policy#SUPPRESSED_EFFECT_STATUS_BAR}
+     * is set for this entry.
+     */
+    public boolean shouldSuppressStatusBar() {
+        return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_STATUS_BAR);
+    }
+
+    /**
+     * Returns whether {@link Policy#SUPPRESSED_EFFECT_AMBIENT}
+     * is set for this entry.
+     */
+    public boolean shouldSuppressAmbient() {
+        return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_AMBIENT);
+    }
+
+    /**
+     * Returns whether {@link Policy#SUPPRESSED_EFFECT_NOTIFICATION_LIST}
+     * is set for this entry.
+     */
+    public boolean shouldSuppressNotificationList() {
+        return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_NOTIFICATION_LIST);
+    }
+
+    /**
+     * Categories that are explicitly called out on DND settings screens are always blocked, if
+     * DND has flagged them, even if they are foreground or system notifications that might
+     * otherwise visually bypass DND.
+     */
+    private static boolean isNotificationBlockedByPolicy(Notification n) {
+        return isCategory(CATEGORY_CALL, n)
+                || isCategory(CATEGORY_MESSAGE, n)
+                || isCategory(CATEGORY_ALARM, n)
+                || isCategory(CATEGORY_EVENT, n)
+                || isCategory(CATEGORY_REMINDER, n);
+    }
+
+    private static boolean isCategory(String category, Notification n) {
+        return Objects.equals(n.category, category);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 9f02e54..3eec38e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -21,29 +21,36 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationStats;
+import android.service.notification.StatusBarNotification;
 import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.Dependency;
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Handles notification logging, in particular, logging which notifications are visible and which
  * are not.
  */
+@Singleton
 public class NotificationLogger implements StateListener {
     private static final String TAG = "NotificationLogger";
 
@@ -55,11 +62,10 @@
             new ArraySet<>();
 
     // Dependencies:
-    private final NotificationListenerService mNotificationListener =
-            Dependency.get(NotificationListener.class);
-    private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
-    protected NotificationEntryManager mEntryManager
-            = Dependency.get(NotificationEntryManager.class);
+    private final NotificationListenerService mNotificationListener;
+    private final UiOffloadThread mUiOffloadThread;
+    private final NotificationEntryManager mEntryManager;
+    private HeadsUpManager mHeadsUpManager;
 
     protected Handler mHandler = new Handler();
     protected IStatusBarService mBarService;
@@ -109,11 +115,11 @@
             //    notifications.
             // 3. Report newly visible and no-longer visible notifications.
             // 4. Keep currently visible notifications for next report.
-            ArrayList<NotificationData.Entry> activeNotifications = mEntryManager
+            ArrayList<NotificationEntry> activeNotifications = mEntryManager
                     .getNotificationData().getActiveNotifications();
             int N = activeNotifications.size();
             for (int i = 0; i < N; i++) {
-                NotificationData.Entry entry = activeNotifications.get(i);
+                NotificationEntry entry = activeNotifications.get(i);
                 String key = entry.notification.getKey();
                 boolean isVisible = mListContainer.isInVisibleLocation(entry);
                 NotificationVisibility visObj = NotificationVisibility.obtain(key, i, N, isVisible);
@@ -145,17 +151,47 @@
         }
     };
 
-    public NotificationLogger() {
+    @Inject
+    public NotificationLogger(NotificationListener notificationListener,
+            UiOffloadThread uiOffloadThread,
+            NotificationEntryManager entryManager,
+            StatusBarStateController statusBarStateController) {
+        mNotificationListener = notificationListener;
+        mUiOffloadThread = uiOffloadThread;
+        mEntryManager = entryManager;
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
         // Not expected to be destroyed, don't need to unsubscribe
-        Dependency.get(StatusBarStateController.class).addCallback(this);
+        statusBarStateController.addCallback(this);
+
+        entryManager.addNotificationEntryListener(new NotificationEntryListener() {
+            @Override
+            public void onEntryRemoved(
+                    NotificationEntry entry,
+                    NotificationVisibility visibility,
+                    boolean removedByUser) {
+                if (removedByUser && visibility != null) {
+                    logNotificationClear(entry.key, entry.notification, visibility);
+                }
+            }
+
+            @Override
+            public void onInflationError(
+                    StatusBarNotification notification,
+                    Exception exception) {
+                logNotificationError(notification, exception);
+            }
+        });
     }
 
     public void setUpWithContainer(NotificationListContainer listContainer) {
         mListContainer = listContainer;
     }
 
+    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+        mHeadsUpManager = headsUpManager;
+    }
+
     public void stopNotificationLogging() {
         // Report all notifications as invisible and turn down the
         // reporter.
@@ -185,6 +221,45 @@
         }
     }
 
+    private void logNotificationClear(String key, StatusBarNotification notification,
+            NotificationVisibility nv) {
+        final String pkg = notification.getPackageName();
+        final String tag = notification.getTag();
+        final int id = notification.getId();
+        final int userId = notification.getUserId();
+        try {
+            int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
+            if (mHeadsUpManager.isAlerting(key)) {
+                dismissalSurface = NotificationStats.DISMISSAL_PEEK;
+            } else if (mListContainer.hasPulsingNotifications()) {
+                dismissalSurface = NotificationStats.DISMISSAL_AOD;
+            }
+            int dismissalSentiment = NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+            mBarService.onNotificationClear(pkg, tag, id, userId, notification.getKey(),
+                    dismissalSurface,
+                    dismissalSentiment, nv);
+        } catch (RemoteException ex) {
+            // system process is dead if we're here.
+        }
+    }
+
+    private void logNotificationError(
+            StatusBarNotification notification,
+            Exception exception) {
+        try {
+            mBarService.onNotificationError(
+                    notification.getPackageName(),
+                    notification.getTag(),
+                    notification.getId(),
+                    notification.getUid(),
+                    notification.getInitialPid(),
+                    exception.getMessage(),
+                    notification.getUserId());
+        } catch (RemoteException ex) {
+            // The end is nigh.
+        }
+    }
+
     private void logNotificationVisibilityChanges(
             Collection<NotificationVisibility> newlyVisible,
             Collection<NotificationVisibility> noLongerVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 1d79152..8b0a682 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -800,7 +800,7 @@
     }
 
     @Override
-    public void performRemoveAnimation(long duration, long delay,
+    public long performRemoveAnimation(long duration, long delay,
             float translationDirection, boolean isHeadsUpAnimation, float endLocation,
             Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener) {
         enableAppearDrawing(true);
@@ -812,6 +812,7 @@
         } else if (onFinishedRunnable != null) {
             onFinishedRunnable.run();
         }
+        return 0;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 383446c..9e0dd84 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -86,9 +86,10 @@
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationCounters;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
@@ -198,7 +199,7 @@
     private ExpansionLogger mLogger;
     private String mLoggingKey;
     private NotificationGuts mGuts;
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
     private StatusBarNotification mStatusBarNotification;
     private String mAppName;
 
@@ -450,7 +451,7 @@
      *
      * @param entry the entry this row is tied to
      */
-    public void setEntry(@NonNull NotificationData.Entry entry) {
+    public void setEntry(@NonNull NotificationEntry entry) {
         mEntry = entry;
         mStatusBarNotification = entry.notification;
         cacheIsSystemNotification();
@@ -684,7 +685,7 @@
         return mStatusBarNotification;
     }
 
-    public NotificationData.Entry getEntry() {
+    public NotificationEntry getEntry() {
         return mEntry;
     }
 
@@ -1421,7 +1422,7 @@
 
     public void performDismiss(boolean fromAccessibility) {
         if (isOnlyChildInGroup()) {
-            NotificationData.Entry groupSummary =
+            NotificationEntry groupSummary =
                     mGroupManager.getLogicalGroupSummary(getStatusBarNotification());
             if (groupSummary.isClearable()) {
                 // If this is the only child in the group, dismiss the group, but don't try to show
@@ -1689,14 +1690,17 @@
         mPublicLayout.showAppOpsIcons(activeOps);
     }
 
-    /** Sets whether the notification being displayed audibly alerted the user. */
-    public void setAudiblyAlerted(boolean audiblyAlerted) {
+    /** Sets the last time the notification being displayed audibly alerted the user. */
+    public void setLastAudiblyAlertedMs(long lastAudiblyAlertedMs) {
         if (NotificationUtils.useNewInterruptionModel(mContext)) {
+            boolean recentlyAudiblyAlerted = System.currentTimeMillis() - lastAudiblyAlertedMs
+                    < NotificationEntryManager.RECENTLY_ALERTED_THRESHOLD_MS;
             if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() != null) {
-                mChildrenContainer.getHeaderView().setAudiblyAlerted(audiblyAlerted);
+                mChildrenContainer.getHeaderView().setRecentlyAudiblyAlerted(
+                        recentlyAudiblyAlerted);
             }
-            mPrivateLayout.setAudiblyAlerted(audiblyAlerted);
-            mPublicLayout.setAudiblyAlerted(audiblyAlerted);
+            mPrivateLayout.setRecentlyAudiblyAlerted(recentlyAudiblyAlerted);
+            mPublicLayout.setRecentlyAudiblyAlerted(recentlyAudiblyAlerted);
         }
     }
 
@@ -2534,7 +2538,7 @@
     /**
      * @return Whether this view is allowed to be dismissed. Only valid for visible notifications as
      *         otherwise some state might not be updated. To request about the general clearability
-     *         see {@link NotificationData.Entry#isClearable()}.
+     *         see {@link NotificationEntry#isClearable()}.
      */
     public boolean canViewBeDismissed() {
         return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
@@ -2608,6 +2612,29 @@
     }
 
     @Override
+    public long performRemoveAnimation(long duration, long delay, float translationDirection,
+            boolean isHeadsUpAnimation, float endLocation, Runnable onFinishedRunnable,
+            AnimatorListenerAdapter animationListener) {
+        if (mMenuRow.isMenuVisible()) {
+            Animator anim = getTranslateViewAnimator(0f, null /* listener */);
+            if (anim != null) {
+                anim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        ExpandableNotificationRow.super.performRemoveAnimation(
+                                duration, delay, translationDirection, isHeadsUpAnimation,
+                                endLocation, onFinishedRunnable, animationListener);
+                    }
+                });
+                anim.start();
+                return anim.getDuration();
+            }
+        }
+        return super.performRemoveAnimation(duration, delay, translationDirection,
+                isHeadsUpAnimation, endLocation, onFinishedRunnable, animationListener);
+    }
+
+    @Override
     protected void onAppearAnimationFinished(boolean wasAppearing) {
         super.onAppearAnimationFinished(wasAppearing);
         if (wasAppearing) {
@@ -2942,7 +2969,7 @@
     }
 
     public interface OnExpandClickListener {
-        void onExpandClicked(NotificationData.Entry clickedEntry, boolean nowExpanded);
+        void onExpandClicked(NotificationEntry clickedEntry, boolean nowExpanded);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index b1fa6a5..d1a89b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -312,16 +312,19 @@
      * @param duration The duration of the remove animation.
      * @param delay The delay of the animation
      * @param translationDirection The direction value from [-1 ... 1] indicating in which the
- *                             animation should be performed. A value of -1 means that The
- *                             remove animation should be performed upwards,
- *                             such that the  child appears to be going away to the top. 1
- *                             Should mean the opposite.
+     *                             animation should be performed. A value of -1 means that The
+     *                             remove animation should be performed upwards,
+     *                             such that the  child appears to be going away to the top. 1
+     *                             Should mean the opposite.
      * @param isHeadsUpAnimation Is this a headsUp animation.
      * @param endLocation The location where the horizonal heads up disappear animation should end.
      * @param onFinishedRunnable A runnable which should be run when the animation is finished.
      * @param animationListener An animation listener to add to the animation.
+     *
+     * @return The additional delay, in milliseconds, that this view needs to add before the
+     * animation starts.
      */
-    public abstract void performRemoveAnimation(long duration,
+    public abstract long performRemoveAnimation(long duration,
             long delay, float translationDirection, boolean isHeadsUpAnimation, float endLocation,
             Runnable onFinishedRunnable,
             AnimatorListenerAdapter animationListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
index 16796dd..607d96d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
@@ -34,10 +34,14 @@
 import java.util.HashSet;
 import java.util.Set;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Manager for the notification blocking helper - tracks and helps create the blocking helper
  * affordance.
  */
+@Singleton
 public class NotificationBlockingHelperManager {
     /** Enables debug logging and always makes the blocking helper show up after a dismiss. */
     private static final boolean DEBUG = false;
@@ -54,6 +58,7 @@
      */
     private boolean mIsShadeExpanded;
 
+    @Inject
     public NotificationBlockingHelperManager(Context context) {
         mContext = context;
         mNonBlockablePkgs = new HashSet<>();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index edd54ca..bf30cf9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -46,8 +46,8 @@
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.TransformableView;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationCustomViewWrapper;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -1231,7 +1231,7 @@
         updateAllSingleLineViews();
     }
 
-    public void onNotificationUpdated(NotificationData.Entry entry) {
+    public void onNotificationUpdated(NotificationEntry entry) {
         mStatusBarNotification = entry.notification;
         mOnContentViewInactiveListeners.clear();
         mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
@@ -1292,7 +1292,7 @@
         }
     }
 
-    private void applyRemoteInputAndSmartReply(final NotificationData.Entry entry) {
+    private void applyRemoteInputAndSmartReply(final NotificationEntry entry) {
         if (mRemoteInputController == null) {
             return;
         }
@@ -1313,7 +1313,7 @@
     @VisibleForTesting
     static SmartRepliesAndActions chooseSmartRepliesAndActions(
             SmartReplyConstants smartReplyConstants,
-            final NotificationData.Entry entry) {
+            final NotificationEntry entry) {
         boolean enableAppGeneratedSmartReplies = (smartReplyConstants.isEnabled()
                 && (!smartReplyConstants.requiresTargetingP()
                 || entry.targetSdk >= Build.VERSION_CODES.P));
@@ -1370,7 +1370,7 @@
                 smartReplies, smartActions, freeformRemoteInputActionPair != null);
     }
 
-    private void applyRemoteInput(NotificationData.Entry entry, boolean hasFreeformRemoteInput) {
+    private void applyRemoteInput(NotificationEntry entry, boolean hasFreeformRemoteInput) {
         View bigContentView = mExpandedChild;
         if (bigContentView != null) {
             mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasFreeformRemoteInput,
@@ -1402,7 +1402,7 @@
         mCachedHeadsUpRemoteInput = null;
     }
 
-    private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry,
+    private RemoteInputView applyRemoteInput(View view, NotificationEntry entry,
             boolean hasRemoteInput, PendingIntent existingPendingIntent,
             RemoteInputView cachedView, NotificationViewWrapper wrapper) {
         View actionContainerCandidate = view.findViewById(
@@ -1470,13 +1470,23 @@
     }
 
     private void applySmartReplyView(SmartRepliesAndActions smartRepliesAndActions,
-            NotificationData.Entry entry) {
+            NotificationEntry entry) {
         if (mExpandedChild != null) {
             mExpandedSmartReplyView =
                     applySmartReplyView(mExpandedChild, smartRepliesAndActions, entry);
-            if (mExpandedSmartReplyView != null && smartRepliesAndActions.smartReplies != null) {
-                mSmartReplyController.smartRepliesAdded(
-                        entry, smartRepliesAndActions.smartReplies.choices.length);
+            if (mExpandedSmartReplyView != null) {
+                if (smartRepliesAndActions.smartReplies != null
+                        || smartRepliesAndActions.smartActions != null) {
+                    int numSmartReplies = smartRepliesAndActions.smartReplies == null
+                            ? 0 : smartRepliesAndActions.smartReplies.choices.length;
+                    int numSmartActions = smartRepliesAndActions.smartActions == null
+                            ? 0 : smartRepliesAndActions.smartActions.actions.size();
+                    boolean fromAssistant = smartRepliesAndActions.smartReplies == null
+                            ? smartRepliesAndActions.smartActions.fromAssistant
+                            : smartRepliesAndActions.smartReplies.fromAssistant;
+                    mSmartReplyController.smartSuggestionsAdded(entry, numSmartReplies,
+                            numSmartActions, fromAssistant);
+                }
             }
         }
         if (mHeadsUpChild != null) {
@@ -1486,7 +1496,7 @@
     }
 
     private SmartReplyView applySmartReplyView(View view,
-            SmartRepliesAndActions smartRepliesAndActions, NotificationData.Entry entry) {
+            SmartRepliesAndActions smartRepliesAndActions, NotificationEntry entry) {
         View smartReplyContainerCandidate = view.findViewById(
                 com.android.internal.R.id.smart_reply_container);
         if (!(smartReplyContainerCandidate instanceof LinearLayout)) {
@@ -1615,15 +1625,15 @@
     }
 
     /** Sets whether the notification being displayed audibly alerted the user. */
-    public void setAudiblyAlerted(boolean audiblyAlerted) {
+    public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) {
         if (mContractedChild != null && mContractedWrapper.getNotificationHeader() != null) {
-            mContractedWrapper.getNotificationHeader().setAudiblyAlerted(audiblyAlerted);
+            mContractedWrapper.getNotificationHeader().setRecentlyAudiblyAlerted(audiblyAlerted);
         }
         if (mExpandedChild != null && mExpandedWrapper.getNotificationHeader() != null) {
-            mExpandedWrapper.getNotificationHeader().setAudiblyAlerted(audiblyAlerted);
+            mExpandedWrapper.getNotificationHeader().setRecentlyAudiblyAlerted(audiblyAlerted);
         }
         if (mHeadsUpChild != null && mHeadsUpWrapper.getNotificationHeader() != null) {
-            mHeadsUpWrapper.getNotificationHeader().setAudiblyAlerted(audiblyAlerted);
+            mHeadsUpWrapper.getNotificationHeader().setRecentlyAudiblyAlerted(audiblyAlerted);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 2e45527..bd1dfb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -48,7 +48,7 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -57,10 +57,14 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
  * closing guts, and keeping track of the currently exposed notification guts.
  */
+@Singleton
 public class NotificationGutsManager implements Dumpable, NotificationLifetimeExtender {
     private static final String TAG = "NotificationGutsManager";
 
@@ -91,6 +95,7 @@
     @VisibleForTesting
     protected String mKeyToRemoveOnGutsClosed;
 
+    @Inject
     public NotificationGutsManager(Context context) {
         mContext = context;
         mAccessibilityManager = (AccessibilityManager)
@@ -111,7 +116,7 @@
         mNotificationActivityStarter = notificationActivityStarter;
     }
 
-    public void onDensityOrFontScaleChanged(NotificationData.Entry entry) {
+    public void onDensityOrFontScaleChanged(NotificationEntry entry) {
         setExposedGuts(entry.getGuts());
         bindGuts(entry.getRow());
     }
@@ -424,7 +429,7 @@
     }
 
     @Override
-    public boolean shouldExtendLifetime(NotificationData.Entry entry) {
+    public boolean shouldExtendLifetime(NotificationEntry entry) {
         return entry != null
                 &&(mNotificationGutsExposed != null
                     && entry.getGuts() != null
@@ -433,7 +438,7 @@
     }
 
     @Override
-    public void setShouldManageLifetime(NotificationData.Entry entry, boolean shouldExtend) {
+    public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) {
         if (shouldExtend) {
             mKeyToRemoveOnGutsClosed = entry.key;
             if (Log.isLoggable(TAG, Log.DEBUG)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
index 9908049..42ebfce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
@@ -37,7 +37,7 @@
 import com.android.systemui.statusbar.InflationTask;
 import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.MediaNotificationProcessor;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.util.Assert;
@@ -614,7 +614,7 @@
             @Nullable InflationCallback endListener, ExpandableNotificationRow row,
             boolean redactAmbient) {
         Assert.isMainThread();
-        NotificationData.Entry entry = row.getEntry();
+        NotificationEntry entry = row.getEntry();
         NotificationContentView privateLayout = row.getPrivateLayout();
         NotificationContentView publicLayout = row.getPublicLayout();
         if (runningInflations.isEmpty()) {
@@ -724,7 +724,7 @@
          * @param entry the entry with the content views set
          * @param inflatedFlags the flags associated with the content views that were inflated
          */
-        void onAsyncInflationFinished(NotificationData.Entry entry,
+        void onAsyncInflationFinished(NotificationEntry entry,
                 @InflationFlag int inflatedFlags);
 
         /**
@@ -782,7 +782,7 @@
             mRedactAmbient = redactAmbient;
             mRemoteViewClickHandler = remoteViewClickHandler;
             mCallback = callback;
-            NotificationData.Entry entry = row.getEntry();
+            NotificationEntry entry = row.getEntry();
             entry.setInflationTask(this);
         }
 
@@ -857,7 +857,7 @@
         }
 
         @Override
-        public void onAsyncInflationFinished(NotificationData.Entry entry,
+        public void onAsyncInflationFinished(NotificationEntry entry,
                 @InflationFlag int inflatedFlags) {
             mRow.getEntry().onInflationTaskFinished();
             mRow.onNotificationUpdated();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 5329541..b1eab80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -40,6 +40,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Drawable;
+import android.metrics.LogMaker;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.service.notification.StatusBarNotification;
@@ -150,6 +151,7 @@
         // Reset exit counter that we'll log and record an undo event separately (not an exit event)
         mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
         logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_UNDO);
+        mMetricsLogger.write(importanceChangeLogMaker().setType(MetricsEvent.TYPE_DISMISS));
         swapContent(ACTION_UNDO, true /* animate */);
     };
 
@@ -381,6 +383,17 @@
         }
     }
 
+    /**
+     * Returns an initialized LogMaker for logging importance changes.
+     * The caller may override the type (to DISMISS) before passing it to mMetricsLogger.
+     * @return new LogMaker
+     */
+    private LogMaker importanceChangeLogMaker() {
+        return new LogMaker(MetricsEvent.ACTION_SAVE_IMPORTANCE)
+                .setType(MetricsEvent.TYPE_ACTION)
+                .setSubtype(mChosenImportance - mStartingChannelImportance);
+    }
+
     private boolean hasImportanceChanged() {
         return mSingleNotificationChannel != null
                 && mStartingChannelImportance != mChosenImportance;
@@ -397,8 +410,7 @@
      * Commits the updated importance values on the background thread.
      */
     private void updateImportance() {
-        MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
-                mChosenImportance - mStartingChannelImportance);
+        mMetricsLogger.write(importanceChangeLogMaker());
 
         Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
         bgHandler.post(new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
index 8c8bad2..a5411ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
@@ -87,8 +87,8 @@
 
             try {
                 drawable = mResolver.resolveImage(target);
-            } catch (IOException ex) {
-                Log.d(TAG, "PreloadImageTask: Resolve failed from " + target);
+            } catch (IOException | SecurityException ex) {
+                Log.d(TAG, "PreloadImageTask: Resolve failed from " + target, ex);
             }
 
             return drawable;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
index 588246f..a3e1305 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
@@ -81,8 +81,8 @@
         Drawable result = null;
         try {
             result = hasCache() ? mImageCache.get(uri) : resolveImage(uri);
-        } catch (IOException ex) {
-            Log.d(TAG, "loadImage: Can't load image from " + uri);
+        } catch (IOException | SecurityException ex) {
+            Log.d(TAG, "loadImage: Can't load image from " + uri, ex);
         }
         return result;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
index 1741a0b..0160c547 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
@@ -25,7 +25,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 /**
  * An inflater task that asynchronously inflates a ExpandableNotificationRow
@@ -36,14 +36,14 @@
     private static final boolean TRACE_ORIGIN = true;
 
     private RowInflationFinishedListener mListener;
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
     private boolean mCancelled;
     private Throwable mInflateOrigin;
 
     /**
      * Inflates a new notificationView. This should not be called twice on this object
      */
-    public void inflate(Context context, ViewGroup parent, NotificationData.Entry entry,
+    public void inflate(Context context, ViewGroup parent, NotificationEntry entry,
             RowInflationFinishedListener listener) {
         if (TRACE_ORIGIN) {
             mInflateOrigin = new Throwable("inflate requested here");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index 1b40c06..eaa2eaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -190,12 +190,13 @@
     }
 
     @Override
-    public void performRemoveAnimation(long duration, long delay,
+    public long performRemoveAnimation(long duration, long delay,
             float translationDirection, boolean isHeadsUpAnimation, float endLocation,
             Runnable onFinishedRunnable,
             AnimatorListenerAdapter animationListener) {
         // TODO: Use duration
         setContentVisible(false);
+        return 0;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 670908f..cbec37e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -25,7 +25,7 @@
 import com.android.systemui.statusbar.AmbientPulseManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -358,7 +358,7 @@
         mPulsing = hasPulsing;
     }
 
-    public boolean isPulsing(NotificationData.Entry entry) {
+    public boolean isPulsing(NotificationEntry entry) {
         if (!mPulsing || mAmbientPulseManager == null) {
             return false;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
index f0a2653..f771be0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -16,15 +16,14 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
-        .ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
 
 import android.view.View;
 import android.view.ViewGroup;
 
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -113,19 +112,12 @@
     void setMaxDisplayedNotifications(int maxNotifications);
 
     /**
-     * Handle snapping a non-dismissable row back if the user tried to dismiss it.
-     *
-     * @param entry the entry whose row needs to snap back
-     */
-    void snapViewIfNeeded(NotificationData.Entry entry);
-
-    /**
      * Get the view parent for a notification entry. For example, NotificationStackScrollLayout.
      *
      * @param entry entry to get the view parent for
      * @return the view parent for entry
      */
-    ViewGroup getViewParentForNotification(NotificationData.Entry entry);
+    ViewGroup getViewParentForNotification(NotificationEntry entry);
 
     /**
      * Resets the currently exposed menu view.
@@ -148,7 +140,7 @@
      *
      * @param entry the entry whose view's view state needs to be cleaned up (say that 5 times fast)
      */
-    void cleanUpViewStateForEntry(NotificationData.Entry entry);
+    void cleanUpViewStateForEntry(NotificationEntry entry);
 
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index 4f0831f1..9418601 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -18,7 +18,7 @@
 
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.NUM_SECTIONS;
 
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -49,12 +49,12 @@
     }
 
     @Override
-    public void onHeadsUpPinned(NotificationData.Entry headsUp) {
+    public void onHeadsUpPinned(NotificationEntry headsUp) {
         updateView(headsUp.getRow(), false /* animate */);
     }
 
     @Override
-    public void onHeadsUpUnPinned(NotificationData.Entry headsUp) {
+    public void onHeadsUpUnPinned(NotificationEntry headsUp) {
         updateView(headsUp.getRow(), true /* animate */);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index dbe6e8e..e4b00dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -16,11 +16,11 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
-        .ExpandAnimationParameters;
-import static com.android.systemui.statusbar.notification.stack.StackStateAnimator
-        .ANIMATION_DURATION_SWIPE;
+import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
+import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
 import static com.android.systemui.statusbar.phone.NotificationIconAreaController.LOW_PRIORITY;
+import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -96,12 +96,13 @@
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.statusbar.notification.FakeShadowView;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.ShadeViewRefactor;
 import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -138,6 +139,9 @@
 import java.util.List;
 import java.util.function.BiConsumer;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+
 /**
  * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
  */
@@ -167,6 +171,7 @@
     private final Paint mBackgroundPaint = new Paint();
     private final boolean mShouldDrawNotificationBackground;
     private boolean mLowPriorityBeforeSpeedBump;
+    private final boolean mAllowLongPress;
 
     private float mExpandedHeight;
     private int mOwnScrollY;
@@ -454,27 +459,16 @@
     private final NotificationGutsManager
             mNotificationGutsManager = Dependency.get(NotificationGutsManager.class);
 
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public NotificationStackScrollLayout(Context context) {
-        this(context, null);
-    }
-
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
+    @Inject
+    public NotificationStackScrollLayout(
+            @Named(VIEW_CONTEXT) Context context,
+            AttributeSet attrs,
+            @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress) {
+        super(context, attrs, 0, 0);
         Resources res = getResources();
 
+        mAllowLongPress = allowLongPress;
+
         for (int i = 0; i < NUM_SECTIONS; i++) {
             mSections[i] = new NotificationSection(this);
         }
@@ -524,6 +518,17 @@
                 mLowPriorityBeforeSpeedBump = "1".equals(newValue);
             }
         }, LOW_PRIORITY);
+
+        mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
+            @Override
+            public void onEntryUpdated(NotificationEntry entry) {
+                if (!entry.notification.isClearable()) {
+                    // The user may have performed a dismiss action on the notification, since it's
+                    // not clearable we should snap it back.
+                    snapViewIfNeeded(entry);
+                }
+            }
+        });
     }
 
     @Override
@@ -534,7 +539,9 @@
         inflateEmptyShadeView();
         inflateFooterView();
         mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation);
-        setLongPressListener(mEntryManager.getNotificationLongClicker());
+        if (mAllowLongPress) {
+            setLongPressListener(mGutsManager::openGuts);
+        }
     }
 
     @Override
@@ -595,14 +602,14 @@
   @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
   public RemoteInputController.Delegate createDelegate() {
         return new RemoteInputController.Delegate() {
-            public void setRemoteInputActive(NotificationData.Entry entry,
+            public void setRemoteInputActive(NotificationEntry entry,
                     boolean remoteInputActive) {
                 mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
                 entry.notifyHeightChanged(true /* needsAnimation */);
                 updateFooter();
             }
 
-            public void lockScrollTo(NotificationData.Entry entry) {
+            public void lockScrollTo(NotificationEntry entry) {
                 NotificationStackScrollLayout.this.lockScrollTo(entry.getRow());
             }
 
@@ -651,6 +658,8 @@
                 < mSections[NUM_SECTIONS - 1].getCurrentBounds().bottom
                 || mAmbientState.isDark())) {
             drawBackground(canvas);
+        } else if (mInHeadsUpPinnedMode || mHeadsUpAnimatingAway) {
+            drawHeadsUpBackground(canvas);
         }
 
         if (DEBUG) {
@@ -749,6 +758,32 @@
                 mCornerRadius, mCornerRadius, mBackgroundPaint);
     }
 
+    private void drawHeadsUpBackground(Canvas canvas) {
+        int left = mSidePaddings;
+        int right = getWidth() - mSidePaddings;
+
+        float top = getHeight();
+        float bottom = 0;
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            if (child.getVisibility() != View.GONE
+                    && child instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+                if ((row.isPinned() || row.isHeadsUpAnimatingAway()) && row.getTranslation() < 0) {
+                    top = Math.min(top, row.getTranslationY());
+                    bottom = Math.max(bottom, row.getTranslationY() + row.getActualHeight());
+                }
+            }
+        }
+
+        if (top < bottom) {
+            canvas.drawRoundRect(
+                    left, top, right, bottom,
+                    mCornerRadius, mCornerRadius, mBackgroundPaint);
+        }
+    }
+
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void updateBackgroundDimming() {
         // No need to update the background color if it's not being drawn.
@@ -887,7 +922,7 @@
 
     @Override
     @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
-    public boolean isInVisibleLocation(NotificationData.Entry entry) {
+    public boolean isInVisibleLocation(NotificationEntry entry) {
         ExpandableNotificationRow row = entry.getRow();
         ExpandableViewState childViewState = row.getViewState();
 
@@ -1201,13 +1236,13 @@
      */
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private int getTopHeadsUpPinnedHeight() {
-        NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
+        NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
         if (topEntry == null) {
             return 0;
         }
         ExpandableNotificationRow row = topEntry.getRow();
         if (row.isChildInGroup()) {
-            final NotificationData.Entry groupSummary
+            final NotificationEntry groupSummary
                     = mGroupManager.getGroupSummary(row.getStatusBarNotification());
             if (groupSummary != null) {
                 row = groupSummary.getRow();
@@ -1382,7 +1417,7 @@
                     && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
                 if (slidingChild instanceof ExpandableNotificationRow) {
                     ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
-                    NotificationData.Entry entry = row.getEntry();
+                    NotificationEntry entry = row.getEntry();
                     if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
                             && mHeadsUpManager.getTopEntry().getRow() != row
                             && mGroupManager.getGroupSummary(
@@ -1515,9 +1550,8 @@
                 true /* isDismissAll */);
     }
 
-    @Override
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
-    public void snapViewIfNeeded(NotificationData.Entry entry) {
+    private void snapViewIfNeeded(NotificationEntry entry) {
         ExpandableNotificationRow child = entry.getRow();
         boolean animate = mIsExpanded || isPinnedHeadsUp(child);
         // If the child is showing the notification menu snap to that
@@ -1527,7 +1561,7 @@
 
     @Override
     @ShadeViewRefactor(RefactorComponent.ADAPTER)
-    public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
+    public ViewGroup getViewParentForNotification(NotificationEntry entry) {
         return this;
     }
 
@@ -2030,7 +2064,7 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    private boolean isPulsing(NotificationData.Entry entry) {
+    private boolean isPulsing(NotificationEntry entry) {
         return mAmbientState.isPulsing(entry);
     }
 
@@ -2157,19 +2191,7 @@
             }
             return;
         }
-        NotificationSection firstSection = getFirstVisibleSection();
-        int top = 0;
-        if (firstSection != null) {
-            ActivatableNotificationView firstView = firstSection.getFirstVisibleChild();
-            // Round Y up to avoid seeing the background during animation
-            int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
-            if (mAnimateNextBackgroundTop || firstSection.isTargetTop(finalTranslationY)) {
-                // we're ending up at the same location as we are now, lets just skip the animation
-                top = finalTranslationY;
-            } else {
-                top = (int) Math.ceil(firstView.getTranslationY());
-            }
-        }
+        int top = getSectionTopOrFinalTop(getFirstVisibleSection(), mAnimateNextBackgroundTop);
         NotificationSection lastSection = getLastVisibleSection();
         ActivatableNotificationView lastView =
                 mShelf.hasItemsInStableShelf() && mShelf.getVisibility() != GONE
@@ -2177,21 +2199,8 @@
                         : lastSection == null ? null : lastSection.getLastVisibleChild();
         int bottom;
         if (lastView != null) {
-            int finalTranslationY;
-            if (lastView == mShelf) {
-                finalTranslationY = (int) mShelf.getTranslationY();
-            } else {
-                finalTranslationY = (int) ViewState.getFinalTranslationY(lastView);
-            }
-            int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
-            int finalBottom = finalTranslationY + finalHeight - lastView.getClipBottomAmount();
-            if (mAnimateNextBackgroundBottom || lastSection.isTargetBottom(finalBottom)) {
-                // we're ending up at the same location as we are now, lets just skip the animation
-                bottom = finalBottom;
-            } else {
-                bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
-                        - lastView.getClipBottomAmount());
-            }
+            bottom = getSectionBottomOrFinalBottom(
+                    lastSection, lastView, mAnimateNextBackgroundBottom);
         } else {
             top = mTopPadding;
             bottom = top;
@@ -2207,6 +2216,57 @@
         setSectionBoundsByPriority(left, right, top, bottom, mSections[0], mSections[1]);
     }
 
+    private int getSectionTopOrFinalTop(
+            @Nullable NotificationSection section, boolean alreadyAnimating) {
+        int top = 0;
+        if (section != null) {
+            ActivatableNotificationView firstView = section.getFirstVisibleChild();
+            // Round Y up to avoid seeing the background during animation
+            int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
+            if (alreadyAnimating || section.isTargetTop(finalTranslationY)) {
+                // we're ending up at the same location as we are now, let's just skip the animation
+                top = finalTranslationY;
+            } else {
+                top = (int) Math.ceil(firstView.getTranslationY());
+            }
+        }
+        return top;
+    }
+
+    private int getSectionBottomOrFinalBottom(
+            @Nullable NotificationSection section, boolean alreadyAnimating) {
+        return section == null ? 0
+                : getSectionBottomOrFinalBottom(
+                        section, section.getLastVisibleChild(), alreadyAnimating);
+    }
+
+    private int getSectionBottomOrFinalBottom(
+            NotificationSection section,
+            ActivatableNotificationView lastView,
+            boolean alreadyAnimating) {
+        int bottom = 0;
+        if (lastView != null) {
+            float finalTranslationY;
+            if (lastView == mShelf) {
+                finalTranslationY = mShelf.getTranslationY();
+            } else {
+                finalTranslationY = ViewState.getFinalTranslationY(lastView);
+            }
+            int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
+            // Round Y down to avoid seeing the background during animation
+            int finalBottom = (int) Math.floor(
+                    finalTranslationY + finalHeight - lastView.getClipBottomAmount());
+            if (alreadyAnimating || section.isTargetBottom(finalBottom)) {
+                // we're ending up at the same location as we are now, lets just skip the animation
+                bottom = finalBottom;
+            } else {
+                bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
+                        - lastView.getClipBottomAmount());
+            }
+        }
+        return bottom;
+    }
+
     private void setSectionBoundsByPriority(int left, int right, int top, int bottom,
             NotificationSection highPrioritySection, NotificationSection lowPrioritySection) {
         if (NotificationUtils.useNewInterruptionModel(mContext)) {
@@ -2214,13 +2274,14 @@
             ActivatableNotificationView lastChildAboveGap = getLastHighPriorityChild();
             ActivatableNotificationView firstChildBelowGap = getFirstLowPriorityChild();
             if (lastChildAboveGap != null && firstChildBelowGap != null) {
-                int gapTop =
-                        (int) Math.max(top,
-                                Math.min(lastChildAboveGap.getTranslationY()
-                                                + lastChildAboveGap.getActualHeight(),
-                                        bottom));
-                int gapBottom = (int) Math.max(top,
-                        Math.min(firstChildBelowGap.getTranslationY(), bottom));
+                int gapTop = getSectionBottomOrFinalBottom(
+                        highPrioritySection, mAnimateNextSectionBoundsChange);
+                gapTop = Math.max(top, Math.min(gapTop, bottom));
+
+                int gapBottom = getSectionTopOrFinalTop(
+                        lowPrioritySection, mAnimateNextSectionBoundsChange);
+                gapBottom = Math.max(top, Math.min(gapBottom, bottom));
+
                 highPrioritySection.getBounds().set(left, top, right, gapTop);
                 lowPrioritySection.getBounds().set(left, gapBottom, right, bottom);
             } else if (lastChildAboveGap != null) {
@@ -2508,7 +2569,7 @@
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     @Override
-    public void cleanUpViewStateForEntry(NotificationData.Entry entry) {
+    public void cleanUpViewStateForEntry(NotificationEntry entry) {
         View child = entry.getRow();
         if (child == mSwipeHelper.getTranslatingParentView()) {
             mSwipeHelper.clearTranslatingParentView();
@@ -2636,7 +2697,7 @@
     private boolean isChildInInvisibleGroup(View child) {
         if (child instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-            NotificationData.Entry groupSummary =
+            NotificationEntry groupSummary =
                     mGroupManager.getGroupSummary(row.getStatusBarNotification());
             if (groupSummary != null && groupSummary.getRow() != row) {
                 return row.getVisibility() == View.INVISIBLE;
@@ -4655,7 +4716,7 @@
         mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
     }
 
-    public void generateHeadsUpAnimation(NotificationData.Entry entry, boolean isHeadsUp) {
+    public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
         ExpandableNotificationRow row = entry.getHeadsUpAnimationView();
         generateHeadsUpAnimation(row, isHeadsUp);
     }
@@ -5574,15 +5635,21 @@
             if (translatingParentView != null && row == translatingParentView) {
                 mSwipeHelper.clearExposedMenuView();
                 mSwipeHelper.clearTranslatingParentView();
+                if (row instanceof ExpandableNotificationRow) {
+                    mHeadsUpManager.setMenuShown(
+                            ((ExpandableNotificationRow) row).getEntry(), false);
+
+                }
             }
         }
 
         @Override
         public void onMenuShown(View row) {
             if (row instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row;
                 MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
-                        ((ExpandableNotificationRow) row).getStatusBarNotification()
-                                .getPackageName());
+                        notificationRow.getStatusBarNotification().getPackageName());
+                mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true);
             }
             mSwipeHelper.onMenuShown(row);
         }
@@ -5764,7 +5831,7 @@
                         (int) (dragLengthY / mDisplayMetrics.density),
                         0 /* velocityDp - N/A */);
 
-                if (mNotificationPanel.onDraggedDown() || startingChild != null) {
+                if (!mAmbientState.isDark() || startingChild != null) {
                     // We have notifications, go to locked shade.
                     mShadeController.goToLockedShade(startingChild);
                     if (startingChild instanceof ExpandableNotificationRow) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index f1d9549..975aee5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -189,10 +189,13 @@
         boolean isFastNonDismissGesture =
                 gestureFastEnough && !gestureTowardsMenu && !isDismissGesture;
         boolean isMenuRevealingGestureAwayFromMenu = slowSwipedFarEnough || isFastNonDismissGesture;
-        if (isNonDismissGestureTowardsMenu
-                || (!isFalseGesture(ev) && isMenuRevealingGestureAwayFromMenu)) {
+        int menuSnapTarget = menuRow.getMenuSnapTarget();
+        boolean isNonFalseMenuRevealingGesture =
+                !isFalseGesture(ev) && isMenuRevealingGestureAwayFromMenu;
+        if ((isNonDismissGestureTowardsMenu || isNonFalseMenuRevealingGesture)
+                && menuSnapTarget != 0) {
             // Menu has not been snapped to previously and this is menu revealing gesture
-            snapOpen(animView, menuRow.getMenuSnapTarget(), velocity);
+            snapOpen(animView, menuSnapTarget, velocity);
             menuRow.onSnapOpen();
         } else if (isDismissGesture(ev) && !gestureTowardsMenu) {
             dismiss(animView, velocity);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index d690547..19fce48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -406,13 +406,8 @@
 
                 }
                 changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
-                        0 /* delay */, translationDirection,  false /* isHeadsUpAppear */,
-                        0, new Runnable() {
-                    @Override
-                    public void run() {
-                        removeTransientView(changingView);
-                    }
-                }, null);
+                        0 /* delay */, translationDirection, false /* isHeadsUpAppear */,
+                        0, () -> removeTransientView(changingView), null);
             } else if (event.animationType ==
                 NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
                 if (Math.abs(changingView.getTranslation()) == changingView.getWidth()
@@ -507,10 +502,11 @@
                     // We need to add the global animation listener, since once no animations are
                     // running anymore, the panel will instantly hide itself. We need to wait until
                     // the animation is fully finished for this though.
-                    changingView.performRemoveAnimation(ANIMATION_DURATION_HEADS_UP_DISAPPEAR
-                                    + ANIMATION_DELAY_HEADS_UP, extraDelay, 0.0f,
-                            true /* isHeadsUpAppear */, targetLocation, endRunnable,
-                            getGlobalAnimationFinishedListener());
+                    long removeAnimationDelay = changingView.performRemoveAnimation(
+                            ANIMATION_DURATION_HEADS_UP_DISAPPEAR + ANIMATION_DELAY_HEADS_UP,
+                            extraDelay, 0.0f, true /* isHeadsUpAppear */, targetLocation,
+                            endRunnable, getGlobalAnimationFinishedListener());
+                    mAnimationProperties.delay += removeAnimationDelay;
                 } else if (endRunnable != null) {
                     endRunnable.run();
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
new file mode 100644
index 0000000..b9425d4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IWindowManager;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/** A controller to control all auto-hide things. */
+public class AutoHideController implements CommandQueue.Callbacks {
+    private static final String TAG = "AutoHideController";
+
+    private final IWindowManager mWindowManagerService;
+
+    private final Handler mHandler;
+    private final NotificationRemoteInputManager mRemoteInputManager;
+    private final CommandQueue mCommandQueue;
+    private StatusBar mStatusBar;
+    private NavigationBarFragment mNavigationBar;
+
+    private int mDisplayId;
+    private int mSystemUiVisibility;
+    // last value sent to window manager
+    private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE;
+
+    private boolean mAutoHideSuspended;
+
+    private static final long AUTOHIDE_TIMEOUT_MS = 2250;
+
+    private final Runnable mAutoHide = () -> {
+        int requested = mSystemUiVisibility & ~getTransientMask();
+        if (mSystemUiVisibility != requested) {
+            notifySystemUiVisibilityChanged(requested);
+        }
+    };
+
+    @Inject
+    public AutoHideController(Context context, @Named(MAIN_HANDLER_NAME) Handler handler) {
+        mCommandQueue = SysUiServiceProvider.getComponent(context, CommandQueue.class);
+        mCommandQueue.addCallback(this);
+        mHandler = handler;
+        mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
+        mWindowManagerService = Dependency.get(IWindowManager.class);
+
+        mDisplayId = context.getDisplayId();
+    }
+
+    void setStatusBar(StatusBar statusBar) {
+        mStatusBar = statusBar;
+    }
+
+    void setNavigationBar(NavigationBarFragment navigationBar) {
+        mNavigationBar = navigationBar;
+    }
+
+    @Override
+    public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
+            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+        if (displayId != mDisplayId) {
+            return;
+        }
+        int oldVal = mSystemUiVisibility;
+        int newVal = (oldVal & ~mask) | (vis & mask);
+        int diff = newVal ^ oldVal;
+
+        if (diff != 0) {
+            mSystemUiVisibility = newVal;
+
+            // ready to unhide
+            if (hasStatusBar() && (vis & View.STATUS_BAR_UNHIDE) != 0) {
+                mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
+            }
+
+            if (hasNavigationBar() && (vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
+                mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
+            }
+
+            // Re-send setSystemUiVisibility to update un-hide status.
+            if (mSystemUiVisibility != newVal) {
+                mCommandQueue.setSystemUiVisibility(mDisplayId, mSystemUiVisibility,
+                        fullscreenStackVis, dockedStackVis, mask, fullscreenStackBounds,
+                        dockedStackBounds);
+            }
+
+            notifySystemUiVisibilityChanged(mSystemUiVisibility);
+        }
+    }
+
+    private void notifySystemUiVisibilityChanged(int vis) {
+        try {
+            if (mLastDispatchedSystemUiVisibility != vis) {
+                mWindowManagerService.statusBarVisibilityChanged(mDisplayId, vis);
+                mLastDispatchedSystemUiVisibility = vis;
+            }
+        } catch (RemoteException ex) {
+            Log.w(TAG, "Cannot get WindowManager");
+        }
+    }
+
+    void resumeSuspendedAutoHide() {
+        if (mAutoHideSuspended) {
+            scheduleAutoHide();
+            Runnable checkBarModesRunnable = getCheckBarModesRunnable();
+            if (checkBarModesRunnable != null) {
+                mHandler.postDelayed(checkBarModesRunnable, 500); // longer than home -> launcher
+            }
+        }
+    }
+
+    void suspendAutoHide() {
+        mHandler.removeCallbacks(mAutoHide);
+        Runnable checkBarModesRunnable = getCheckBarModesRunnable();
+        if (checkBarModesRunnable != null) {
+            mHandler.removeCallbacks(checkBarModesRunnable);
+        }
+        mAutoHideSuspended = (mSystemUiVisibility & getTransientMask()) != 0;
+    }
+
+    void touchAutoHide() {
+        // update transient bar auto hide
+        if ((hasStatusBar() && mStatusBar.getStatusBarMode() == MODE_SEMI_TRANSPARENT)
+                || hasNavigationBar() && mNavigationBar.isSemiTransparent()) {
+            scheduleAutoHide();
+        } else {
+            cancelAutoHide();
+        }
+    }
+
+    private Runnable getCheckBarModesRunnable() {
+        if (hasStatusBar()) {
+            return () -> mStatusBar.checkBarModes();
+        } else if (hasNavigationBar()) {
+            return () -> mNavigationBar.checkNavBarModes();
+        } else {
+            return null;
+        }
+    }
+
+    private void cancelAutoHide() {
+        mAutoHideSuspended = false;
+        mHandler.removeCallbacks(mAutoHide);
+    }
+
+    private void scheduleAutoHide() {
+        cancelAutoHide();
+        mHandler.postDelayed(mAutoHide, AUTOHIDE_TIMEOUT_MS);
+    }
+
+    void checkUserAutoHide(MotionEvent event) {
+        boolean shouldAutoHide =
+                (mSystemUiVisibility & getTransientMask()) != 0  // a transient bar is revealed.
+                && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar.
+                && event.getX() == 0 && event.getY() == 0;
+        if (hasStatusBar()) {
+            // a touch outside both bars
+            shouldAutoHide &= !mRemoteInputManager.getController().isRemoteInputActive();
+        }
+        if (shouldAutoHide) {
+            userAutoHide();
+        }
+    }
+
+    private void userAutoHide() {
+        cancelAutoHide();
+        mHandler.postDelayed(mAutoHide, 350); // longer than app gesture -> flag clear
+    }
+
+    private int getTransientMask() {
+        int mask = 0;
+        if (hasStatusBar()) {
+            mask |= View.STATUS_BAR_TRANSIENT;
+        }
+        if (hasNavigationBar()) {
+            mask |= View.NAVIGATION_BAR_TRANSIENT;
+        }
+        return mask;
+    }
+
+    private boolean hasNavigationBar() {
+        return mNavigationBar != null;
+    }
+
+    private boolean hasStatusBar() {
+        return mStatusBar != null;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 1d7e899..fac4dbb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -30,6 +30,9 @@
 import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.statusbar.policy.HotspotController.Callback;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+
 /**
  * Manages which tiles should be automatically added to QS.
  */
@@ -44,24 +47,31 @@
     private final QSTileHost mHost;
     private final Handler mHandler;
     private final AutoAddTracker mAutoTracker;
+    private final HotspotController mHotspotController;
+    private final DataSaverController mDataSaverController;
+    private final ManagedProfileController mManagedProfileController;
+    private final ColorDisplayController mColorDisplayController;
 
-    public AutoTileManager(Context context, QSTileHost host) {
-        this(context, new AutoAddTracker(context), host,
-            new Handler(Dependency.get(Dependency.BG_LOOPER)));
-    }
-
-    @VisibleForTesting
-    AutoTileManager(Context context, AutoAddTracker autoAddTracker, QSTileHost host,
-            Handler handler) {
+    @Inject
+    public AutoTileManager(Context context, AutoAddTracker autoAddTracker, QSTileHost host,
+            @Named(Dependency.BG_HANDLER_NAME) Handler handler,
+            HotspotController hotspotController,
+            DataSaverController dataSaverController,
+            ManagedProfileController managedProfileController,
+            ColorDisplayController colorDisplayController) {
         mAutoTracker = autoAddTracker;
         mContext = context;
         mHost = host;
         mHandler = handler;
+        mHotspotController = hotspotController;
+        mDataSaverController = dataSaverController;
+        mManagedProfileController = managedProfileController;
+        mColorDisplayController = colorDisplayController;
         if (!mAutoTracker.isAdded(HOTSPOT)) {
-            Dependency.get(HotspotController.class).addCallback(mHotspotCallback);
+            hotspotController.addCallback(mHotspotCallback);
         }
         if (!mAutoTracker.isAdded(SAVER)) {
-            Dependency.get(DataSaverController.class).addCallback(mDataSaverListener);
+            dataSaverController.addCallback(mDataSaverListener);
         }
         if (!mAutoTracker.isAdded(INVERSION)) {
             mColorsSetting = new SecureSetting(mContext, mHandler,
@@ -79,11 +89,11 @@
             mColorsSetting.setListening(true);
         }
         if (!mAutoTracker.isAdded(WORK)) {
-            Dependency.get(ManagedProfileController.class).addCallback(mProfileCallback);
+            managedProfileController.addCallback(mProfileCallback);
         }
         if (!mAutoTracker.isAdded(NIGHT)
                 && ColorDisplayManager.isNightDisplayAvailable(mContext)) {
-            Dependency.get(ColorDisplayController.class).setListener(mColorDisplayCallback);
+            colorDisplayController.setListener(mColorDisplayCallback);
         }
     }
 
@@ -92,11 +102,11 @@
             mColorsSetting.setListening(false);
         }
         mAutoTracker.destroy();
-        Dependency.get(HotspotController.class).removeCallback(mHotspotCallback);
-        Dependency.get(DataSaverController.class).removeCallback(mDataSaverListener);
-        Dependency.get(ManagedProfileController.class).removeCallback(mProfileCallback);
+        mHotspotController.removeCallback(mHotspotCallback);
+        mDataSaverController.removeCallback(mDataSaverListener);
+        mManagedProfileController.removeCallback(mProfileCallback);
         if (ColorDisplayManager.isNightDisplayAvailable(mContext)) {
-            Dependency.get(ColorDisplayController.class).setListener(null);
+            mColorDisplayController.setListener(null);
         }
     }
 
@@ -109,7 +119,7 @@
                 @Override
                 public void onManagedProfileChanged() {
                     if (mAutoTracker.isAdded(WORK)) return;
-                    if (Dependency.get(ManagedProfileController.class).hasActiveProfile()) {
+                    if (mManagedProfileController.hasActiveProfile()) {
                         mHost.addTile(WORK);
                         mAutoTracker.setTileAdded(WORK);
                     }
@@ -129,8 +139,7 @@
             if (isDataSaving) {
                 mHost.addTile(SAVER);
                 mAutoTracker.setTileAdded(SAVER);
-                mHandler.post(() -> Dependency.get(DataSaverController.class).removeCallback(
-                        mDataSaverListener));
+                mHandler.post(() -> mDataSaverController.removeCallback(mDataSaverListener));
             }
         }
     };
@@ -142,8 +151,7 @@
             if (enabled) {
                 mHost.addTile(HOTSPOT);
                 mAutoTracker.setTileAdded(HOTSPOT);
-                mHandler.post(() -> Dependency.get(HotspotController.class)
-                        .removeCallback(mHotspotCallback));
+                mHandler.post(() -> mHotspotController.removeCallback(mHotspotCallback));
             }
         }
     };
@@ -170,8 +178,7 @@
             if (mAutoTracker.isAdded(NIGHT)) return;
             mHost.addTile(NIGHT);
             mAutoTracker.setTileAdded(NIGHT);
-            mHandler.post(() -> Dependency.get(ColorDisplayController.class)
-                    .setListener(null));
+            mHandler.post(() -> mColorDisplayController.setListener(null));
         }
     };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index 3d81473..7905617 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.annotation.IntDef;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Canvas;
@@ -36,6 +37,9 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 public class BarTransitions {
     private static final boolean DEBUG = false;
     private static final boolean DEBUG_COLORS = false;
@@ -48,6 +52,18 @@
     public static final int MODE_WARNING = 5;
     public static final int MODE_LIGHTS_OUT_TRANSPARENT = 6;
 
+    @IntDef(flag = true, prefix = { "MODE_" }, value = {
+            MODE_OPAQUE,
+            MODE_SEMI_TRANSPARENT,
+            MODE_TRANSLUCENT,
+            MODE_LIGHTS_OUT,
+            MODE_TRANSPARENT,
+            MODE_WARNING,
+            MODE_LIGHTS_OUT_TRANSPARENT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TransitionMode {}
+
     public static final int LIGHTS_IN_DURATION = 250;
     public static final int LIGHTS_OUT_DURATION = 1500;
     public static final int BACKGROUND_DURATION = 200;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 24570ae..f907b65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -70,7 +70,7 @@
     private SignalCallback mSignalCallback = new SignalCallback() {
         @Override
         public void setIsAirplaneMode(NetworkController.IconState icon) {
-            mCommandQueue.recomputeDisableFlags(true /* animate */);
+            mCommandQueue.recomputeDisableFlags(getContext().getDisplayId(), true /* animate */);
         }
     };
 
@@ -155,7 +155,10 @@
     }
 
     @Override
-    public void disable(int state1, int state2, boolean animate) {
+    public void disable(int displayId, int state1, int state2, boolean animate) {
+        if (displayId != getContext().getDisplayId()) {
+            return;
+        }
         state1 = adjustDisableFlags(state1);
         final int old1 = mDisabled1;
         final int diff1 = state1 ^ old1;
@@ -362,6 +365,6 @@
 
     @Override
     public void onDozingChanged(boolean isDozing) {
-        disable(mDisabled1, mDisabled1, false /* animate */);
+        disable(getContext().getDisplayId(), mDisabled1, mDisabled1, false /* animate */);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
index 80c4eb0..08a10dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
@@ -14,7 +14,8 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint;
+import static com.android.systemui.plugins.DarkIconDispatcher.DEFAULT_ICON_TINT;
+import static com.android.systemui.plugins.DarkIconDispatcher.getTint;
 
 import android.animation.ArgbEvaluator;
 import android.content.Context;
@@ -24,9 +25,18 @@
 import android.widget.ImageView;
 
 import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 
-public class DarkIconDispatcherImpl implements DarkIconDispatcher {
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
+public class DarkIconDispatcherImpl implements SysuiDarkIconDispatcher {
 
     private final LightBarTransitionsController mTransitionsController;
     private final Rect mTintArea = new Rect();
@@ -37,6 +47,9 @@
     private int mDarkModeIconColorSingleTone;
     private int mLightModeIconColorSingleTone;
 
+    /**
+     */
+    @Inject
     public DarkIconDispatcherImpl(Context context) {
         mDarkModeIconColorSingleTone = context.getColor(R.color.dark_mode_icon_color_single_tone);
         mLightModeIconColorSingleTone = context.getColor(R.color.light_mode_icon_color_single_tone);
@@ -74,7 +87,7 @@
     }
 
     /**
-     * Sets the dark area so {@link #setIconsDark} only affects the icons in the specified area.
+     * Sets the dark area so {@link #applyDark} only affects the icons in the specified area.
      *
      * @param darkArea the area in which icons should change it's tint, in logical screen
      *                 coordinates
@@ -103,4 +116,12 @@
             mReceivers.valueAt(i).onDarkChanged(mTintArea, mDarkIntensity, mIconTint);
         }
     }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("DarkIconDispatcher: ");
+        pw.println("  mIconTint: 0x" + Integer.toHexString(mIconTint));
+        pw.println("  mDarkIntensity: " + mDarkIntensity + "f");
+        pw.println("  mTintArea: " + mTintArea);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index 3425dd2..236c72c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -29,14 +29,14 @@
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.DemoMode;
 import com.android.systemui.R;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.StatusBarMobileView;
 import com.android.systemui.statusbar.StatusBarWifiView;
 import com.android.systemui.statusbar.StatusIconDisplayable;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
 
 import java.util.ArrayList;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 92a9efe..6f2b63d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -158,8 +158,11 @@
      * @return duration in millis.
      */
     public long getWallpaperAodDuration() {
-        return shouldControlScreenOff() ? DozeScreenState.ENTER_DOZE_HIDE_WALLPAPER_DELAY
-                : mAlwaysOnPolicy.wallpaperVisibilityDuration;
+        if (mAmbientDisplayConfiguration.wakeLockScreenGestureEnabled(UserHandle.USER_CURRENT)
+                || shouldControlScreenOff()) {
+            return DozeScreenState.ENTER_DOZE_HIDE_WALLPAPER_DELAY;
+        }
+        return mAlwaysOnPolicy.wallpaperVisibilityDuration;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index cfa751c..280dda0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -65,10 +65,11 @@
             if (!mDozing) {
                 return;
             }
-            // All pulses except notifications should time out on their own.  Pulses due to
-            // notifications should instead be managed externally based off the notification's
-            // lifetime.
-            if (mPulseReason != DozeLog.PULSE_REASON_NOTIFICATION) {
+            // Notifications should time out on their own.  Pulses due to notifications should
+            // instead be managed externally based off the notification's lifetime.
+            // Dock also controls the time out by self.
+            if (mPulseReason != DozeLog.PULSE_REASON_NOTIFICATION
+                    && mPulseReason != DozeLog.PULSE_REASON_DOCKING) {
                 mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
                 mHandler.postDelayed(mPulseOutExtended,
                         mDozeParameters.getPulseVisibleDurationExtended());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 3c8cad7..876b902 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -25,12 +25,12 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.HeadsUpStatusBarView;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 
 import java.util.function.BiConsumer;
@@ -143,7 +143,7 @@
     }
 
     @Override
-    public void onHeadsUpPinned(NotificationData.Entry entry) {
+    public void onHeadsUpPinned(NotificationEntry entry) {
         updateTopEntry();
         updateHeader(entry);
     }
@@ -206,11 +206,11 @@
     }
 
     private void updateTopEntry() {
-        NotificationData.Entry newEntry = null;
+        NotificationEntry newEntry = null;
         if (!mIsExpanded && mHeadsUpManager.hasPinnedHeadsUp()) {
             newEntry = mHeadsUpManager.getTopEntry();
         }
-        NotificationData.Entry previousEntry = mHeadsUpStatusBarView.getShowingEntry();
+        NotificationEntry previousEntry = mHeadsUpStatusBarView.getShowingEntry();
         mHeadsUpStatusBarView.setEntry(newEntry);
         if (newEntry != previousEntry) {
             boolean animateIsolation = false;
@@ -298,7 +298,7 @@
     }
 
     @Override
-    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
+    public void onHeadsUpUnPinned(NotificationEntry entry) {
         updateTopEntry();
         updateHeader(entry);
     }
@@ -338,7 +338,7 @@
         });
     }
 
-    public void updateHeader(NotificationData.Entry entry) {
+    public void updateHeader(NotificationEntry entry) {
         ExpandableNotificationRow row = entry.getRow();
         float headerVisibleAmount = 1.0f;
         if (row.isPinned() || row.isHeadsUpAnimatingAway() || row == mTrackedChild) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index aa0b7b6..0fada66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -41,8 +41,8 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -72,8 +72,8 @@
     private int mDisplayCutoutTouchableRegionSize;
     private boolean mTrackingHeadsUp;
     private HashSet<String> mSwipedOutKeys = new HashSet<>();
-    private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
-    private ArraySet<NotificationData.Entry> mEntriesToRemoveWhenReorderingAllowed
+    private HashSet<NotificationEntry> mEntriesToRemoveAfterExpand = new HashSet<>();
+    private ArraySet<NotificationEntry> mEntriesToRemoveWhenReorderingAllowed
             = new ArraySet<>();
     private boolean mIsExpanded;
     private int[] mTmpTwoArray = new int[2];
@@ -187,7 +187,7 @@
             releaseAllImmediately();
             mReleaseOnExpandFinish = false;
         } else {
-            for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
+            for (NotificationEntry entry : mEntriesToRemoveAfterExpand) {
                 if (isAlerting(entry.key)) {
                     // Maybe the heads-up was removed already
                     removeAlertEntry(entry.key);
@@ -252,7 +252,7 @@
      * @param remoteInputActive True to notify active, False to notify inactive.
      */
     public void setRemoteInputActive(
-            @NonNull NotificationData.Entry entry, boolean remoteInputActive) {
+            @NonNull NotificationEntry entry, boolean remoteInputActive) {
         HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.key);
         if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
             headsUpEntry.remoteInputActive = remoteInputActive;
@@ -264,6 +264,17 @@
         }
     }
 
+    /**
+     * Sets whether an entry's menu row is exposed and therefore it should stick in the heads up
+     * area if it's pinned until it's hidden again.
+     */
+    public void setMenuShown(@NonNull NotificationEntry entry, boolean menuShown) {
+        HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key);
+        if (headsUpEntry instanceof HeadsUpEntryPhone && entry.isRowPinned()) {
+            ((HeadsUpEntryPhone) headsUpEntry).setMenuShownPinned(menuShown);
+        }
+    }
+
     ///////////////////////////////////////////////////////////////////////////////////////////////
     //  HeadsUpManager public methods overrides:
 
@@ -304,9 +315,9 @@
             return;
         }
         if (hasPinnedHeadsUp()) {
-            NotificationData.Entry topEntry = getTopEntry();
+            NotificationEntry topEntry = getTopEntry();
             if (topEntry.isChildInGroup()) {
-                final NotificationData.Entry groupSummary
+                final NotificationEntry groupSummary
                         = mGroupManager.getGroupSummary(topEntry.notification);
                 if (groupSummary != null) {
                     topEntry = groupSummary;
@@ -363,7 +374,7 @@
     @Override
     public void onReorderingAllowed() {
         mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
-        for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) {
+        for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) {
             if (isAlerting(entry.key)) {
                 // Maybe the heads-up was removed already
                 removeAlertEntry(entry.key);
@@ -388,7 +399,7 @@
     }
 
     @Override
-    protected boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) {
+    protected boolean shouldHeadsUpBecomePinned(NotificationEntry entry) {
           return mStatusBarState != StatusBarState.KEYGUARD && !mIsExpanded
                   || super.shouldHeadsUpBecomePinned(entry);
     }
@@ -469,7 +480,15 @@
     //  HeadsUpEntryPhone:
 
     protected class HeadsUpEntryPhone extends HeadsUpManager.HeadsUpEntry {
-        public void setEntry(@NonNull final NotificationData.Entry entry) {
+
+        private boolean mMenuShownPinned;
+
+        @Override
+        protected boolean isSticky() {
+            return super.isSticky() || mMenuShownPinned;
+        }
+
+        public void setEntry(@NonNull final NotificationEntry entry) {
            Runnable removeHeadsUpRunnable = () -> {
                 if (!mVisualStabilityManager.isReorderingAllowed()) {
                     mEntriesToRemoveWhenReorderingAllowed.add(entry);
@@ -510,6 +529,25 @@
                 updateEntry(false /* updatePostTime */);
             }
         }
+
+        public void setMenuShownPinned(boolean menuShownPinned) {
+            if (mMenuShownPinned == menuShownPinned) {
+                return;
+            }
+
+            mMenuShownPinned = menuShownPinned;
+            if (menuShownPinned) {
+                removeAutoRemovalCallbacks();
+            } else {
+                updateEntry(false /* updatePostTime */);
+            }
+        }
+
+        @Override
+        public void reset() {
+            super.reset();
+            mMenuShownPinned = false;
+        }
     }
 
     public interface AnimationStateHandler {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index 9c1c71a..dd200da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -21,7 +21,7 @@
 import android.view.ViewConfiguration;
 
 import com.android.systemui.Gefingerpoken;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 
@@ -83,7 +83,7 @@
                 } else if (child == null && !mCallback.isExpanded()) {
                     // We might touch above the visible heads up child, but then we still would
                     // like to capture it.
-                    NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
+                    NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
                     if (topEntry != null && topEntry.isRowPinned()) {
                         mPickedChild = topEntry.getRow();
                         mTouchingHeadsUpView = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 21c506b..2e9d9bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -19,7 +19,6 @@
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
 import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 
-import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
 import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_BUTTON;
 import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_UNLOCK;
 import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_BUTTON;
@@ -54,7 +53,6 @@
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.util.MathUtils;
 import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewGroup;
@@ -172,8 +170,6 @@
     private boolean mDozing;
     private int mIndicationBottomMargin;
     private float mDarkAmount;
-    private int mBurnInXOffset;
-    private int mBurnInYOffset;
 
     public KeyguardBottomAreaView(Context context) {
         this(context, null);
@@ -250,8 +246,6 @@
         mIndicationText = findViewById(R.id.keyguard_indication_text);
         mIndicationBottomMargin = getResources().getDimensionPixelSize(
                 R.dimen.keyguard_indication_margin_bottom);
-        mBurnInYOffset = getResources().getDimensionPixelSize(
-                R.dimen.default_burn_in_prevention_offset);
         updateCameraVisibility();
         mUnlockMethodCache = UnlockMethodCache.getInstance(getContext());
         mUnlockMethodCache.addListener(this);
@@ -322,8 +316,6 @@
         super.onConfigurationChanged(newConfig);
         mIndicationBottomMargin = getResources().getDimensionPixelSize(
                 R.dimen.keyguard_indication_margin_bottom);
-        mBurnInYOffset = getResources().getDimensionPixelSize(
-                R.dimen.default_burn_in_prevention_offset);
         MarginLayoutParams mlp = (MarginLayoutParams) mIndicationArea.getLayoutParams();
         if (mlp.bottomMargin != mIndicationBottomMargin) {
             mlp.bottomMargin = mIndicationBottomMargin;
@@ -569,7 +561,15 @@
         mDarkAmount = darkAmount;
         mIndicationController.setDarkAmount(darkAmount);
         mLockIcon.setDarkAmount(darkAmount);
-        dozeTimeTick();
+    }
+
+    /**
+     * When keyguard is in pulsing (AOD2) state.
+     * @param pulsing {@code true} when pulsing.
+     * @param animated if transition should be animated.
+     */
+    public void setPulsing(boolean pulsing, boolean animated) {
+        mLockIcon.setPulsing(pulsing, animated);
     }
 
     private static boolean isSuccessfulLaunch(int result) {
@@ -830,6 +830,7 @@
 
         updateCameraVisibility();
         updateLeftAffordanceIcon();
+        mLockIcon.setDozing(dozing);
 
         if (dozing) {
             mOverlayContainer.setVisibility(INVISIBLE);
@@ -841,21 +842,6 @@
         }
     }
 
-    public void dozeTimeTick() {
-        // Move views every minute to avoid burn-in
-        int burnInYOffset = -getBurnInOffset(mBurnInYOffset, false /* xAxis */);
-        burnInYOffset = (int) MathUtils.lerp(0, burnInYOffset, mDarkAmount);
-        mLockIcon.setTranslationY(burnInYOffset);
-    }
-
-    public void setBurnInXOffset(int burnInXOffset) {
-        if (mBurnInXOffset == burnInXOffset) {
-            return;
-        }
-        mBurnInXOffset = burnInXOffset;
-        mLockIcon.setTranslationX(burnInXOffset);
-    }
-
     private class DefaultLeftButton implements IntentButton {
 
         private IconState mIconState = new IconState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
index b3d0bf8..e541e14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
@@ -20,15 +20,23 @@
 
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Executes actions that require the screen to be unlocked. Delegates the actual handling to an
  * implementation passed via {@link #setDismissHandler}.
  */
+@Singleton
 public class KeyguardDismissUtil implements KeyguardDismissHandler {
     private static final String TAG = "KeyguardDismissUtil";
 
     private volatile KeyguardDismissHandler mDismissHandler;
 
+    @Inject
+    public KeyguardDismissUtil() {
+    }
+
     /** Sets the actual {@link DismissHandler} implementation. */
     public void setDismissHandler(KeyguardDismissHandler dismissHandler) {
         mDismissHandler = dismissHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
index b3423a8..925a19d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
@@ -22,8 +22,7 @@
 
 import com.android.systemui.Dependency;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
 public class KeyguardEnvironmentImpl implements KeyguardEnvironment {
@@ -34,8 +33,6 @@
             Dependency.get(NotificationLockscreenUserManager.class);
     private final DeviceProvisionedController mDeviceProvisionedController =
             Dependency.get(DeviceProvisionedController.class);
-    private final NotificationMediaManager mMediaManager =
-            Dependency.get(NotificationMediaManager.class);
 
     public KeyguardEnvironmentImpl() {
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 96b7536..03375d20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -45,13 +45,13 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.policy.UserInfoController;
 import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
@@ -466,6 +466,14 @@
                 .onDensityOrFontScaleChanged();
     }
 
+    @Override
+    public void onOverlayChanged() {
+        mCarrierLabel.setTextAppearance(
+                Utils.getThemeAttr(mContext, com.android.internal.R.attr.textAppearanceSmall));
+        onThemeChanged();
+        mBatteryView.updatePercentView();
+    }
+
     private void updateIconsAndTextColors() {
         @ColorInt int textColor = Utils.getColorAttrDefaultColor(mContext,
                 R.attr.wallpaperTextColor);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index e0c5516..b590ca7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -25,23 +25,26 @@
 import android.view.View;
 
 import com.android.internal.colorextraction.ColorExtractor.GradientColors;
-import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
+import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Controls how light status bar flag applies to the icons.
  */
+@Singleton
 public class LightBarController implements BatteryController.BatteryStateChangeCallback, Dumpable {
 
     private static final float NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD = 0.1f;
 
-    private final DarkIconDispatcher mStatusBarIconController;
+    private final SysuiDarkIconDispatcher mStatusBarIconController;
     private final BatteryController mBatteryController;
     private BiometricUnlockController mBiometricUnlockController;
 
@@ -76,10 +79,12 @@
     private final Rect mLastDockedBounds = new Rect();
     private boolean mQsCustomizing;
 
-    public LightBarController(Context ctx) {
+    @Inject
+    public LightBarController(Context ctx, DarkIconDispatcher darkIconDispatcher,
+            BatteryController batteryController) {
         mDarkModeColor = Color.valueOf(ctx.getColor(R.color.dark_mode_icon_color_single_tone));
-        mStatusBarIconController = Dependency.get(DarkIconDispatcher.class);
-        mBatteryController = Dependency.get(BatteryController.class);
+        mStatusBarIconController = (SysuiDarkIconDispatcher) darkIconDispatcher;
+        mBatteryController = batteryController;
         mBatteryController.addCallback(this);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index 57cc7d6..1944c3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -16,11 +16,16 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.statusbar.phone.NavBarTintController.DEFAULT_COLOR_ADAPT_TRANSITION_TIME;
+import static com.android.systemui.statusbar.phone.NavBarTintController.MIN_COLOR_ADAPT_TRANSITION_TIME;
+import static com.android.systemui.statusbar.phone.NavBarTintController.NAV_COLOR_TRANSITION_TIME_SETTING;
+
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.SystemClock;
+import android.provider.Settings;
 import android.util.MathUtils;
 import android.util.TimeUtils;
 
@@ -42,13 +47,14 @@
 public class LightBarTransitionsController implements Dumpable, Callbacks,
         StatusBarStateController.StateListener {
 
-    public static final long DEFAULT_TINT_ANIMATION_DURATION = 120;
+    public static final int DEFAULT_TINT_ANIMATION_DURATION = 120;
     private static final String EXTRA_DARK_INTENSITY = "dark_intensity";
 
     private final Handler mHandler;
     private final DarkIntensityApplier mApplier;
     private final KeyguardMonitor mKeyguardMonitor;
     private final StatusBarStateController mStatusBarStateController;
+    private NavBarTintController mColorAdaptionController;
 
     private boolean mTransitionDeferring;
     private long mTransitionDeferringStartTime;
@@ -60,6 +66,7 @@
     private float mDarkIntensity;
     private float mNextDarkIntensity;
     private float mDozeAmount;
+    private int mDisplayId;
     private final Runnable mTransitionDeferringDoneRunnable = new Runnable() {
         @Override
         public void run() {
@@ -67,6 +74,8 @@
         }
     };
 
+    private final Context mContext;
+
     public LightBarTransitionsController(Context context, DarkIntensityApplier applier) {
         mApplier = applier;
         mHandler = new Handler();
@@ -76,6 +85,8 @@
                 .addCallback(this);
         mStatusBarStateController.addCallback(this);
         mDozeAmount = mStatusBarStateController.getDozeAmount();
+        mContext = context;
+        mDisplayId = mContext.getDisplayId();
     }
 
     public void destroy(Context context) {
@@ -95,25 +106,29 @@
     }
 
     @Override
-    public void appTransitionPending(boolean forced) {
-        if (mKeyguardMonitor.isKeyguardGoingAway() && !forced) {
+    public void appTransitionPending(int displayId, boolean forced) {
+        if (mDisplayId != displayId || mKeyguardMonitor.isKeyguardGoingAway() && !forced) {
             return;
         }
         mTransitionPending = true;
     }
 
     @Override
-    public void appTransitionCancelled() {
+    public void appTransitionCancelled(int displayId) {
+        if (mDisplayId != displayId) {
+            return;
+        }
         if (mTransitionPending && mTintChangePending) {
             mTintChangePending = false;
-            animateIconTint(mPendingDarkIntensity, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION);
+            animateIconTint(mPendingDarkIntensity, 0 /* delay */, getTintAnimationDuration());
         }
         mTransitionPending = false;
     }
 
     @Override
-    public void appTransitionStarting(long startTime, long duration, boolean forced) {
-        if (mKeyguardMonitor.isKeyguardGoingAway() && !forced) {
+    public void appTransitionStarting(int displayId, long startTime, long duration,
+            boolean forced) {
+        if (mDisplayId != displayId || mKeyguardMonitor.isKeyguardGoingAway() && !forced) {
             return;
         }
         if (mTransitionPending && mTintChangePending) {
@@ -146,10 +161,19 @@
                     Math.max(0, mTransitionDeferringStartTime - SystemClock.uptimeMillis()),
                     mTransitionDeferringDuration);
         } else {
-            animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION);
+            animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, getTintAnimationDuration());
         }
     }
 
+    public long getTintAnimationDuration() {
+        if (NavBarTintController.isEnabled(mContext)) {
+            return Math.max(Settings.Global.getInt(mContext.getContentResolver(),
+                    NAV_COLOR_TRANSITION_TIME_SETTING, DEFAULT_COLOR_ADAPT_TRANSITION_TIME),
+                    MIN_COLOR_ADAPT_TRANSITION_TIME);
+        }
+        return DEFAULT_TINT_ANIMATION_DURATION;
+    }
+
     public float getCurrentDarkIntensity() {
         return mDarkIntensity;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 1be3975..d934d95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -45,8 +45,6 @@
     private static final int STATE_FACE_UNLOCK = 2;
     private static final int STATE_FINGERPRINT = 3;
     private static final int STATE_FINGERPRINT_ERROR = 4;
-    private static final boolean HOLLOW_PILL = SystemProperties
-            .getBoolean("persist.sysui.hollow_pill", false);
 
     private int mLastState = 0;
     private boolean mLastDeviceInteractive;
@@ -60,6 +58,8 @@
     private boolean mHasFingerPrintIcon;
     private boolean mHasFaceUnlockIcon;
     private int mDensity;
+    private boolean mPulsing;
+    private boolean mDozing;
 
     private final Runnable mDrawOffTimeout = () -> update(true /* forceUpdate */);
     private float mDarkAmount;
@@ -159,6 +159,7 @@
             mLastScreenOn = mScreenOn;
         }
 
+        setVisibility(mDozing && !mPulsing ? GONE : VISIBLE);
         updateClickability();
     }
 
@@ -224,16 +225,6 @@
                 throw new IllegalArgumentException();
         }
 
-        if (HOLLOW_PILL && deviceInteractive) {
-            switch (state) {
-                case STATE_FINGERPRINT:
-                case STATE_LOCK_OPEN:
-                case STATE_LOCKED:
-                case STATE_FACE_UNLOCK:
-                    iconRes = R.drawable.ic_home_button_outline;
-            }
-        }
-
         return mContext.getDrawable(iconRes);
     }
 
@@ -281,6 +272,24 @@
         updateDarkTint();
     }
 
+    /**
+     * When keyguard is in pulsing (AOD2) state.
+     * @param pulsing {@code true} when pulsing.
+     * @param animated if transition should be animated.
+     */
+    public void setPulsing(boolean pulsing, boolean animated) {
+        mPulsing = pulsing;
+        update();
+    }
+
+    /**
+     * Sets the dozing state of the keyguard.
+     */
+    public void setDozing(boolean dozing) {
+        mDozing = dozing;
+        update();
+    }
+
     private void updateDarkTint() {
         Drawable drawable = getDrawable().mutate();
         int color = ColorUtils.blendARGB(Color.TRANSPARENT, Color.WHITE, mDarkAmount);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
index 7621887..1281953 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
@@ -25,16 +25,21 @@
 import com.android.systemui.EventLogConstants;
 import com.android.systemui.EventLogTags;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Wrapper that emits both new- and old-style gesture logs.
  * TODO: delete this once the old logs are no longer needed.
  */
+@Singleton
 public class LockscreenGestureLogger {
     private ArrayMap<Integer, Integer> mLegacyMap;
     private LogMaker mLogMaker = new LogMaker(MetricsEvent.VIEW_UNKNOWN)
             .setType(MetricsEvent.TYPE_ACTION);
     private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
 
+    @Inject
     public LockscreenGestureLogger() {
         mLegacyMap = new ArrayMap<>(EventLogConstants.METRICS_GESTURE_TYPE_MAP.length);
         for (int i = 0; i < EventLogConstants.METRICS_GESTURE_TYPE_MAP.length ; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
index 0f8d59b..fbd8d8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
@@ -28,6 +28,12 @@
 import java.util.LinkedList;
 import java.util.List;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
 public class ManagedProfileControllerImpl implements ManagedProfileController {
 
     private final List<Callback> mCallbacks = new ArrayList<>();
@@ -38,6 +44,9 @@
     private boolean mListening;
     private int mCurrentUser;
 
+    /**
+     */
+    @Inject
     public ManagedProfileControllerImpl(Context context) {
         mContext = context;
         mUserManager = UserManager.get(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
new file mode 100644
index 0000000..b4f850b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.provider.Settings;
+import android.util.DisplayMetrics;
+import android.view.SurfaceControl;
+
+public class NavBarTintController {
+    public static final String NAV_COLOR_TRANSITION_TIME_SETTING = "navbar_color_adapt_transition";
+    public static final int MIN_COLOR_ADAPT_TRANSITION_TIME = 400;
+    public static final int DEFAULT_COLOR_ADAPT_TRANSITION_TIME = 1500;
+
+    private final HandlerThread mColorAdaptHandlerThread = new HandlerThread("ColorExtractThread");
+    private Handler mColorAdaptionHandler;
+
+    // Poll time for each iteration to color sample
+    private static final int COLOR_ADAPTION_TIMEOUT = 300;
+
+    // Passing the threshold of this luminance value will make the button black otherwise white
+    private static final float LUMINANCE_THRESHOLD = 0.3f;
+
+    // The home button's icon is actually smaller than the button's size, the percentage will
+    // cut into the button's size to determine the icon size
+    private static final float PERCENTAGE_BUTTON_PADDING = 0.3f;
+
+    // The distance from the home button to color sample around
+    private static final int COLOR_SAMPLE_MARGIN = 20;
+
+    private boolean mRunning;
+
+    private final NavigationBarView mNavigationBarView;
+    private final LightBarTransitionsController mLightBarController;
+    private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+
+    public NavBarTintController(NavigationBarView navigationBarView,
+            LightBarTransitionsController lightBarController) {
+        mNavigationBarView = navigationBarView;
+        mLightBarController = lightBarController;
+    }
+
+    public void start() {
+        if (!isEnabled(mNavigationBarView.getContext())) {
+            return;
+        }
+        if (mColorAdaptionHandler == null) {
+            mColorAdaptHandlerThread.start();
+            mColorAdaptionHandler = new Handler(mColorAdaptHandlerThread.getLooper());
+        }
+        mColorAdaptionHandler.removeCallbacksAndMessages(null);
+        mColorAdaptionHandler.post(this::updateTint);
+        mRunning = true;
+    }
+
+    public void end() {
+        if (mColorAdaptionHandler != null) {
+            mColorAdaptionHandler.removeCallbacksAndMessages(null);
+        }
+        mRunning = false;
+    }
+
+    public void stop() {
+        end();
+        if (mColorAdaptionHandler != null) {
+            mColorAdaptHandlerThread.quitSafely();
+        }
+    }
+
+    private void updateTint() {
+        int[] navPos = new int[2];
+        int[] butPos = new int[2];
+        if (mNavigationBarView.getHomeButton().getCurrentView() == null) {
+            return;
+        }
+
+        // Determine the area of the home icon in the larger home button
+        mNavigationBarView.getHomeButton().getCurrentView().getLocationInSurface(butPos);
+        final int navWidth = mNavigationBarView.getHomeButton().getCurrentView().getWidth();
+        final int navHeight = mNavigationBarView.getHomeButton().getCurrentView().getHeight();
+        final int xPadding = (int) (PERCENTAGE_BUTTON_PADDING * navWidth);
+        final int yPadding = (int) (PERCENTAGE_BUTTON_PADDING * navHeight);
+        final Rect homeButtonRect = new Rect(butPos[0] + xPadding, butPos[1] + yPadding,
+                navWidth + butPos[0]  - xPadding, navHeight + butPos[1] - yPadding);
+        if (mNavigationBarView.getCurrentView() == null || homeButtonRect.isEmpty()) {
+            scheduleColorAdaption();
+            return;
+        }
+        mNavigationBarView.getCurrentView().getLocationOnScreen(navPos);
+        homeButtonRect.offset(navPos[0], navPos[1]);
+
+        // Apply a margin area around the button region to sample the colors, crop from screenshot
+        final Rect cropRect = new Rect(homeButtonRect);
+        cropRect.inset(-COLOR_SAMPLE_MARGIN, -COLOR_SAMPLE_MARGIN);
+        if (cropRect.isEmpty()) {
+            scheduleColorAdaption();
+            return;
+        }
+
+        // Determine the size of the home area
+        Rect homeArea = new Rect(COLOR_SAMPLE_MARGIN, COLOR_SAMPLE_MARGIN,
+                homeButtonRect.width() + COLOR_SAMPLE_MARGIN,
+                homeButtonRect.height() + COLOR_SAMPLE_MARGIN);
+
+        // Get the screenshot around the home button icon to determine the color
+        DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+        mNavigationBarView.getContext().getDisplay().getRealMetrics(mDisplayMetrics);
+        final Bitmap hardBitmap = SurfaceControl
+                .screenshot(new Rect(), mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
+                        mNavigationBarView.getContext().getDisplay().getRotation());
+        if (hardBitmap != null && cropRect.bottom <= hardBitmap.getHeight()) {
+            final Bitmap cropBitmap = Bitmap.createBitmap(hardBitmap, cropRect.left, cropRect.top,
+                    cropRect.width(), cropRect.height());
+            final Bitmap softBitmap = cropBitmap.copy(Config.ARGB_8888, false);
+
+            // Get the luminance value to determine if the home button should be black or white
+            final int[] pixels = new int[softBitmap.getByteCount() / 4];
+            softBitmap.getPixels(pixels, 0, softBitmap.getWidth(), 0, 0, softBitmap.getWidth(),
+                    softBitmap.getHeight());
+            float r = 0, g = 0, blue = 0;
+
+            int width = cropRect.width();
+            int total = 0;
+            for (int i = 0; i < pixels.length; i += 4) {
+                int x = i % width;
+                int y = i / width;
+                if (!homeArea.contains(x, y)) {
+                    r += Color.red(pixels[i]);
+                    g += Color.green(pixels[i]);
+                    blue += Color.blue(pixels[i]);
+                    total++;
+                }
+            }
+
+            r /= total;
+            g /= total;
+            blue /= total;
+
+            r = Math.max(Math.min(r / 255f, 1), 0);
+            g = Math.max(Math.min(g / 255f, 1), 0);
+            blue = Math.max(Math.min(blue / 255f, 1), 0);
+
+            if (r <= 0.03928) {
+                r /= 12.92;
+            } else {
+                r = (float) Math.pow((r + 0.055) / 1.055, 2.4);
+            }
+            if (g <= 0.03928) {
+                g /= 12.92;
+            } else {
+                g = (float) Math.pow((g + 0.055) / 1.055, 2.4);
+            }
+            if (blue <= 0.03928) {
+                blue /= 12.92;
+            } else {
+                blue = (float) Math.pow((blue + 0.055) / 1.055, 2.4);
+            }
+
+            if (r * 0.2126 + g * 0.7152 + blue * 0.0722 > LUMINANCE_THRESHOLD) {
+                // Black
+                mMainHandler.post(
+                        () -> mLightBarController
+                                .setIconsDark(true /* dark */, true /* animate */));
+            } else {
+                // White
+                mMainHandler.post(
+                        () -> mLightBarController
+                                .setIconsDark(false /* dark */, true /* animate */));
+            }
+            cropBitmap.recycle();
+            hardBitmap.recycle();
+        }
+        scheduleColorAdaption();
+    }
+
+    private void scheduleColorAdaption() {
+        mColorAdaptionHandler.removeCallbacksAndMessages(null);
+        if (!mRunning || !isEnabled(mNavigationBarView.getContext())) {
+            return;
+        }
+        mColorAdaptionHandler.postDelayed(this::updateTint, COLOR_ADAPTION_TIMEOUT);
+    }
+
+    public static boolean isEnabled(Context context) {
+        return Settings.Global.getInt(context.getContentResolver(),
+                NavigationPrototypeController.NAV_COLOR_ADAPT_ENABLE_SETTING, 0) == 1;
+    }
+}
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 ae0a14529..6d97d67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -16,14 +16,21 @@
 
 import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
 import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
+import static android.app.StatusBarManager.WindowType;
+import static android.app.StatusBarManager.WindowVisibleState;
 import static android.app.StatusBarManager.windowStateToString;
 
 import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
 import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
 import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE;
 import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions;
 
@@ -84,6 +91,8 @@
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CommandQueue.Callbacks;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.ContextualButton.ContextButtonListener;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
@@ -97,6 +106,8 @@
 import java.util.Locale;
 import java.util.function.Consumer;
 
+import javax.inject.Inject;
+
 /**
  * Fragment containing the NavigationBarFragment. Contains logic for what happens
  * on clicks and view states of the nav bar.
@@ -110,21 +121,22 @@
 
     /** Allow some time inbetween the long press for back and recents. */
     private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
+    private static final long AUTODIM_TIMEOUT_MS = 2250;
 
-    private final DeviceProvisionedController mDeviceProvisionedController =
-            Dependency.get(DeviceProvisionedController.class);
+    private final AccessibilityManagerWrapper mAccessibilityManagerWrapper;
+    protected final AssistManager mAssistManager;
+    private final MetricsLogger mMetricsLogger;
+    private final DeviceProvisionedController mDeviceProvisionedController;
 
     protected NavigationBarView mNavigationBarView = null;
-    protected AssistManager mAssistManager;
 
-    private int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
+    private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
 
     private int mNavigationIconHints = 0;
-    private int mNavigationBarMode;
+    private @TransitionMode int mNavigationBarMode;
     private AccessibilityManager mAccessibilityManager;
     private MagnificationContentObserver mMagnificationObserver;
     private ContentResolver mContentResolver;
-    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
 
     private int mDisabledFlags1;
     private int mDisabledFlags2;
@@ -140,12 +152,16 @@
 
     private int mSystemUiVisibility;
     private LightBarController mLightBarController;
+    private AutoHideController mAutoHideController;
 
     private OverviewProxyService mOverviewProxyService;
 
-    private boolean mIsOnDefaultDisplay = true;
+    private int mDisplayId;
+    private boolean mIsOnDefaultDisplay;
     public boolean mHomeBlockedThisTouch;
 
+    private Handler mHandler = Dependency.get(Dependency.MAIN_HANDLER);
+
     private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
         @Override
         public void onConnectionChanged(boolean isConnected) {
@@ -182,17 +198,27 @@
         }
     };
 
-    private final ContextButtonListener mRotationButtonListener = new ContextButtonListener() {
-        @Override
-        public void onVisibilityChanged(ContextualButton button, boolean visible) {
-            if (visible) {
-                // If the button will actually become visible and the navbar is about to hide,
-                // tell the statusbar to keep it around for longer
-                mStatusBar.touchAutoHide();
-            }
+    private final ContextButtonListener mRotationButtonListener = (button, visible) -> {
+        if (visible) {
+            // 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();
         }
     };
 
+    private final Runnable mAutoDim = () -> getBarTransitions().setAutoDim(true);
+
+    @Inject
+    public NavigationBarFragment(AccessibilityManagerWrapper accessibilityManagerWrapper,
+            DeviceProvisionedController deviceProvisionedController, MetricsLogger metricsLogger,
+            AssistManager assistManager, OverviewProxyService overviewProxyService) {
+        mAccessibilityManagerWrapper = accessibilityManagerWrapper;
+        mDeviceProvisionedController = deviceProvisionedController;
+        mMetricsLogger = metricsLogger;
+        mAssistManager = assistManager;
+        mOverviewProxyService = overviewProxyService;
+    }
+
     // ----- Fragment Lifecycle Callbacks -----
 
     @Override
@@ -205,8 +231,6 @@
         mDivider = SysUiServiceProvider.getComponent(getContext(), Divider.class);
         mWindowManager = getContext().getSystemService(WindowManager.class);
         mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class);
-        Dependency.get(AccessibilityManagerWrapper.class).addCallback(
-                mAccessibilityListener);
         mContentResolver = getContext().getContentResolver();
         mMagnificationObserver = new MagnificationContentObserver(
                 getContext().getMainThreadHandler());
@@ -218,15 +242,13 @@
             mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0);
             mDisabledFlags2 = savedInstanceState.getInt(EXTRA_DISABLE2_STATE, 0);
         }
-        mAssistManager = Dependency.get(AssistManager.class);
-        mOverviewProxyService = Dependency.get(OverviewProxyService.class);
+        mAccessibilityManagerWrapper.addCallback(mAccessibilityListener);
     }
 
     @Override
     public void onDestroy() {
         super.onDestroy();
-        Dependency.get(AccessibilityManagerWrapper.class).removeCallback(
-                mAccessibilityListener);
+        mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener);
         mContentResolver.unregisterContentObserver(mMagnificationObserver);
     }
 
@@ -243,7 +265,8 @@
         final Display display = view.getDisplay();
         // It may not have display when running unit test.
         if (display != null) {
-            mIsOnDefaultDisplay = display.getDisplayId() == Display.DEFAULT_DISPLAY;
+            mDisplayId = display.getDisplayId();
+            mIsOnDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
         }
 
         mNavigationBarView.setComponents(mStatusBar.getPanel());
@@ -343,8 +366,11 @@
     // ----- CommandQueue Callbacks -----
 
     @Override
-    public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
+    public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
             boolean showImeSwitcher) {
+        if (displayId != mDisplayId) {
+            return;
+        }
         boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
         int hints = mNavigationIconHints;
         switch (backDisposition) {
@@ -373,19 +399,21 @@
         if (mNavigationBarView != null) {
             mNavigationBarView.setNavigationIconHints(hints);
         }
-        mStatusBar.checkBarModes();
+        checkBarModes();
     }
 
     @Override
-    public void topAppWindowChanged(boolean showMenu) {
-        if (mNavigationBarView != null) {
+    public void topAppWindowChanged(int displayId, boolean showMenu) {
+        if (displayId == mDisplayId && mNavigationBarView != null) {
             mNavigationBarView.setMenuVisibility(showMenu);
         }
     }
 
     @Override
-    public void setWindowState(int window, int state) {
-        if (mNavigationBarView != null
+    public void setWindowState(
+            int displayId, @WindowType int window, @WindowVisibleState int state) {
+        if (displayId == mDisplayId
+                && mNavigationBarView != null
                 && window == StatusBarManager.WINDOW_NAVIGATION_BAR
                 && mNavigationBarWindowState != state) {
             mNavigationBarWindowState = state;
@@ -418,28 +446,30 @@
                 .onRotationProposal(rotation, winRotation, isValid);
     }
 
-    // Injected from StatusBar at creation.
-    public void setCurrentSysuiVisibility(int systemUiVisibility) {
+    /**
+     * Sets System UI flags to {@link NavigationBarFragment}.
+     *
+     * @see View#setSystemUiVisibility(int)
+     */
+    public void setSystemUiVisibility(int systemUiVisibility) {
         mSystemUiVisibility = systemUiVisibility;
-        final int barMode = mStatusBar.computeBarMode(0, mSystemUiVisibility,
-                View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
-                View.NAVIGATION_BAR_TRANSPARENT);
+        final int barMode = computeBarMode(0, mSystemUiVisibility);
         if (barMode != -1) {
             mNavigationBarMode = barMode;
         }
         checkNavBarModes();
-        mStatusBar.touchAutoHide();
+        mAutoHideController.touchAutoHide();
 
-        // TODO(115978725): Support light bar controller on external nav bars.
-        if (mLightBarController != null) {
-            mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */,
-                    true /* nbModeChanged */, mNavigationBarMode);
-        }
+        mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */,
+                true /* nbModeChanged */, mNavigationBarMode);
     }
 
     @Override
-    public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
-            int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+    public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
+            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+        if (displayId != mDisplayId) {
+            return;
+        }
         final int oldVal = mSystemUiVisibility;
         final int newVal = (oldVal & ~mask) | (vis & mask);
         final int diff = newVal ^ oldVal;
@@ -449,9 +479,7 @@
 
             // update navigation bar mode
             final int nbMode = getView() == null
-                    ? -1 : mStatusBar.computeBarMode(oldVal, newVal,
-                    View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
-                    View.NAVIGATION_BAR_TRANSPARENT);
+                    ? -1 : computeBarMode(oldVal, newVal);
             nbModeChanged = nbMode != -1;
             if (nbModeChanged) {
                 if (mNavigationBarMode != nbMode) {
@@ -462,19 +490,45 @@
                     mNavigationBarMode = nbMode;
                     checkNavBarModes();
                 }
-                mStatusBar.touchAutoHide();
+                mAutoHideController.touchAutoHide();
             }
         }
+        mLightBarController.onNavigationVisibilityChanged(
+                vis, mask, nbModeChanged, mNavigationBarMode);
+    }
 
-        // TODO(115978725): Support light bar controller on external nav bars.
-        if (mLightBarController != null) {
-            mLightBarController.onNavigationVisibilityChanged(vis, mask, nbModeChanged,
-                    mNavigationBarMode);
+    private @TransitionMode int computeBarMode(int oldVis, int newVis) {
+        final int oldMode = barMode(oldVis);
+        final int newMode = barMode(newVis);
+        if (oldMode == newMode) {
+            return -1; // no mode change
+        }
+        return newMode;
+    }
+
+    private @TransitionMode int barMode(int vis) {
+        final int lightsOutTransparent =
+                View.SYSTEM_UI_FLAG_LOW_PROFILE | View.NAVIGATION_BAR_TRANSIENT;
+        if ((vis & View.NAVIGATION_BAR_TRANSIENT) != 0) {
+            return MODE_SEMI_TRANSPARENT;
+        } else if ((vis & View.NAVIGATION_BAR_TRANSLUCENT) != 0) {
+            return MODE_TRANSLUCENT;
+        } else if ((vis & lightsOutTransparent) == lightsOutTransparent) {
+            return MODE_LIGHTS_OUT_TRANSPARENT;
+        } else if ((vis & View.NAVIGATION_BAR_TRANSPARENT) != 0) {
+            return MODE_TRANSPARENT;
+        } else if ((vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
+            return MODE_LIGHTS_OUT;
+        } else {
+            return MODE_OPAQUE;
         }
     }
 
     @Override
-    public void disable(int state1, int state2, boolean animate) {
+    public void disable(int displayId, int state1, int state2, boolean animate) {
+        if (displayId != mDisplayId) {
+            return;
+        }
         // Navigation bar flags are in both state1 and state2.
         final int masked = state1 & (StatusBarManager.DISABLE_HOME
                 | StatusBarManager.DISABLE_RECENT
@@ -503,7 +557,7 @@
         }
     }
 
-    // ----- Internal stuffz -----
+    // ----- Internal stuff -----
 
     private void refreshLayout(int layoutDirection) {
         if (mNavigationBarView != null) {
@@ -602,7 +656,7 @@
     }
 
     private boolean onNavigationTouch(View v, MotionEvent event) {
-        mStatusBar.checkUserAutohide(event);
+        mAutoHideController.checkUserAutoHide(event);
         return false;
     }
 
@@ -792,30 +846,69 @@
         mNavigationBarView.setAccessibilityButtonState(showAccessibilityButton, targetSelection);
     }
 
-    // ----- Methods that StatusBar talks to (should be minimized) -----
+    // ----- Methods that DisplayNavigationBarController talks to -----
+
+    /** Applies auto dimming animation on navigation bar when touched. */
+    public void touchAutoDim() {
+        getBarTransitions().setAutoDim(false);
+        mHandler.removeCallbacks(mAutoDim);
+        int state = Dependency.get(StatusBarStateController.class).getState();
+        if (state != StatusBarState.KEYGUARD && state != StatusBarState.SHADE_LOCKED) {
+            mHandler.postDelayed(mAutoDim, AUTODIM_TIMEOUT_MS);
+        }
+    }
 
     public void setLightBarController(LightBarController lightBarController) {
         mLightBarController = lightBarController;
         mLightBarController.setNavigationBar(mNavigationBarView.getLightTransitionsController());
     }
 
+    /** Sets {@link AutoHideController} to the navigation bar. */
+    public void setAutoHideController(AutoHideController autoHideController) {
+        mAutoHideController = autoHideController;
+        mAutoHideController.setNavigationBar(this);
+    }
+
     public boolean isSemiTransparent() {
         return mNavigationBarMode == MODE_SEMI_TRANSPARENT;
     }
 
+    private void checkBarModes() {
+        // We only have status bar on default display now.
+        if (mIsOnDefaultDisplay) {
+            mStatusBar.checkBarModes();
+        } else {
+            checkNavBarModes();
+        }
+    }
+
+    /**
+     * Checks current navigation bar mode and make transitions.
+     */
+    public void checkNavBarModes() {
+        final boolean anim = mStatusBar.isDeviceInteractive()
+                && mNavigationBarWindowState != WINDOW_STATE_HIDDEN;
+        mNavigationBarView.getBarTransitions().transitionTo(mNavigationBarMode, anim);
+    }
+
     public void disableAnimationsDuringHide(long delay) {
         mNavigationBarView.setLayoutTransitionsEnabled(false);
         mNavigationBarView.postDelayed(() -> mNavigationBarView.setLayoutTransitionsEnabled(true),
                 delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
     }
 
-    public BarTransitions getBarTransitions() {
-        return mNavigationBarView.getBarTransitions();
+    /**
+     * Performs transitions on navigation bar.
+     *
+     * @param barMode transition bar mode.
+     * @param animate shows animations if {@code true}.
+     */
+    public void transitionTo(@TransitionMode int barMode, boolean animate) {
+        getBarTransitions().transitionTo(barMode, animate);
     }
 
-    public void checkNavBarModes() {
-        mStatusBar.checkBarMode(mNavigationBarMode,
-                mNavigationBarWindowState, mNavigationBarView.getBarTransitions());
+    private BarTransitions getBarTransitions() {
+        return mNavigationBarView.getBarTransitions();
     }
 
     public void finishBarAnimations() {
@@ -851,16 +944,25 @@
             if (Intent.ACTION_SCREEN_OFF.equals(action)
                     || Intent.ACTION_SCREEN_ON.equals(action)) {
                 notifyNavigationBarScreenOn();
+
+                if (Intent.ACTION_SCREEN_ON.equals(action)) {
+                    // Enabled and screen is on, start it again if enabled
+                    if (NavBarTintController.isEnabled(getContext())) {
+                        mNavigationBarView.getColorAdaptionController().start();
+                    }
+                } else {
+                    // Screen off disable it
+                    mNavigationBarView.getColorAdaptionController().end();
+                }
             }
             if (Intent.ACTION_USER_SWITCHED.equals(action)) {
                 // The accessibility settings may be different for the new user
                 updateAccessibilityServicesState(mAccessibilityManager);
-            };
+            }
         }
     };
 
     public static View create(Context context, FragmentListener listener) {
-        final int displayId = context.getDisplay().getDisplayId();
         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
                 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
@@ -872,7 +974,7 @@
                         | WindowManager.LayoutParams.FLAG_SLIPPERY,
                 PixelFormat.TRANSLUCENT);
         lp.token = new Binder();
-        lp.setTitle("NavigationBar" + displayId);
+        lp.setTitle("NavigationBar" + context.getDisplayId());
         lp.accessibilityTitle = context.getString(R.string.nav_bar);
         lp.windowAnimations = 0;
 
@@ -882,7 +984,8 @@
         if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
         if (navigationBarView == null) return null;
 
-        final NavigationBarFragment fragment = new NavigationBarFragment();
+        final NavigationBarFragment fragment = FragmentHostManager.get(navigationBarView)
+                .create(NavigationBarFragment.class);
         navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
             @Override
             public void onViewAttachedToWindow(View v) {
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 30e8409..2fc7b78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -149,6 +149,7 @@
     private RecentsOnboarding mRecentsOnboarding;
     private NotificationPanelView mPanelView;
 
+    private NavBarTintController mColorAdaptionController;
     private NavigationPrototypeController mPrototypeController;
     private NavigationGestureAction[] mDefaultGestureMap;
     private QuickScrubAction mQuickScrubAction;
@@ -275,7 +276,23 @@
 
         @Override
         public void onBackButtonVisibilityChanged(boolean visible) {
-            getBackButton().setVisibility(visible ? VISIBLE : GONE);
+            if (!inScreenPinning()) {
+                getBackButton().setVisibility(visible ? VISIBLE : GONE);
+            }
+        }
+
+        @Override
+        public void onHomeButtonVisibilityChanged(boolean visible) {
+            getHomeButton().setVisibility(visible ? VISIBLE : GONE);
+        }
+
+        @Override
+        public void onColorAdaptChanged(boolean enabled) {
+            if (enabled) {
+                mColorAdaptionController.start();
+            } else {
+                mColorAdaptionController.end();
+            }
         }
     };
 
@@ -334,6 +351,11 @@
         mPrototypeController = new NavigationPrototypeController(mHandler, mContext);
         mPrototypeController.register();
         mPrototypeController.setOnPrototypeChangedListener(mPrototypeListener);
+        mColorAdaptionController = new NavBarTintController(this, getLightTransitionsController());
+    }
+
+    public NavBarTintController getColorAdaptionController() {
+        return mColorAdaptionController;
     }
 
     public BarTransitions getBarTransitions() {
@@ -655,6 +677,7 @@
         // TODO(b/113914868): investigation log for disappearing home button
         Log.i(TAG, "updateNavButtonIcons (b/113914868): home disabled=" + disableHome
                 + " mDisabledFlags=" + mDisabledFlags);
+        disableHome |= mPrototypeController.hideHomeButton();
 
         // Always disable recents when alternate car mode UI is active and for secondary displays.
         boolean disableRecent = isRecentsButtonDisabled();
@@ -925,6 +948,10 @@
     public void showPinningEnterExitToast(boolean entering) {
         if (entering) {
             mScreenPinningNotify.showPinningStartToast();
+
+            // TODO(b/112934365): remove after prototype finished, only needed to escape from pin
+            getBackButton().setVisibility(VISIBLE);
+            getHomeButton().setVisibility(VISIBLE);
         } else {
             mScreenPinningNotify.showPinningExitToast();
         }
@@ -1097,6 +1124,7 @@
         Dependency.get(PluginManager.class).addPluginListener(this,
                 NavGesture.class, false /* Only one */);
         setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
+        mColorAdaptionController.start();
     }
 
     @Override
@@ -1107,6 +1135,7 @@
             mGestureHelper.destroy();
         }
         mPrototypeController.unregister();
+        mColorAdaptionController.stop();
         setUpSwipeUpOnboarding(false);
         for (int i = 0; i < mButtonDispatchers.size(); ++i) {
             mButtonDispatchers.valueAt(i).onDestroy();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
index b11b6d4..fb6254b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
@@ -34,9 +34,11 @@
  */
 public class NavigationPrototypeController extends ContentObserver {
     private static final String HIDE_BACK_BUTTON_SETTING = "quickstepcontroller_hideback";
+    private static final String HIDE_HOME_BUTTON_SETTING = "quickstepcontroller_hidehome";
 
     static final String NAVBAR_EXPERIMENTS_DISABLED = "navbarexperiments_disabled";
     private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map";
+    public static final String NAV_COLOR_ADAPT_ENABLE_SETTING = "navbar_color_adapt_enable";
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({ACTION_DEFAULT, ACTION_QUICKSTEP, ACTION_QUICKSCRUB, ACTION_BACK})
@@ -72,7 +74,9 @@
      */
     public void register() {
         registerObserver(HIDE_BACK_BUTTON_SETTING);
+        registerObserver(HIDE_HOME_BUTTON_SETTING);
         registerObserver(GESTURE_MATCH_SETTING);
+        registerObserver(NAV_COLOR_ADAPT_ENABLE_SETTING);
     }
 
     /**
@@ -86,19 +90,20 @@
     public void onChange(boolean selfChange, Uri uri) {
         super.onChange(selfChange, uri);
         if (!selfChange && mListener != null) {
-            try {
-                final String path = uri.getPath();
-                if (path.endsWith(GESTURE_MATCH_SETTING)) {
-                    // Get the settings gesture map corresponding to each action
-                    // {@see updateSwipeLTRBackSetting}
-                    updateSwipeLTRBackSetting();
-                    mListener.onGestureRemap(mActionMap);
-                } else if (path.endsWith(HIDE_BACK_BUTTON_SETTING)) {
-                    mListener.onBackButtonVisibilityChanged(
-                            !getGlobalBool(HIDE_BACK_BUTTON_SETTING));
-                }
-            } catch (SettingNotFoundException e) {
-                e.printStackTrace();
+            final String path = uri.getPath();
+            if (path.endsWith(GESTURE_MATCH_SETTING)) {
+                // Get the settings gesture map corresponding to each action
+                // {@see updateSwipeLTRBackSetting}
+                updateSwipeLTRBackSetting();
+                mListener.onGestureRemap(mActionMap);
+            } else if (path.endsWith(HIDE_BACK_BUTTON_SETTING)) {
+                mListener.onBackButtonVisibilityChanged(
+                        !getGlobalBool(HIDE_BACK_BUTTON_SETTING, false));
+            } else if (path.endsWith(HIDE_HOME_BUTTON_SETTING)) {
+                mListener.onHomeButtonVisibilityChanged(!hideHomeButton());
+            } else if (path.endsWith(NAV_COLOR_ADAPT_ENABLE_SETTING)) {
+                mListener.onColorAdaptChanged(
+                        NavBarTintController.isEnabled(mContext));
             }
         }
     }
@@ -112,6 +117,13 @@
     }
 
     /**
+     * @return if home button should be invisible
+     */
+    boolean hideHomeButton() {
+        return getGlobalBool(HIDE_HOME_BUTTON_SETTING, false /* default */);
+    }
+
+    /**
      * Since Settings.Global cannot pass arrays, use a string to represent each character as a
      * gesture map to actions corresponding to {@see GestureAction}. The number is represented as:
      * Number: [up] [down] [left] [right]
@@ -126,8 +138,8 @@
         }
     }
 
-    private boolean getGlobalBool(String name) throws SettingNotFoundException {
-        return Settings.Global.getInt(mContext.getContentResolver(), name) == 1;
+    private boolean getGlobalBool(String name, boolean defaultVal) {
+        return Settings.Global.getInt(mContext.getContentResolver(), name, defaultVal ? 1 : 0) == 1;
     }
 
     private void registerObserver(String name) {
@@ -138,5 +150,7 @@
     public interface OnPrototypeChangedListener {
         void onGestureRemap(@GestureAction int[] actions);
         void onBackButtonVisibilityChanged(boolean visible);
+        void onHomeButtonVisibilityChanged(boolean visible);
+        void onColorAdaptChanged(boolean enabled);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index 2a68fa5..d364356 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -17,11 +17,13 @@
 package com.android.systemui.statusbar.phone;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.Notification;
 import android.os.SystemClock;
 import android.service.notification.StatusBarNotification;
 import android.util.ArrayMap;
 
+import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dependency;
 import com.android.systemui.statusbar.AlertingNotificationManager;
 import com.android.systemui.statusbar.AmbientPulseManager;
@@ -29,7 +31,9 @@
 import com.android.systemui.statusbar.InflationTask;
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.AsyncInflationTask;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 import com.android.systemui.statusbar.phone.NotificationGroupManager.NotificationGroup;
@@ -38,17 +42,19 @@
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
 import java.util.Objects;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * A helper class dealing with the alert interactions between {@link NotificationGroupManager},
  * {@link HeadsUpManager}, {@link AmbientPulseManager}. In particular, this class deals with keeping
  * the correct notification in a group alerting based off the group suppression.
  */
-public class NotificationGroupAlertTransferHelper implements OnGroupChangeListener,
-        OnHeadsUpChangedListener, OnAmbientChangedListener, StateListener {
+@Singleton
+public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedListener,
+        OnAmbientChangedListener, StateListener {
 
     private static final long ALERT_TRANSFER_TIMEOUT = 300;
 
@@ -69,22 +75,32 @@
     private final NotificationGroupManager mGroupManager =
             Dependency.get(NotificationGroupManager.class);
 
-    // TODO(b/119637830): It would be good if GroupManager already had all pending notifications as
-    // normal children (i.e. add notifications to GroupManager before inflation) so that we don't
-    // have to have this dependency. We'd also have to worry less about the suppression not being up
-    // to date.
-    /**
-     * Notifications that are currently inflating for the first time. Used to remove an incorrectly
-     * alerting notification faster.
-     */
-    private HashMap<String, Entry> mPendingNotifications;
+    private NotificationEntryManager mEntryManager;
 
     private boolean mIsDozing;
 
+    @Inject
     public NotificationGroupAlertTransferHelper() {
         Dependency.get(StatusBarStateController.class).addCallback(this);
     }
 
+    /** Causes the TransferHelper to register itself as a listener to the appropriate classes. */
+    public void bind(NotificationEntryManager entryManager,
+            NotificationGroupManager groupManager) {
+        if (mEntryManager != null) {
+            throw new IllegalStateException("Already bound.");
+        }
+
+        // TODO(b/119637830): It would be good if GroupManager already had all pending notifications
+        // as normal children (i.e. add notifications to GroupManager before inflation) so that we
+        // don't have to have this dependency. We'd also have to worry less about the suppression
+        // not being up to date.
+        mEntryManager = entryManager;
+
+        mEntryManager.addNotificationEntryListener(mNotificationEntryListener);
+        groupManager.addOnGroupChangeListener(mOnGroupChangeListener);
+    }
+
     /**
      * Whether or not a notification has transferred its alert state to the notification and
      * the notification should alert after inflating.
@@ -92,30 +108,15 @@
      * @param entry notification to check
      * @return true if the entry was transferred to and should inflate + alert
      */
-    public boolean isAlertTransferPending(@NonNull Entry entry) {
+    public boolean isAlertTransferPending(@NonNull NotificationEntry entry) {
         PendingAlertInfo alertInfo = mPendingAlerts.get(entry.key);
         return alertInfo != null && alertInfo.isStillValid();
     }
 
-    /**
-     * Removes any alerts pending on this entry. Note that this will not stop any inflation tasks
-     * started by a transfer, so this should only be used as clean-up for when inflation is stopped
-     * and the pending alert no longer needs to happen.
-     *
-     * @param key notification key that may have info that needs to be cleaned up
-     */
-    public void cleanUpPendingAlertInfo(@NonNull String key) {
-        mPendingAlerts.remove(key);
-    }
-
     public void setHeadsUpManager(HeadsUpManager headsUpManager) {
         mHeadsUpManager = headsUpManager;
     }
 
-    public void setPendingEntries(HashMap<String, Entry> pendingNotifications) {
-        mPendingNotifications = pendingNotifications;
-    }
-
     @Override
     public void onStateChanged(int newState) {}
 
@@ -130,92 +131,103 @@
         mIsDozing = isDozing;
     }
 
-    @Override
-    public void onGroupCreated(NotificationGroup group, String groupKey) {
-        mGroupAlertEntries.put(groupKey, new GroupAlertEntry(group));
-    }
+    private final OnGroupChangeListener mOnGroupChangeListener = new OnGroupChangeListener() {
+        @Override
+        public void onGroupCreated(NotificationGroup group, String groupKey) {
+            mGroupAlertEntries.put(groupKey, new GroupAlertEntry(group));
+        }
 
-    @Override
-    public void onGroupRemoved(NotificationGroup group, String groupKey) {
-        mGroupAlertEntries.remove(groupKey);
-    }
+        @Override
+        public void onGroupRemoved(NotificationGroup group, String groupKey) {
+            mGroupAlertEntries.remove(groupKey);
+        }
 
-    @Override
-    public void onGroupSuppressionChanged(NotificationGroup group, boolean suppressed) {
-        AlertingNotificationManager alertManager = getActiveAlertManager();
-        if (suppressed) {
-            if (alertManager.isAlerting(group.summary.key)) {
-                handleSuppressedSummaryAlerted(group.summary, alertManager);
-            }
-        } else {
-            // Group summary can be null if we are no longer suppressed because the summary was
-            // removed. In that case, we don't need to alert the summary.
-            if (group.summary == null) {
-                return;
-            }
-            GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(mGroupManager.getGroupKey(
-                    group.summary.notification));
-            // Group is no longer suppressed. We should check if we need to transfer the alert
-            // back to the summary now that it's no longer suppressed.
-            if (groupAlertEntry.mAlertSummaryOnNextAddition) {
-                if (!alertManager.isAlerting(group.summary.key)) {
-                    alertNotificationWhenPossible(group.summary, alertManager);
+        @Override
+        public void onGroupSuppressionChanged(NotificationGroup group, boolean suppressed) {
+            AlertingNotificationManager alertManager = getActiveAlertManager();
+            if (suppressed) {
+                if (alertManager.isAlerting(group.summary.key)) {
+                    handleSuppressedSummaryAlerted(group.summary, alertManager);
                 }
-                groupAlertEntry.mAlertSummaryOnNextAddition = false;
             } else {
-                checkShouldTransferBack(groupAlertEntry);
+                // Group summary can be null if we are no longer suppressed because the summary was
+                // removed. In that case, we don't need to alert the summary.
+                if (group.summary == null) {
+                    return;
+                }
+                GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(mGroupManager.getGroupKey(
+                        group.summary.notification));
+                // Group is no longer suppressed. We should check if we need to transfer the alert
+                // back to the summary now that it's no longer suppressed.
+                if (groupAlertEntry.mAlertSummaryOnNextAddition) {
+                    if (!alertManager.isAlerting(group.summary.key)) {
+                        alertNotificationWhenPossible(group.summary, alertManager);
+                    }
+                    groupAlertEntry.mAlertSummaryOnNextAddition = false;
+                } else {
+                    checkShouldTransferBack(groupAlertEntry);
+                }
             }
         }
-    }
+    };
 
     @Override
-    public void onAmbientStateChanged(Entry entry, boolean isAmbient) {
+    public void onAmbientStateChanged(NotificationEntry entry, boolean isAmbient) {
         onAlertStateChanged(entry, isAmbient, mAmbientPulseManager);
     }
 
     @Override
-    public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) {
+    public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
         onAlertStateChanged(entry, isHeadsUp, mHeadsUpManager);
     }
 
-    private void onAlertStateChanged(Entry entry, boolean isAlerting,
+    private void onAlertStateChanged(NotificationEntry entry, boolean isAlerting,
             AlertingNotificationManager alertManager) {
         if (isAlerting && mGroupManager.isSummaryOfSuppressedGroup(entry.notification)) {
             handleSuppressedSummaryAlerted(entry, alertManager);
         }
     }
 
-    /**
-     * Called when the entry's reinflation has finished. If there is an alert pending, we then
-     * show the alert.
-     *
-     * @param entry entry whose inflation has finished
-     */
-    public void onInflationFinished(@NonNull Entry entry) {
-        PendingAlertInfo alertInfo = mPendingAlerts.remove(entry.key);
-        if (alertInfo != null) {
-            if (alertInfo.isStillValid()) {
-                alertNotificationWhenPossible(entry, getActiveAlertManager());
-            } else {
-                // The transfer is no longer valid. Free the content.
-                entry.getRow().freeContentViewWhenSafe(alertInfo.mAlertManager.getContentFlag());
+    private final NotificationEntryListener mNotificationEntryListener =
+            new NotificationEntryListener() {
+        // Called when a new notification has been posted but is not inflated yet. We use this to
+        // see as early as we can if we need to abort a transfer.
+        @Override
+        public void onPendingEntryAdded(NotificationEntry entry) {
+            String groupKey = mGroupManager.getGroupKey(entry.notification);
+            GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(groupKey);
+            if (groupAlertEntry != null) {
+                checkShouldTransferBack(groupAlertEntry);
             }
         }
-    }
 
-    /**
-     * Called when a new notification has been posted but is not inflated yet. We use this to see
-     * as early as we can if we need to abort a transfer.
-     *
-     * @param entry entry that has been added
-     */
-    public void onPendingEntryAdded(@NonNull Entry entry) {
-        String groupKey = mGroupManager.getGroupKey(entry.notification);
-        GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(groupKey);
-        if (groupAlertEntry != null) {
-            checkShouldTransferBack(groupAlertEntry);
+        // Called when the entry's reinflation has finished. If there is an alert pending, we
+        // then show the alert.
+        @Override
+        public void onEntryReinflated(NotificationEntry entry) {
+            PendingAlertInfo alertInfo = mPendingAlerts.remove(entry.key);
+            if (alertInfo != null) {
+                if (alertInfo.isStillValid()) {
+                    alertNotificationWhenPossible(entry, getActiveAlertManager());
+                } else {
+                    // The transfer is no longer valid. Free the content.
+                    entry.getRow().freeContentViewWhenSafe(
+                            alertInfo.mAlertManager.getContentFlag());
+                }
+            }
         }
-    }
+
+        @Override
+        public void onEntryRemoved(
+                @Nullable NotificationEntry entry,
+                NotificationVisibility visibility,
+                boolean removedByUser) {
+            // Removes any alerts pending on this entry. Note that this will not stop any inflation
+            // tasks started by a transfer, so this should only be used as clean-up for when
+            // inflation is stopped and the pending alert no longer needs to happen.
+            mPendingAlerts.remove(entry.key);
+        }
+    };
 
     /**
      * Gets the number of new notifications pending inflation that will be added to the group
@@ -225,12 +237,12 @@
      * @return the number of new notifications that will be added to the group
      */
     private int getPendingChildrenNotAlerting(@NonNull NotificationGroup group) {
-        if (mPendingNotifications == null) {
+        if (mEntryManager == null) {
             return 0;
         }
         int number = 0;
-        Collection<Entry> values = mPendingNotifications.values();
-        for (Entry entry : values) {
+        Iterable<NotificationEntry> values = mEntryManager.getPendingNotificationsIterator();
+        for (NotificationEntry entry : values) {
             if (isPendingNotificationInGroup(entry, group) && onlySummaryAlerts(entry)) {
                 number++;
             }
@@ -245,11 +257,11 @@
      * @return true if a pending notification will add to this group
      */
     private boolean pendingInflationsWillAddChildren(@NonNull NotificationGroup group) {
-        if (mPendingNotifications == null) {
+        if (mEntryManager == null) {
             return false;
         }
-        Collection<Entry> values = mPendingNotifications.values();
-        for (Entry entry : values) {
+        Iterable<NotificationEntry> values = mEntryManager.getPendingNotificationsIterator();
+        for (NotificationEntry entry : values) {
             if (isPendingNotificationInGroup(entry, group)) {
                 return true;
             }
@@ -264,7 +276,7 @@
      * @param group group to check
      * @return true if the notification will add to the group, false o/w
      */
-    private boolean isPendingNotificationInGroup(@NonNull Entry entry,
+    private boolean isPendingNotificationInGroup(@NonNull NotificationEntry entry,
             @NonNull NotificationGroup group) {
         String groupKey = mGroupManager.getGroupKey(group.summary.notification);
         return mGroupManager.isGroupChild(entry.notification)
@@ -281,7 +293,7 @@
      * @param summary the summary that is suppressed and alerting
      * @param alertManager the alert manager that manages the alerting summary
      */
-    private void handleSuppressedSummaryAlerted(@NonNull Entry summary,
+    private void handleSuppressedSummaryAlerted(@NonNull NotificationEntry summary,
             @NonNull AlertingNotificationManager alertManager) {
         StatusBarNotification sbn = summary.notification;
         GroupAlertEntry groupAlertEntry =
@@ -297,7 +309,7 @@
             return;
         }
 
-        Entry child = mGroupManager.getLogicalChildren(summary.notification).iterator().next();
+        NotificationEntry child = mGroupManager.getLogicalChildren(summary.notification).iterator().next();
         if (child != null) {
             if (child.getRow().keepInParent()
                     || child.isRowRemoved()
@@ -321,7 +333,7 @@
      * @param toEntry entry to transfer to
      * @param alertManager alert manager for the alert type
      */
-    private void transferAlertState(@NonNull Entry fromEntry, @NonNull Entry toEntry,
+    private void transferAlertState(@NonNull NotificationEntry fromEntry, @NonNull NotificationEntry toEntry,
             @NonNull AlertingNotificationManager alertManager) {
         alertManager.removeNotification(fromEntry.key, true /* releaseImmediately */);
         alertNotificationWhenPossible(toEntry, alertManager);
@@ -341,13 +353,13 @@
     private void checkShouldTransferBack(@NonNull GroupAlertEntry groupAlertEntry) {
         if (SystemClock.elapsedRealtime() - groupAlertEntry.mLastAlertTransferTime
                 < ALERT_TRANSFER_TIMEOUT) {
-            Entry summary = groupAlertEntry.mGroup.summary;
+            NotificationEntry summary = groupAlertEntry.mGroup.summary;
             AlertingNotificationManager alertManager = getActiveAlertManager();
 
             if (!onlySummaryAlerts(summary)) {
                 return;
             }
-            ArrayList<Entry> children = mGroupManager.getLogicalChildren(summary.notification);
+            ArrayList<NotificationEntry> children = mGroupManager.getLogicalChildren(summary.notification);
             int numChildren = children.size();
             int numPendingChildren = getPendingChildrenNotAlerting(groupAlertEntry.mGroup);
             numChildren += numPendingChildren;
@@ -356,7 +368,7 @@
             }
             boolean releasedChild = false;
             for (int i = 0; i < children.size(); i++) {
-                Entry entry = children.get(i);
+                NotificationEntry entry = children.get(i);
                 if (onlySummaryAlerts(entry) && alertManager.isAlerting(entry.key)) {
                     releasedChild = true;
                     alertManager.removeNotification(entry.key, true /* releaseImmediately */);
@@ -387,7 +399,7 @@
      * @param entry entry to show
      * @param alertManager alert manager for the alert type
      */
-    private void alertNotificationWhenPossible(@NonNull Entry entry,
+    private void alertNotificationWhenPossible(@NonNull NotificationEntry entry,
             @NonNull AlertingNotificationManager alertManager) {
         @InflationFlag int contentFlag = alertManager.getContentFlag();
         if (!entry.getRow().isInflationFlagSet(contentFlag)) {
@@ -407,7 +419,7 @@
         return mIsDozing ? mAmbientPulseManager : mHeadsUpManager;
     }
 
-    private boolean onlySummaryAlerts(Entry entry) {
+    private boolean onlySummaryAlerts(NotificationEntry entry) {
         return entry.notification.getNotification().getGroupAlertBehavior()
                 == Notification.GROUP_ALERT_SUMMARY;
     }
@@ -427,7 +439,7 @@
          * the transfer is still valid if the notification is updated.
          */
         final StatusBarNotification mOriginalNotification;
-        final Entry mEntry;
+        final NotificationEntry mEntry;
 
         /**
          * The notification is still pending inflation but we've decided that we no longer need
@@ -438,7 +450,7 @@
          */
         boolean mAbortOnInflation;
 
-        PendingAlertInfo(Entry entry, AlertingNotificationManager alertManager) {
+        PendingAlertInfo(NotificationEntry entry, AlertingNotificationManager alertManager) {
             mOriginalNotification = entry.notification;
             mEntry = entry;
             mAlertManager = alertManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 8f4369a..bb9e418 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -27,7 +27,7 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
@@ -39,9 +39,13 @@
 import java.util.Map;
 import java.util.Objects;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * A class to handle notifications and their corresponding groups.
  */
+@Singleton
 public class NotificationGroupManager implements OnHeadsUpChangedListener,
         OnAmbientChangedListener, StateListener {
 
@@ -54,6 +58,7 @@
     private AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
     private boolean mIsUpdatingUnchangedGroup;
 
+    @Inject
     public NotificationGroupManager() {
         Dependency.get(StatusBarStateController.class).addCallback(this);
     }
@@ -92,7 +97,7 @@
         }
     }
 
-    public void onEntryRemoved(NotificationData.Entry removed) {
+    public void onEntryRemoved(NotificationEntry removed) {
         onEntryRemovedInternal(removed, removed.notification);
         mIsolatedEntries.remove(removed.key);
     }
@@ -104,7 +109,7 @@
      * @param sbn the notification the entry has, which doesn't need to be the same as it's internal
      *            notification
      */
-    private void onEntryRemovedInternal(NotificationData.Entry removed,
+    private void onEntryRemovedInternal(NotificationEntry removed,
             final StatusBarNotification sbn) {
         String groupKey = getGroupKey(sbn);
         final NotificationGroup group = mGroupMap.get(groupKey);
@@ -131,7 +136,7 @@
         }
     }
 
-    public void onEntryAdded(final NotificationData.Entry added) {
+    public void onEntryAdded(final NotificationEntry added) {
         if (added.isRowRemoved()) {
             added.setDebugThrowable(new Throwable());
         }
@@ -147,7 +152,7 @@
             }
         }
         if (isGroupChild) {
-            NotificationData.Entry existing = group.children.get(added.key);
+            NotificationEntry existing = group.children.get(added.key);
             if (existing != null && existing != added) {
                 Throwable existingThrowable = existing.getDebugThrowable();
                 Log.wtf(TAG, "Inconsistent entries found with the same key " + added.key
@@ -164,9 +169,9 @@
             group.expanded = added.areChildrenExpanded();
             updateSuppression(group);
             if (!group.children.isEmpty()) {
-                ArrayList<NotificationData.Entry> childrenCopy
+                ArrayList<NotificationEntry> childrenCopy
                         = new ArrayList<>(group.children.values());
-                for (NotificationData.Entry child : childrenCopy) {
+                for (NotificationEntry child : childrenCopy) {
                     onEntryBecomingChild(child);
                 }
                 for (OnGroupChangeListener listener : mListeners) {
@@ -176,7 +181,7 @@
         }
     }
 
-    private void onEntryBecomingChild(NotificationData.Entry entry) {
+    private void onEntryBecomingChild(NotificationEntry entry) {
         if (shouldIsolate(entry)) {
             isolateNotification(entry);
         }
@@ -216,7 +221,7 @@
         return count;
     }
 
-    private NotificationData.Entry getIsolatedChild(String groupKey) {
+    private NotificationEntry getIsolatedChild(String groupKey) {
         for (StatusBarNotification sbn : mIsolatedEntries.values()) {
             if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn)) {
                 return mGroupMap.get(sbn.getKey()).summary;
@@ -225,7 +230,7 @@
         return null;
     }
 
-    public void onEntryUpdated(NotificationData.Entry entry,
+    public void onEntryUpdated(NotificationEntry entry,
             StatusBarNotification oldNotification) {
         String oldKey = oldNotification.getGroupKey();
         String newKey = entry.notification.getGroupKey();
@@ -262,7 +267,7 @@
         if (!isOnlyChild(sbn)) {
             return false;
         }
-        NotificationData.Entry logicalGroupSummary = getLogicalGroupSummary(sbn);
+        NotificationEntry logicalGroupSummary = getLogicalGroupSummary(sbn);
         return logicalGroupSummary != null
                 && !logicalGroupSummary.notification.equals(sbn);
     }
@@ -338,7 +343,7 @@
      * Get the summary of a specified status bar notification. For isolated notification this return
      * itself.
      */
-    public NotificationData.Entry getGroupSummary(StatusBarNotification sbn) {
+    public NotificationEntry getGroupSummary(StatusBarNotification sbn) {
         return getGroupSummary(getGroupKey(sbn));
     }
 
@@ -347,12 +352,12 @@
      * but the logical summary, i.e when a child is isolated, it still returns the summary as if
      * it wasn't isolated.
      */
-    public NotificationData.Entry getLogicalGroupSummary(StatusBarNotification sbn) {
+    public NotificationEntry getLogicalGroupSummary(StatusBarNotification sbn) {
         return getGroupSummary(sbn.getGroupKey());
     }
 
     @Nullable
-    private NotificationData.Entry getGroupSummary(String groupKey) {
+    private NotificationEntry getGroupSummary(String groupKey) {
         NotificationGroup group = mGroupMap.get(groupKey);
         //TODO: see if this can become an Entry
         return group == null ? null
@@ -366,13 +371,13 @@
      * @param summary summary of a group
      * @return list of the children
      */
-    public ArrayList<NotificationData.Entry> getLogicalChildren(StatusBarNotification summary) {
+    public ArrayList<NotificationEntry> getLogicalChildren(StatusBarNotification summary) {
         NotificationGroup group = mGroupMap.get(summary.getGroupKey());
         if (group == null) {
             return null;
         }
-        ArrayList<NotificationData.Entry> children = new ArrayList<>(group.children.values());
-        NotificationData.Entry isolatedChild = getIsolatedChild(summary.getGroupKey());
+        ArrayList<NotificationEntry> children = new ArrayList<>(group.children.values());
+        NotificationEntry isolatedChild = getIsolatedChild(summary.getGroupKey());
         if (isolatedChild != null) {
             children.add(isolatedChild);
         }
@@ -438,24 +443,24 @@
     }
 
     @Override
-    public void onHeadsUpPinned(NotificationData.Entry entry) {
+    public void onHeadsUpPinned(NotificationEntry entry) {
     }
 
     @Override
-    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
+    public void onHeadsUpUnPinned(NotificationEntry entry) {
     }
 
     @Override
-    public void onAmbientStateChanged(NotificationData.Entry entry, boolean isAmbient) {
+    public void onAmbientStateChanged(NotificationEntry entry, boolean isAmbient) {
         onAlertStateChanged(entry, isAmbient);
     }
 
     @Override
-    public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+    public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
         onAlertStateChanged(entry, isHeadsUp);
     }
 
-    private void onAlertStateChanged(NotificationData.Entry entry, boolean isAlerting) {
+    private void onAlertStateChanged(NotificationEntry entry, boolean isAlerting) {
         if (isAlerting) {
             if (shouldIsolate(entry)) {
                 isolateNotification(entry);
@@ -474,7 +479,7 @@
      * @return true if the entry should be isolated
      */
 
-    private boolean shouldIsolate(NotificationData.Entry entry) {
+    private boolean shouldIsolate(NotificationEntry entry) {
         StatusBarNotification sbn = entry.notification;
         NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
         if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) {
@@ -494,7 +499,7 @@
      *
      * @param entry the notification to isolate
      */
-    private void isolateNotification(NotificationData.Entry entry) {
+    private void isolateNotification(NotificationEntry entry) {
         StatusBarNotification sbn = entry.notification;
 
         // We will be isolated now, so lets update the groups
@@ -518,7 +523,7 @@
      *
      * @param entry the notification to un-isolate
      */
-    private void stopIsolatingNotification(NotificationData.Entry entry) {
+    private void stopIsolatingNotification(NotificationEntry entry) {
         StatusBarNotification sbn = entry.notification;
         if (mIsolatedEntries.containsKey(sbn.getKey())) {
             // not isolated anymore, we need to update the groups
@@ -559,8 +564,8 @@
     }
 
     public static class NotificationGroup {
-        public final HashMap<String, NotificationData.Entry> children = new HashMap<>();
-        public NotificationData.Entry summary;
+        public final HashMap<String, NotificationEntry> children = new HashMap<>();
+        public NotificationEntry summary;
         public boolean expanded;
         /**
          * Is this notification group suppressed, i.e its summary is hidden
@@ -575,7 +580,7 @@
                             ? Log.getStackTraceString(summary.getDebugThrowable())
                             : "");
             result += "\n    children size: " + children.size();
-            for (NotificationData.Entry child : children.values()) {
+            for (NotificationEntry child : children.values()) {
                 result += "\n      " + child.notification
                 + (child.getDebugThrowable() != null
                         ? Log.getStackTraceString(child.getDebugThrowable())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 2d5d562..1fb5484 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -19,14 +19,14 @@
 import com.android.internal.widget.ViewClippingUtil;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.tuner.TunerService;
 
 import java.util.ArrayList;
@@ -182,7 +182,7 @@
         return mStatusBar.getStatusBarHeight();
     }
 
-    protected boolean shouldShowNotificationIcon(NotificationData.Entry entry,
+    protected boolean shouldShowNotificationIcon(NotificationEntry entry,
             boolean showAmbient, boolean showLowPriority, boolean hideDismissed,
             boolean hideRepliedMessages) {
         if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) {
@@ -207,7 +207,7 @@
         }
 
         // showAmbient == show in shade but not shelf
-        if (!showAmbient && mEntryManager.getNotificationData().shouldSuppressStatusBar(entry)) {
+        if (!showAmbient && entry.shouldSuppressStatusBar()) {
             return false;
         }
 
@@ -247,7 +247,7 @@
      * @param hideDismissed should dismissed icons be hidden
      * @param hideRepliedMessages should messages that have been replied to be hidden
      */
-    private void updateIconsForLayout(Function<NotificationData.Entry, StatusBarIconView> function,
+    private void updateIconsForLayout(Function<NotificationEntry, StatusBarIconView> function,
             NotificationIconContainer hostLayout, boolean showAmbient, boolean showLowPriority,
             boolean hideDismissed, boolean hideRepliedMessages) {
         ArrayList<StatusBarIconView> toShow = new ArrayList<>(
@@ -257,7 +257,7 @@
         for (int i = 0; i < mNotificationScrollLayout.getChildCount(); i++) {
             View view = mNotificationScrollLayout.getChildAt(i);
             if (view instanceof ExpandableNotificationRow) {
-                NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry();
+                NotificationEntry ent = ((ExpandableNotificationRow) view).getEntry();
                 if (shouldShowNotificationIcon(ent, showAmbient, showLowPriority, hideDismissed,
                         hideRepliedMessages)) {
                     toShow.add(function.apply(ent));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index c7e4d34..d873b0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -38,7 +38,6 @@
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.os.PowerManager;
-import android.os.SystemProperties;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.MathUtils;
@@ -53,6 +52,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.keyguard.KeyguardClockSwitch;
 import com.android.keyguard.KeyguardStatusView;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Dependency;
@@ -76,9 +76,9 @@
 import com.android.systemui.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -106,23 +106,6 @@
     private static final boolean DEBUG = false;
 
     /**
-     * If passive interrupts expand the NSSL or not
-     */
-    private static final boolean EXPAND_ON_PASSIVE_INTERRUPT = SystemProperties.getBoolean(
-            "persist.sysui.expand_shade_on_wake_up", true);
-    /**
-     * If the notification panel should remain collapsed when the phone wakes up, even if the user
-     * presses power.
-     */
-    private static final boolean NEVER_EXPAND_WHEN_WAKING_UP = SystemProperties.getBoolean(
-            "persist.sysui.defer_notifications_on_lock_screen", false);
-    /**
-     * If waking up the phone should take you to SHADE_LOCKED instead of KEYGUARD
-     */
-    private static final boolean WAKE_UP_TO_SHADE = SystemProperties.getBoolean(
-            "persist.sysui.go_to_shade_on_wake_up", false);
-
-    /**
      * Fling expanding QS.
      */
     public static final int FLING_EXPAND = 0;
@@ -133,7 +116,7 @@
     public static final int FLING_COLLAPSE = 1;
 
     /**
-     * Fing until QS is completely hidden.
+     * Fling until QS is completely hidden.
      */
     public static final int FLING_HIDE = 2;
 
@@ -282,12 +265,6 @@
      */
     private float mLinearDarkAmount;
 
-    /**
-     * State where the device isn't dozing anymore, but the lock screen isn't fully awake.
-     * The screen will be dimmed down with the shade collapsed.
-     */
-    private boolean mSemiAwake;
-
     private boolean mPulsing;
     private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
     private boolean mNoVisibleNotifications = true;
@@ -335,6 +312,7 @@
             Dependency.get(NotificationLockscreenUserManager.class);
     private final ShadeController mShadeController =
             Dependency.get(ShadeController.class);
+    private int mDisplayId;
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -346,6 +324,7 @@
         mAlphaPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
         setPanelAlpha(255, false /* animate */);
         mCommandQueue = getComponent(context, CommandQueue.class);
+        mDisplayId = context.getDisplayId();
     }
 
     private void setStatusBar(StatusBar bar) {
@@ -359,6 +338,10 @@
         mKeyguardStatusBar = findViewById(R.id.keyguard_header);
         mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
 
+        KeyguardClockSwitch keyguardClockSwitch = findViewById(R.id.keyguard_clock_container);
+        ViewGroup bigClockContainer = findViewById(R.id.big_clock_container);
+        keyguardClockSwitch.setBigClockContainer(bigClockContainer);
+
         mNotificationContainerParent = findViewById(R.id.notification_container_parent);
         mNotificationStackScroller = findViewById(R.id.notification_stack_scroller);
         mNotificationStackScroller.setOnHeightChangedListener(this);
@@ -598,7 +581,6 @@
         }
         mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
         mNotificationStackScroller.setAntiBurnInOffsetX(mClockPositionResult.clockX);
-        mKeyguardBottomArea.setBurnInXOffset(mClockPositionResult.clockX);
 
         mStackScrollerMeasuringPass++;
         requestScrollerTopPaddingUpdate(animate);
@@ -1251,11 +1233,6 @@
             updateDozingVisibilities(false /* animate */);
         }
 
-        // Expand notification shade if the device was is semi-awake state
-        if (mBarState == StatusBarState.SHADE && isSemiAwake()) {
-            mNotificationStackScroller.setDark(false /* dark */, false /* animated */,
-                    null /* touchLocation */);
-        }
         resetVerticalPanelPosition();
         updateQsState();
     }
@@ -2512,12 +2489,12 @@
     }
 
     @Override
-    public void onHeadsUpPinned(NotificationData.Entry entry) {
+    public void onHeadsUpPinned(NotificationEntry entry) {
         mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(), true);
     }
 
     @Override
-    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
+    public void onHeadsUpUnPinned(NotificationEntry entry) {
 
         // When we're unpinning the notification via active edge they remain heads-upped,
         // we need to make sure that an animation happens in this case, otherwise the notification
@@ -2530,7 +2507,7 @@
     }
 
     @Override
-    public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+    public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
         mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp);
     }
 
@@ -2621,7 +2598,7 @@
         }
         if (showIconsWhenExpanded != mShowIconsWhenExpanded) {
             mShowIconsWhenExpanded = showIconsWhenExpanded;
-            mCommandQueue.recomputeDisableFlags(false);
+            mCommandQueue.recomputeDisableFlags(mDisplayId, false);
         }
     }
 
@@ -2785,35 +2762,30 @@
         mNotificationStackScroller.setAnimationsEnabled(!disabled);
     }
 
-    public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation,
-            boolean passivelyInterrupted) {
+    /**
+     * Sets the dozing state.
+     *
+     * @param dozing {@code true} when dozing.
+     * @param animate if transition should be animated.
+     * @param wakeUpTouchLocation touch event location - if woken up by SLPI sensor.
+     */
+    public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation) {
         if (dozing == mDozing) return;
         mDozing = dozing;
-        boolean doNotExpand = (!EXPAND_ON_PASSIVE_INTERRUPT && passivelyInterrupted)
-                || NEVER_EXPAND_WHEN_WAKING_UP;
-        mSemiAwake = doNotExpand && !mDozing;
-        if (!mSemiAwake) {
-            mNotificationStackScroller.setDark(mDozing, animate, wakeUpTouchLocation);
-        }
+        mNotificationStackScroller.setDark(mDozing, animate, wakeUpTouchLocation);
 
         if (mBarState == StatusBarState.KEYGUARD
                 || mBarState == StatusBarState.SHADE_LOCKED) {
             updateDozingVisibilities(animate);
         }
 
-        final float darkAmount = dozing && !mSemiAwake ? 1 : 0;
-        if (!mSemiAwake) {
-            mStatusBarStateController.setDozeAmount(darkAmount, animate);
-        }
+        final float darkAmount = dozing ? 1 : 0;
+        mStatusBarStateController.setDozeAmount(darkAmount, animate);
         if (animate) {
             mNotificationStackScroller.notifyDarkAnimationStart(mDozing);
         }
     }
 
-    public boolean isSemiAwake() {
-        return mSemiAwake;
-    }
-
     @Override
     public void onDozeAmountChanged(float linearAmount, float amount) {
         mInterpolatedDarkAmount = amount;
@@ -2833,8 +2805,14 @@
         if (animatePulse) {
             mAnimateNextPositionUpdate = true;
         }
+        // Do not animate the clock when waking up from a pulse.
+        // The height callback will take care of pushing the clock to the right position.
+        if (!mPulsing && !mDozing) {
+            mAnimateNextPositionUpdate = false;
+        }
         mNotificationStackScroller.setPulsing(pulsing, animatePulse);
         mKeyguardStatusView.setPulsing(pulsing, animatePulse);
+        mKeyguardBottomArea.setPulsing(pulsing, animatePulse);
     }
 
     public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) {
@@ -2847,7 +2825,6 @@
     public void dozeTimeTick() {
         mKeyguardStatusBar.dozeTimeTick();
         mKeyguardStatusView.dozeTimeTick();
-        mKeyguardBottomArea.dozeTimeTick();
         if (mInterpolatedDarkAmount > 0) {
             positionClockAndNotifications();
         }
@@ -2886,7 +2863,7 @@
             if (hideIcons != mHideIconsDuringNotificationLaunch) {
                 mHideIconsDuringNotificationLaunch = hideIcons;
                 if (!hideIcons) {
-                    mCommandQueue.recomputeDisableFlags(true /* animate */);
+                    mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
                 }
             }
         }
@@ -3006,23 +2983,4 @@
     public void showTransientIndication(int id) {
         mKeyguardBottomArea.showTransientIndication(id);
     }
-
-    /**
-     * Whenever a user drags down on the empty area (pulling down the shade and clock) and lets go.
-     *
-     * @return {@code true} if dragging down should take the user to SHADE_LOCKED.
-     */
-    public boolean onDraggedDown() {
-        if (isSemiAwake()) {
-            mSemiAwake = false;
-            mNotificationStackScroller.setDark(false /* dark */, true /* animate */,
-                    null /* touchLocation */);
-            mStatusBarStateController.setDozeAmount(0f, true /* animated */);
-            mNotificationStackScroller.notifyDarkAnimationStart(mDozing);
-            mStatusBar.updateScrimController();
-
-            return WAKE_UP_TO_SHADE;
-        }
-        return true;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index ee1eb42..43c35f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -85,7 +85,6 @@
 import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.LocationController.LocationChangeCallback;
 import com.android.systemui.statusbar.policy.NextAlarmController;
 import com.android.systemui.statusbar.policy.RotationLockController;
 import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
@@ -102,9 +101,8 @@
  * strictly doesn't need to.
  */
 public class PhoneStatusBarPolicy implements Callback, Callbacks,
-        RotationLockControllerCallback, Listener, LocationChangeCallback,
-        ZenModeController.Callback, DeviceProvisionedListener, KeyguardMonitor.Callback,
-        PrivacyItemController.Callback {
+        RotationLockControllerCallback, Listener, ZenModeController.Callback,
+        DeviceProvisionedListener, KeyguardMonitor.Callback, PrivacyItemController.Callback {
     private static final String TAG = "PhoneStatusBarPolicy";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -255,6 +253,9 @@
         mIconController.setIconVisibility(mSlotMicrophone, false);
         mIconController.setIcon(mSlotCamera, R.drawable.stat_sys_camera, null);
         mIconController.setIconVisibility(mSlotCamera, false);
+        mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
+                mContext.getString(R.string.accessibility_location_active));
+        mIconController.setIconVisibility(mSlotLocation, false);
 
         mRotationLockController.addCallback(this);
         mBluetooth.addCallback(this);
@@ -265,7 +266,6 @@
         mNextAlarmController.addCallback(mNextAlarmCallback);
         mDataSaver.addCallback(this);
         mKeyguardMonitor.addCallback(this);
-        mLocationController.addCallback(this);
         mPrivacyItemController.setListening(true);
 
         SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallback(this);
@@ -294,7 +294,6 @@
         mNextAlarmController.removeCallback(mNextAlarmCallback);
         mDataSaver.removeCallback(this);
         mKeyguardMonitor.removeCallback(this);
-        mLocationController.removeCallback(this);
         mPrivacyItemController.setListening(false);
         SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallback(this);
         mContext.unregisterReceiver(mIntentReceiver);
@@ -314,21 +313,6 @@
         updateVolumeZen();
     }
 
-    @Override
-    public void onLocationActiveChanged(boolean active) {
-        updateLocation();
-    }
-
-    // Updates the status view based on the current state of location requests.
-    private void updateLocation() {
-        if (mLocationController.isLocationActive()) {
-            mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
-                    mContext.getString(R.string.accessibility_location_active));
-        } else {
-            mIconController.removeAllIconsForSlot(mSlotLocation);
-        }
-    }
-
     private void updateAlarm() {
         final AlarmClockInfo alarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
         final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0;
@@ -752,9 +736,12 @@
             };
 
     @Override
-    public void appTransitionStarting(long startTime, long duration, boolean forced) {
-        updateManagedProfile();
-        updateForegroundInstantApps();
+    public void appTransitionStarting(int displayId, long startTime, long duration,
+            boolean forced) {
+        if (mContext.getDisplayId() == displayId) {
+            updateManagedProfile();
+            updateForegroundInstantApps();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 2129835..7a3d03f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -43,9 +43,9 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
 
 import java.util.Objects;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 9f34cbb..e25c829 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -93,12 +93,12 @@
      * A scrim varies its opacity based on a busyness factor, for example
      * how many notifications are currently visible.
      */
-    public static final float GRADIENT_SCRIM_ALPHA_BUSY = 0.70f;
+    public static final float GRADIENT_SCRIM_ALPHA_BUSY = 0.7f;
     /**
-     * A scrim varies its opacity based on a busyness factor, for example
-     * how many notifications are currently visible.
+     * Scrim opacity when a wallpaper doesn't support ambient mode.
      */
-    public static final float GRADIENT_SCRIM_DARK_KEYGUARD = 0.80f;
+    public static final float PULSING_WALLPAPER_SCRIM_ALPHA = 0.6f;
+
     /**
      * The most common scrim, the one under the keyguard.
      */
@@ -282,8 +282,7 @@
                 mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
                         AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
             }
-        // Do not re-schedule timeout when pulsing, let's save some extra battery.
-        } else if (mState != ScrimState.PULSING) {
+        } else {
             mTimeTicker.cancel();
             mWallpaperVisibilityTimedOut = false;
         }
@@ -365,7 +364,7 @@
             mExpansionFraction = fraction;
 
             final boolean keyguardOrUnlocked = mState == ScrimState.UNLOCKED
-                    || mState == ScrimState.KEYGUARD || mState == ScrimState.DARK_KEYGUARD;
+                    || mState == ScrimState.KEYGUARD;
             if (!keyguardOrUnlocked || !mExpansionAffectsAlpha) {
                 return;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 75f81c3..fb3c4aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar.phone;
 
 import android.graphics.Color;
-import android.os.SystemProperties;
 import android.os.Trace;
 import android.util.MathUtils;
 
@@ -68,27 +67,6 @@
     },
 
     /**
-     * On semi-awake lock screen.
-     */
-    DARK_KEYGUARD(7) {
-
-        @Override
-        public void prepare(ScrimState previousState) {
-            mBlankScreen = mDisplayRequiresBlanking && previousState != ScrimState.AOD;
-            mAnimationDuration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
-            String opacity = SystemProperties.get("persist.sysui.aod2_scrim_opacity", "0.8");
-            try {
-                mCurrentBehindAlpha = Float.parseFloat(opacity);
-            } catch (RuntimeException e) {
-                mCurrentBehindAlpha = ScrimController.GRADIENT_SCRIM_DARK_KEYGUARD;
-            }
-            mCurrentInFrontAlpha = 0;
-            mCurrentInFrontTint = Color.BLACK;
-            mCurrentBehindTint = Color.BLACK;
-        }
-    },
-
-    /**
      * Showing password challenge on the keyguard.
      */
     BOUNCER(1) {
@@ -163,7 +141,8 @@
 
         @Override
         public float getBehindAlpha(float busyness) {
-            return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f : 1f;
+            return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f
+                    : ScrimController.PULSING_WALLPAPER_SCRIM_ALPHA;
         }
     },
 
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 1d6a1e8..6d78f72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -20,9 +20,10 @@
 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
+import static android.app.StatusBarManager.WindowType;
+import static android.app.StatusBarManager.WindowVisibleState;
 import static android.app.StatusBarManager.windowStateToString;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
-import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
@@ -37,6 +38,7 @@
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
+import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -71,7 +73,6 @@
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
 import android.media.AudioAttributes;
 import android.metrics.LogMaker;
 import android.net.Uri;
@@ -79,6 +80,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.RemoteException;
@@ -150,15 +152,17 @@
 import com.android.systemui.doze.DozeReceiver;
 import com.android.systemui.fragments.ExtensionFragmentListener;
 import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.keyguard.KeyguardSliceProvider;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.PluginDependencyProvider;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
 import com.android.systemui.qs.QSFragment;
 import com.android.systemui.qs.QSPanel;
-import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.ScreenPinningRequest;
 import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -168,11 +172,11 @@
 import com.android.systemui.statusbar.BackDropView;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.DisplayNavigationBarController;
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.GestureRecorder;
 import com.android.systemui.statusbar.KeyboardShortcuts;
 import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.NavigationBarController;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationMediaManager;
@@ -186,10 +190,13 @@
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.NotificationClicker;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -200,7 +207,6 @@
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
 import com.android.systemui.statusbar.policy.ExtensionController;
@@ -216,6 +222,7 @@
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.util.InjectionInflationController;
 import com.android.systemui.volume.VolumeComponent;
 
 import java.io.FileDescriptor;
@@ -224,6 +231,10 @@
 import java.util.ArrayList;
 import java.util.Map;
 
+import javax.inject.Inject;
+
+import dagger.Subcomponent;
+
 public class StatusBar extends SystemUI implements DemoMode,
         ActivityStarter, OnUnlockMethodChangedListener,
         OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
@@ -279,10 +290,6 @@
 
     protected static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
 
-    private static final int STATUS_OR_NAV_TRANSIENT =
-            View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
-    private static final long AUTOHIDE_TIMEOUT_MS = 2250;
-
     /**
      * The delay to reset the hint text when the hint animation is finished running.
      */
@@ -331,6 +338,8 @@
     protected BiometricUnlockController mBiometricUnlockController;
     private LightBarController mLightBarController;
     protected LockscreenWallpaper mLockscreenWallpaper;
+    @VisibleForTesting
+    protected AutoHideController mAutoHideController;
 
     private int mNaturalBarHeight = -1;
 
@@ -349,6 +358,8 @@
     private final Object mQueueLock = new Object();
 
     protected StatusBarIconController mIconController;
+    @Inject
+    InjectionInflationController mInjectionInflater;
 
     // expanded notifications
     protected NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
@@ -374,11 +385,15 @@
     private NotificationGutsManager mGutsManager;
     protected NotificationLogger mNotificationLogger;
     protected NotificationEntryManager mEntryManager;
+    private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+    private NotificationRowBinder mNotificationRowBinder;
     protected NotificationViewHierarchyManager mViewHierarchyManager;
     protected ForegroundServiceController mForegroundServiceController;
     protected AppOpsController mAppOpsController;
     protected KeyguardViewMediator mKeyguardViewMediator;
     private ZenModeController mZenController;
+    private final NotificationAlertingManager mNotificationAlertingManager =
+            Dependency.get(NotificationAlertingManager.class);
 
     // for disabling the status bar
     private int mDisabled1 = 0;
@@ -389,9 +404,6 @@
     private final Rect mLastFullscreenStackBounds = new Rect();
     private final Rect mLastDockedStackBounds = new Rect();
 
-    // last value sent to window manager
-    private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE;
-
     private final DisplayMetrics mDisplayMetrics = Dependency.get(DisplayMetrics.class);
 
     // XXX: gesture research
@@ -431,21 +443,13 @@
     protected final H mHandler = createHandler();
 
     private int mInteractingWindows;
-    private boolean mAutohideSuspended;
-    private int mStatusBarMode;
+    private @TransitionMode int mStatusBarMode;
 
     private ViewMediatorCallback mKeyguardViewMediatorCallback;
     protected ScrimController mScrimController;
     protected DozeScrimController mDozeScrimController;
     private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
 
-    private final Runnable mAutohide = () -> {
-        int requested = mSystemUiVisibility & ~STATUS_OR_NAV_TRANSIENT;
-        if (mSystemUiVisibility != requested) {
-            notifyUiVisibilityChanged(requested);
-        }
-    };
-
     protected boolean mDozing;
     private boolean mDozingRequested;
 
@@ -484,7 +488,7 @@
 
     private Runnable mLaunchTransitionEndRunnable;
     protected boolean mLaunchTransitionFadingAway;
-    private NotificationData.Entry mDraggedDownEntry;
+    private NotificationEntry mDraggedDownEntry;
     private boolean mLaunchCameraOnScreenTurningOn;
     private boolean mLaunchCameraOnFinishedGoingToSleep;
     private int mLastCameraLaunchSource;
@@ -568,11 +572,7 @@
                     mEntryManager.updateNotifications();
                 }
             };
-
-    protected DisplayManager mDisplayManager;
-
-    private NavigationBarFragment mNavigationBar;
-    private View mNavigationBarView;
+    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
 
     private HeadsUpAppearanceController mHeadsUpAppearanceController;
     private boolean mVibrateOnOpening;
@@ -617,6 +617,9 @@
         mGutsManager = Dependency.get(NotificationGutsManager.class);
         mMediaManager = Dependency.get(NotificationMediaManager.class);
         mEntryManager = Dependency.get(NotificationEntryManager.class);
+        mNotificationInterruptionStateProvider =
+                Dependency.get(NotificationInterruptionStateProvider.class);
+        mNotificationRowBinder = Dependency.get(NotificationRowBinder.class);
         mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class);
         mForegroundServiceController = Dependency.get(ForegroundServiceController.class);
         mAppOpsController = Dependency.get(AppOpsController.class);
@@ -624,9 +627,15 @@
         mKeyguardViewMediator = getComponent(KeyguardViewMediator.class);
         mColorExtractor = Dependency.get(SysuiColorExtractor.class);
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
-        mNavigationBarController = Dependency.get(DisplayNavigationBarController.class);
+        mNavigationBarController = Dependency.get(NavigationBarController.class);
         mBubbleController = Dependency.get(BubbleController.class);
         mBubbleController.setExpandListener(mBubbleExpandListener);
+        KeyguardSliceProvider sliceProvider = KeyguardSliceProvider.getAttachedInstance();
+        if (sliceProvider != null) {
+            sliceProvider.initDependencies(mMediaManager, mStatusBarStateController);
+        } else {
+            Log.w(TAG, "Cannot init KeyguardSliceProvider dependencies");
+        }
 
         mColorExtractor.addOnColorsChangedListener(this);
         mStatusBarStateController.addCallback(this, StatusBarStateController.RANK_STATUS_BAR);
@@ -636,6 +645,7 @@
                 ServiceManager.checkService(DreamService.DREAM_SERVICE));
 
         mDisplay = mWindowManager.getDefaultDisplay();
+        mDisplayId = mDisplay.getDisplayId();
         updateDisplaySize();
 
         Resources res = mContext.getResources();
@@ -691,11 +701,11 @@
         // Set up the initial notification state. This needs to happen before CommandQueue.disable()
         setUpPresenter();
 
-        setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
+        setSystemUiVisibility(mDisplayId, switches[1], switches[7], switches[8], 0xffffffff,
                 fullscreenStackBounds, dockedStackBounds);
-        topAppWindowChanged(switches[2] != 0);
+        topAppWindowChanged(mDisplayId, switches[2] != 0);
         // StatusBarManagerService has a back up of IME token and it's restored here.
-        setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
+        setImeWindowStatus(mDisplayId, binders.get(0), switches[3], switches[4], switches[5] != 0);
 
         // Set up the initial icon state
         int N = iconSlots.size();
@@ -784,6 +794,9 @@
         mNotificationIconAreaController.setupShelf(mNotificationShelf);
 
         Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mNotificationIconAreaController);
+        // Allow plugins to reference DarkIconDispatcher
+        Dependency.get(PluginDependencyProvider.class)
+                .allowPluginDependency(DarkIconDispatcher.class);
         FragmentHostManager.get(mStatusBarWindow)
                 .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
                     CollapsedStatusBarFragment statusBarFragment =
@@ -844,6 +857,7 @@
         mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
         mGroupManager.setHeadsUpManager(mHeadsUpManager);
         mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager);
+        mNotificationLogger.setHeadsUpManager(mHeadsUpManager);
         putComponent(HeadsUpManager.class, mHeadsUpManager);
 
         createNavigationBar();
@@ -878,11 +892,10 @@
             }
         });
 
-        // TODO(115978725): Support light bar controller on external nav bars.
+        mAutoHideController = Dependency.get(AutoHideController.class);
+        mAutoHideController.setStatusBar(this);
+
         mLightBarController = Dependency.get(LightBarController.class);
-        if (mNavigationBar != null) {
-            mNavigationBar.setLightBarController(mLightBarController);
-        }
 
         ScrimView scrimBehind = mStatusBarWindow.findViewById(R.id.scrim_behind);
         ScrimView scrimInFront = mStatusBarWindow.findViewById(R.id.scrim_in_front);
@@ -923,8 +936,6 @@
                             .withPlugin(QS.class)
                             .withDefault(this::createDefaultQSFragment)
                             .build());
-            final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
-                    mIconController);
             mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow,
                     (visible) -> {
                         mBrightnessMirrorVisible = visible;
@@ -933,7 +944,6 @@
             fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
                 QS qs = (QS) f;
                 if (qs instanceof QSFragment) {
-                    ((QSFragment) qs).setHost(qsh);
                     mQSPanel = ((QSFragment) qs).getQsPanel();
                     mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
                 }
@@ -1010,10 +1020,10 @@
     }
 
     protected QS createDefaultQSFragment() {
-        return new QSFragment();
+        return FragmentHostManager.get(mStatusBarWindow).create(QSFragment.class);
     }
 
-    protected void setUpPresenter() {
+    private void setUpPresenter() {
         // Set up the initial notification state.
         mActivityLaunchAnimator = new ActivityLaunchAnimator(
                 mStatusBarWindow, this, mNotificationPanel,
@@ -1021,7 +1031,8 @@
 
         mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel,
                 mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController,
-                mScrimController, mActivityLaunchAnimator, mStatusBarKeyguardViewManager);
+                mScrimController, mActivityLaunchAnimator, mStatusBarKeyguardViewManager,
+                mNotificationAlertingManager);
 
         mAppOpsController.addCallback(APP_OPS, this);
         mNotificationListener.setUpWithPresenter(mPresenter);
@@ -1031,7 +1042,10 @@
         mNotificationActivityStarter = new StatusBarNotificationActivityStarter(
                 mContext, mNotificationPanel, mPresenter, mHeadsUpManager, mActivityLaunchAnimator);
         mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
-        mEntryManager.setNotificationActivityStarter(mNotificationActivityStarter);
+        mNotificationRowBinder.setNotificationClicker(new NotificationClicker(
+                this, Dependency.get(BubbleController.class), mNotificationActivityStarter));
+
+        mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager);
     }
 
     /**
@@ -1040,7 +1054,7 @@
      * @param state2 disable2 flags
      */
     protected void setUpDisableFlags(int state1, int state2) {
-        mCommandQueue.disable(state1, state2, false /* animate */);
+        mCommandQueue.disable(mDisplayId, state1, state2, false /* animate */);
     }
 
     @Override
@@ -1067,25 +1081,10 @@
         }
     }
 
+    // TODO(b/117478341): This was left such that CarStatusBar can override this method.
+    // Try to remove this.
     protected void createNavigationBar() {
-        try {
-            // TODO(117478341): Move this into DisplayNavigationBarController#createNavigationBars
-            // for-loop. We will also move the whole navigation bar logic together.
-            final boolean showNav = mWindowManagerService.hasNavigationBar(DEFAULT_DISPLAY);
-            if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
-            if (!showNav) return;
-
-            mNavigationBarView = NavigationBarFragment.create(mContext, (tag, fragment) -> {
-                mNavigationBar = (NavigationBarFragment) fragment;
-                if (mLightBarController != null) {
-                    mNavigationBar.setLightBarController(mLightBarController);
-                }
-                mNavigationBar.setCurrentSysuiVisibility(mSystemUiVisibility);
-            });
-        } catch (RemoteException ex) {
-            // no window manager? good luck with that
-        }
-        mNavigationBarController.createNavigationBars();
+        mNavigationBarController.createNavigationBars(true /* includeDefaultDisplay */);
     }
 
     /**
@@ -1094,7 +1093,7 @@
      */
     protected View.OnTouchListener getStatusBarWindowTouchListener() {
         return (v, event) -> {
-            checkUserAutohide(event);
+            mAutoHideController.checkUserAutoHide(event);
             mRemoteInputManager.checkRemoteInputOutside(event);
             if (event.getAction() == MotionEvent.ACTION_DOWN) {
                 if (mExpandedVisible) {
@@ -1157,6 +1156,10 @@
         if (mBrightnessMirrorController != null) {
             mBrightnessMirrorController.onOverlayChanged();
         }
+        // We need the new R.id.keyguard_indication_area before recreating
+        // mKeyguardIndicationController
+        mNotificationPanel.onThemeChanged();
+        onThemeChanged();
     }
 
     @Override
@@ -1174,8 +1177,8 @@
     }
 
     protected void inflateStatusBarWindow(Context context) {
-        mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
-                R.layout.super_status_bar, null);
+        mStatusBarWindow = (StatusBarWindowView) mInjectionInflater.injectable(
+                LayoutInflater.from(context)).inflate(R.layout.super_status_bar, null);
     }
 
     protected void startKeyguard() {
@@ -1225,8 +1228,7 @@
         }
         int dockSide = WindowManagerProxy.getInstance().getDockSide();
         if (dockSide == WindowManager.DOCKED_INVALID) {
-            final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition(
-                    mDisplay.getDisplayId());
+            final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition(mDisplayId);
             if (navbarPos == NAV_BAR_POS_INVALID) {
                 return false;
             }
@@ -1335,7 +1337,10 @@
      * State is one or more of the DISABLE constants from StatusBarManager.
      */
     @Override
-    public void disable(int state1, int state2, boolean animate) {
+    public void disable(int displayId, int state1, int state2, boolean animate) {
+        if (displayId != mDisplayId) {
+            return;
+        }
         state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
 
         animate &= mStatusBarWindowState != WINDOW_STATE_HIDDEN;
@@ -1399,7 +1404,7 @@
         }
 
         if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
-            mEntryManager.setDisableNotificationAlerts(
+            mNotificationInterruptionStateProvider.setDisableNotificationAlerts(
                     (state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0);
         }
 
@@ -1504,21 +1509,21 @@
     }
 
     @Override
-    public void onHeadsUpPinned(NotificationData.Entry entry) {
+    public void onHeadsUpPinned(NotificationEntry entry) {
         dismissVolumeDialog();
     }
 
     @Override
-    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
+    public void onHeadsUpUnPinned(NotificationEntry entry) {
     }
 
     @Override
-    public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) {
+    public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
         mEntryManager.updateNotificationRanking(null /* rankingMap */);
     }
 
     @Override
-    public void onAmbientStateChanged(Entry entry, boolean isAmbient) {
+    public void onAmbientStateChanged(NotificationEntry entry, boolean isAmbient) {
         mEntryManager.updateNotificationRanking(null);
         if (isAmbient) {
             mDozeServiceHost.fireNotificationPulse();
@@ -1613,10 +1618,10 @@
                 mWereIconsJustHidden = true;
                 mHandler.postDelayed(() -> {
                     mWereIconsJustHidden = false;
-                    mCommandQueue.recomputeDisableFlags(true);
+                    mCommandQueue.recomputeDisableFlags(mDisplayId, true);
                 }, 500);
             } else {
-                mCommandQueue.recomputeDisableFlags(animate);
+                mCommandQueue.recomputeDisableFlags(mDisplayId, animate);
             }
         }
         if (shouldHideIconsForBouncer) {
@@ -1782,7 +1787,7 @@
         mStatusBarWindowController.setPanelVisible(true);
 
         visibilityChanged(true);
-        mCommandQueue.recomputeDisableFlags(!force /* animate */);
+        mCommandQueue.recomputeDisableFlags(mDisplayId, !force /* animate */);
         setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
     }
 
@@ -1955,7 +1960,7 @@
             Log.d(TAG, "Not showing bouncer due to activity showing over lockscreen");
         }
         mCommandQueue.recomputeDisableFlags(
-                mNotificationPanel.hideStatusBarIconsWhenExpanded() /* animate */);
+                mDisplayId, mNotificationPanel.hideStatusBarIconsWhenExpanded() /* animate */);
 
         // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
         // the bouncer appear animation.
@@ -2012,7 +2017,11 @@
     }
 
     @Override // CommandQueue
-    public void setWindowState(int window, int state) {
+    public void setWindowState(
+            int displayId, @WindowType int window, @WindowVisibleState int state) {
+        if (displayId != mDisplayId) {
+            return;
+        }
         boolean showing = state == WINDOW_STATE_SHOWING;
         if (mStatusBarWindow != null
                 && window == StatusBarManager.WINDOW_STATUS_BAR
@@ -2031,14 +2040,17 @@
     }
 
     @Override // CommandQueue
-    public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
-            int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+    public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
+            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+        if (displayId != mDisplayId) {
+            return;
+        }
         final int oldVal = mSystemUiVisibility;
         final int newVal = (oldVal&~mask) | (vis&mask);
         final int diff = newVal ^ oldVal;
         if (DEBUG) Log.d(TAG, String.format(
-                "setSystemUiVisibility vis=%s mask=%s oldVal=%s newVal=%s diff=%s",
-                Integer.toHexString(vis), Integer.toHexString(mask),
+                "setSystemUiVisibility displayId=%d vis=%s mask=%s oldVal=%s newVal=%s diff=%s",
+                displayId, Integer.toHexString(vis), Integer.toHexString(mask),
                 Integer.toHexString(oldVal), Integer.toHexString(newVal),
                 Integer.toHexString(diff)));
         boolean sbModeChanged = false;
@@ -2052,7 +2064,6 @@
 
             // ready to unhide
             if ((vis & View.STATUS_BAR_UNHIDE) != 0) {
-                mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
                 mNoAnimationOnNextBarModeChange = true;
             }
 
@@ -2063,17 +2074,9 @@
             if (sbModeChanged && sbMode != mStatusBarMode) {
                 mStatusBarMode = sbMode;
                 checkBarModes();
-                touchAutoHide();
+                mAutoHideController.touchAutoHide();
             }
-
-            if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
-                mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
-            }
-
-            // send updated sysui visibility to window manager
-            notifyUiVisibilityChanged(mSystemUiVisibility);
         }
-
         mLightBarController.onSystemUiVisibilityChanged(fullscreenStackVis, dockedStackVis,
                 mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode);
     }
@@ -2101,52 +2104,45 @@
         }
     }
 
-    // TODO(115978725): Support auto hide on external nav bars.
-    void touchAutoHide() {
-        // update transient bar autohide
-        if (mStatusBarMode == MODE_SEMI_TRANSPARENT || (mNavigationBar != null
-                && mNavigationBar.isSemiTransparent())) {
-            scheduleAutohide();
-        } else {
-            cancelAutohide();
-        }
-    }
-
-    protected int computeStatusBarMode(int oldVal, int newVal) {
-        return computeBarMode(oldVal, newVal, View.STATUS_BAR_TRANSIENT,
-                View.STATUS_BAR_TRANSLUCENT, View.STATUS_BAR_TRANSPARENT);
+    protected @TransitionMode int computeStatusBarMode(int oldVal, int newVal) {
+        return computeBarMode(oldVal, newVal);
     }
 
     protected BarTransitions getStatusBarTransitions() {
         return mStatusBarView.getBarTransitions();
     }
 
-    protected int computeBarMode(int oldVis, int newVis,
-            int transientFlag, int translucentFlag, int transparentFlag) {
-        final int oldMode = barMode(oldVis, transientFlag, translucentFlag, transparentFlag);
-        final int newMode = barMode(newVis, transientFlag, translucentFlag, transparentFlag);
+    protected @TransitionMode int computeBarMode(int oldVis, int newVis) {
+        final int oldMode = barMode(oldVis);
+        final int newMode = barMode(newVis);
         if (oldMode == newMode) {
             return -1; // no mode change
         }
         return newMode;
     }
 
-    private int barMode(int vis, int transientFlag, int translucentFlag, int transparentFlag) {
-        int lightsOutTransparent = View.SYSTEM_UI_FLAG_LOW_PROFILE | transparentFlag;
-        return (vis & transientFlag) != 0 ? MODE_SEMI_TRANSPARENT
-                : (vis & translucentFlag) != 0 ? MODE_TRANSLUCENT
-                : (vis & lightsOutTransparent) == lightsOutTransparent ? MODE_LIGHTS_OUT_TRANSPARENT
-                : (vis & transparentFlag) != 0 ? MODE_TRANSPARENT
-                : (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0 ? MODE_LIGHTS_OUT
-                : MODE_OPAQUE;
+    private @TransitionMode int barMode(int vis) {
+        int lightsOutTransparent = View.SYSTEM_UI_FLAG_LOW_PROFILE | View.STATUS_BAR_TRANSPARENT;
+        if ((vis & View.STATUS_BAR_TRANSIENT) != 0) {
+            return MODE_SEMI_TRANSPARENT;
+        } else if ((vis & View.STATUS_BAR_TRANSLUCENT) != 0) {
+            return MODE_TRANSLUCENT;
+        } else if ((vis & lightsOutTransparent) == lightsOutTransparent) {
+            return MODE_LIGHTS_OUT_TRANSPARENT;
+        } else if ((vis & View.STATUS_BAR_TRANSPARENT) != 0) {
+            return MODE_TRANSPARENT;
+        } else if ((vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
+            return MODE_LIGHTS_OUT;
+        } else {
+            return MODE_OPAQUE;
+        }
     }
 
-    // TODO(115978725): Support animations on external nav bars.
     void checkBarModes() {
         if (mDemoMode) return;
         if (mStatusBarView != null) checkBarMode(mStatusBarMode, mStatusBarWindowState,
                 getStatusBarTransitions());
-        if (mNavigationBar != null) mNavigationBar.checkNavBarModes();
+        mNavigationBarController.checkNavBarModes(mDisplayId);
         mNoAnimationOnNextBarModeChange = false;
     }
 
@@ -2155,20 +2151,18 @@
         mNotificationPanel.setQsScrimEnabled(scrimEnabled);
     }
 
-    void checkBarMode(int mode, int windowState, BarTransitions transitions) {
+    void checkBarMode(@TransitionMode int mode, @WindowVisibleState int windowState,
+            BarTransitions transitions) {
         final boolean anim = !mNoAnimationOnNextBarModeChange && mDeviceInteractive
                 && windowState != WINDOW_STATE_HIDDEN;
         transitions.transitionTo(mode, anim);
     }
 
-    // TODO(115978725): Support animations on external nav bars.
     private void finishBarAnimations() {
         if (mStatusBarView != null) {
             mStatusBarView.getBarTransitions().finishAnimations();
         }
-        if (mNavigationBar != null) {
-            mNavigationBar.finishBarAnimations();
-        }
+        mNavigationBarController.finishBarAnimations(mDisplayId);
     }
 
     private final Runnable mCheckBarModes = this::checkBarModes;
@@ -2179,13 +2173,13 @@
                 ? (mInteractingWindows | barWindow)
                 : (mInteractingWindows & ~barWindow);
         if (mInteractingWindows != 0) {
-            suspendAutohide();
+            mAutoHideController.suspendAutoHide();
         } else {
-            resumeSuspendedAutohide();
+            mAutoHideController.resumeSuspendedAutoHide();
         }
         // manually dismiss the volume panel when interacting with the nav bar
         if (changing && interacting && barWindow == StatusBarManager.WINDOW_NAVIGATION_BAR) {
-            touchAutoDim();
+            mNavigationBarController.touchAutoDim(mDisplayId);
             dismissVolumeDialog();
         }
         checkBarModes();
@@ -2197,54 +2191,6 @@
         }
     }
 
-    private void resumeSuspendedAutohide() {
-        if (mAutohideSuspended) {
-            scheduleAutohide();
-            mHandler.postDelayed(mCheckBarModes, 500); // longer than home -> launcher
-        }
-    }
-
-    private void suspendAutohide() {
-        mHandler.removeCallbacks(mAutohide);
-        mHandler.removeCallbacks(mCheckBarModes);
-        mAutohideSuspended = (mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0;
-    }
-
-    private void cancelAutohide() {
-        mAutohideSuspended = false;
-        mHandler.removeCallbacks(mAutohide);
-    }
-
-    private void scheduleAutohide() {
-        cancelAutohide();
-        mHandler.postDelayed(mAutohide, AUTOHIDE_TIMEOUT_MS);
-    }
-
-    public void touchAutoDim() {
-        if (mNavigationBar != null) {
-            mNavigationBar.getBarTransitions().setAutoDim(false);
-        }
-        mHandler.removeCallbacks(mAutoDim);
-        if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
-            mHandler.postDelayed(mAutoDim, AUTOHIDE_TIMEOUT_MS);
-        }
-    }
-
-    void checkUserAutohide(MotionEvent event) {
-        if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0  // a transient bar is revealed
-                && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
-                && event.getX() == 0 && event.getY() == 0  // a touch outside both bars
-                && !mRemoteInputManager.getController()
-                        .isRemoteInputActive()) { // not due to typing in IME
-            userAutohide();
-        }
-    }
-
-    private void userAutohide() {
-        cancelAutohide();
-        mHandler.postDelayed(mAutohide, 350); // longer than app gesture -> flag clear
-    }
-
     private boolean areLightsOn() {
         return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
     }
@@ -2252,30 +2198,21 @@
     public void setLightsOn(boolean on) {
         Log.v(TAG, "setLightsOn(" + on + ")");
         if (on) {
-            setSystemUiVisibility(0, 0, 0, View.SYSTEM_UI_FLAG_LOW_PROFILE,
+            setSystemUiVisibility(mDisplayId, 0, 0, 0, View.SYSTEM_UI_FLAG_LOW_PROFILE,
                     mLastFullscreenStackBounds, mLastDockedStackBounds);
         } else {
-            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, 0, 0,
+            setSystemUiVisibility(mDisplayId, View.SYSTEM_UI_FLAG_LOW_PROFILE, 0, 0,
                     View.SYSTEM_UI_FLAG_LOW_PROFILE, mLastFullscreenStackBounds,
                     mLastDockedStackBounds);
         }
     }
 
-    private void notifyUiVisibilityChanged(int vis) {
-        try {
-            if (mLastDispatchedSystemUiVisibility != vis) {
-                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
-                mWindowManagerService.statusBarVisibilityChanged(Display.DEFAULT_DISPLAY, vis);
-                mLastDispatchedSystemUiVisibility = vis;
-            }
-        } catch (RemoteException ex) {
-        }
-    }
-
     @Override
-    public void topAppWindowChanged(boolean showMenu) {
+    public void topAppWindowChanged(int displayId, boolean showMenu) {
+        if (mDisplayId != displayId) return;
         if (SPEW) {
-            Log.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
+            Log.d(TAG, "display#" + displayId + ": "
+                    + (showMenu ? "showing" : "hiding") + " the MENU button");
         }
 
         // See above re: lights-out policy for legacy apps.
@@ -2613,11 +2550,11 @@
     };
 
     public void resetUserExpandedStates() {
-        ArrayList<Entry> activeNotifications = mEntryManager.getNotificationData()
+        ArrayList<NotificationEntry> activeNotifications = mEntryManager.getNotificationData()
                 .getActiveNotifications();
         final int notificationCount = activeNotifications.size();
         for (int i = 0; i < notificationCount; i++) {
-            NotificationData.Entry entry = activeNotifications.get(i);
+            NotificationEntry entry = activeNotifications.get(i);
             entry.resetUserExpansion();
         }
     }
@@ -2890,10 +2827,6 @@
             mWindowManager.removeViewImmediate(mStatusBarWindow);
             mStatusBarWindow = null;
         }
-        if (mNavigationBarView != null) {
-            mWindowManager.removeViewImmediate(mNavigationBarView);
-            mNavigationBarView = null;
-        }
         mNavigationBarController.destroy();
         mContext.unregisterReceiver(mBroadcastReceiver);
         mContext.unregisterReceiver(mDemoReceiver);
@@ -2963,15 +2896,12 @@
                     "transparent".equals(mode) ? MODE_TRANSPARENT :
                     "warning".equals(mode) ? MODE_WARNING :
                     -1;
-            // TODO(115978725): Support external nav bar transitions
             if (barMode != -1) {
                 boolean animate = true;
                 if (mStatusBarView != null) {
                     mStatusBarView.getBarTransitions().transitionTo(barMode, animate);
                 }
-                if (mNavigationBar != null) {
-                    mNavigationBar.getBarTransitions().transitionTo(barMode, animate);
-                }
+                mNavigationBarController.transitionTo(mDisplayId, barMode, animate);
             }
         }
         if (modeChange || command.equals(COMMAND_OPERATOR)) {
@@ -3109,7 +3039,7 @@
                     .setDuration(FADE_KEYGUARD_DURATION)
                     .withLayer()
                     .withEndAction(this::onLaunchTransitionFadingEnded);
-            mCommandQueue.appTransitionStarting(SystemClock.uptimeMillis(),
+            mCommandQueue.appTransitionStarting(mDisplayId, SystemClock.uptimeMillis(),
                     LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
         };
         if (mNotificationPanel.isLaunchTransitionRunning()) {
@@ -3197,12 +3127,9 @@
                 mDraggedDownEntry = null;
             }
 
-            // TODO(115978725): Support animations on external nav bars.
             // Disable layout transitions in navbar for this transition because the load is just
             // too heavy for the CPU and GPU on any device.
-            if (mNavigationBar != null) {
-                mNavigationBar.disableAnimationsDuringHide(delay);
-            }
+            mNavigationBarController.disableAnimationsDuringHide(mDisplayId, delay);
         } else if (!mNotificationPanel.isCollapsing()) {
             instantCollapseNotificationPanel();
         }
@@ -3235,7 +3162,7 @@
         // Treat Keyguard exit animation as an app transition to achieve nice transition for status
         // bar.
         mKeyguardMonitor.notifyKeyguardGoingAway(true);
-        mCommandQueue.appTransitionPending(true);
+        mCommandQueue.appTransitionPending(mDisplayId, true /* forced */);
     }
 
     /**
@@ -3246,11 +3173,11 @@
      * @param fadeoutDuration the duration of the exit animation, in milliseconds
      */
     public void setKeyguardFadingAway(long startTime, long delay, long fadeoutDuration) {
-        mCommandQueue.appTransitionStarting(startTime + fadeoutDuration
+        mCommandQueue.appTransitionStarting(mDisplayId, startTime + fadeoutDuration
                         - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION,
                 LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
-        mCommandQueue.recomputeDisableFlags(fadeoutDuration > 0 /* animate */);
-        mCommandQueue.appTransitionStarting(
+        mCommandQueue.recomputeDisableFlags(mDisplayId, fadeoutDuration > 0 /* animate */);
+        mCommandQueue.appTransitionStarting(mDisplayId,
                     startTime - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION,
                     LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
         mKeyguardMonitor.notifyKeyguardFadingAway(delay, fadeoutDuration);
@@ -3289,12 +3216,7 @@
         boolean animate = (!mDozing && mDozeServiceHost.shouldAnimateWakeup())
                 || (mDozing && mDozeServiceHost.shouldAnimateScreenOff() && sleepingFromKeyguard);
 
-        mNotificationPanel.setDozing(mDozing, animate, mWakeUpTouchLocation,
-                mDozeServiceHost.wasPassivelyInterrupted());
-        if (mNotificationPanel.isSemiAwake()
-                && SystemProperties.getBoolean("persist.systemui.show_swipe_up", false)) {
-            mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock);
-        }
+        mNotificationPanel.setDozing(mDozing, animate, mWakeUpTouchLocation);
         updateQsExpansionEnabled();
         Trace.endSection();
     }
@@ -3381,7 +3303,7 @@
         // Make our window larger and the panel expanded.
         makeExpandedVisible(true);
         mNotificationPanel.expand(false /* animate */);
-        mCommandQueue.recomputeDisableFlags(false /* animate */);
+        mCommandQueue.recomputeDisableFlags(mDisplayId, false /* animate */);
     }
 
     @Override
@@ -3437,7 +3359,7 @@
         updateReportRejectedTouchVisibility();
         updateDozing();
         updateTheme();
-        touchAutoDim();
+        mNavigationBarController.touchAutoDim(mDisplayId);
         Trace.beginSection("StatusBar#updateKeyguardState");
         if (mState == StatusBarState.KEYGUARD) {
             mKeyguardIndicationController.setVisible(true);
@@ -3558,11 +3480,7 @@
 
     // TODO: Figure out way to remove these.
     public NavigationBarView getNavigationBarView() {
-        return (mNavigationBar != null ? (NavigationBarView) mNavigationBar.getView() : null);
-    }
-
-    public View getNavigationBarWindow() {
-        return mNavigationBarView;
+        return mNavigationBarController.getDefaultNavigationBarView();
     }
 
     /**
@@ -3587,7 +3505,7 @@
 
         int userId = mLockscreenUserManager.getCurrentUserId();
         ExpandableNotificationRow row = null;
-        NotificationData.Entry entry = null;
+        NotificationEntry entry = null;
         if (expandView instanceof ExpandableNotificationRow) {
             entry = ((ExpandableNotificationRow) expandView).getEntry();
             entry.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
@@ -3626,7 +3544,7 @@
         mBouncerShowing = bouncerShowing;
         if (mStatusBarView != null) mStatusBarView.setBouncerShowing(bouncerShowing);
         updateHideIconsForBouncer(true /* animate */);
-        mCommandQueue.recomputeDisableFlags(true /* animate */);
+        mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
         updateScrimController();
     }
 
@@ -3751,13 +3669,17 @@
     }
 
     @Override
-    public void appTransitionCancelled() {
-        getComponent(Divider.class).onAppTransitionFinished();
+    public void appTransitionCancelled(int displayId) {
+        if (displayId == mDisplayId) {
+            getComponent(Divider.class).onAppTransitionFinished();
+        }
     }
 
     @Override
-    public void appTransitionFinished() {
-        getComponent(Divider.class).onAppTransitionFinished();
+    public void appTransitionFinished(int displayId) {
+        if (displayId == mDisplayId) {
+            getComponent(Divider.class).onAppTransitionFinished();
+        }
     }
 
     @Override
@@ -3860,9 +3782,6 @@
             // FLAG_DISMISS_KEYGUARD_ACTIVITY.
             ScrimState state = mStatusBarKeyguardViewManager.bouncerNeedsScrimming()
                     ? ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
-            if (mNotificationPanel.isSemiAwake()) {
-                state = ScrimState.DARK_KEYGUARD;
-            }
             mScrimController.transitionTo(state);
         } else if (isInLaunchTransition() || mLaunchCameraOnScreenTurningOn
                 || launchingAffordanceWithPreview) {
@@ -3875,8 +3794,7 @@
         } else if (mDozing && !wakeAndUnlocking) {
             mScrimController.transitionTo(ScrimState.AOD);
         } else if (mIsKeyguard && !wakeAndUnlocking) {
-            mScrimController.transitionTo(mNotificationPanel.isSemiAwake()
-                    ? ScrimState.DARK_KEYGUARD : ScrimState.KEYGUARD);
+            mScrimController.transitionTo(ScrimState.KEYGUARD);
         } else if (mBubbleController.isStackExpanded()) {
             mScrimController.transitionTo(ScrimState.BUBBLE_EXPANDED);
         } else {
@@ -3899,7 +3817,6 @@
         private boolean mAnimateWakeup;
         private boolean mAnimateScreenOff;
         private boolean mIgnoreTouchWhilePulsing;
-        private boolean mPassivelyInterrupted;
 
         @Override
         public String toString() {
@@ -3991,11 +3908,6 @@
         }
 
         @Override
-        public void setPassiveInterrupt(boolean passiveInterrupt) {
-            mPassivelyInterrupted = passiveInterrupt;
-        }
-
-        @Override
         public void onIgnoreTouchWhilePulsing(boolean ignore) {
             if (ignore != mIgnoreTouchWhilePulsing) {
                 DozeLog.tracePulseTouchDisabledByProx(mContext, ignore);
@@ -4042,11 +3954,6 @@
         }
 
         @Override
-        public void startPendingIntentDismissingKeyguard(PendingIntent intent) {
-            StatusBar.this.startPendingIntentDismissingKeyguard(intent);
-        }
-
-        @Override
         public void extendPulse() {
             if (mDozeScrimController.isPulsing() && mAmbientPulseManager.hasNotifications()) {
                 mAmbientPulseManager.extendPulse();
@@ -4056,6 +3963,13 @@
         }
 
         @Override
+        public void stopPulsing() {
+            if (mDozeScrimController.isPulsing()) {
+                mDozeScrimController.pulseOutNow();
+            }
+        }
+
+        @Override
         public void setAnimateWakeup(boolean animateWakeup) {
             if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_AWAKE
                     || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_WAKING) {
@@ -4108,10 +4022,6 @@
         public boolean shouldAnimateScreenOff() {
             return mAnimateScreenOff;
         }
-
-        public boolean wasPassivelyInterrupted() {
-            return mPassivelyInterrupted;
-        }
     }
 
     public boolean shouldIgnoreTouch() {
@@ -4156,7 +4066,7 @@
     private DeviceProvisionedController mDeviceProvisionedController
             = Dependency.get(DeviceProvisionedController.class);
 
-    protected DisplayNavigationBarController mNavigationBarController;
+    protected NavigationBarController mNavigationBarController;
 
     // UI-specific methods
 
@@ -4165,6 +4075,7 @@
     private IDreamManager mDreamManager;
 
     protected Display mDisplay;
+    private int mDisplayId;
 
     protected Recents mRecents;
 
@@ -4292,7 +4203,7 @@
             // Immediately update the icon hidden state, since that should only apply if we're
             // staying fullscreen.
             mWereIconsJustHidden = false;
-            mCommandQueue.recomputeDisableFlags(true);
+            mCommandQueue.recomputeDisableFlags(mDisplayId, true);
         }
         updateHideIconsForBouncer(true /* animate */);
     }
@@ -4351,7 +4262,7 @@
 
     @Override
     public void startPendingIntentDismissingKeyguard(
-            final PendingIntent intent, @Nullable final Runnable intentSentCallback) {
+            final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback) {
         final boolean afterKeyguardGone = intent.isActivity()
                 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
                 mLockscreenUserManager.getCurrentUserId());
@@ -4370,12 +4281,16 @@
             if (intent.isActivity()) {
                 mAssistManager.hideAssist();
             }
-            if (intentSentCallback != null) {
-                intentSentCallback.run();
+            if (intentSentUiThreadCallback != null) {
+                postOnUiThread(intentSentUiThreadCallback);
             }
         }, afterKeyguardGone);
     }
 
+    private void postOnUiThread(Runnable runnable) {
+        mMainThreadHandler.post(runnable);
+    }
+
     public static Bundle getActivityOptions(@Nullable RemoteAnimationAdapter animationAdapter) {
         ActivityOptions options;
         if (animationAdapter != null) {
@@ -4484,14 +4399,16 @@
     }
     // End Extra BaseStatusBarMethods.
 
-    // TODO(115978725): Handle dimming for external nav bars
-    private final Runnable mAutoDim = () -> {
-        if (mNavigationBar != null) {
-            mNavigationBar.getBarTransitions().setAutoDim(true);
-        }
-    };
-
     public NotificationGutsManager getGutsManager() {
         return mGutsManager;
     }
+
+    @Subcomponent
+    public interface StatusBarInjector {
+        void createStatusBar(StatusBar statusbar);
+    }
+
+    public @TransitionMode int getStatusBarMode() {
+        return mStatusBarMode;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 26c9d28..2e2ff1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -38,14 +38,14 @@
 import com.android.systemui.DemoMode;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.StatusBarMobileView;
 import com.android.systemui.statusbar.StatusBarWifiView;
 import com.android.systemui.statusbar.StatusIconDisplayable;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.util.Utils.DisableStateTracker;
 
 import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 4f25349..db7589d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -45,11 +45,15 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Receives the callbacks from CommandQueue related to icons and tracks the state of
  * all the icons. Dispatches this state to any IconManagers that are currently
  * registered with it.
  */
+@Singleton
 public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable,
         ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController {
 
@@ -66,6 +70,7 @@
 
     private boolean mIsDark = false;
 
+    @Inject
     public StatusBarIconControllerImpl(Context context) {
         super(context.getResources().getStringArray(
                 com.android.internal.R.array.config_statusBarIcons));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index c93d151..4f61009 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -24,6 +24,7 @@
 import android.app.ActivityTaskManager;
 import android.app.KeyguardManager;
 import android.app.Notification;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.TaskStackBuilder;
 import android.content.Context;
@@ -33,15 +34,21 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
 import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
+import android.util.EventLog;
 import android.util.Log;
 import android.view.RemoteAnimationAdapter;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.Dependency;
+import com.android.systemui.EventLogTags;
+import com.android.systemui.UiOffloadThread;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.CommandQueue;
@@ -53,8 +60,10 @@
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.HeadsUpUtil;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -66,6 +75,7 @@
 public class StatusBarNotificationActivityStarter implements NotificationActivityStarter {
 
     private static final String TAG = "NotificationClickHandler";
+    protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final AssistManager mAssistManager = Dependency.get(AssistManager.class);
     private final NotificationGroupManager mGroupManager =
@@ -84,6 +94,9 @@
             Dependency.get(NotificationEntryManager.class);
     private final StatusBarStateController mStatusBarStateController =
             Dependency.get(StatusBarStateController.class);
+    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+            Dependency.get(NotificationInterruptionStateProvider.class);
+    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
 
     private final Context mContext;
     private final NotificationPanelView mNotificationPanel;
@@ -94,6 +107,7 @@
     private final ActivityLaunchAnimator mActivityLaunchAnimator;
     private final IStatusBarService mBarService;
     private final CommandQueue mCommandQueue;
+    private final IDreamManager mDreamManager;
 
     private boolean mIsCollapsingToShowActivityOverLockscreen;
 
@@ -112,6 +126,15 @@
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
         mCommandQueue = getComponent(context, CommandQueue.class);
+        mDreamManager = IDreamManager.Stub.asInterface(
+                ServiceManager.checkService(DreamService.DREAM_SERVICE));
+
+        mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
+            @Override
+            public void onPendingEntryAdded(NotificationEntry entry) {
+                handleFullScreenIntent(entry);
+            }
+        });
     }
 
     /**
@@ -244,7 +267,7 @@
             }
         }
         Intent fillInIntent = null;
-        NotificationData.Entry entry = row.getEntry();
+        NotificationEntry entry = row.getEntry();
         CharSequence remoteInputText = null;
         if (!TextUtils.isEmpty(entry.remoteInputText)) {
             remoteInputText = entry.remoteInputText;
@@ -322,6 +345,45 @@
         }, null, false /* afterKeyguardGone */);
     }
 
+    private void handleFullScreenIntent(NotificationEntry entry) {
+        boolean isHeadsUped = mNotificationInterruptionStateProvider.shouldHeadsUp(entry);
+        if (!isHeadsUped && entry.notification.getNotification().fullScreenIntent != null) {
+            if (shouldSuppressFullScreenIntent(entry)) {
+                if (DEBUG) {
+                    Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + entry.key);
+                }
+            } else if (entry.importance < NotificationManager.IMPORTANCE_HIGH) {
+                if (DEBUG) {
+                    Log.d(TAG, "No Fullscreen intent: not important enough: " + entry.key);
+                }
+            } else {
+                // Stop screensaver if the notification has a fullscreen intent.
+                // (like an incoming phone call)
+                Dependency.get(UiOffloadThread.class).submit(() -> {
+                    try {
+                        mDreamManager.awaken();
+                    } catch (RemoteException e) {
+                        e.printStackTrace();
+                    }
+                });
+
+                // not immersive & a fullscreen alert should be shown
+                if (DEBUG) {
+                    Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
+                }
+                try {
+                    EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
+                            entry.key);
+                    entry.notification.getNotification().fullScreenIntent.send();
+                    entry.notifyFullScreenIntentLaunched();
+                    mMetricsLogger.count("note_fullscreen", 1);
+                } catch (PendingIntent.CanceledException e) {
+                    // ignore
+                }
+            }
+        }
+    }
+
     @Override
     public boolean isCollapsingToShowActivityOverLockscreen() {
         return mIsCollapsingToShowActivityOverLockscreen;
@@ -351,6 +413,14 @@
                 || !mActivityLaunchAnimator.isAnimationPending();
     }
 
+    private boolean shouldSuppressFullScreenIntent(NotificationEntry entry) {
+        if (mPresenter.isDeviceInVrMode()) {
+            return true;
+        }
+
+        return entry.shouldSuppressFullScreenIntent();
+    }
+
     private void removeNotification(StatusBarNotification notification) {
         // We have to post it to the UI thread for synchronization
         Dependency.get(MAIN_HANDLER).post(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 261f117..16c8e62 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -20,6 +20,7 @@
 import static com.android.systemui.statusbar.phone.StatusBar.MULTIUSER_DEBUG;
 import static com.android.systemui.statusbar.phone.StatusBar.SPEW;
 
+import android.annotation.Nullable;
 import android.app.KeyguardManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -37,10 +38,12 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
 import com.android.internal.widget.MessagingGroup;
 import com.android.internal.widget.MessagingMessage;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.Dependency;
+import com.android.systemui.ForegroundServiceNotificationListener;
 import com.android.systemui.InitController;
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
@@ -56,8 +59,13 @@
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -88,8 +96,14 @@
             Dependency.get(StatusBarStateController.class);
     private final NotificationEntryManager mEntryManager =
             Dependency.get(NotificationEntryManager.class);
+    private final NotificationRowBinder mNotificationRowBinder =
+            Dependency.get(NotificationRowBinder.class);
+    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+            Dependency.get(NotificationInterruptionStateProvider.class);
     private final NotificationMediaManager mMediaManager =
             Dependency.get(NotificationMediaManager.class);
+    private final VisualStabilityManager mVisualStabilityManager =
+            Dependency.get(VisualStabilityManager.class);
     protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
 
     private final NotificationPanelView mNotificationPanel;
@@ -122,7 +136,8 @@
             DozeScrimController dozeScrimController,
             ScrimController scrimController,
             ActivityLaunchAnimator activityLaunchAnimator,
-            StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
+            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+            NotificationAlertingManager notificationAlertingManager) {
         mContext = context;
         mNotificationPanel = panel;
         mHeadsUpManager = headsUp;
@@ -166,16 +181,60 @@
 
         NotificationListContainer notifListContainer = (NotificationListContainer) stackScroller;
         Dependency.get(InitController.class).addPostInitTask(() -> {
+            NotificationEntryListener notificationEntryListener = new NotificationEntryListener() {
+                @Override
+                public void onNotificationAdded(NotificationEntry entry) {
+                    // Recalculate the position of the sliding windows and the titles.
+                    mShadeController.updateAreThereNotifications();
+                }
+
+                @Override
+                public void onEntryUpdated(NotificationEntry entry) {
+                    mShadeController.updateAreThereNotifications();
+                }
+
+                @Override
+                public void onEntryRemoved(
+                        @Nullable NotificationEntry entry,
+                        NotificationVisibility visibility,
+                        boolean removedByUser) {
+                    StatusBarNotificationPresenter.this.onNotificationRemoved(
+                            entry.key, entry.notification);
+                    if (removedByUser) {
+                        maybeEndAmbientPulse();
+                    }
+                }
+            };
+
+            NotificationGutsManager gutsManager = Dependency.get(NotificationGutsManager.class);
+
             mViewHierarchyManager.setUpWithPresenter(this, notifListContainer);
-            mEntryManager.setUpWithPresenter(this, notifListContainer, this, mHeadsUpManager);
+            mEntryManager.setUpWithPresenter(this, notifListContainer, mHeadsUpManager);
+            mEntryManager.addNotificationEntryListener(notificationEntryListener);
+            mEntryManager.addNotificationLifetimeExtender(mHeadsUpManager);
+            mEntryManager.addNotificationLifetimeExtender(mAmbientPulseManager);
+            mEntryManager.addNotificationLifetimeExtender(gutsManager);
+            mEntryManager.addNotificationLifetimeExtenders(
+                    remoteInputManager.getLifetimeExtenders());
+            mNotificationRowBinder.setUpWithPresenter(this, notifListContainer, mHeadsUpManager,
+                    mEntryManager, this);
+            mNotificationInterruptionStateProvider.setUpWithPresenter(
+                    this, mHeadsUpManager, this::canHeadsUp);
             mLockscreenUserManager.setUpWithPresenter(this);
             mMediaManager.setUpWithPresenter(this);
-            Dependency.get(NotificationGutsManager.class).setUpWithPresenter(this,
+            mVisualStabilityManager.setUpWithPresenter(this);
+            gutsManager.setUpWithPresenter(this,
                     notifListContainer, mCheckSaveListener, mOnSettingsClickListener);
+            // ForegroundServiceControllerListener adds its listener in its constructor
+            // but we need to request it here in order for it to be instantiated.
+            // TODO: figure out how to do this correctly once Dependency.get() is gone.
+            Dependency.get(ForegroundServiceNotificationListener.class);
 
             onUserSwitched(mLockscreenUserManager.getCurrentUserId());
         });
         Dependency.get(ConfigurationController.class).addCallback(this);
+
+        notificationAlertingManager.setHeadsUpManager(mHeadsUpManager);
     }
 
     @Override
@@ -199,10 +258,10 @@
     }
 
     private void updateNotificationOnUiModeChanged() {
-        ArrayList<Entry> userNotifications
+        ArrayList<NotificationEntry> userNotifications
                 = mEntryManager.getNotificationData().getNotificationsForCurrentUser();
         for (int i = 0; i < userNotifications.size(); i++) {
-            Entry entry = userNotifications.get(i);
+            NotificationEntry entry = userNotifications.get(i);
             ExpandableNotificationRow row = entry.getRow();
             if (row != null) {
                 row.onUiModeChanged();
@@ -217,10 +276,9 @@
                 || mActivityLaunchAnimator.isAnimationRunning();
     }
 
-    @Override
-    public void onPerformRemoveNotification(StatusBarNotification n) {
+    private void maybeEndAmbientPulse() {
         if (mNotificationPanel.hasPulsingNotifications() &&
-                    !mAmbientPulseManager.hasNotifications()) {
+                !mAmbientPulseManager.hasNotifications()) {
             // We were showing a pulse for a notification, but no notifications are pulsing anymore.
             // Finish the pulse.
             mDozeScrimController.pulseOutNow();
@@ -244,18 +302,6 @@
         mNotificationPanel.updateNotificationViews();
     }
 
-    @Override
-    public void onNotificationAdded(Entry shadeEntry) {
-        // Recalculate the position of the sliding windows and the titles.
-        mShadeController.updateAreThereNotifications();
-    }
-
-    @Override
-    public void onNotificationUpdated(StatusBarNotification notification) {
-        mShadeController.updateAreThereNotifications();
-    }
-
-    @Override
     public void onNotificationRemoved(String key, StatusBarNotification old) {
         if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
 
@@ -277,8 +323,7 @@
         return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
     }
 
-    @Override
-    public boolean canHeadsUp(Entry entry, StatusBarNotification sbn) {
+    public boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn) {
         if (mShadeController.isDozing()) {
             return false;
         }
@@ -336,7 +381,7 @@
     }
 
     @Override
-    public void onBindRow(Entry entry, PackageManager pmUser,
+    public void onBindRow(NotificationEntry entry, PackageManager pmUser,
             StatusBarNotification sbn, ExpandableNotificationRow row) {
         row.setAboveShelfChangedListener(mAboveShelfObserver);
         row.setSecureStateProvider(mUnlockMethodCache::canSkipBouncer);
@@ -394,7 +439,7 @@
     }
 
     @Override
-    public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
+    public void onExpandClicked(NotificationEntry clickedEntry, boolean nowExpanded) {
         mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
         if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD && nowExpanded) {
             mShadeController.goToLockedShade(clickedEntry.getRow());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 78f5374..3ddfc0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -47,6 +47,12 @@
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.PreviewInflater;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
 public class StatusBarRemoteInputCallback implements Callback, Callbacks {
 
     private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
@@ -65,6 +71,9 @@
     private int mDisabled2;
     protected BroadcastReceiver mChallengeReceiver = new ChallengeReceiver();
 
+    /**
+     */
+    @Inject
     public StatusBarRemoteInputCallback(Context context) {
         mContext = context;
         mContext.registerReceiverAsUser(mChallengeReceiver, UserHandle.ALL,
@@ -229,8 +238,10 @@
     }
 
     @Override
-    public void disable(int state1, int state2, boolean animate) {
-        mDisabled2 = state2;
+    public void disable(int displayId, int state1, int state2, boolean animate) {
+        if (displayId == mContext.getDisplayId()) {
+            mDisabled2 = state2;
+        }
     }
 
     protected class ChallengeReceiver extends BroadcastReceiver {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index cb6e300..88f9048 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -54,9 +54,13 @@
 import java.io.PrintWriter;
 import java.lang.reflect.Field;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Encapsulates all logic for the status bar window state management.
  */
+@Singleton
 public class StatusBarWindowController implements Callback, Dumpable, ConfigurationListener {
 
     private static final String TAG = "StatusBarWindowController";
@@ -78,6 +82,7 @@
 
     private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class);
 
+    @Inject
     public StatusBarWindowController(Context context) {
         this(context, context.getSystemService(WindowManager.class), ActivityManager.getService(),
                 DozeParameters.getInstance(context));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SysuiDarkIconDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SysuiDarkIconDispatcher.java
new file mode 100644
index 0000000..d537721
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SysuiDarkIconDispatcher.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.plugins.DarkIconDispatcher;
+
+/**
+ * Dispatches events to {@link DarkReceiver}s about changes in darkness, tint area
+ * and dark intensity.
+ */
+public interface SysuiDarkIconDispatcher extends DarkIconDispatcher, Dumpable {
+
+    /**
+     * @return LightBarTransitionsController
+     */
+    LightBarTransitionsController getTransitionsController();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
index 6ee6cb2..53d0228 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
@@ -28,7 +28,6 @@
 import com.android.settingslib.wifi.AccessPoint;
 import com.android.settingslib.wifi.WifiTracker;
 import com.android.settingslib.wifi.WifiTracker.WifiListener;
-import com.android.systemui.R;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -43,13 +42,7 @@
     // network credentials.  This is used by quick settings for secured networks.
     private static final String EXTRA_START_CONNECT_SSID = "wifi_start_connect_ssid";
 
-    private static final int[] ICONS = {
-        R.drawable.ic_qs_wifi_full_0,
-        R.drawable.ic_qs_wifi_full_1,
-        R.drawable.ic_qs_wifi_full_2,
-        R.drawable.ic_qs_wifi_full_3,
-        R.drawable.ic_qs_wifi_full_4,
-    };
+    private static final int[] ICONS = WifiIcons.WIFI_FULL_ICONS;
 
     private final Context mContext;
     private final ArrayList<AccessPointCallback> mCallbacks = new ArrayList<AccessPointCallback>();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityController.java
index cc431dd..ebfdb3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityController.java
@@ -23,6 +23,12 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
 public class AccessibilityController implements
         AccessibilityManager.AccessibilityStateChangeListener,
         AccessibilityManager.TouchExplorationStateChangeListener {
@@ -32,6 +38,9 @@
     private boolean mAccessibilityEnabled;
     private boolean mTouchExplorationEnabled;
 
+    /**
+     */
+    @Inject
     public AccessibilityController(Context context) {
         AccessibilityManager am =
                 (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java
index 67da8a5..1395e13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java
@@ -19,14 +19,19 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * For mocking because AccessibilityManager is final for some reason...
  */
+@Singleton
 public class AccessibilityManagerWrapper implements
         CallbackController<AccessibilityServicesStateChangeListener> {
 
     private final AccessibilityManager mAccessibilityManager;
 
+    @Inject
     public AccessibilityManagerWrapper(Context context) {
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index ddcfbf6..6190c8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -30,7 +30,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.fuelgauge.BatterySaverUtils;
 import com.android.settingslib.utils.PowerUtil;
-import com.android.systemui.Dependency;
 import com.android.systemui.power.EnhancedEstimates;
 import com.android.systemui.power.Estimate;
 
@@ -39,10 +38,14 @@
 import java.text.NumberFormat;
 import java.util.ArrayList;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Default implementation of a {@link BatteryController}. This controller monitors for battery
  * level change events that are broadcasted by the system.
  */
+@Singleton
 public class BatteryControllerImpl extends BroadcastReceiver implements BatteryController {
     private static final String TAG = "BatteryController";
 
@@ -51,7 +54,7 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     private static final int UPDATE_GRANULARITY_MSEC = 1000 * 60;
 
-    private final EnhancedEstimates mEstimates = Dependency.get(EnhancedEstimates.class);
+    private final EnhancedEstimates mEstimates;
     private final ArrayList<BatteryController.BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
     private final PowerManager mPowerManager;
     private final Handler mHandler;
@@ -68,15 +71,18 @@
     private Estimate mEstimate;
     private long mLastEstimateTimestamp = -1;
 
-    public BatteryControllerImpl(Context context) {
-        this(context, context.getSystemService(PowerManager.class));
+    @Inject
+    public BatteryControllerImpl(Context context, EnhancedEstimates enhancedEstimates) {
+        this(context, enhancedEstimates, context.getSystemService(PowerManager.class));
     }
 
     @VisibleForTesting
-    BatteryControllerImpl(Context context, PowerManager powerManager) {
+    BatteryControllerImpl(Context context, EnhancedEstimates enhancedEstimates,
+            PowerManager powerManager) {
         mContext = context;
         mHandler = new Handler();
         mPowerManager = powerManager;
+        mEstimates = enhancedEstimates;
 
         registerReceiver();
         updatePowerSave();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 6f64a563..8916242 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static com.android.systemui.Dependency.BG_LOOPER_NAME;
+
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
@@ -32,7 +35,6 @@
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
-import com.android.systemui.Dependency;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -42,6 +44,13 @@
 import java.util.List;
 import java.util.WeakHashMap;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
 public class BluetoothControllerImpl implements BluetoothController, BluetoothCallback,
         CachedBluetoothDevice.Callback, LocalBluetoothProfileManager.ServiceListener {
     private static final String TAG = "BluetoothController";
@@ -61,8 +70,12 @@
     private final H mHandler = new H(Looper.getMainLooper());
     private int mState;
 
-    public BluetoothControllerImpl(Context context, Looper bgLooper) {
-        mLocalBluetoothManager = Dependency.get(LocalBluetoothManager.class);
+    /**
+     */
+    @Inject
+    public BluetoothControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper,
+            @Nullable LocalBluetoothManager localBluetoothManager) {
+        mLocalBluetoothManager = localBluetoothManager;
         mBgHandler = new Handler(bgLooper);
         if (mLocalBluetoothManager != null) {
             mLocalBluetoothManager.getEventManager().registerCallback(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
index ccfe073..c7d337a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
@@ -44,8 +44,12 @@
 import java.util.Set;
 import java.util.UUID;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 
 /** Platform implementation of the cast controller. **/
+@Singleton
 public class CastControllerImpl implements CastController {
     private static final String TAG = "CastController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -63,6 +67,7 @@
     private boolean mCallbackRegistered;
     private MediaProjectionInfo mProjection;
 
+    @Inject
     public CastControllerImpl(Context context) {
         mContext = context;
         mMediaRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index aafdcd5..de7ef3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -44,11 +44,12 @@
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 
@@ -298,7 +299,10 @@
     }
 
     @Override
-    public void disable(int state1, int state2, boolean animate) {
+    public void disable(int displayId, int state1, int state2, boolean animate) {
+        if (displayId != getDisplay().getDisplayId()) {
+            return;
+        }
         boolean clockVisibleByPolicy = (state1 & StatusBarManager.DISABLE_CLOCK) == 0;
         if (clockVisibleByPolicy != mClockVisibleByPolicy) {
             setClockVisibilityByPolicy(clockVisibleByPolicy);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java
deleted file mode 100644
index 945ed76..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.statusbar.policy;
-
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.view.View;
-import android.widget.ImageView;
-
-import com.android.systemui.statusbar.phone.LightBarTransitionsController;
-
-public interface DarkIconDispatcher {
-
-    void setIconsDarkArea(Rect r);
-    LightBarTransitionsController getTransitionsController();
-
-    void addDarkReceiver(DarkReceiver receiver);
-    void addDarkReceiver(ImageView imageView);
-
-    // Must have been previously been added through one of the addDarkReceive methods above.
-    void removeDarkReceiver(DarkReceiver object);
-    void removeDarkReceiver(ImageView object);
-
-    // Used to reapply darkness on an object, must have previously been added through
-    // addDarkReceiver.
-    void applyDark(DarkReceiver object);
-
-    int DEFAULT_ICON_TINT = Color.WHITE;
-    Rect sTmpRect = new Rect();
-    int[] sTmpInt2 = new int[2];
-
-    /**
-     * @return the tint to apply to {@param view} depending on the desired tint {@param color} and
-     *         the screen {@param tintArea} in which to apply that tint
-     */
-    static int getTint(Rect tintArea, View view, int color) {
-        if (isInArea(tintArea, view)) {
-            return color;
-        } else {
-            return DEFAULT_ICON_TINT;
-        }
-    }
-
-    /**
-     * @return the dark intensity to apply to {@param view} depending on the desired dark
-     *         {@param intensity} and the screen {@param tintArea} in which to apply that intensity
-     */
-    static float getDarkIntensity(Rect tintArea, View view, float intensity) {
-        if (isInArea(tintArea, view)) {
-            return intensity;
-        } else {
-            return 0f;
-        }
-    }
-
-    /**
-     * @return true if more than half of the {@param view} area are in {@param area}, false
-     *         otherwise
-     */
-    static boolean isInArea(Rect area, View view) {
-        if (area.isEmpty()) {
-            return true;
-        }
-        sTmpRect.set(area);
-        view.getLocationOnScreen(sTmpInt2);
-        int left = sTmpInt2[0];
-
-        int intersectStart = Math.max(left, area.left);
-        int intersectEnd = Math.min(left + view.getWidth(), area.right);
-        int intersectAmount = Math.max(0, intersectEnd - intersectStart);
-
-        boolean coversFullStatusBar = area.top <= 0;
-        boolean majorityOfWidth = 2 * intersectAmount > view.getWidth();
-        return majorityOfWidth && coversFullStatusBar;
-    }
-
-    interface DarkReceiver {
-        void onDarkChanged(Rect area, float darkIntensity, int tint);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
index 4a11754..54502e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
@@ -24,10 +24,10 @@
 import android.view.MotionEvent;
 import android.view.Surface;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.statusbar.NavigationBarController;
 import com.android.systemui.statusbar.phone.NavigationBarView;
-import com.android.systemui.statusbar.phone.StatusBar;
 
 /**
  * The "dead zone" consumes unintentional taps along the top edge of the navigation bar.
@@ -44,7 +44,7 @@
     public static final int VERTICAL = 1;  // Consume taps along the left edge.
 
     private static final boolean CHATTY = true; // print to logcat when we eat a click
-    private final StatusBar mStatusBar;
+    private final NavigationBarController mNavBarController;
     private final NavigationBarView mNavigationBarView;
 
     private boolean mShouldFlash;
@@ -58,6 +58,7 @@
     private boolean mVertical;
     private long mLastPokeTime;
     private int mDisplayRotation;
+    private final int mDisplayId;
 
     private final Runnable mDebugFlash = new Runnable() {
         @Override
@@ -68,8 +69,8 @@
 
     public DeadZone(NavigationBarView view) {
         mNavigationBarView = view;
-        mStatusBar = SysUiServiceProvider.getComponent(mNavigationBarView.getContext(),
-                StatusBar.class);
+        mNavBarController = Dependency.get(NavigationBarController.class);
+        mDisplayId = view.getContext().getDisplayId();
         onConfigurationChanged(HORIZONTAL);
     }
 
@@ -133,7 +134,7 @@
             if (DEBUG) {
                 Slog.v(TAG, this + " ACTION_DOWN: " + event.getX() + "," + event.getY());
             }
-            if (mStatusBar != null) mStatusBar.touchAutoDim();
+            mNavBarController.touchAutoDim(mDisplayId);
             int size = (int) getSize(event.getEventTime());
             // In the vertical orientation consume taps along the left edge.
             // In horizontal orientation consume taps along the top edge.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
index f2283a5..f5e745f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
@@ -14,6 +14,8 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+
 import android.app.ActivityManager;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -21,12 +23,19 @@
 import android.net.Uri;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
+import android.os.Handler;
 
-import com.android.systemui.Dependency;
 import com.android.systemui.settings.CurrentUserTracker;
 
 import java.util.ArrayList;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
 public class DeviceProvisionedControllerImpl extends CurrentUserTracker implements
         DeviceProvisionedController {
 
@@ -35,13 +44,28 @@
     private final Context mContext;
     private final Uri mDeviceProvisionedUri;
     private final Uri mUserSetupUri;
+    protected final ContentObserver mSettingsObserver;
 
-    public DeviceProvisionedControllerImpl(Context context) {
+    /**
+     */
+    @Inject
+    public DeviceProvisionedControllerImpl(Context context,
+            @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
         super(context);
         mContext = context;
         mContentResolver = context.getContentResolver();
         mDeviceProvisionedUri = Global.getUriFor(Global.DEVICE_PROVISIONED);
         mUserSetupUri = Secure.getUriFor(Secure.USER_SETUP_COMPLETE);
+        mSettingsObserver = new ContentObserver(mainHandler) {
+            @Override
+            public void onChange(boolean selfChange, Uri uri, int userId) {
+                if (mUserSetupUri.equals(uri)) {
+                    notifySetupChanged();
+                } else {
+                    notifyProvisionedChanged();
+                }
+            }
+        };
     }
 
     @Override
@@ -118,17 +142,4 @@
             mListeners.get(i).onDeviceProvisionedChanged();
         }
     }
-
-    protected final ContentObserver mSettingsObserver = new ContentObserver(Dependency.get(
-            Dependency.MAIN_HANDLER)) {
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri, int userId) {
-            if (mUserSetupUri.equals(uri)) {
-                notifySetupChanged();
-            } else {
-                notifyProvisionedChanged();
-            }
-        }
-    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
index a6146a6..fd030d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
@@ -19,7 +19,6 @@
 import android.os.Handler;
 import android.util.ArrayMap;
 
-import com.android.systemui.Dependency;
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.shared.plugins.PluginManager;
@@ -34,6 +33,12 @@
 import java.util.function.Consumer;
 import java.util.function.Supplier;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
 public class ExtensionControllerImpl implements ExtensionController {
 
     public static final int SORT_ORDER_PLUGIN  = 0;
@@ -43,9 +48,24 @@
     public static final int SORT_ORDER_DEFAULT = 4;
 
     private final Context mDefaultContext;
+    private final LeakDetector mLeakDetector;
+    private final PluginManager mPluginManager;
+    private final TunerService mTunerService;
+    private final ConfigurationController mConfigurationController;
 
-    public ExtensionControllerImpl(Context context) {
+    /**
+     */
+    @Inject
+    public ExtensionControllerImpl(Context context,
+            LeakDetector leakDetector,
+            PluginManager pluginManager,
+            TunerService tunerService,
+            ConfigurationController configurationController) {
         mDefaultContext = context;
+        mLeakDetector = leakDetector;
+        mPluginManager = pluginManager;
+        mTunerService = tunerService;
+        mConfigurationController = configurationController;
     }
 
     @Override
@@ -159,14 +179,14 @@
         @Override
         public void clearItem(boolean isDestroyed) {
             if (isDestroyed && mItem != null) {
-                Dependency.get(LeakDetector.class).trackGarbage(mItem);
+                mLeakDetector.trackGarbage(mItem);
             }
             mItem = null;
         }
 
         private void notifyChanged() {
             if (mItem != null) {
-                Dependency.get(LeakDetector.class).trackGarbage(mItem);
+                mLeakDetector.trackGarbage(mItem);
             }
             mItem = null;
             for (int i = 0; i < mProducers.size(); i++) {
@@ -207,7 +227,7 @@
 
             public PluginItem(String action, Class<P> cls, PluginConverter<T, P> converter) {
                 mConverter = converter;
-                Dependency.get(PluginManager.class).addPluginListener(action, this, cls);
+                mPluginManager.addPluginListener(action, this, cls);
             }
 
             @Override
@@ -235,7 +255,7 @@
 
             @Override
             public void destroy() {
-                Dependency.get(PluginManager.class).removePluginListener(this);
+                mPluginManager.removePluginListener(this);
             }
 
             @Override
@@ -251,7 +271,7 @@
 
             public TunerItem(TunerFactory<T> factory, String... setting) {
                 mFactory = factory;
-                Dependency.get(TunerService.class).addTunable(this, setting);
+                mTunerService.addTunable(this, setting);
             }
 
             @Override
@@ -261,7 +281,7 @@
 
             @Override
             public void destroy() {
-                Dependency.get(TunerService.class).removeTunable(this);
+                mTunerService.removeTunable(this);
             }
 
             @Override
@@ -289,7 +309,7 @@
                 mSupplier = supplier;
                 mUiMode = mDefaultContext.getResources().getConfiguration().uiMode
                         & Configuration.UI_MODE_TYPE_MASK;
-                Dependency.get(ConfigurationController.class).addCallback(this);
+                mConfigurationController.addCallback(this);
             }
 
             @Override
@@ -309,7 +329,7 @@
 
             @Override
             public void destroy() {
-                Dependency.get(ConfigurationController.class).removeCallback(this);
+                mConfigurationController.removeCallback(this);
             }
 
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
index e23ce2d..41ff9d10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
@@ -35,9 +35,13 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Manages the flashlight.
  */
+@Singleton
 public class FlashlightControllerImpl implements FlashlightController {
 
     private static final String TAG = "FlashlightController";
@@ -64,6 +68,7 @@
     private String mCameraId;
     private boolean mTorchAvailable;
 
+    @Inject
     public FlashlightControllerImpl(Context context) {
         mContext = context;
         mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index e7280643..fd3f680 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -16,8 +16,7 @@
 
 package com.android.systemui.statusbar.policy;
 
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
-        .FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -31,8 +30,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.AlertingNotificationManager;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 
 import java.io.FileDescriptor;
@@ -110,11 +108,11 @@
         }
     }
 
-    protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationData.Entry entry) {
+    protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationEntry entry) {
         return hasFullScreenIntent(entry);
     }
 
-    protected boolean hasFullScreenIntent(@NonNull NotificationData.Entry entry) {
+    protected boolean hasFullScreenIntent(@NonNull NotificationEntry entry) {
         return entry.notification.getNotification().fullScreenIntent != null;
     }
 
@@ -123,7 +121,7 @@
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "setEntryPinned: " + isPinned);
         }
-        NotificationData.Entry entry = headsUpEntry.mEntry;
+        NotificationEntry entry = headsUpEntry.mEntry;
         if (entry.isRowPinned() != isPinned) {
             entry.setRowPinned(isPinned);
             updatePinnedMode();
@@ -143,7 +141,7 @@
 
     @Override
     protected void onAlertEntryAdded(AlertEntry alertEntry) {
-        NotificationData.Entry entry = alertEntry.mEntry;
+        NotificationEntry entry = alertEntry.mEntry;
         entry.setHeadsUp(true);
         setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(entry));
         for (OnHeadsUpChangedListener listener : mListeners) {
@@ -153,7 +151,7 @@
 
     @Override
     protected void onAlertEntryRemoved(AlertEntry alertEntry) {
-        NotificationData.Entry entry = alertEntry.mEntry;
+        NotificationEntry entry = alertEntry.mEntry;
         entry.setHeadsUp(false);
         setEntryPinned((HeadsUpEntry) alertEntry, false /* isPinned */);
         for (OnHeadsUpChangedListener listener : mListeners) {
@@ -224,7 +222,7 @@
      * Returns the top Heads Up Notification, which appears to show at first.
      */
     @Nullable
-    public NotificationData.Entry getTopEntry() {
+    public NotificationEntry getTopEntry() {
         HeadsUpEntry topEntry = getTopHeadsUpEntry();
         return (topEntry != null) ? topEntry.mEntry : null;
     }
@@ -325,7 +323,7 @@
      * @return -1 if the first argument should be ranked higher than the second, 1 if the second
      * one should be ranked higher and 0 if they are equal.
      */
-    public int compare(@NonNull NotificationData.Entry a, @NonNull NotificationData.Entry b) {
+    public int compare(@NonNull NotificationEntry a, @NonNull NotificationEntry b) {
         AlertEntry aEntry = getHeadsUpEntry(a.key);
         AlertEntry bEntry = getHeadsUpEntry(b.key);
         if (aEntry == null || bEntry == null) {
@@ -338,7 +336,7 @@
      * Set an entry to be expanded and therefore stick in the heads up area if it's pinned
      * until it's collapsed again.
      */
-    public void setExpanded(@NonNull NotificationData.Entry entry, boolean expanded) {
+    public void setExpanded(@NonNull NotificationEntry entry, boolean expanded) {
         HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key);
         if (headsUpEntry != null && entry.isRowPinned()) {
             headsUpEntry.setExpanded(expanded);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index 0a72c3f..b561ac1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -16,19 +16,27 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+
 import android.app.ActivityManager;
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.wifi.WifiManager;
+import android.os.Handler;
 import android.os.UserManager;
 import android.util.Log;
 
-import com.android.systemui.Dependency;
-
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
 public class HotspotControllerImpl implements HotspotController, WifiManager.SoftApCallback {
 
     private static final String TAG = "HotspotController";
@@ -37,17 +45,22 @@
     private final ArrayList<Callback> mCallbacks = new ArrayList<>();
     private final ConnectivityManager mConnectivityManager;
     private final WifiManager mWifiManager;
+    private final Handler mMainHandler;
     private final Context mContext;
 
     private int mHotspotState;
     private int mNumConnectedDevices;
     private boolean mWaitingForTerminalState;
 
-    public HotspotControllerImpl(Context context) {
+    /**
+     */
+    @Inject
+    public HotspotControllerImpl(Context context, @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
         mContext = context;
         mConnectivityManager =
                 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+        mMainHandler = mainHandler;
     }
 
     @Override
@@ -109,10 +122,13 @@
      * @param shouldListen whether we should start listening to various wifi statuses
      */
     private void updateWifiStateListeners(boolean shouldListen) {
+        if (mWifiManager == null) {
+            return;
+        }
         if (shouldListen) {
             mWifiManager.registerSoftApCallback(
                     this,
-                    Dependency.get(Dependency.MAIN_HANDLER));
+                    mMainHandler);
         } else {
             mWifiManager.unregisterSoftApCallback(this);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLoggerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLoggerImpl.java
index aee021c..ba6369e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLoggerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLoggerImpl.java
@@ -18,6 +18,7 @@
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_STATUS_ICONS;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.STATUS_BAR_ICONS_CHANGED;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION;
+import static com.android.systemui.Dependency.BG_LOOPER_NAME;
 
 import android.content.Context;
 import android.metrics.LogMaker;
@@ -32,6 +33,9 @@
 import java.util.Arrays;
 import java.util.List;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+
 public class IconLoggerImpl implements IconLogger {
 
     // Minimum ms between log statements.
@@ -46,7 +50,9 @@
     private final List<String> mIconIndex;
     private long mLastLog = System.currentTimeMillis();
 
-    public IconLoggerImpl(Context context, Looper bgLooper, MetricsLogger logger) {
+    @Inject
+    public IconLoggerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper,
+            MetricsLogger logger) {
         mContext = context;
         mHandler = new Handler(bgLooper);
         mLogger = logger;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 2c756ce..d404982 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -33,6 +33,7 @@
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.util.TypedValue;
 import android.view.HapticFeedbackConstants;
 import android.view.InputDevice;
@@ -304,6 +305,10 @@
                 .setSubtype(mCode)
                 .addTaggedData(MetricsEvent.FIELD_NAV_ACTION, action)
                 .addTaggedData(MetricsEvent.FIELD_FLAGS, flags));
+        // TODO(b/122195391): Added logs to make sure sysui is sending back button events
+        if (mCode == KeyEvent.KEYCODE_BACK && flags != KeyEvent.FLAG_LONG_PRESS) {
+            Log.i(TAG, "Back button event: " + KeyEvent.actionToString(action));
+        }
         final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0;
         final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount,
                 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
index 5eb0fb7..3c8ed6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
@@ -25,6 +25,12 @@
 
 import java.util.ArrayList;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
 public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback
         implements KeyguardMonitor {
 
@@ -47,6 +53,9 @@
     private boolean mKeyguardGoingAway;
     private boolean mLaunchTransitionFadingAway;
 
+    /**
+     */
+    @Inject
     public KeyguardMonitorImpl(Context context) {
         mContext = context;
         mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 840e77e..683cdbb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.policy;
 
 import static com.android.settingslib.Utils.updateLocationEnabled;
+import static com.android.systemui.Dependency.BG_LOOPER_NAME;
 
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
@@ -40,9 +41,14 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
 /**
  * A controller to manage changes of location related states and update the views accordingly.
  */
+@Singleton
 public class LocationControllerImpl extends BroadcastReceiver implements LocationController {
 
     private static final int[] mHighPowerRequestAppOpArray
@@ -59,7 +65,8 @@
             new ArrayList<LocationChangeCallback>();
     private final H mHandler = new H();
 
-    public LocationControllerImpl(Context context, Looper bgLooper) {
+    @Inject
+    public LocationControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper) {
         mContext = context;
 
         // Register to listen for changes in location settings.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index e943261..3deede0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -22,6 +22,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.Settings.Global;
+import android.telephony.NetworkRegistrationState;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
@@ -437,7 +438,13 @@
                 mCurrentState.level = mSignalStrength.getLevel();
             }
         }
-        if (mNetworkToIconLookup.indexOfKey(mDataNetType) >= 0) {
+
+        // When the device is camped on a 5G Non-Standalone network, the data network type is still
+        // LTE. In this case, we first check which 5G icon should be shown.
+        MobileIconGroup nr5GIconGroup = getNr5GIconGroup();
+        if (nr5GIconGroup != null) {
+            mCurrentState.iconGroup = nr5GIconGroup;
+        } else if (mNetworkToIconLookup.indexOfKey(mDataNetType) >= 0) {
             mCurrentState.iconGroup = mNetworkToIconLookup.get(mDataNetType);
         } else {
             mCurrentState.iconGroup = mDefaultIcons;
@@ -464,6 +471,36 @@
         notifyListenersIfNecessary();
     }
 
+    private MobileIconGroup getNr5GIconGroup() {
+        if (mServiceState == null) return null;
+
+        int nrStatus = mServiceState.getNrStatus();
+        if (nrStatus == NetworkRegistrationState.NR_STATUS_CONNECTED) {
+            // Check if the NR 5G is using millimeter wave and the icon is config.
+            if (mServiceState.getNrFrequencyRange() == ServiceState.FREQUENCY_RANGE_MMWAVE) {
+                if (mConfig.nr5GIconMap.containsKey(Config.NR_CONNECTED_MMWAVE)) {
+                    return mConfig.nr5GIconMap.get(Config.NR_CONNECTED_MMWAVE);
+                }
+            }
+
+            // If NR 5G is not using millimeter wave or there is no icon for millimeter wave, we
+            // check the normal 5G icon.
+            if (mConfig.nr5GIconMap.containsKey(Config.NR_CONNECTED)) {
+                return mConfig.nr5GIconMap.get(Config.NR_CONNECTED);
+            }
+        } else if (nrStatus == NetworkRegistrationState.NR_STATUS_NOT_RESTRICTED) {
+            if (mConfig.nr5GIconMap.containsKey(Config.NR_NOT_RESTRICTED)) {
+                return mConfig.nr5GIconMap.get(Config.NR_NOT_RESTRICTED);
+            }
+        } else if (nrStatus == NetworkRegistrationState.NR_STATUS_RESTRICTED) {
+            if (mConfig.nr5GIconMap.containsKey(Config.NR_RESTRICTED)) {
+                return mConfig.nr5GIconMap.get(Config.NR_RESTRICTED);
+            }
+        }
+
+        return null;
+    }
+
     private boolean isDataDisabled() {
         return !mPhone.getDataEnabled(mSubscriptionInfo.getSubscriptionId());
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 70a3589..9422101 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -22,6 +22,8 @@
 import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE;
 import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT;
 
+import static com.android.systemui.Dependency.BG_LOOPER_NAME;
+
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -60,6 +62,7 @@
 import com.android.systemui.R;
 import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
+import com.android.systemui.statusbar.policy.MobileSignalController.MobileIconGroup;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -67,10 +70,17 @@
 import java.util.BitSet;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
 
 /** Platform implementation of the network controller. **/
+@Singleton
 public class NetworkControllerImpl extends BroadcastReceiver
         implements NetworkController, DemoMode, DataUsageController.NetworkNameProvider,
         ConfigurationChangedReceiver, Dumpable {
@@ -150,7 +160,8 @@
     /**
      * Construct this controller object and register for updates.
      */
-    public NetworkControllerImpl(Context context, Looper bgLooper,
+    @Inject
+    public NetworkControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper,
             DeviceProvisionedController deviceProvisionedController) {
         this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE),
                 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE),
@@ -1029,6 +1040,13 @@
 
     @VisibleForTesting
     static class Config {
+        static final int NR_CONNECTED_MMWAVE = 1;
+        static final int NR_CONNECTED = 2;
+        static final int NR_NOT_RESTRICTED = 3;
+        static final int NR_RESTRICTED = 4;
+
+        Map<Integer, MobileIconGroup> nr5GIconMap = new HashMap<>();
+
         boolean showAtLeast3G = false;
         boolean alwaysShowCdmaRssi = false;
         boolean show4gForLte = false;
@@ -1037,6 +1055,19 @@
         boolean inflateSignalStrengths = false;
         boolean alwaysShowDataRatIcon = false;
 
+        /**
+         * Mapping from NR 5G status string to an integer. The NR 5G status string should match
+         * those in carrier config.
+         */
+        private static final Map<String, Integer> NR_STATUS_STRING_TO_INDEX;
+        static {
+            NR_STATUS_STRING_TO_INDEX = new HashMap<>(4);
+            NR_STATUS_STRING_TO_INDEX.put("connected_mmwave", NR_CONNECTED_MMWAVE);
+            NR_STATUS_STRING_TO_INDEX.put("connected", NR_CONNECTED);
+            NR_STATUS_STRING_TO_INDEX.put("not_restricted", NR_NOT_RESTRICTED);
+            NR_STATUS_STRING_TO_INDEX.put("restricted", NR_RESTRICTED);
+        }
+
         static Config readConfig(Context context) {
             Config config = new Config();
             Resources res = context.getResources();
@@ -1061,8 +1092,46 @@
                         CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL);
                 config.hideLtePlus = b.getBoolean(
                         CarrierConfigManager.KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL);
+                String nr5GIconConfiguration =
+                        b.getString(CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING);
+                if (!TextUtils.isEmpty(nr5GIconConfiguration)) {
+                    String[] nr5GIconConfigPairs = nr5GIconConfiguration.trim().split(",");
+                    for (String pair : nr5GIconConfigPairs) {
+                        add5GIconMapping(pair, config);
+                    }
+                }
             }
+
             return config;
         }
+
+        /**
+         * Add a mapping from NR 5G status to the 5G icon. All the icon resources come from
+         * {@link TelephonyIcons}.
+         *
+         * @param keyValuePair the NR 5G status and icon name separated by a colon.
+         * @param config container that used to store the parsed configs.
+         */
+        @VisibleForTesting
+        static void add5GIconMapping(String keyValuePair, Config config) {
+            String[] kv = (keyValuePair.trim().toLowerCase()).split(":");
+
+            if (kv.length != 2) {
+                if (DEBUG) Log.e(TAG, "Invalid 5G icon configuration, config = " + keyValuePair);
+                return;
+            }
+
+            String key = kv[0], value = kv[1];
+
+            // There is no icon config for the specific 5G status.
+            if (value.equals("none")) return;
+
+            if (NR_STATUS_STRING_TO_INDEX.containsKey(key)
+                    && TelephonyIcons.ICON_NAME_TO_ICON.containsKey(value)) {
+                config.nr5GIconMap.put(
+                        NR_STATUS_STRING_TO_INDEX.get(key),
+                        TelephonyIcons.ICON_NAME_TO_ICON.get(value));
+            }
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
index dac878c..288b3af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
@@ -27,6 +27,13 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Implementation of {@link NextAlarmController}
+ */
+@Singleton
 public class NextAlarmControllerImpl extends BroadcastReceiver
         implements NextAlarmController {
 
@@ -35,6 +42,9 @@
     private AlarmManager mAlarmManager;
     private AlarmManager.AlarmClockInfo mNextAlarm;
 
+    /**
+     */
+    @Inject
     public NextAlarmControllerImpl(Context context) {
         mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
         IntentFilter filter = new IntentFilter();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
index 7ad547a..438226a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
@@ -16,8 +16,7 @@
 
 package com.android.systemui.statusbar.policy;
 
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 /**
  * A listener to heads up changes
@@ -33,12 +32,12 @@
     /**
      * A notification was just pinned to the top.
      */
-    default void onHeadsUpPinned(NotificationData.Entry entry) {}
+    default void onHeadsUpPinned(NotificationEntry entry) {}
 
     /**
      * A notification was just unpinned from the top.
      */
-    default void onHeadsUpUnPinned(NotificationData.Entry entry) {}
+    default void onHeadsUpUnPinned(NotificationEntry entry) {}
 
     /**
      * A notification just became a heads up or turned back to its normal state.
@@ -46,5 +45,5 @@
      * @param entry     the entry of the changed notification
      * @param isHeadsUp whether the notification is now a headsUp notification
      */
-    default void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {}
+    default void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
index c2933e1..2b60274 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
@@ -21,15 +21,18 @@
 import android.content.res.Configuration;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.qs.QSFragment;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.StatusBar;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Let {@link RemoteInputView} to control the visibility of QuickSetting.
  */
+@Singleton
 public class RemoteInputQuickSettingsDisabler
         implements ConfigurationController.ConfigurationListener {
 
@@ -39,11 +42,13 @@
     private int mLastOrientation;
     @VisibleForTesting CommandQueue mCommandQueue;
 
-    public RemoteInputQuickSettingsDisabler(Context context) {
+    @Inject
+    public RemoteInputQuickSettingsDisabler(Context context,
+            ConfigurationController configController) {
         mContext = context;
         mCommandQueue = SysUiServiceProvider.getComponent(context, CommandQueue.class);
         mLastOrientation = mContext.getResources().getConfiguration().orientation;
-        Dependency.get(ConfigurationController.class).addCallback(this);
+        configController.addCallback(this);
     }
 
     public int adjustDisableFlags(int state) {
@@ -77,6 +82,6 @@
      * to modify the disable flags according to the status of mRemoteInputActive and misLandscape.
      */
     private void recomputeDisableFlags() {
-        mCommandQueue.recomputeDisableFlags(true);
+        mCommandQueue.recomputeDisableFlags(mContext.getDisplayId(), true);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 866015e..dfb02cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -56,7 +56,7 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.RemoteInputController;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 
@@ -83,7 +83,7 @@
     private RemoteInputController mController;
     private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
 
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
 
     private boolean mRemoved;
 
@@ -180,7 +180,7 @@
     }
 
     public static RemoteInputView inflate(Context context, ViewGroup root,
-            NotificationData.Entry entry,
+            NotificationEntry entry,
             RemoteInputController controller) {
         RemoteInputView v = (RemoteInputView)
                 LayoutInflater.from(context).inflate(R.layout.remote_input, root, false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
index 5418dc1..1f368e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
@@ -23,7 +23,11 @@
 
 import java.util.concurrent.CopyOnWriteArrayList;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /** Platform implementation of the rotation lock controller. **/
+@Singleton
 public final class RotationLockControllerImpl implements RotationLockController {
     private final Context mContext;
     private final CopyOnWriteArrayList<RotationLockControllerCallback> mCallbacks =
@@ -37,6 +41,7 @@
         }
     };
 
+    @Inject
     public RotationLockControllerImpl(Context context) {
         mContext = context;
         setListening(true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 29a6b95..d88ae78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -15,6 +15,8 @@
  */
 package com.android.systemui.statusbar.policy;
 
+import static com.android.systemui.Dependency.BG_HANDLER_NAME;
+
 import android.app.ActivityManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
@@ -47,7 +49,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.net.LegacyVpnInfo;
 import com.android.internal.net.VpnConfig;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.settings.CurrentUserTracker;
 
@@ -55,6 +56,13 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
 public class SecurityControllerImpl extends CurrentUserTracker implements SecurityController {
 
     private static final String TAG = "SecurityController";
@@ -78,6 +86,7 @@
     private final DevicePolicyManager mDevicePolicyManager;
     private final PackageManager mPackageManager;
     private final UserManager mUserManager;
+    private final Handler mBgHandler;
 
     @GuardedBy("mCallbacks")
     private final ArrayList<SecurityControllerCallback> mCallbacks = new ArrayList<>();
@@ -90,13 +99,18 @@
     // Needs to be cached here since the query has to be asynchronous
     private ArrayMap<Integer, Boolean> mHasCACerts = new ArrayMap<Integer, Boolean>();
 
-    public SecurityControllerImpl(Context context) {
-        this(context, null);
+    /**
+     */
+    @Inject
+    public SecurityControllerImpl(Context context, @Named(BG_HANDLER_NAME) Handler bgHandler) {
+        this(context, bgHandler, null);
     }
 
-    public SecurityControllerImpl(Context context, SecurityControllerCallback callback) {
+    public SecurityControllerImpl(Context context, Handler bgHandler,
+            SecurityControllerCallback callback) {
         super(context);
         mContext = context;
+        mBgHandler = bgHandler;
         mDevicePolicyManager = (DevicePolicyManager)
                 context.getSystemService(Context.DEVICE_POLICY_SERVICE);
         mConnectivityManager = (ConnectivityManager)
@@ -112,7 +126,7 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED);
         context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null,
-                new Handler(Dependency.get(Dependency.BG_LOOPER)));
+                bgHandler);
 
         // TODO: re-register network callback on user change.
         mConnectivityManager.registerNetworkCallback(REQUEST, mNetworkCallback);
@@ -404,7 +418,7 @@
                 return new Pair<Integer, Boolean>(userId[0], hasCACerts);
             } catch (RemoteException | InterruptedException | AssertionError e) {
                 Log.i(TAG, "failed to get CA certs", e);
-                new Handler(Dependency.get(Dependency.BG_LOOPER)).postDelayed(
+                mBgHandler.postDelayed(
                         () -> new CACertLoader().execute(userId[0]),
                         CA_CERT_LOADING_RETRY_TIME_IN_MS);
                 return new Pair<Integer, Boolean>(userId[0], null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
index 71d6e54..6193159 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.ContentObserver;
@@ -27,6 +29,11 @@
 
 import com.android.systemui.R;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+@Singleton
 public final class SmartReplyConstants extends ContentObserver {
 
     private static final String TAG = "SmartReplyConstants";
@@ -47,7 +54,8 @@
     private final Context mContext;
     private final KeyValueListParser mParser = new KeyValueListParser(',');
 
-    public SmartReplyConstants(Handler handler, Context context) {
+    @Inject
+    public SmartReplyConstants(@Named(MAIN_HANDLER_NAME) Handler handler, Context context) {
         super(handler);
 
         mContext = context;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 4fa8321..5e5fc42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -16,8 +16,6 @@
 import android.graphics.drawable.InsetDrawable;
 import android.graphics.drawable.RippleDrawable;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
 import android.text.Layout;
 import android.text.TextPaint;
 import android.text.method.TransformationMethod;
@@ -38,8 +36,8 @@
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.SmartReplyController;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 
 import java.text.BreakIterator;
@@ -65,7 +63,6 @@
     private final SmartReplyConstants mConstants;
     private final KeyguardDismissUtil mKeyguardDismissUtil;
     private final NotificationRemoteInputManager mRemoteInputManager;
-    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
 
     /**
      * The upper bound for the height of this view in pixels. Notifications are automatically
@@ -197,7 +194,7 @@
      */
     public void addRepliesFromRemoteInput(
             SmartReplies smartReplies,
-            SmartReplyController smartReplyController, NotificationData.Entry entry) {
+            SmartReplyController smartReplyController, NotificationEntry entry) {
         if (smartReplies.remoteInput != null && smartReplies.pendingIntent != null) {
             if (smartReplies.choices != null) {
                 for (int i = 0; i < smartReplies.choices.length; ++i) {
@@ -215,7 +212,7 @@
      * notification are shown.
      */
     public void addSmartActions(SmartActions smartActions,
-            SmartReplyController smartReplyController, NotificationData.Entry entry,
+            SmartReplyController smartReplyController, NotificationEntry entry,
             HeadsUpManager headsUpManager) {
         int numSmartActions = smartActions.actions.size();
         for (int n = 0; n < numSmartActions; n++) {
@@ -238,7 +235,7 @@
     @VisibleForTesting
     Button inflateReplyButton(Context context, ViewGroup root, int replyIndex,
             SmartReplies smartReplies, SmartReplyController smartReplyController,
-            NotificationData.Entry entry) {
+            NotificationEntry entry) {
         Button b = (Button) LayoutInflater.from(context).inflate(
                 R.layout.smart_reply_button, root, false);
         CharSequence choice = smartReplies.choices[replyIndex];
@@ -292,7 +289,7 @@
     @VisibleForTesting
     Button inflateActionButton(Context context, ViewGroup root, int actionIndex,
             SmartActions smartActions, SmartReplyController smartReplyController,
-            NotificationData.Entry entry, HeadsUpManager headsUpManager) {
+            NotificationEntry entry, HeadsUpManager headsUpManager) {
         Notification.Action action = smartActions.actions.get(actionIndex);
         Button button = (Button) LayoutInflater.from(context).inflate(
                 R.layout.smart_action_button, root, false);
@@ -311,8 +308,7 @@
                         () -> {
                             smartReplyController.smartActionClicked(
                                     entry, actionIndex, action, smartActions.fromAssistant);
-                            postOnUiThread(() ->
-                                    headsUpManager.removeNotification(entry.key, true));
+                            headsUpManager.removeNotification(entry.key, true);
                         }));
 
         // TODO(b/119010281): handle accessibility
@@ -323,10 +319,6 @@
         return button;
     }
 
-    private void postOnUiThread(Runnable runnable) {
-        mMainThreadHandler.post(runnable);
-    }
-
     @Override
     public LayoutParams generateLayoutParams(AttributeSet attrs) {
         return new LayoutParams(mContext, attrs);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
index bd76820..7347f66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
@@ -19,6 +19,9 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.MobileSignalController.MobileIconGroup;
 
+import java.util.HashMap;
+import java.util.Map;
+
 class TelephonyIcons {
     //***** Data connection icons
     static final int FLIGHT_MODE_ICON = R.drawable.stat_sys_airplane_mode;
@@ -33,6 +36,8 @@
     static final int ICON_4G = R.drawable.ic_4g_mobiledata;
     static final int ICON_4G_PLUS = R.drawable.ic_4g_plus_mobiledata;
     static final int ICON_1X = R.drawable.ic_1x_mobiledata;
+    static final int ICON_5G = R.drawable.ic_5g_mobiledata;
+    static final int ICON_5G_PLUS = R.drawable.ic_5g_plus_mobiledata;
 
     static final MobileIconGroup CARRIER_NETWORK_CHANGE = new MobileIconGroup(
             "CARRIER_NETWORK_CHANGE",
@@ -199,6 +204,34 @@
             TelephonyIcons.ICON_LTE_PLUS,
             true);
 
+    static final MobileIconGroup NR_5G = new MobileIconGroup(
+            "5G",
+            null,
+            null,
+            AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
+            0,
+            0,
+            0,
+            0,
+            AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
+            R.string.data_connection_5g,
+            TelephonyIcons.ICON_5G,
+            true);
+
+    static final MobileIconGroup NR_5G_PLUS = new MobileIconGroup(
+            "5G_PLUS",
+            null,
+            null,
+            AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
+            0,
+            0,
+            0,
+            0,
+            AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
+            R.string.data_connection_5g_plus,
+            TelephonyIcons.ICON_5G_PLUS,
+            true);
+
     static final MobileIconGroup DATA_DISABLED = new MobileIconGroup(
             "DataDisabled",
             null,
@@ -211,5 +244,27 @@
             R.string.cell_data_off_content_description,
             0,
             false);
+
+    /** Mapping icon name(lower case) to the icon object. */
+    static final Map<String, MobileIconGroup> ICON_NAME_TO_ICON;
+    static {
+        ICON_NAME_TO_ICON = new HashMap<>();
+        ICON_NAME_TO_ICON.put("carrier_network_change", CARRIER_NETWORK_CHANGE);
+        ICON_NAME_TO_ICON.put("3g", THREE_G);
+        ICON_NAME_TO_ICON.put("wfc", WFC);
+        ICON_NAME_TO_ICON.put("unknown", UNKNOWN);
+        ICON_NAME_TO_ICON.put("e", E);
+        ICON_NAME_TO_ICON.put("1x", ONE_X);
+        ICON_NAME_TO_ICON.put("g", G);
+        ICON_NAME_TO_ICON.put("h", H);
+        ICON_NAME_TO_ICON.put("h+", H_PLUS);
+        ICON_NAME_TO_ICON.put("4g", FOUR_G);
+        ICON_NAME_TO_ICON.put("4g+", FOUR_G_PLUS);
+        ICON_NAME_TO_ICON.put("lte", LTE);
+        ICON_NAME_TO_ICON.put("lte+", LTE_PLUS);
+        ICON_NAME_TO_ICON.put("5g", NR_5G);
+        ICON_NAME_TO_ICON.put("5g_plus", NR_5G_PLUS);
+        ICON_NAME_TO_ICON.put("datadisable", DATA_DISABLED);
+    }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java
index fed8032..0ca6ff6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java
@@ -40,6 +40,12 @@
 
 import java.util.ArrayList;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
 public class UserInfoControllerImpl implements UserInfoController {
 
     private static final String TAG = "UserInfoController";
@@ -53,6 +59,9 @@
     private Drawable mUserDrawable;
     private String mUserAccount;
 
+    /**
+     */
+    @Inject
     public UserInfoControllerImpl(Context context) {
         mContext = context;
         IntentFilter filter = new IntentFilter();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 15b2f2b..9343bf1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.policy;
 
 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
 
 import android.app.ActivityManager;
 import android.app.Dialog;
@@ -50,7 +51,6 @@
 import com.android.internal.util.UserIcons;
 import com.android.settingslib.RestrictedLockUtilsInternal;
 import com.android.settingslib.Utils;
-import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
 import com.android.systemui.GuestResumeSessionReceiver;
 import com.android.systemui.Prefs;
@@ -68,9 +68,14 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
 /**
  * Keeps a list of all users on the device for user switching.
  */
+@Singleton
 public class UserSwitcherController implements Dumpable {
 
     private static final String TAG = "UserSwitcherController";
@@ -102,8 +107,9 @@
     private Intent mSecondaryUserServiceIntent;
     private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
 
+    @Inject
     public UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor,
-            Handler handler, ActivityStarter activityStarter) {
+            @Named(MAIN_HANDLER_NAME) Handler handler, ActivityStarter activityStarter) {
         mContext = context;
         mGuestResumeSessionReceiver.register(context);
         mKeyguardMonitor = keyguardMonitor;
@@ -590,7 +596,7 @@
 
         protected BaseUserAdapter(UserSwitcherController controller) {
             mController = controller;
-            mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+            mKeyguardMonitor = controller.mKeyguardMonitor;
             controller.addAdapter(new WeakReference<>(this));
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
index 374408d..f629863 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
@@ -32,19 +32,24 @@
               R.drawable.stat_sys_wifi_signal_4_fully }
         };
 
+    static final int[] WIFI_FULL_ICONS = {
+            com.android.internal.R.drawable.ic_wifi_signal_0,
+            com.android.internal.R.drawable.ic_wifi_signal_1,
+            com.android.internal.R.drawable.ic_wifi_signal_2,
+            com.android.internal.R.drawable.ic_wifi_signal_3,
+            com.android.internal.R.drawable.ic_wifi_signal_4
+    };
+
     public static final int[][] QS_WIFI_SIGNAL_STRENGTH = {
             { R.drawable.ic_qs_wifi_0,
               R.drawable.ic_qs_wifi_1,
               R.drawable.ic_qs_wifi_2,
               R.drawable.ic_qs_wifi_3,
               R.drawable.ic_qs_wifi_4 },
-            { R.drawable.ic_qs_wifi_full_0,
-              R.drawable.ic_qs_wifi_full_1,
-              R.drawable.ic_qs_wifi_full_2,
-              R.drawable.ic_qs_wifi_full_3,
-              R.drawable.ic_qs_wifi_full_4 }
+            WIFI_FULL_ICONS
         };
 
+    public static final int QS_WIFI_DISABLED = com.android.internal.R.drawable.ic_wifi_signal_0;
     static final int QS_WIFI_NO_NETWORK = R.drawable.ic_qs_wifi_no_network;
     static final int WIFI_NO_NETWORK = R.drawable.stat_sys_wifi_signal_null;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 693df88..6f63544 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -53,7 +53,9 @@
                 connectivityManager, this::handleStatusUpdated);
         mWifiTracker.setListening(true);
         mHasMobileData = hasMobileData;
-        wifiManager.registerTrafficStateCallback(new WifiTrafficStateCallback(),  null);
+        if (wifiManager != null) {
+            wifiManager.registerTrafficStateCallback(new WifiTrafficStateCallback(), null);
+        }
         // WiFi only has one state.
         mCurrentState.iconGroup = mLastState.iconGroup = new IconGroup(
                 "Wi-Fi Icons",
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index 89ccff0..29c42d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+
 import android.app.ActivityManager;
 import android.app.AlarmManager;
 import android.app.NotificationManager;
@@ -48,7 +50,12 @@
 import java.util.ArrayList;
 import java.util.Objects;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
 /** Platform implementation of the zen mode controller. **/
+@Singleton
 public class ZenModeControllerImpl extends CurrentUserTracker
         implements ZenModeController, Dumpable {
     private static final String TAG = "ZenModeController";
@@ -71,7 +78,8 @@
     private long mZenUpdateTime;
     private NotificationManager.Policy mConsolidatedNotificationPolicy;
 
-    public ZenModeControllerImpl(Context context, Handler handler) {
+    @Inject
+    public ZenModeControllerImpl(Context context, @Named(MAIN_HANDLER_NAME) Handler handler) {
         super(context);
         mContext = context;
         mModeSetting = new GlobalSetting(mContext, handler, Global.ZEN_MODE) {
@@ -109,8 +117,8 @@
     @Override
     public boolean areNotificationsHiddenInShade() {
         if (mZenMode != Global.ZEN_MODE_OFF) {
-            return (mConfig.suppressedVisualEffects & NotificationManager.Policy
-                    .SUPPRESSED_EFFECT_NOTIFICATION_LIST) != 0;
+            return (mConsolidatedNotificationPolicy.suppressedVisualEffects
+                    & NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST) != 0;
         }
         return false;
     }
@@ -252,7 +260,6 @@
         }
     }
 
-
     @VisibleForTesting
     protected void updateZenModeConfig() {
         final ZenModeConfig config = mNoMan.getZenModeConfig();
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
index dae1472..0a29e04 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
@@ -184,7 +184,11 @@
                         mInfo.services[i].name);
 
                 if (mPluginEnabler.isEnabled(componentName) != isEnabled) {
-                    mPluginEnabler.setEnabled(componentName, isEnabled);
+                    if (isEnabled) {
+                        mPluginEnabler.setEnabled(componentName);
+                    } else {
+                        mPluginEnabler.setDisabled(componentName, PluginEnabler.DISABLED_MANUALLY);
+                    }
                     shouldSendBroadcast = true;
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java b/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java
index e85dee8..8f3a8f6 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java
@@ -21,6 +21,9 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.tuner.TunerService.Tunable;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Version of Space that can be resized by a tunable setting.
  */
@@ -35,8 +38,9 @@
     private final View mView;
     private final int mDefaultSize;
     private final float mDensity;
+    private final TunerService mTunerService;
 
-    private TunablePadding(String key, int def, int flags, View view) {
+    private TunablePadding(String key, int def, int flags, View view, TunerService tunerService) {
         mDefaultSize = def;
         mFlags = flags;
         mView = view;
@@ -44,7 +48,8 @@
         view.getContext().getSystemService(WindowManager.class)
                 .getDefaultDisplay().getMetrics(metrics);
         mDensity = metrics.density;
-        Dependency.get(TunerService.class).addTunable(this, key);
+        mTunerService = tunerService;
+        mTunerService.addTunable(this, key);
     }
 
     @Override
@@ -66,16 +71,29 @@
     }
 
     public void destroy() {
-        Dependency.get(TunerService.class).removeTunable(this);
+        mTunerService.removeTunable(this);
     }
 
-    // Exists for easy injecting in tests.
+    /**
+     * Exists for easy injecting in tests.
+     */
+    @Singleton
     public static class TunablePaddingService {
+
+        private final TunerService mTunerService;
+
+        /**
+         */
+        @Inject
+        public TunablePaddingService(TunerService tunerService) {
+            mTunerService = tunerService;
+        }
+
         public TunablePadding add(View view, String key, int defaultSize, int flags) {
             if (view == null) {
                 throw new IllegalArgumentException();
             }
-            return new TunablePadding(key, defaultSize, flags, view);
+            return new TunablePadding(key, defaultSize, flags, view, mTunerService);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 196d9bc..6185063 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -15,6 +15,8 @@
  */
 package com.android.systemui.tuner;
 
+import static com.android.systemui.Dependency.BG_HANDLER_NAME;
+
 import android.app.ActivityManager;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -33,7 +35,6 @@
 
 import com.android.internal.util.ArrayUtils;
 import com.android.systemui.DemoMode;
-import com.android.systemui.Dependency;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -43,7 +44,14 @@
 import java.util.HashSet;
 import java.util.Set;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
 
+
+/**
+ */
+@Singleton
 public class TunerServiceImpl extends TunerService {
 
     private static final String TUNER_VERSION = "sysui_tuner_version";
@@ -65,19 +73,25 @@
     // Set of all tunables, used for leak detection.
     private final HashSet<Tunable> mTunables = LeakDetector.ENABLED ? new HashSet<>() : null;
     private final Context mContext;
+    private final LeakDetector mLeakDetector;
 
     private ContentResolver mContentResolver;
     private int mCurrentUser;
     private CurrentUserTracker mUserTracker;
 
-    public TunerServiceImpl(Context context) {
+    /**
+     */
+    @Inject
+    public TunerServiceImpl(Context context, @Named(BG_HANDLER_NAME) Handler bgHandler,
+            LeakDetector leakDetector) {
         mContext = context;
         mContentResolver = mContext.getContentResolver();
+        mLeakDetector = leakDetector;
 
         for (UserInfo user : UserManager.get(mContext).getUsers()) {
             mCurrentUser = user.getUserHandle().getIdentifier();
             if (getValue(TUNER_VERSION, 0) != CURRENT_TUNER_VERSION) {
-                upgradeTuner(getValue(TUNER_VERSION, 0), CURRENT_TUNER_VERSION);
+                upgradeTuner(getValue(TUNER_VERSION, 0), CURRENT_TUNER_VERSION, bgHandler);
             }
         }
 
@@ -98,7 +112,7 @@
         mUserTracker.stopTracking();
     }
 
-    private void upgradeTuner(int oldVersion, int newVersion) {
+    private void upgradeTuner(int oldVersion, int newVersion, Handler bgHandler) {
         if (oldVersion < 1) {
             String blacklistStr = getValue(StatusBarIconController.ICON_BLACKLIST);
             if (blacklistStr != null) {
@@ -120,7 +134,7 @@
         if (oldVersion < 4) {
             // Delay this so that we can wait for everything to be registered first.
             final int user = mCurrentUser;
-            new Handler(Dependency.get(Dependency.BG_LOOPER)).postDelayed(
+            bgHandler.postDelayed(
                     () -> clearAllFromUser(user), 5000);
         }
         setValue(TUNER_VERSION, newVersion);
@@ -167,7 +181,7 @@
         mTunableLookup.get(key).add(tunable);
         if (LeakDetector.ENABLED) {
             mTunables.add(tunable);
-            Dependency.get(LeakDetector.class).trackCollection(mTunables, "TunerService.mTunables");
+            mLeakDetector.trackCollection(mTunables, "TunerService.mTunables");
         }
         Uri uri = Settings.Secure.getUriFor(key);
         if (!mListeningUris.containsKey(uri)) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java b/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java
index 0dd8937..31f4991 100644
--- a/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java
+++ b/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java
@@ -38,6 +38,9 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Wrapper around sensor manager that hides potential sources of latency.
  *
@@ -45,6 +48,7 @@
  * without blocking. Note that this means registering listeners now always appears successful even
  * if it is not.
  */
+@Singleton
 public class AsyncSensorManager extends SensorManager
         implements PluginListener<SensorManagerPlugin> {
 
@@ -56,8 +60,14 @@
     @VisibleForTesting final Handler mHandler;
     private final List<SensorManagerPlugin> mPlugins;
 
-    public AsyncSensorManager(SensorManager inner, PluginManager pluginManager) {
-        mInner = inner;
+    @Inject
+    public AsyncSensorManager(Context context, PluginManager pluginManager) {
+        this(context.getSystemService(SensorManager.class), pluginManager);
+    }
+
+    @VisibleForTesting
+    AsyncSensorManager(SensorManager sensorManager, PluginManager pluginManager) {
+        mInner = sensorManager;
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
         mSensorCache = mInner.getSensorList(Sensor.TYPE_ALL);
@@ -76,8 +86,9 @@
     }
 
     @Override
-    protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor, int delayUs,
-            Handler handler, int maxReportLatencyUs, int reservedFlags) {
+    protected boolean registerListenerImpl(SensorEventListener listener,
+            Sensor sensor, int delayUs, Handler handler, int maxReportLatencyUs,
+            int reservedFlags) {
         mHandler.post(() -> {
             if (!mInner.registerListener(listener, sensor, delayUs, maxReportLatencyUs, handler)) {
                 Log.e(TAG, "Registering " + listener + " for " + sensor + " failed.");
@@ -146,23 +157,28 @@
      * @param sensor
      * @param listener
      */
-    public void requestPluginTriggerSensor(SensorManagerPlugin.Sensor sensor,
-            SensorManagerPlugin.TriggerEventListener listener) {
+    public void registerPluginListener(SensorManagerPlugin.Sensor sensor,
+            SensorManagerPlugin.SensorEventListener listener) {
         if (mPlugins.isEmpty()) {
             Log.w(TAG, "No plugins registered");
         }
         mHandler.post(() -> {
             for (int i = 0; i < mPlugins.size(); i++) {
-                mPlugins.get(i).registerTriggerEvent(sensor, listener);
+                mPlugins.get(i).registerListener(sensor, listener);
             }
         });
     }
 
-    public void cancelPluginTriggerSensor(SensorManagerPlugin.Sensor sensor,
-            SensorManagerPlugin.TriggerEventListener listener) {
+    /**
+     * Unregisters all sensors that match the give type for all plugins.
+     * @param sensor
+     * @param listener
+     */
+    public void unregisterPluginListener(SensorManagerPlugin.Sensor sensor,
+            SensorManagerPlugin.SensorEventListener listener) {
         mHandler.post(() -> {
             for (int i = 0; i < mPlugins.size(); i++) {
-                mPlugins.get(i).unregisterTriggerEvent(sensor, listener);
+                mPlugins.get(i).unregisterListener(sensor, listener);
             }
         });
     }
@@ -185,7 +201,8 @@
     }
 
     @Override
-    protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) {
+    protected void unregisterListenerImpl(SensorEventListener listener,
+            Sensor sensor) {
         mHandler.post(() -> {
             if (sensor == null) {
                 mInner.unregisterListener(listener);
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
new file mode 100644
index 0000000..faebf60
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util;
+
+import android.content.Context;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+import android.view.InflateException;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.qs.QSFooterImpl;
+import com.android.systemui.qs.QuickStatusBarHeader;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+
+/**
+ * Manages inflation that requires dagger injection.
+ * See docs/dagger.md for details.
+ */
+@Singleton
+public class InjectionInflationController {
+
+    public static final String VIEW_CONTEXT = "view_context";
+    private final ViewCreator mViewCreator;
+    private final ArrayMap<String, Method> mInjectionMap = new ArrayMap<>();
+    private final LayoutInflater.Factory2 mFactory = new InjectionFactory();
+
+    @Inject
+    public InjectionInflationController(SystemUIFactory.SystemUIRootComponent rootComponent) {
+        mViewCreator = rootComponent.createViewCreator();
+        initInjectionMap();
+    }
+
+    ArrayMap<String, Method> getInjectionMap() {
+        return mInjectionMap;
+    }
+
+    ViewCreator getFragmentCreator() {
+        return mViewCreator;
+    }
+
+    /**
+     * Wraps a {@link LayoutInflater} to support creating dagger injected views.
+     * See docs/dagger.md for details.
+     */
+    public LayoutInflater injectable(LayoutInflater inflater) {
+        LayoutInflater ret = inflater.cloneInContext(inflater.getContext());
+        ret.setPrivateFactory(mFactory);
+        return ret;
+    }
+
+    private void initInjectionMap() {
+        for (Method method : ViewInstanceCreator.class.getDeclaredMethods()) {
+            if (View.class.isAssignableFrom(method.getReturnType())
+                    && (method.getModifiers() & Modifier.PUBLIC) != 0) {
+                mInjectionMap.put(method.getReturnType().getName(), method);
+            }
+        }
+    }
+
+    /**
+     * The subcomponent of dagger that holds all views that need injection.
+     */
+    @Subcomponent
+    public interface ViewCreator {
+        /**
+         * Creates another subcomponent to actually generate the view.
+         */
+        ViewInstanceCreator createInstanceCreator(ViewAttributeProvider attributeProvider);
+    }
+
+    /**
+     * Secondary sub-component that actually creates the views.
+     *
+     * Having two subcomponents lets us hide the complexity of providing the named context
+     * and AttributeSet from the SystemUIRootComponent, instead we have one subcomponent that
+     * creates a new ViewInstanceCreator any time we need to inflate a view.
+     */
+    @Subcomponent(modules = ViewAttributeProvider.class)
+    public interface ViewInstanceCreator {
+        /**
+         * Creates the QuickStatusBarHeader.
+         */
+        QuickStatusBarHeader createQsHeader();
+        /**
+         * Creates the QSFooterImpl.
+         */
+        QSFooterImpl createQsFooter();
+
+        /**
+         * Creates the NotificationStackScrollLayout.
+         */
+        NotificationStackScrollLayout createNotificationStackScrollLayout();
+    }
+
+    /**
+     * Module for providing view-specific constructor objects.
+     */
+    @Module
+    public class ViewAttributeProvider {
+        private final Context mContext;
+        private final AttributeSet mAttrs;
+
+        private ViewAttributeProvider(Context context, AttributeSet attrs) {
+            mContext = context;
+            mAttrs = attrs;
+        }
+
+        /**
+         * Provides the view-themed context (as opposed to the global sysui application context).
+         */
+        @Provides
+        @Named(VIEW_CONTEXT)
+        public Context provideContext() {
+            return mContext;
+        }
+
+        /**
+         * Provides the AttributeSet for the current view being inflated.
+         */
+        @Provides
+        public AttributeSet provideAttributeSet() {
+            return mAttrs;
+        }
+    }
+
+    private class InjectionFactory implements LayoutInflater.Factory2 {
+
+        @Override
+        public View onCreateView(String name, Context context, AttributeSet attrs) {
+            Method creationMethod = mInjectionMap.get(name);
+            if (creationMethod != null) {
+                ViewAttributeProvider provider = new ViewAttributeProvider(context, attrs);
+                try {
+                    return (View) creationMethod.invoke(
+                            mViewCreator.createInstanceCreator(provider));
+                } catch (IllegalAccessException e) {
+                    throw new InflateException("Could not inflate " + name, e);
+                } catch (InvocationTargetException e) {
+                    throw new InflateException("Could not inflate " + name, e);
+                }
+            }
+            return null;
+        }
+
+        @Override
+        public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+            return onCreateView(name, context, attrs);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index 490cdd5c..6b4f7ed 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -69,10 +69,13 @@
 
         /**
          * Sets visibility of this {@link View} given the states passed from
-         * {@link com.android.systemui.statusbar.CommandQueue.Callbacks#disable(int, int)}.
+         * {@link com.android.systemui.statusbar.CommandQueue.Callbacks#disable(int, int, int)}.
          */
         @Override
-        public void disable(int state1, int state2, boolean animate) {
+        public void disable(int displayId, int state1, int state2, boolean animate) {
+            if (displayId != mView.getDisplay().getDisplayId()) {
+                return;
+            }
             final boolean disabled = ((state1 & mMask1) != 0) || ((state2 & mMask2) != 0);
             if (disabled == mDisabled) return;
             mDisabled = disabled;
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index b2cc269..b590e77 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -17,6 +17,7 @@
 package com.android.systemui.util.leak;
 
 import static com.android.internal.logging.MetricsLogger.VIEW_UNKNOWN;
+import static com.android.systemui.Dependency.BG_LOOPER_NAME;
 
 import android.app.ActivityManager;
 import android.content.Context;
@@ -42,15 +43,22 @@
 import android.util.Log;
 import android.util.LongSparseArray;
 
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
+import com.android.systemui.SystemUIFactory;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 
 import java.util.ArrayList;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
 public class GarbageMonitor {
     private static final boolean LEAK_REPORTING_ENABLED =
             Build.IS_DEBUGGABLE
@@ -85,9 +93,12 @@
 
     private long mHeapLimit;
 
+    /**
+     */
+    @Inject
     public GarbageMonitor(
             Context context,
-            Looper bgLooper,
+            @Named(BG_LOOPER_NAME) Looper bgLooper,
             LeakDetector leakDetector,
             LeakReporter leakReporter) {
         mContext = context.getApplicationContext();
@@ -335,9 +346,10 @@
         private final GarbageMonitor gm;
         private ProcessMemInfo pmi;
 
+        @Inject
         public MemoryTile(QSHost host) {
             super(host);
-            gm = Dependency.get(GarbageMonitor.class);
+            gm = SystemUIFactory.getInstance().getRootComponent().createGarbageMonitor();
         }
 
         @Override
@@ -442,7 +454,8 @@
                     Settings.Secure.getInt(
                                     mContext.getContentResolver(), FORCE_ENABLE_LEAK_REPORTING, 0)
                             != 0;
-            mGarbageMonitor = Dependency.get(GarbageMonitor.class);
+            mGarbageMonitor = SystemUIFactory.getInstance().getRootComponent()
+                   .createGarbageMonitor();
             if (LEAK_REPORTING_ENABLED || forceEnable) {
                 mGarbageMonitor.startLeakMonitor();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java b/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java
index a47e99d..b25df5f 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.util.leak;
 
+import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME;
+
+import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
@@ -39,9 +42,14 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
 /**
  * Dumps data to debug leaks and posts a notification to share the data.
  */
+@Singleton
 public class LeakReporter {
 
     static final String TAG = "LeakReporter";
@@ -56,7 +64,9 @@
     private final LeakDetector mLeakDetector;
     private final String mLeakReportEmail;
 
-    public LeakReporter(Context context, LeakDetector leakDetector, String leakReportEmail) {
+    @Inject
+    public LeakReporter(Context context, LeakDetector leakDetector,
+            @Nullable @Named(LEAK_REPORT_EMAIL_NAME) String leakReportEmail) {
         mContext = context;
         mLeakDetector = leakDetector;
         mLeakReportEmail = leakReportEmail;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index e20e267..32bc01c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -67,6 +67,9 @@
 import java.util.Map;
 import java.util.Objects;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  *  Source of truth for all state / events related to the volume dialog.  No presentation.
  *
@@ -74,6 +77,7 @@
  *
  *  Methods ending in "W" must be called on the worker thread.
  */
+@Singleton
 public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpable {
     private static final String TAG = Util.logTag(VolumeDialogControllerImpl.class);
 
@@ -131,6 +135,7 @@
 
     protected final VC mVolumeController = new VC();
 
+    @Inject
     public VolumeDialogControllerImpl(Context context) {
         mContext = context.getApplicationContext();
         mNotificationManager = (NotificationManager) mContext.getSystemService(
@@ -545,7 +550,8 @@
     }
 
     private boolean updateZenConfig() {
-        final NotificationManager.Policy policy = mNotificationManager.getNotificationPolicy();
+        final NotificationManager.Policy policy =
+                mNotificationManager.getConsolidatedNotificationPolicy();
         boolean disallowAlarms = (policy.priorityCategories & NotificationManager.Policy
                 .PRIORITY_CATEGORY_ALARMS) == 0;
         boolean disallowMedia = (policy.priorityCategories & NotificationManager.Policy
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 7ca5423..1844df5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -35,12 +35,17 @@
 import android.testing.TestableLooper.RunWithLooper;
 import android.text.TextPaint;
 import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
 import android.widget.TextClock;
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.ClockPlugin;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -51,10 +56,15 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWithLooper
 @RunWith(AndroidTestingRunner.class)
+// Need to run on the main thread because KeyguardSliceView$Row init checks for
+// the main thread before acquiring a wake lock. This class is constructed when
+// the keyguard_clcok_switch layout is inflated.
+@RunWithLooper(setAsMainLooper = true)
 public class KeyguardClockSwitchTest extends SysuiTestCase {
     private PluginManager mPluginManager;
+    private FrameLayout mClockContainer;
+    private StatusBarStateController.StateListener mStateListener;
 
     @Mock
     TextClock mClockView;
@@ -67,8 +77,10 @@
         LayoutInflater layoutInflater = LayoutInflater.from(getContext());
         mKeyguardClockSwitch =
                 (KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null);
+        mClockContainer = mKeyguardClockSwitch.findViewById(R.id.clock_view);
         MockitoAnnotations.initMocks(this);
         when(mClockView.getPaint()).thenReturn(mock(TextPaint.class));
+        mStateListener = mKeyguardClockSwitch.getStateListener();
     }
 
     @Test
@@ -97,7 +109,26 @@
         listener.onPluginConnected(plugin, null);
 
         verify(mClockView).setVisibility(GONE);
-        assertThat(plugin.getView().getParent()).isEqualTo(mKeyguardClockSwitch);
+        assertThat(plugin.getView().getParent()).isEqualTo(mClockContainer);
+    }
+
+    @Test
+    public void onPluginConnected_showPluginBigClock() {
+        // GIVEN that the container for the big clock has visibility GONE
+        FrameLayout bigClockContainer = new FrameLayout(getContext());
+        bigClockContainer.setVisibility(GONE);
+        mKeyguardClockSwitch.setBigClockContainer(bigClockContainer);
+        // AND the plugin returns a view for the big clock
+        ClockPlugin plugin = mock(ClockPlugin.class);
+        TextClock pluginView = new TextClock(getContext());
+        when(plugin.getBigClockView()).thenReturn(pluginView);
+        PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
+        // WHEN the plugin is connected
+        listener.onPluginConnected(plugin, null);
+        // THEN the big clock container is visible and it is the parent of the
+        // big clock view.
+        assertThat(bigClockContainer.getVisibility()).isEqualTo(VISIBLE);
+        assertThat(pluginView.getParent()).isEqualTo(bigClockContainer);
     }
 
     @Test
@@ -120,7 +151,7 @@
         when(plugin2.getView()).thenReturn(new TextClock(getContext()));
         listener.onPluginConnected(plugin2, null);
         // THEN only the view from the second plugin should be a child of KeyguardClockSwitch.
-        assertThat(plugin2.getView().getParent()).isEqualTo(mKeyguardClockSwitch);
+        assertThat(plugin2.getView().getParent()).isEqualTo(mClockContainer);
         assertThat(plugin1.getView().getParent()).isNull();
     }
 
@@ -140,6 +171,26 @@
     }
 
     @Test
+    public void onPluginDisconnected_hidePluginBigClock() {
+        // GIVEN that the big clock container is visible
+        FrameLayout bigClockContainer = new FrameLayout(getContext());
+        bigClockContainer.setVisibility(VISIBLE);
+        mKeyguardClockSwitch.setBigClockContainer(bigClockContainer);
+        // AND the plugin returns a view for the big clock
+        ClockPlugin plugin = mock(ClockPlugin.class);
+        TextClock pluginView = new TextClock(getContext());
+        when(plugin.getBigClockView()).thenReturn(pluginView);
+        PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
+        listener.onPluginConnected(plugin, null);
+        // WHEN the plugin is disconnected
+        listener.onPluginDisconnected(plugin);
+        // THEN the big lock container is GONE and the big clock view doesn't have
+        // a parent.
+        assertThat(bigClockContainer.getVisibility()).isEqualTo(GONE);
+        assertThat(pluginView.getParent()).isNull();
+    }
+
+    @Test
     public void onPluginDisconnected_nullView() {
         ClockPlugin plugin = mock(ClockPlugin.class);
         PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
@@ -161,7 +212,7 @@
         // WHEN the first plugin is disconnected
         listener.onPluginDisconnected(plugin1);
         // THEN the view from the second plugin is still a child of KeyguardClockSwitch.
-        assertThat(plugin2.getView().getParent()).isEqualTo(mKeyguardClockSwitch);
+        assertThat(plugin2.getView().getParent()).isEqualTo(mClockContainer);
         assertThat(plugin1.getView().getParent()).isNull();
     }
 
@@ -227,4 +278,28 @@
 
         verify(plugin).setStyle(style);
     }
+
+    @Test
+    public void onStateChanged_InvisibleInShade() {
+        // GIVEN that the big clock container is visible
+        ViewGroup container = mock(ViewGroup.class);
+        when(container.getVisibility()).thenReturn(View.VISIBLE);
+        mKeyguardClockSwitch.setBigClockContainer(container);
+        // WHEN transitioned to SHADE state
+        mStateListener.onStateChanged(StatusBarState.SHADE);
+        // THEN the container is invisible.
+        verify(container).setVisibility(View.INVISIBLE);
+    }
+
+    @Test
+    public void onStateChanged_VisibleInKeyguard() {
+        // GIVEN that the big clock container is invisible
+        ViewGroup container = mock(ViewGroup.class);
+        when(container.getVisibility()).thenReturn(View.INVISIBLE);
+        mKeyguardClockSwitch.setBigClockContainer(container);
+        // WHEN transitioned to KEYGUARD state
+        mStateListener.onStateChanged(StatusBarState.KEYGUARD);
+        // THEN the container is visible.
+        verify(container).setVisibility(View.VISIBLE);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
index 3426e11..61bfa75 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
@@ -69,4 +69,10 @@
         mDependency.onConfigurationChanged(null);
         verify(d).onConfigurationChanged(eq(null));
     }
+
+    @Test
+    public void testInitDependency() {
+        Dependency.clearDependencies();
+        Dependency.initDependencies(mContext);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index f278a17..9400d6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -22,6 +22,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.annotation.UserIdInt;
@@ -35,197 +36,95 @@
 import android.widget.RemoteViews;
 
 import com.android.internal.messages.nano.SystemMessageProto;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class ForegroundServiceControllerTest extends SysuiTestCase {
-    public static @UserIdInt int USERID_ONE = 10; // UserManagerService.MIN_USER_ID;
-    public static @UserIdInt int USERID_TWO = USERID_ONE + 1;
+    @UserIdInt private static final int USERID_ONE = 10; // UserManagerService.MIN_USER_ID;
+    @UserIdInt private static final int USERID_TWO = USERID_ONE + 1;
 
-    private ForegroundServiceController fsc;
+    private ForegroundServiceController mFsc;
+    private ForegroundServiceNotificationListener mListener;
+    private NotificationEntryListener mEntryListener;
 
     @Before
     public void setUp() throws Exception {
-        fsc = new ForegroundServiceControllerImpl(mContext);
-    }
-
-    @Test
-    public void testNotificationCRUD_dungeon() {
-        StatusBarNotification sbn_user1_app1_fg = makeMockFgSBN(USERID_ONE, "com.example.app1");
-        StatusBarNotification sbn_user2_app2_fg = makeMockFgSBN(USERID_TWO, "com.example.app2");
-        StatusBarNotification sbn_user1_app3_fg = makeMockFgSBN(USERID_ONE, "com.example.app3");
-        StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, "com.example.app1",
-                5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
-        StatusBarNotification sbn_user2_app1 = makeMockSBN(USERID_TWO, "com.example.app1",
-                5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
-
-        assertFalse(fsc.removeNotification(sbn_user1_app3_fg));
-        assertFalse(fsc.removeNotification(sbn_user2_app2_fg));
-        assertFalse(fsc.removeNotification(sbn_user1_app1_fg));
-        assertFalse(fsc.removeNotification(sbn_user1_app1));
-        assertFalse(fsc.removeNotification(sbn_user2_app1));
-
-        fsc.addNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT);
-        fsc.addNotification(sbn_user2_app2_fg, NotificationManager.IMPORTANCE_DEFAULT);
-        fsc.addNotification(sbn_user1_app3_fg, NotificationManager.IMPORTANCE_DEFAULT);
-        fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT);
-        fsc.addNotification(sbn_user2_app1, NotificationManager.IMPORTANCE_DEFAULT);
-
-        // these are never added to the tracker
-        assertFalse(fsc.removeNotification(sbn_user1_app1));
-        assertFalse(fsc.removeNotification(sbn_user2_app1));
-
-        fsc.updateNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT);
-        fsc.updateNotification(sbn_user2_app1, NotificationManager.IMPORTANCE_DEFAULT);
-        // should still not be there
-        assertFalse(fsc.removeNotification(sbn_user1_app1));
-        assertFalse(fsc.removeNotification(sbn_user2_app1));
-
-        fsc.updateNotification(sbn_user2_app2_fg, NotificationManager.IMPORTANCE_DEFAULT);
-        fsc.updateNotification(sbn_user1_app3_fg, NotificationManager.IMPORTANCE_DEFAULT);
-        fsc.updateNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT);
-
-        assertTrue(fsc.removeNotification(sbn_user1_app3_fg));
-        assertFalse(fsc.removeNotification(sbn_user1_app3_fg));
-
-        assertTrue(fsc.removeNotification(sbn_user2_app2_fg));
-        assertFalse(fsc.removeNotification(sbn_user2_app2_fg));
-
-        assertTrue(fsc.removeNotification(sbn_user1_app1_fg));
-        assertFalse(fsc.removeNotification(sbn_user1_app1_fg));
-
-        assertFalse(fsc.removeNotification(sbn_user1_app1));
-        assertFalse(fsc.removeNotification(sbn_user2_app1));
-    }
-
-    @Test
-    public void testNotificationCRUD_stdLayout() {
-        StatusBarNotification sbn_user1_app1_fg =
-                makeMockFgSBN(USERID_ONE, "com.example.app1", 0, true);
-        StatusBarNotification sbn_user2_app2_fg =
-                makeMockFgSBN(USERID_TWO, "com.example.app2", 1, true);
-        StatusBarNotification sbn_user1_app3_fg =
-                makeMockFgSBN(USERID_ONE, "com.example.app3", 2, true);
-        StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, "com.example.app1",
-                5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
-        StatusBarNotification sbn_user2_app1 = makeMockSBN(USERID_TWO, "com.example.app1",
-                5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
-
-        assertFalse(fsc.removeNotification(sbn_user1_app3_fg));
-        assertFalse(fsc.removeNotification(sbn_user2_app2_fg));
-        assertFalse(fsc.removeNotification(sbn_user1_app1_fg));
-        assertFalse(fsc.removeNotification(sbn_user1_app1));
-        assertFalse(fsc.removeNotification(sbn_user2_app1));
-
-        fsc.addNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
-        fsc.addNotification(sbn_user2_app2_fg, NotificationManager.IMPORTANCE_MIN);
-        fsc.addNotification(sbn_user1_app3_fg, NotificationManager.IMPORTANCE_MIN);
-        fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_MIN);
-        fsc.addNotification(sbn_user2_app1, NotificationManager.IMPORTANCE_MIN);
-
-        // these are never added to the tracker
-        assertFalse(fsc.removeNotification(sbn_user1_app1));
-        assertFalse(fsc.removeNotification(sbn_user2_app1));
-
-        fsc.updateNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_MIN);
-        fsc.updateNotification(sbn_user2_app1, NotificationManager.IMPORTANCE_MIN);
-        // should still not be there
-        assertFalse(fsc.removeNotification(sbn_user1_app1));
-        assertFalse(fsc.removeNotification(sbn_user2_app1));
-
-        fsc.updateNotification(sbn_user2_app2_fg, NotificationManager.IMPORTANCE_MIN);
-        fsc.updateNotification(sbn_user1_app3_fg, NotificationManager.IMPORTANCE_MIN);
-        fsc.updateNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
-
-        assertTrue(fsc.removeNotification(sbn_user1_app3_fg));
-        assertFalse(fsc.removeNotification(sbn_user1_app3_fg));
-
-        assertTrue(fsc.removeNotification(sbn_user2_app2_fg));
-        assertFalse(fsc.removeNotification(sbn_user2_app2_fg));
-
-        assertTrue(fsc.removeNotification(sbn_user1_app1_fg));
-        assertFalse(fsc.removeNotification(sbn_user1_app1_fg));
-
-        assertFalse(fsc.removeNotification(sbn_user1_app1));
-        assertFalse(fsc.removeNotification(sbn_user2_app1));
+        mFsc = new ForegroundServiceController();
+        NotificationEntryManager notificationEntryManager = mock(NotificationEntryManager.class);
+        mListener = new ForegroundServiceNotificationListener(
+                mContext, mFsc, notificationEntryManager);
+        ArgumentCaptor<NotificationEntryListener> entryListenerCaptor =
+                ArgumentCaptor.forClass(NotificationEntryListener.class);
+        verify(notificationEntryManager).addNotificationEntryListener(
+                entryListenerCaptor.capture());
+        mEntryListener = entryListenerCaptor.getValue();
     }
 
     @Test
     public void testAppOpsCRUD() {
         // no crash on remove that doesn't exist
-        fsc.onAppOpChanged(9, 1000, "pkg1", false);
-        assertNull(fsc.getAppOps(0, "pkg1"));
+        mFsc.onAppOpChanged(9, 1000, "pkg1", false);
+        assertNull(mFsc.getAppOps(0, "pkg1"));
 
         // multiuser & multipackage
-        fsc.onAppOpChanged(8, 50, "pkg1", true);
-        fsc.onAppOpChanged(1, 60, "pkg3", true);
-        fsc.onAppOpChanged(7, 500000, "pkg2", true);
+        mFsc.onAppOpChanged(8, 50, "pkg1", true);
+        mFsc.onAppOpChanged(1, 60, "pkg3", true);
+        mFsc.onAppOpChanged(7, 500000, "pkg2", true);
 
-        assertEquals(1, fsc.getAppOps(0, "pkg1").size());
-        assertTrue(fsc.getAppOps(0, "pkg1").contains(8));
+        assertEquals(1, mFsc.getAppOps(0, "pkg1").size());
+        assertTrue(mFsc.getAppOps(0, "pkg1").contains(8));
 
-        assertEquals(1, fsc.getAppOps(UserHandle.getUserId(500000), "pkg2").size());
-        assertTrue(fsc.getAppOps(UserHandle.getUserId(500000), "pkg2").contains(7));
+        assertEquals(1, mFsc.getAppOps(UserHandle.getUserId(500000), "pkg2").size());
+        assertTrue(mFsc.getAppOps(UserHandle.getUserId(500000), "pkg2").contains(7));
 
-        assertEquals(1, fsc.getAppOps(0, "pkg3").size());
-        assertTrue(fsc.getAppOps(0, "pkg3").contains(1));
+        assertEquals(1, mFsc.getAppOps(0, "pkg3").size());
+        assertTrue(mFsc.getAppOps(0, "pkg3").contains(1));
 
         // multiple ops for the same package
-        fsc.onAppOpChanged(9, 50, "pkg1", true);
-        fsc.onAppOpChanged(5, 50, "pkg1", true);
+        mFsc.onAppOpChanged(9, 50, "pkg1", true);
+        mFsc.onAppOpChanged(5, 50, "pkg1", true);
 
-        assertEquals(3, fsc.getAppOps(0, "pkg1").size());
-        assertTrue(fsc.getAppOps(0, "pkg1").contains(8));
-        assertTrue(fsc.getAppOps(0, "pkg1").contains(9));
-        assertTrue(fsc.getAppOps(0, "pkg1").contains(5));
+        assertEquals(3, mFsc.getAppOps(0, "pkg1").size());
+        assertTrue(mFsc.getAppOps(0, "pkg1").contains(8));
+        assertTrue(mFsc.getAppOps(0, "pkg1").contains(9));
+        assertTrue(mFsc.getAppOps(0, "pkg1").contains(5));
 
-        assertEquals(1, fsc.getAppOps(UserHandle.getUserId(500000), "pkg2").size());
-        assertTrue(fsc.getAppOps(UserHandle.getUserId(500000), "pkg2").contains(7));
+        assertEquals(1, mFsc.getAppOps(UserHandle.getUserId(500000), "pkg2").size());
+        assertTrue(mFsc.getAppOps(UserHandle.getUserId(500000), "pkg2").contains(7));
 
         // remove one of the multiples
-        fsc.onAppOpChanged(9, 50, "pkg1", false);
-        assertEquals(2, fsc.getAppOps(0, "pkg1").size());
-        assertTrue(fsc.getAppOps(0, "pkg1").contains(8));
-        assertTrue(fsc.getAppOps(0, "pkg1").contains(5));
+        mFsc.onAppOpChanged(9, 50, "pkg1", false);
+        assertEquals(2, mFsc.getAppOps(0, "pkg1").size());
+        assertTrue(mFsc.getAppOps(0, "pkg1").contains(8));
+        assertTrue(mFsc.getAppOps(0, "pkg1").contains(5));
 
         // remove last op
-        fsc.onAppOpChanged(1, 60, "pkg3", false);
-        assertNull(fsc.getAppOps(0, "pkg3"));
+        mFsc.onAppOpChanged(1, 60, "pkg3", false);
+        assertNull(mFsc.getAppOps(0, "pkg3"));
     }
 
     @Test
-    public void testDungeonPredicate() {
+    public void testDisclosurePredicate() {
         StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, "com.example.app1",
                 5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
-        StatusBarNotification sbn_user1_dungeon = makeMockSBN(USERID_ONE, "android",
+        StatusBarNotification sbn_user1_disclosure = makeMockSBN(USERID_ONE, "android",
                 SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICES,
                 null, Notification.FLAG_NO_CLEAR);
 
-        assertTrue(fsc.isDungeonNotification(sbn_user1_dungeon));
-        assertFalse(fsc.isDungeonNotification(sbn_user1_app1));
+        assertTrue(mFsc.isDisclosureNotification(sbn_user1_disclosure));
+        assertFalse(mFsc.isDisclosureNotification(sbn_user1_app1));
     }
 
     @Test
-    public void testDungeonCRUD() {
-        StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, "com.example.app1",
-                5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
-        StatusBarNotification sbn_user1_dungeon = makeMockSBN(USERID_ONE, "android",
-                SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICES,
-                null, Notification.FLAG_NO_CLEAR);
-
-        fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT);
-        fsc.addNotification(sbn_user1_dungeon, NotificationManager.IMPORTANCE_DEFAULT);
-
-        fsc.removeNotification(sbn_user1_dungeon);
-        assertFalse(fsc.removeNotification(sbn_user1_app1));
-    }
-
-    @Test
-    public void testNeedsDungeonAfterRemovingUnrelatedNotification() {
+    public void testNeedsDisclosureAfterRemovingUnrelatedNotification() {
         final String PKG1 = "com.example.app100";
 
         StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, PKG1,
@@ -233,21 +132,21 @@
         StatusBarNotification sbn_user1_app1_fg = makeMockFgSBN(USERID_ONE, PKG1);
 
         // first add a normal notification
-        fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT);
+        entryAdded(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT);
         // nothing required yet
-        assertFalse(fsc.isDungeonNeededForUser(USERID_ONE));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE));
         // now the app starts a fg service
-        fsc.addNotification(makeMockDungeon(USERID_ONE, new String[]{ PKG1 }),
+        entryAdded(makeMockDisclosure(USERID_ONE, new String[]{PKG1}),
                 NotificationManager.IMPORTANCE_DEFAULT);
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); // should be required!
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required!
         // add the fg notification
-        fsc.addNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT);
-        assertFalse(fsc.isDungeonNeededForUser(USERID_ONE)); // app1 has got it covered
+        entryAdded(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT);
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE)); // app1 has got it covered
         // remove the boring notification
-        fsc.removeNotification(sbn_user1_app1);
-        assertFalse(fsc.isDungeonNeededForUser(USERID_ONE)); // app1 has STILL got it covered
-        assertTrue(fsc.removeNotification(sbn_user1_app1_fg));
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); // should be required!
+        entryRemoved(sbn_user1_app1);
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE)); // app1 has STILL got it covered
+        entryRemoved(sbn_user1_app1_fg);
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required!
     }
 
     @Test
@@ -257,115 +156,117 @@
 
         StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, PKG1,
                 5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
-        fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT);
+        entryAdded(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT);
 
         // no services are "running"
-        fsc.addNotification(makeMockDungeon(USERID_ONE, null),
+        entryAdded(makeMockDisclosure(USERID_ONE, null),
                 NotificationManager.IMPORTANCE_DEFAULT);
 
-        assertFalse(fsc.isDungeonNeededForUser(USERID_ONE));
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
 
-        fsc.updateNotification(makeMockDungeon(USERID_ONE, new String[]{PKG1}),
+        entryUpdated(makeMockDisclosure(USERID_ONE, new String[]{PKG1}),
                 NotificationManager.IMPORTANCE_DEFAULT);
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); // should be required!
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required!
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
 
         // switch to different package
-        fsc.updateNotification(makeMockDungeon(USERID_ONE, new String[]{PKG2}),
+        entryUpdated(makeMockDisclosure(USERID_ONE, new String[]{PKG2}),
                 NotificationManager.IMPORTANCE_DEFAULT);
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE));
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
 
-        fsc.updateNotification(makeMockDungeon(USERID_TWO, new String[]{PKG1}),
+        entryUpdated(makeMockDisclosure(USERID_TWO, new String[]{PKG1}),
                 NotificationManager.IMPORTANCE_DEFAULT);
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE));
-        assertTrue(fsc.isDungeonNeededForUser(USERID_TWO)); // finally user2 needs one too
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE));
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_TWO)); // finally user2 needs one too
 
-        fsc.updateNotification(makeMockDungeon(USERID_ONE, new String[]{PKG2, PKG1}),
+        entryUpdated(makeMockDisclosure(USERID_ONE, new String[]{PKG2, PKG1}),
                 NotificationManager.IMPORTANCE_DEFAULT);
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE));
-        assertTrue(fsc.isDungeonNeededForUser(USERID_TWO));
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE));
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_TWO));
 
-        fsc.removeNotification(makeMockDungeon(USERID_ONE, null /*unused*/));
-        assertFalse(fsc.isDungeonNeededForUser(USERID_ONE));
-        assertTrue(fsc.isDungeonNeededForUser(USERID_TWO));
+        entryRemoved(makeMockDisclosure(USERID_ONE, null /*unused*/));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE));
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_TWO));
 
-        fsc.removeNotification(makeMockDungeon(USERID_TWO, null /*unused*/));
-        assertFalse(fsc.isDungeonNeededForUser(USERID_ONE));
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        entryRemoved(makeMockDisclosure(USERID_TWO, null /*unused*/));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
     }
 
     @Test
-    public void testDungeonBasic() {
+    public void testDisclosureBasic() {
         final String PKG1 = "com.example.app0";
 
         StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, PKG1,
                 5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
         StatusBarNotification sbn_user1_app1_fg = makeMockFgSBN(USERID_ONE, PKG1);
 
-        fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT); // not fg
-        fsc.addNotification(makeMockDungeon(USERID_ONE, new String[]{ PKG1 }),
+        entryAdded(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT); // not fg
+        entryAdded(makeMockDisclosure(USERID_ONE, new String[]{PKG1}),
                 NotificationManager.IMPORTANCE_DEFAULT);
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); // should be required!
-        fsc.addNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT);
-        assertFalse(fsc.isDungeonNeededForUser(USERID_ONE)); // app1 has got it covered
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required!
+        entryAdded(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT);
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE)); // app1 has got it covered
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
 
         // let's take out the other notification and see what happens.
 
-        fsc.removeNotification(sbn_user1_app1);
-        assertFalse(fsc.isDungeonNeededForUser(USERID_ONE)); // still covered by sbn_user1_app1_fg
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        entryRemoved(sbn_user1_app1);
+        assertFalse(
+                mFsc.isDisclosureNeededForUser(USERID_ONE)); // still covered by sbn_user1_app1_fg
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
 
         // let's attempt to downgrade the notification from FLAG_FOREGROUND and see what we get
         StatusBarNotification sbn_user1_app1_fg_sneaky = makeMockFgSBN(USERID_ONE, PKG1);
         sbn_user1_app1_fg_sneaky.getNotification().flags = 0;
-        fsc.updateNotification(sbn_user1_app1_fg_sneaky, NotificationManager.IMPORTANCE_DEFAULT);
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); // should be required!
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        entryUpdated(sbn_user1_app1_fg_sneaky,
+                NotificationManager.IMPORTANCE_DEFAULT);
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required!
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
 
         // ok, ok, we'll put it back
         sbn_user1_app1_fg_sneaky.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
-        fsc.updateNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT);
-        assertFalse(fsc.isDungeonNeededForUser(USERID_ONE));
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        entryUpdated(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT);
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
 
-        assertTrue(fsc.removeNotification(sbn_user1_app1_fg_sneaky));
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); // should be required!
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        entryRemoved(sbn_user1_app1_fg_sneaky);
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required!
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
 
         // now let's test an upgrade
-        fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT);
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE));
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        entryAdded(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT);
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
         sbn_user1_app1.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
-        fsc.updateNotification(sbn_user1_app1,
+        entryUpdated(sbn_user1_app1,
                 NotificationManager.IMPORTANCE_DEFAULT); // this is now a fg notification
 
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
-        assertFalse(fsc.isDungeonNeededForUser(USERID_ONE));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE));
 
         // remove it, make sure we're out of compliance again
-        assertTrue(fsc.removeNotification(sbn_user1_app1)); // was fg, should return true
-        assertFalse(fsc.removeNotification(sbn_user1_app1));
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE));
+        entryRemoved(sbn_user1_app1); // was fg, should return true
+        entryRemoved(sbn_user1_app1);
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE));
 
         // importance upgrade
-        fsc.addNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE));
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        entryAdded(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
         sbn_user1_app1.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
-        fsc.updateNotification(sbn_user1_app1_fg,
+        entryUpdated(sbn_user1_app1_fg,
                 NotificationManager.IMPORTANCE_DEFAULT); // this is now a fg notification
 
         // finally, let's turn off the service
-        fsc.addNotification(makeMockDungeon(USERID_ONE, null),
+        entryAdded(makeMockDisclosure(USERID_ONE, null),
                 NotificationManager.IMPORTANCE_DEFAULT);
 
-        assertFalse(fsc.isDungeonNeededForUser(USERID_ONE));
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
     }
 
     @Test
@@ -375,8 +276,8 @@
         StatusBarNotification sbn_user1_overlay = makeMockSBN(USERID_ONE, "android",
                 0, "AlertWindowNotification", Notification.FLAG_NO_CLEAR);
 
-        assertTrue(fsc.isSystemAlertNotification(sbn_user1_overlay));
-        assertFalse(fsc.isSystemAlertNotification(sbn_user1_app1));
+        assertTrue(mFsc.isSystemAlertNotification(sbn_user1_overlay));
+        assertFalse(mFsc.isSystemAlertNotification(sbn_user1_app1));
     }
 
     @Test
@@ -386,54 +287,54 @@
         StatusBarNotification sbn_user1_app1 = makeMockFgSBN(USERID_ONE, PKG1, 0, true);
         sbn_user1_app1.getNotification().flags = 0;
         StatusBarNotification sbn_user1_app1_fg = makeMockFgSBN(USERID_ONE, PKG1, 1, true);
-        fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_MIN); // not fg
-        assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
-        fsc.addNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // app1 has got it covered
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "otherpkg"));
+        entryAdded(sbn_user1_app1, NotificationManager.IMPORTANCE_MIN); // not fg
+        assertTrue(mFsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
+        entryAdded(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // app1 has got it covered
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_TWO, "otherpkg"));
         // let's take out the non-fg notification and see what happens.
-        fsc.removeNotification(sbn_user1_app1);
+        entryRemoved(sbn_user1_app1);
         // still covered by sbn_user1_app1_fg
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "anyPkg"));
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_TWO, "anyPkg"));
 
         // let's attempt to downgrade the notification from FLAG_FOREGROUND and see what we get
         StatusBarNotification sbn_user1_app1_fg_sneaky = makeMockFgSBN(USERID_ONE, PKG1, 1, true);
         sbn_user1_app1_fg_sneaky.getNotification().flags = 0;
-        fsc.updateNotification(sbn_user1_app1_fg_sneaky, NotificationManager.IMPORTANCE_MIN);
-        assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "anything"));
+        entryUpdated(sbn_user1_app1_fg_sneaky, NotificationManager.IMPORTANCE_MIN);
+        assertTrue(mFsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_TWO, "anything"));
         // ok, ok, we'll put it back
         sbn_user1_app1_fg_sneaky.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
-        fsc.updateNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "whatever"));
+        entryUpdated(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_TWO, "whatever"));
 
-        assertTrue(fsc.removeNotification(sbn_user1_app1_fg_sneaky));
-        assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "a"));
+        entryRemoved(sbn_user1_app1_fg_sneaky);
+        assertTrue(mFsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_TWO, "a"));
 
         // let's try a custom layout
         sbn_user1_app1_fg_sneaky = makeMockFgSBN(USERID_ONE, PKG1, 1, false);
-        fsc.updateNotification(sbn_user1_app1_fg_sneaky, NotificationManager.IMPORTANCE_MIN);
-        assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "anything"));
+        entryUpdated(sbn_user1_app1_fg_sneaky, NotificationManager.IMPORTANCE_MIN);
+        assertTrue(mFsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_TWO, "anything"));
         // now let's test an upgrade (non fg to fg)
-        fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_MIN);
-        assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "b"));
+        entryAdded(sbn_user1_app1, NotificationManager.IMPORTANCE_MIN);
+        assertTrue(mFsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_TWO, "b"));
         sbn_user1_app1.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
-        fsc.updateNotification(sbn_user1_app1,
+        entryUpdated(sbn_user1_app1,
                 NotificationManager.IMPORTANCE_MIN); // this is now a fg notification
 
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, PKG1));
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_TWO, PKG1));
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
 
         // remove it, make sure we're out of compliance again
-        assertTrue(fsc.removeNotification(sbn_user1_app1)); // was fg, should return true
-        assertFalse(fsc.removeNotification(sbn_user1_app1));
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, PKG1));
-        assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
+        entryRemoved(sbn_user1_app1); // was fg, should return true
+        entryRemoved(sbn_user1_app1);
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_TWO, PKG1));
+        assertTrue(mFsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
     }
 
     private StatusBarNotification makeMockSBN(int userid, String pkg, int id, String tag,
@@ -475,7 +376,7 @@
         return makeMockSBN(userid, pkg, 1000, "foo", Notification.FLAG_FOREGROUND_SERVICE);
     }
 
-    private StatusBarNotification makeMockDungeon(int userid, String[] pkgs) {
+    private StatusBarNotification makeMockDisclosure(int userid, String[] pkgs) {
         final Notification n = mock(Notification.class);
         n.flags = Notification.FLAG_ONGOING_EVENT;
         final Bundle extras = new Bundle();
@@ -488,4 +389,21 @@
         sbn.getNotification().extras = extras;
         return sbn;
     }
+
+    private void entryRemoved(StatusBarNotification notification) {
+        mEntryListener.onEntryRemoved(new NotificationEntry(notification),
+                null, false);
+    }
+
+    private void entryAdded(StatusBarNotification notification, int importance) {
+        NotificationEntry entry = new NotificationEntry(notification);
+        entry.importance = importance;
+        mEntryListener.onPendingEntryAdded(entry);
+    }
+
+    private void entryUpdated(StatusBarNotification notification, int importance) {
+        NotificationEntry entry = new NotificationEntry(notification);
+        entry.importance = importance;
+        mEntryListener.onEntryUpdated(entry);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index b84f85b..e91a7e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -16,13 +16,13 @@
 
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 
-import  static com.android.systemui.ScreenDecorations.rectsToRegion;
+import static com.android.systemui.ScreenDecorations.rectsToRegion;
 import static com.android.systemui.tuner.TunablePadding.FLAG_END;
 import static com.android.systemui.tuner.TunablePadding.FLAG_START;
 
 import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -87,7 +87,6 @@
         mWindowManager = mock(WindowManager.class);
         mView = spy(new StatusBarWindowView(mContext, null));
         when(mStatusBar.getStatusBarWindow()).thenReturn(mView);
-        when(mStatusBar.getNavigationBarWindow()).thenReturn(mView);
         mContext.putComponent(StatusBar.class, mStatusBar);
 
         Display display = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
@@ -135,9 +134,12 @@
     public void testNoRounding_NoCutout() {
         mContext.getOrCreateTestableResources().addOverride(
                 com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
-        mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 0);
-        mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius_top, 0);
-        mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius_bottom, 0);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius, 0);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius_top, 0);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
         mContext.getOrCreateTestableResources()
                 .addOverride(dimen.rounded_corner_content_padding, 0);
 
@@ -154,7 +156,8 @@
     public void testRounding() {
         mContext.getOrCreateTestableResources().addOverride(
                 com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
-        mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 20);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius, 20);
         mContext.getOrCreateTestableResources()
                 .addOverride(dimen.rounded_corner_content_padding, 20);
 
@@ -174,7 +177,8 @@
     public void testCutout() {
         mContext.getOrCreateTestableResources().addOverride(
                 com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, true);
-        mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 0);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius, 0);
         mContext.getOrCreateTestableResources()
                 .addOverride(dimen.rounded_corner_content_padding, 0);
 
@@ -187,7 +191,8 @@
     public void testDelayedCutout() {
         mContext.getOrCreateTestableResources().addOverride(
                 com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
-        mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 0);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius, 0);
         mContext.getOrCreateTestableResources()
                 .addOverride(dimen.rounded_corner_content_padding, 0);
 
@@ -235,12 +240,14 @@
     public void testUpdateRoundedCorners() {
         mContext.getOrCreateTestableResources().addOverride(
                 com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
-        mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 20);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius, 20);
 
         mScreenDecorations.start();
         assertEquals(mScreenDecorations.mRoundedDefault, 20);
 
-        mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 5);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius, 5);
         mScreenDecorations.onConfigurationChanged(null);
         assertEquals(mScreenDecorations.mRoundedDefault, 5);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
index 0c8d137..18bf75e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
@@ -29,9 +29,10 @@
             mComponents = ((SysuiTestableContext) context).getComponents();
         }
         mContext = context;
-        if (SystemUIFactory.getInstance() == null) {
-            SystemUIFactory.createFromConfig(context);
-        }
+        SystemUIFactory.createFromConfig(context);
+        SystemUIFactory.getInstance().getRootComponent()
+                .createDependency()
+                .createSystemUI(this);
         start();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index b699163..2582946 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -17,8 +17,10 @@
 package com.android.systemui.appops;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -32,7 +34,6 @@
 
 import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.NotificationPresenter;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -48,9 +49,12 @@
     private static final int TEST_UID = 0;
     private static final int TEST_UID_OTHER = 500000;
 
-    @Mock private NotificationPresenter mPresenter;
-    @Mock private AppOpsManager mAppOpsManager;
-    @Mock private AppOpsController.Callback mCallback;
+    @Mock
+    private AppOpsManager mAppOpsManager;
+    @Mock
+    private AppOpsController.Callback mCallback;
+    @Mock
+    private AppOpsControllerImpl.H mMockHandler;
 
     private AppOpsControllerImpl mController;
 
@@ -77,9 +81,13 @@
 
     @Test
     public void addCallback_includedCode() {
-        mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+        mController.addCallback(
+                new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_FINE_LOCATION},
+                mCallback);
         mController.onOpActiveChanged(
                 AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+        mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+                AppOpsManager.MODE_ALLOWED);
         verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO,
                 TEST_UID, TEST_PACKAGE_NAME, true);
     }
@@ -106,7 +114,7 @@
     @Test
     public void addCallback_notSameCode() {
         mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
-        mController.removeCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+        mController.removeCallback(new int[]{AppOpsManager.OP_CAMERA}, mCallback);
         mController.onOpActiveChanged(
                 AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
         verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO,
@@ -128,17 +136,30 @@
                 TEST_UID, TEST_PACKAGE_NAME, true);
         mController.onOpActiveChanged(AppOpsManager.OP_CAMERA,
                 TEST_UID, TEST_PACKAGE_NAME, true);
-        assertEquals(2, mController.getActiveAppOps().size());
+        mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION,
+                TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
+        assertEquals(3, mController.getActiveAppOps().size());
     }
 
-    @Test public void getActiveItemsForUser() {
+    @Test
+    public void getActiveItemsForUser() {
         mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
                 TEST_UID, TEST_PACKAGE_NAME, true);
         mController.onOpActiveChanged(AppOpsManager.OP_CAMERA,
                 TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
-        assertEquals(1,
+        mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION,
+                TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
+        assertEquals(2,
                 mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID)).size());
         assertEquals(1,
-                mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID)).size());
+                mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID_OTHER)).size());
+    }
+
+    @Test
+    public void opNotedScheduledForRemoval() {
+        mController.setBGHandler(mMockHandler);
+        mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+                AppOpsManager.MODE_ALLOWED);
+        verify(mMockHandler).scheduleRemoval(any(AppOpItem.class), anyLong());
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 16ef63f..21d3652 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -18,6 +18,10 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.IActivityManager;
 import android.content.Context;
@@ -29,6 +33,9 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
@@ -36,6 +43,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -45,6 +54,8 @@
 public class BubbleControllerTest extends SysuiTestCase {
 
     @Mock
+    private NotificationEntryManager mNotificationEntryManager;
+    @Mock
     private WindowManager mWindowManager;
     @Mock
     private IActivityManager mActivityManager;
@@ -52,17 +63,23 @@
     private DozeParameters mDozeParameters;
     @Mock
     private FrameLayout mStatusBarView;
+    @Captor
+    private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
 
     private TestableBubbleController mBubbleController;
     private StatusBarWindowController mStatusBarWindowController;
+    private NotificationEntryListener mEntryListener;
 
     private NotificationTestHelper mNotificationTestHelper;
     private ExpandableNotificationRow mRow;
     private ExpandableNotificationRow mRow2;
 
+    private final NotificationData mNotificationData = new NotificationData();
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mDependency.injectTestDependency(NotificationEntryManager.class, mNotificationEntryManager);
 
         // Bubbles get added to status bar window view
         mStatusBarWindowController = new StatusBarWindowController(mContext, mWindowManager,
@@ -74,7 +91,15 @@
         mRow = mNotificationTestHelper.createBubble();
         mRow2 = mNotificationTestHelper.createBubble();
 
+        // Return non-null notification data from the NEM
+        when(mNotificationEntryManager.getNotificationData()).thenReturn(mNotificationData);
+
         mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController);
+
+        // Get a reference to the BubbleController's entry listener
+        verify(mNotificationEntryManager, atLeastOnce())
+                .addNotificationEntryListener(mEntryListenerCaptor.capture());
+        mEntryListener = mEntryListenerCaptor.getValue();
     }
 
     @Test
@@ -102,6 +127,8 @@
 
         mBubbleController.removeBubble(mRow.getEntry().key);
         assertFalse(mStatusBarWindowController.getBubblesShowing());
+        assertTrue(mRow.getEntry().isBubbleDismissed());
+        verify(mNotificationEntryManager).updateNotifications();
     }
 
     @Test
@@ -112,6 +139,7 @@
 
         mBubbleController.dismissStack();
         assertFalse(mStatusBarWindowController.getBubblesShowing());
+        verify(mNotificationEntryManager, times(3)).updateNotifications();
     }
 
     @Test
@@ -140,12 +168,17 @@
         assertFalse(mBubbleController.isStackExpanded());
     }
 
+    @Test
+    public void testMarkNewNotificationAsBubble() {
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        assertTrue(mRow.getEntry().isBubble());
+    }
+
     static class TestableBubbleController extends BubbleController {
 
         TestableBubbleController(Context context,
                 StatusBarWindowController statusBarWindowController) {
-            super(context);
-            mStatusBarWindowController = statusBarWindowController;
+            super(context, statusBarWindowController);
         }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java b/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java
new file mode 100644
index 0000000..b368876
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dock;
+
+/**
+ * A rudimentary fake for DockManager.
+ */
+public class DockManagerFake implements DockManager {
+    DockEventListener mCallback;
+
+    @Override
+    public void addListener(DockEventListener callback) {
+        this.mCallback = callback;
+    }
+
+    @Override
+    public void removeListener(DockEventListener callback) {
+        this.mCallback = null;
+    }
+
+    public void setDockEvent(int event) {
+        mCallback.onEvent(event);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
new file mode 100644
index 0000000..926ff69
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.doze;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Instrumentation;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.internal.hardware.AmbientDisplayConfiguration;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dock.DockManager;
+import com.android.systemui.dock.DockManagerFake;
+import com.android.systemui.doze.DozeMachine.State;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class DozeDockHandlerTest extends SysuiTestCase {
+    private DozeDockHandler mDockHandler;
+    private DozeMachine mMachine;
+    private DozeHostFake mHost;
+    private AmbientDisplayConfiguration mConfig;
+    private Instrumentation mInstrumentation;
+    private DockManagerFake mDockManagerFake;
+
+    @BeforeClass
+    public static void setupSuite() {
+        // We can't use KeyguardUpdateMonitor from tests.
+        DozeLog.setRegisterKeyguardCallback(false);
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mMachine = mock(DozeMachine.class);
+        mHost = spy(new DozeHostFake());
+        mConfig = DozeConfigurationUtil.createMockConfig();
+        doReturn(false).when(mConfig).alwaysOnEnabled(anyInt());
+
+        mDockManagerFake = spy(new DockManagerFake());
+        mContext.putComponent(DockManager.class, mDockManagerFake);
+
+        mDockHandler = new DozeDockHandler(mContext, mMachine, mHost, mConfig,
+                Handler.createAsync(Looper.myLooper()));
+    }
+
+    @Test
+    public void testDockEventListener_registerAndUnregister() {
+        mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
+
+        verify(mDockManagerFake).addListener(any());
+
+        mDockHandler.transitionTo(DozeMachine.State.DOZE, DozeMachine.State.FINISH);
+
+        verify(mDockManagerFake).removeListener(any());
+    }
+
+    @Test
+    public void testOnEvent_dockedWhenDoze_requestPulse() {
+        mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
+        when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
+
+        mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED);
+
+        verify(mMachine).requestPulse(eq(DozeLog.PULSE_REASON_DOCKING));
+    }
+
+    @Test
+    public void testOnEvent_dockedWhenDozeAoD_requestPulse() {
+        mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
+        when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE_AOD);
+
+        mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED);
+
+        verify(mMachine).requestPulse(eq(DozeLog.PULSE_REASON_DOCKING));
+    }
+
+    @Test
+    public void testOnEvent_dockedHideWhenPulsing_requestPulseOut() {
+        mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
+        when(mMachine.getState()).thenReturn(State.DOZE_PULSING);
+        when(mMachine.getPulseReason()).thenReturn(DozeLog.PULSE_REASON_DOCKING);
+
+        mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED_HIDE);
+
+        verify(mHost).stopPulsing();
+    }
+
+    @Test
+    public void testOnEvent_undockedWhenPulsing_requestPulseOut() {
+        mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
+        when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE_PULSING);
+        when(mMachine.getPulseReason()).thenReturn(DozeLog.PULSE_REASON_DOCKING);
+
+        mDockManagerFake.setDockEvent(DockManager.STATE_NONE);
+
+        verify(mHost).stopPulsing();
+    }
+
+    @Test
+    public void testOnEvent_undockedWhenDoze_neverRequestPulseOut() {
+        mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
+        when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
+
+        mDockManagerFake.setDockEvent(DockManager.STATE_NONE);
+
+        verify(mHost, never()).stopPulsing();
+    }
+
+    @Test
+    public void testOnEvent_undockedWhenDozeAndEnabledAoD_requestDozeAoD() {
+        doReturn(true).when(mConfig).alwaysOnEnabled(anyInt());
+        mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
+        when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
+
+        mDockManagerFake.setDockEvent(DockManager.STATE_NONE);
+
+        verify(mMachine).requestState(eq(State.DOZE_AOD));
+    }
+
+    @Test
+    public void testTransitionToDoze_whenDocked_requestPulse() {
+        mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
+        when(mMachine.getState()).thenReturn(DozeMachine.State.INITIALIZED);
+        mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED);
+        when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
+        mDockHandler.transitionTo(State.INITIALIZED, DozeMachine.State.DOZE);
+
+        TestableLooper.get(this).processAllMessages();
+
+        verify(mMachine).requestPulse(eq(DozeLog.PULSE_REASON_DOCKING));
+    }
+
+    @Test
+    public void testTransitionToDozeAoD_whenDocked_requestPulse() {
+        mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
+        when(mMachine.getState()).thenReturn(DozeMachine.State.INITIALIZED);
+        mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED);
+        when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE_AOD);
+        mDockHandler.transitionTo(State.INITIALIZED, DozeMachine.State.DOZE_AOD);
+
+        TestableLooper.get(this).processAllMessages();
+
+        verify(mMachine).requestPulse(eq(DozeLog.PULSE_REASON_DOCKING));
+    }
+
+    @Test
+    public void testTransitionToDoze_whenDockedHide_neverRequestPulse() {
+        mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
+        when(mMachine.getState()).thenReturn(DozeMachine.State.INITIALIZED);
+        mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED_HIDE);
+        when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
+
+        mDockHandler.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
+
+        verify(mMachine, never()).requestPulse(eq(DozeLog.PULSE_REASON_DOCKING));
+    }
+
+    @Test
+    public void testTransitionToDozeAoD_whenDockedHide_requestDoze() {
+        mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
+        when(mMachine.getState()).thenReturn(DozeMachine.State.INITIALIZED);
+        mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED_HIDE);
+        when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE_AOD);
+
+        mDockHandler.transitionTo(DozeMachine.State.INITIALIZED, State.DOZE_AOD);
+
+        verify(mMachine).requestState(eq(State.DOZE));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
index 6abd407..ce28b50 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
@@ -17,9 +17,6 @@
 package com.android.systemui.doze;
 
 import android.annotation.NonNull;
-import android.app.PendingIntent;
-
-import com.android.systemui.util.wakelock.WakeLock;
 
 /**
  * A rudimentary fake for DozeHost.
@@ -85,11 +82,6 @@
     }
 
     @Override
-    public void startPendingIntentDismissingKeyguard(PendingIntent intent) {
-        throw new RuntimeException("not implemented");
-    }
-
-    @Override
     public void onIgnoreTouchWhilePulsing(boolean ignore) {
     }
 
@@ -99,6 +91,9 @@
     }
 
     @Override
+    public void stopPulsing() {}
+
+    @Override
     public void setAnimateWakeup(boolean animateWakeup) {
         this.animateWakeup = animateWakeup;
     }
@@ -115,11 +110,6 @@
     }
 
     @Override
-    public void setPassiveInterrupt(boolean lightInterrupt) {
-
-    }
-
-    @Override
     public void setDozeScreenBrightness(int value) {
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index c1c80ce..cfc19ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -19,13 +19,15 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
 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 android.app.AlarmManager;
 import android.content.ContentResolver;
-import android.content.Intent;
+import android.media.MediaMetadata;
 import android.net.Uri;
 import android.provider.Settings;
 import android.support.test.filters.SmallTest;
@@ -40,7 +42,10 @@
 import androidx.slice.builders.ListBuilder;
 import androidx.slice.core.SliceQuery;
 
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.StatusBarStateController;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -62,6 +67,10 @@
     private ContentResolver mContentResolver;
     @Mock
     private AlarmManager mAlarmManager;
+    @Mock
+    private NotificationMediaManager mNotificationMediaManager;
+    @Mock
+    private StatusBarStateController mStatusBarStateController;
     private TestableKeyguardSliceProvider mProvider;
     private boolean mIsZenMode;
 
@@ -71,6 +80,7 @@
         mIsZenMode = false;
         mProvider = new TestableKeyguardSliceProvider();
         mProvider.attachInfo(getContext(), null);
+        mProvider.initDependencies(mNotificationMediaManager, mStatusBarStateController);
         SliceProvider.setSpecs(new HashSet<>(Arrays.asList(SliceSpecs.LIST)));
     }
 
@@ -90,8 +100,19 @@
     }
 
     @Test
+    public void onBindSlice_readsMedia() {
+        MediaMetadata metadata = mock(MediaMetadata.class);
+        mProvider.onDozingChanged(true);
+        mProvider.onMetadataChanged(metadata);
+        mProvider.onBindSlice(mProvider.getUri());
+        verify(metadata).getText(eq(MediaMetadata.METADATA_KEY_TITLE));
+        verify(metadata).getText(eq(MediaMetadata.METADATA_KEY_ARTIST));
+        verify(mNotificationMediaManager).getMediaIcon();
+    }
+
+    @Test
     public void cleansDateFormat() {
-        mProvider.mIntentReceiver.onReceive(getContext(), new Intent(Intent.ACTION_TIMEZONE_CHANGED));
+        mProvider.mKeyguardUpdateMonitorCallback.onTimeZoneChanged(null);
         TestableLooper.get(this).processAllMessages();
         Assert.assertEquals("Date format should have been cleaned.", 1 /* expected */,
                 mProvider.mCleanDateFormatInvokations);
@@ -99,7 +120,7 @@
 
     @Test
     public void updatesClock() {
-        mProvider.mIntentReceiver.onReceive(getContext(), new Intent(Intent.ACTION_TIME_TICK));
+        mProvider.mKeyguardUpdateMonitorCallback.onTimeChanged();
         TestableLooper.get(this).processAllMessages();
         verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null));
     }
@@ -135,14 +156,44 @@
     public void addZenMode_addedToSlice() {
         ListBuilder listBuilder = spy(new ListBuilder(getContext(), mProvider.getUri(),
             ListBuilder.INFINITY));
-        mProvider.addZenMode(listBuilder);
+        mProvider.addZenModeLocked(listBuilder);
         verify(listBuilder, never()).addRow(any(ListBuilder.RowBuilder.class));
 
         mIsZenMode = true;
-        mProvider.addZenMode(listBuilder);
+        mProvider.addZenModeLocked(listBuilder);
         verify(listBuilder).addRow(any(ListBuilder.RowBuilder.class));
     }
 
+    @Test
+    public void onMetadataChanged_updatesSlice() {
+        mProvider.onMetadataChanged(mock(MediaMetadata.class));
+        mProvider.onDozingChanged(true);
+        verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null));
+
+        // Hides after waking up
+        reset(mContentResolver);
+        mProvider.onDozingChanged(false);
+        verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null));
+
+        // And won't update slice if device is awake
+        reset(mContentResolver);
+        mProvider.onMetadataChanged(mock(MediaMetadata.class));
+        verify(mContentResolver, never()).notifyChange(eq(mProvider.getUri()), eq(null));
+    }
+
+    @Test
+    public void onDozingChanged_updatesSliceIfMedia() {
+        // Show media when dozing
+        mProvider.onMetadataChanged(mock(MediaMetadata.class));
+        mProvider.onDozingChanged(true);
+        verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null));
+
+        // Do not notify again if nothing changed
+        reset(mContentResolver);
+        mProvider.onDozingChanged(true);
+        verify(mContentResolver, never()).notifyChange(eq(mProvider.getUri()), eq(null));
+    }
+
     private class TestableKeyguardSliceProvider extends KeyguardSliceProvider {
         int mCleanDateFormatInvokations;
         private int mCounter;
@@ -165,14 +216,19 @@
         }
 
         @Override
-        void cleanDateFormat() {
-            super.cleanDateFormat();
+        void cleanDateFormatLocked() {
+            super.cleanDateFormatLocked();
             mCleanDateFormatInvokations++;
         }
 
         @Override
-        protected String getFormattedDate() {
-            return super.getFormattedDate() + mCounter++;
+        public KeyguardUpdateMonitor getKeyguardUpdateMonitor() {
+            return mock(KeyguardUpdateMonitor.class);
+        }
+
+        @Override
+        protected String getFormattedDateLocked() {
+            return super.getFormattedDateLocked() + mCounter++;
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
index b23f667..d3b3dae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
@@ -51,4 +51,19 @@
         assertEquals(listOf(Privacy.TYPE_CAMERA), typesList[1])
         assertEquals(listOf(Privacy.TYPE_CAMERA), typesList[2])
     }
+
+    @Test
+    fun testOrder() {
+        // We want location to always go last, so it will go in the "+ other apps"
+        val appCamera = PrivacyItem(PrivacyType.TYPE_CAMERA, PrivacyApplication("Camera", context))
+        val appMicrophone =
+                PrivacyItem(PrivacyType.TYPE_MICROPHONE, PrivacyApplication("Microphone", context))
+        val appLocation =
+                PrivacyItem(PrivacyType.TYPE_LOCATION, PrivacyApplication("Location", context))
+
+        val items = listOf(appLocation, appMicrophone, appCamera)
+        val textBuilder = PrivacyDialogBuilder(context, items)
+        val appList = textBuilder.appsAndTypes.map { it.first }.map { it.packageName }
+        assertEquals(listOf("Camera", "Microphone", "Location"), appList)
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index 24bcca5..e6d7ee7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -19,6 +19,7 @@
 import android.app.ActivityManager
 import android.app.AppOpsManager
 import android.content.Intent
+import android.content.pm.UserInfo
 import android.os.Handler
 import android.os.UserHandle
 import android.os.UserManager
@@ -27,16 +28,20 @@
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
 import com.android.systemui.Dependency
+import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.appops.AppOpItem
 import com.android.systemui.appops.AppOpsController
+import org.junit.Assert.assertEquals
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyList
 import org.mockito.ArgumentMatchers.eq
+import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito.atLeastOnce
 import org.mockito.Mockito.doReturn
@@ -52,8 +57,12 @@
 
     companion object {
         val CURRENT_USER_ID = ActivityManager.getCurrentUser()
-        val OTHER_USER = UserHandle(CURRENT_USER_ID + 1)
+        val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE
+        const val SYSTEM_UID = 1000
+        const val TEST_PACKAGE_NAME = "test"
+        const val DEVICE_SERVICES_STRING = "Device services"
         const val TAG = "PrivacyItemControllerTest"
+        fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
     }
 
     @Mock
@@ -62,6 +71,8 @@
     private lateinit var callback: PrivacyItemController.Callback
     @Mock
     private lateinit var userManager: UserManager
+    @Captor
+    private lateinit var argCaptor: ArgumentCaptor<List<PrivacyItem>>
 
     private lateinit var testableLooper: TestableLooper
     private lateinit var privacyItemController: PrivacyItemController
@@ -75,9 +86,14 @@
         mDependency.injectTestDependency(Dependency.BG_LOOPER, testableLooper.looper)
         mDependency.injectTestDependency(Dependency.MAIN_HANDLER, Handler(testableLooper.looper))
         mContext.addMockSystemService(UserManager::class.java, userManager)
+        mContext.getOrCreateTestableResources().addOverride(R.string.device_services,
+                DEVICE_SERVICES_STRING)
 
-        doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, 0, "", 0)))
-                .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+        doReturn(listOf(object : UserInfo() {
+            init {
+                id = CURRENT_USER_ID
+            }
+        })).`when`(userManager).getProfiles(anyInt())
 
         privacyItemController = PrivacyItemController(mContext, callback)
     }
@@ -100,6 +116,30 @@
     }
 
     @Test
+    fun testDistinctItems() {
+        doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
+                AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 1)))
+                .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+        privacyItemController.setListening(true)
+        testableLooper.processAllMessages()
+        verify(callback).privacyChanged(capture(argCaptor))
+        assertEquals(1, argCaptor.value.size)
+    }
+
+    @Test
+    fun testSystemApps() {
+        doReturn(listOf(AppOpItem(AppOpsManager.OP_COARSE_LOCATION, SYSTEM_UID, TEST_PACKAGE_NAME,
+                0))).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+        privacyItemController.setListening(true)
+        testableLooper.processAllMessages()
+        verify(callback).privacyChanged(capture(argCaptor))
+        assertEquals(1, argCaptor.value.size)
+        assertEquals(context.getString(R.string.device_services),
+                argCaptor.value[0].application.applicationName)
+    }
+
+    @Test
     fun testRegisterReceiver_allUsers() {
         val spiedContext = spy(mContext)
         val itemController = PrivacyItemController(spiedContext, callback)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index bc7d983..823485f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -18,7 +18,10 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 
+import android.app.Fragment;
 import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
 import android.os.Looper;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -32,10 +35,18 @@
 import com.android.keyguard.CarrierText;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
 import com.android.systemui.SysuiBaseFragmentTest;
+import com.android.systemui.qs.tileimpl.QSFactoryImpl;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.phone.AutoTileManager;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.policy.Clock;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.InjectionInflationController;
 
 import org.junit.Before;
 import org.junit.Ignore;
@@ -52,6 +63,7 @@
 
     public QSFragmentTest() {
         super(QSFragment.class);
+        injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
     }
 
     @Before
@@ -70,7 +82,6 @@
         mDependency.injectTestDependency(Dependency.BG_LOOPER,
                 TestableLooper.get(this).getLooper());
         mDependency.injectMockDependency(UserSwitcherController.class);
-        injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
     }
 
     @Test
@@ -80,7 +91,10 @@
         QSFragment qs = (QSFragment) mFragment;
         mFragments.dispatchResume();
         processAllMessages();
-        QSTileHost host = new QSTileHost(mContext, null, mock(StatusBarIconController.class));
+        QSTileHost host = new QSTileHost(mContext, mock(StatusBarIconController.class),
+                mock(QSFactoryImpl.class), new Handler(), Looper.myLooper(),
+                mock(PluginManager.class), mock(TunerService.class),
+                () -> mock(AutoTileManager.class));
         qs.setHost(host);
 
         qs.setListening(true);
@@ -116,4 +130,13 @@
         assertTrue(qs.isListening());
         assertTrue(qs.isExpanded());
     }
+
+    @Override
+    protected Fragment instantiate(Context context, String className, Bundle arguments) {
+        return new QSFragment(
+                new RemoteInputQuickSettingsDisabler(context, mock(ConfigurationController.class)),
+                new InjectionInflationController(SystemUIFactory.getInstance().getRootComponent()),
+                context,
+                mock(QSTileHost.class));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 26fa20d..c3a3e63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -218,4 +218,12 @@
         }
         assertFalse(specs.contains("other"));
     }
+
+    @Test
+    public void testQueryTiles_nullSetting() {
+        Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES, null);
+        mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
+                STOCK_TILES);
+        mTileQueryHelper.queryTiles(mQSTileHost);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index c6597b9..03278b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.Mockito.mock;
 
 import android.content.ComponentName;
+import android.os.Handler;
 import android.os.Looper;
 import android.service.quicksettings.Tile;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -30,8 +31,12 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.tileimpl.QSFactoryImpl;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.phone.AutoTileManager;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.tuner.TunerService;
 
 import org.junit.After;
 import org.junit.Before;
@@ -55,8 +60,14 @@
     public void setUp() throws Exception {
         mDependency.injectMockDependency(BluetoothController.class);
         mManagers = new ArrayList<>();
-        QSTileHost host = new QSTileHost(mContext, null,
-                mock(StatusBarIconController.class));
+        QSTileHost host = new QSTileHost(mContext,
+                mock(StatusBarIconController.class),
+                mock(QSFactoryImpl.class),
+                new Handler(),
+                Looper.myLooper(),
+                mock(PluginManager.class),
+                mock(TunerService.class),
+                () -> mock(AutoTileManager.class));
         mTileService = new TestTileServices(host, Looper.getMainLooper());
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index d9412ec..efcbb76 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -15,8 +15,8 @@
 package com.android.systemui.qs.tiles;
 
 import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertTrue;
 
+import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -26,6 +26,8 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.lifecycle.LifecycleOwner;
+
 import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.ActivityStarter;
@@ -79,7 +81,8 @@
 
         when(mHost.getContext()).thenReturn(mContext);
 
-        mCastTile = new CastTile(mHost);
+        mCastTile = new CastTile(mHost, mController, mKeyguard, mNetworkController,
+                mActivityStarter);
 
         // We are not setting the mocks to listening, so we trigger a first refresh state to
         // set the initial state
@@ -88,7 +91,8 @@
         mCastTile.handleSetListening(true);
         ArgumentCaptor<NetworkController.SignalCallback> signalCallbackArgumentCaptor =
                 ArgumentCaptor.forClass(NetworkController.SignalCallback.class);
-        verify(mNetworkController).addCallback(signalCallbackArgumentCaptor.capture());
+        verify(mNetworkController).observe(any(LifecycleOwner.class),
+                signalCallbackArgumentCaptor.capture());
         mCallback = signalCallbackArgumentCaptor.getValue();
 
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/SensorPrivacyTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/SensorPrivacyTileTest.java
new file mode 100644
index 0000000..751a616
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/SensorPrivacyTileTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.SensorPrivacyManager;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class SensorPrivacyTileTest extends SysuiTestCase {
+
+    @Mock
+    private KeyguardMonitor mKeyguard;
+    @Mock
+    private QSTileHost mHost;
+    @Mock
+    SensorPrivacyManager mSensorPrivacyManager;
+
+    private TestableLooper mTestableLooper;
+
+    private SensorPrivacyTile mSensorPrivacyTile;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mTestableLooper = TestableLooper.get(this);
+        mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
+        mKeyguard = mDependency.injectMockDependency(KeyguardMonitor.class);
+
+        mSensorPrivacyManager = mDependency.injectMockDependency(SensorPrivacyManager.class);
+
+        when(mHost.getContext()).thenReturn(mContext);
+
+        mSensorPrivacyTile = new SensorPrivacyTile(mHost, mSensorPrivacyManager, mKeyguard,
+                mock(ActivityStarter.class));
+    }
+
+    @Test
+    public void testSensorPrivacyListenerAdded_handleListeningTrue() {
+        // To prevent access to privacy related features from apps with WRITE_SECURE_SETTINGS the
+        // sensor privacy state is not stored in Settings; to receive notification apps must add
+        // themselves as a listener with the SensorPrivacyManager. This test verifies when
+        // setListening is called with a value of true the tile adds itself as a listener.
+        mSensorPrivacyTile.handleSetListening(true);
+        mTestableLooper.processAllMessages();
+        verify(mSensorPrivacyManager).addSensorPrivacyListener(mSensorPrivacyTile);
+    }
+
+    @Test
+    public void testSensorPrivacyListenerRemoved_handleListeningFalse() {
+        // Similar to the test above verifies that the tile removes itself as a listener when
+        // setListening is called with a value of false.
+        mSensorPrivacyTile.handleSetListening(false);
+        mTestableLooper.processAllMessages();
+        verify(mSensorPrivacyManager).removeSensorPrivacyListener((mSensorPrivacyTile));
+    }
+
+    @Test
+    public void testSensorPrivacyEnabled_handleClick() {
+        // Verifies when the SensorPrivacy tile is clicked it invokes the SensorPrivacyManager to
+        // set sensor privacy.
+        mSensorPrivacyTile.getState().value = false;
+        mSensorPrivacyTile.handleClick();
+        mTestableLooper.processAllMessages();
+        verify(mSensorPrivacyManager).setSensorPrivacy(true);
+
+        mSensorPrivacyTile.getState().value = true;
+        mSensorPrivacyTile.handleClick();
+        mTestableLooper.processAllMessages();
+        verify(mSensorPrivacyManager).setSensorPrivacy(false);
+    }
+
+    @Test
+    public void testSensorPrivacyNotDisabled_keyguard() {
+        // Verifies when the device is locked that sensor privacy cannot be disabled
+        when(mKeyguard.isSecure()).thenReturn(true);
+        when(mKeyguard.isShowing()).thenReturn(true);
+        mSensorPrivacyTile.getState().value = true;
+        mSensorPrivacyTile.handleClick();
+        mTestableLooper.processAllMessages();
+        verify(mSensorPrivacyManager, never()).setSensorPrivacy(false);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
index 5cc3b3c..4583770 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
@@ -17,6 +17,7 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
+
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
@@ -26,22 +27,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.Plugin;
-import com.android.systemui.plugins.PluginEnablerImpl;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
-import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
-import com.android.systemui.plugins.annotations.Requires;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mockito;
-
 import android.app.Activity;
 import android.app.NotificationManager;
 import android.content.BroadcastReceiver;
@@ -60,6 +45,21 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.annotations.Requires;
+import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
+import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -79,6 +79,9 @@
     private PluginInstanceManager mPluginInstanceManager;
     private PluginManagerImpl mMockManager;
     private VersionInfo mMockVersionInfo;
+    private PluginEnabler mMockEnabler;
+    ComponentName mTestPluginComponentName =
+            new ComponentName(WHITELISTED_PACKAGE, TestPlugin.class.getName());
 
     @Before
     public void setup() throws Exception {
@@ -88,9 +91,9 @@
         mMockPm = mock(PackageManager.class);
         mMockListener = mock(PluginListener.class);
         mMockManager = mock(PluginManagerImpl.class);
-        when(mMockManager.getClassLoader(any(), any()))
-                .thenReturn(getClass().getClassLoader());
-        when(mMockManager.getPluginEnabler()).thenReturn(new PluginEnablerImpl(mMockPm));
+        when(mMockManager.getClassLoader(any(), any())).thenReturn(getClass().getClassLoader());
+        mMockEnabler = mock(PluginEnabler.class);
+        when(mMockManager.getPluginEnabler()).thenReturn(mMockEnabler);
         mMockVersionInfo = mock(VersionInfo.class);
         mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
                 mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
@@ -230,18 +233,13 @@
         // Start with an unrelated class.
         boolean result = mPluginInstanceManager.checkAndDisable(Activity.class.getName());
         assertFalse(result);
-        verify(mMockPm, never()).setComponentEnabledSetting(
-                ArgumentCaptor.forClass(ComponentName.class).capture(),
-                ArgumentCaptor.forClass(int.class).capture(),
-                ArgumentCaptor.forClass(int.class).capture());
+        verify(mMockEnabler, never()).setDisabled(any(ComponentName.class), anyInt());
 
         // Now hand it a real class and make sure it disables the plugin.
         result = mPluginInstanceManager.checkAndDisable(TestPlugin.class.getName());
         assertTrue(result);
-        verify(mMockPm).setComponentEnabledSetting(
-                ArgumentCaptor.forClass(ComponentName.class).capture(),
-                ArgumentCaptor.forClass(int.class).capture(),
-                ArgumentCaptor.forClass(int.class).capture());
+        verify(mMockEnabler).setDisabled(
+                mTestPluginComponentName, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH);
     }
 
     @Test
@@ -250,10 +248,8 @@
 
         mPluginInstanceManager.disableAll();
 
-        verify(mMockPm).setComponentEnabledSetting(
-                ArgumentCaptor.forClass(ComponentName.class).capture(),
-                ArgumentCaptor.forClass(int.class).capture(),
-                ArgumentCaptor.forClass(int.class).capture());
+        verify(mMockEnabler).setDisabled(
+                mTestPluginComponentName, PluginEnabler.DISABLED_FROM_SYSTEM_CRASH);
     }
 
     @Test
@@ -275,8 +271,8 @@
         List<ResolveInfo> list = new ArrayList<>();
         ResolveInfo info = new ResolveInfo();
         info.serviceInfo = mock(ServiceInfo.class);
-        info.serviceInfo.packageName = "com.android.systemui";
-        info.serviceInfo.name = TestPlugin.class.getName();
+        info.serviceInfo.packageName = mTestPluginComponentName.getPackageName();
+        info.serviceInfo.name = mTestPluginComponentName.getClassName();
         when(info.serviceInfo.loadLabel(any())).thenReturn("Test Plugin");
         list.add(info);
         when(mMockPm.queryIntentServices(any(), Mockito.anyInt())).thenReturn(list);
@@ -288,6 +284,7 @@
         ApplicationInfo appInfo = getContext().getApplicationInfo();
         when(mMockPm.getApplicationInfo(Mockito.anyString(), Mockito.anyInt())).thenReturn(
                 appInfo);
+        when(mMockEnabler.isEnabled(mTestPluginComponentName)).thenReturn(true);
     }
 
     private void createPlugin() throws Exception {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
index ff1bc8ab..76e68f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
@@ -27,7 +27,6 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.net.Uri;
-import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -82,17 +81,18 @@
                 .thenReturn(mMockPluginInstance);
 
         mMockPackageManager = mock(PackageManager.class);
-        mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, true,
+        mPluginManager = new PluginManagerImpl(
+                getContext(), mMockFactory, true,
                 mMockExceptionHandler, new PluginInitializerImpl() {
-            @Override
-            public String[] getWhitelistedPlugins(Context context) {
-                return new String[0];
-            }
+                    @Override
+                    public String[] getWhitelistedPlugins(Context context) {
+                        return new String[0];
+                    }
 
-            @Override
-            public PluginEnabler getPluginEnabler(Context context) {
-                return new PluginEnablerImpl(mMockPackageManager);
-            }
+                    @Override
+                    public PluginEnabler getPluginEnabler(Context context) {
+                        return new PluginEnablerImpl(context, mMockPackageManager);
+                    }
         });
         resetExceptionHandler();
         mMockListener = mock(PluginListener.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index 9d0556f..f8957b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -36,7 +36,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 
@@ -67,7 +67,7 @@
 
     private AlertingNotificationManager mAlertingNotificationManager;
 
-    protected NotificationData.Entry mEntry;
+    protected NotificationEntry mEntry;
     protected Handler mTestHandler;
     private StatusBarNotification mSbn;
     protected boolean mTimedOut = false;
@@ -119,7 +119,7 @@
     public void setUp() {
         mTestHandler = Handler.createAsync(Looper.myLooper());
         mSbn = createNewNotification(0 /* id */);
-        mEntry = new NotificationData.Entry(mSbn);
+        mEntry = new NotificationEntry(mSbn);
         mEntry.setRow(mRow);
 
         mAlertingNotificationManager = createAlertingNotificationManager();
@@ -170,7 +170,7 @@
     public void testReleaseAllImmediately() {
         for (int i = 0; i < TEST_NUM_NOTIFICATIONS; i++) {
             StatusBarNotification sbn = createNewNotification(i);
-            NotificationData.Entry entry = new NotificationData.Entry(sbn);
+            NotificationEntry entry = new NotificationEntry(sbn);
             entry.setRow(mRow);
             mAlertingNotificationManager.showNotification(entry);
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index a04e57b..51d94f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -16,6 +16,7 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -42,10 +43,10 @@
 
     @Before
     public void setup() {
-        mCommandQueue = new CommandQueue();
+        mCommandQueue = new CommandQueue(mContext);
         mCallbacks = mock(Callbacks.class);
         mCommandQueue.addCallback(mCallbacks);
-        verify(mCallbacks).disable(eq(0), eq(0), eq(false));
+        verify(mCallbacks).disable(anyInt(), eq(0), eq(0), eq(false));
     }
 
     @After
@@ -73,7 +74,7 @@
         int state2 = 42;
         mCommandQueue.disable(DEFAULT_DISPLAY, state1, state2);
         waitForIdleSync();
-        verify(mCallbacks).disable(eq(state1), eq(state2), eq(true));
+        verify(mCallbacks).disable(eq(DEFAULT_DISPLAY), eq(state1), eq(state2), eq(true));
     }
 
     @Test
@@ -104,7 +105,8 @@
         Rect r = new Rect();
         mCommandQueue.setSystemUiVisibility(DEFAULT_DISPLAY, 1, 2, 3, 4, null, r);
         waitForIdleSync();
-        verify(mCallbacks).setSystemUiVisibility(eq(1), eq(2), eq(3), eq(4), eq(null), eq(r));
+        verify(mCallbacks).setSystemUiVisibility(eq(DEFAULT_DISPLAY), eq(1), eq(2), eq(3), eq(4),
+                eq(null), eq(r));
     }
 
     // TODO(b/117478341): add test case for multi-display
@@ -112,7 +114,7 @@
     public void testTopAppWindowChanged() {
         mCommandQueue.topAppWindowChanged(DEFAULT_DISPLAY, true);
         waitForIdleSync();
-        verify(mCallbacks).topAppWindowChanged(eq(true));
+        verify(mCallbacks).topAppWindowChanged(eq(DEFAULT_DISPLAY), eq(true));
     }
 
     // TODO(b/117478341): add test case for multi-display
@@ -120,7 +122,8 @@
     public void testShowImeButton() {
         mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, 1, 2, true);
         waitForIdleSync();
-        verify(mCallbacks).setImeWindowStatus(eq(null), eq(1), eq(2), eq(true));
+        verify(mCallbacks).setImeWindowStatus(
+                eq(DEFAULT_DISPLAY), eq(null), eq(1), eq(2), eq(true));
     }
 
     @Test
@@ -177,7 +180,7 @@
     public void testSetWindowState() {
         mCommandQueue.setWindowState(DEFAULT_DISPLAY, 1, 2);
         waitForIdleSync();
-        verify(mCallbacks).setWindowState(eq(1), eq(2));
+        verify(mCallbacks).setWindowState(eq(DEFAULT_DISPLAY), eq(1), eq(2));
     }
 
     @Test
@@ -192,7 +195,7 @@
     public void testAppTransitionPending() {
         mCommandQueue.appTransitionPending(DEFAULT_DISPLAY);
         waitForIdleSync();
-        verify(mCallbacks).appTransitionPending(eq(false));
+        verify(mCallbacks).appTransitionPending(eq(DEFAULT_DISPLAY), eq(false));
     }
 
     // TODO(b/117478341): add test case for multi-display
@@ -200,7 +203,7 @@
     public void testAppTransitionCancelled() {
         mCommandQueue.appTransitionCancelled(DEFAULT_DISPLAY);
         waitForIdleSync();
-        verify(mCallbacks).appTransitionCancelled();
+        verify(mCallbacks).appTransitionCancelled(eq(DEFAULT_DISPLAY));
     }
 
     // TODO(b/117478341): add test case for multi-display
@@ -208,7 +211,8 @@
     public void testAppTransitionStarting() {
         mCommandQueue.appTransitionStarting(DEFAULT_DISPLAY, 1, 2);
         waitForIdleSync();
-        verify(mCallbacks).appTransitionStarting(eq(1L), eq(2L), eq(false));
+        verify(mCallbacks).appTransitionStarting(
+                eq(DEFAULT_DISPLAY), eq(1L), eq(2L), eq(false));
     }
 
     // TODO(b/117478341): add test case for multi-display
@@ -216,7 +220,7 @@
     public void testAppTransitionFinished() {
         mCommandQueue.appTransitionFinished(DEFAULT_DISPLAY);
         waitForIdleSync();
-        verify(mCallbacks).appTransitionFinished();
+        verify(mCallbacks).appTransitionFinished(eq(DEFAULT_DISPLAY));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
index f8ff583..425ca58 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -26,6 +26,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.InitController;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -55,7 +56,8 @@
 public class NonPhoneDependencyTest extends SysuiTestCase {
     @Mock private NotificationPresenter mPresenter;
     @Mock private NotificationListContainer mListContainer;
-    @Mock private NotificationEntryManager.Callback mEntryManagerCallback;
+    @Mock
+    private NotificationEntryListener mEntryListener;
     @Mock private HeadsUpManager mHeadsUpManager;
     @Mock private RemoteInputController.Delegate mDelegate;
     @Mock private NotificationRemoteInputManager.Callback mRemoteInputManagerCallback;
@@ -85,8 +87,8 @@
         NotificationViewHierarchyManager viewHierarchyManager =
                 Dependency.get(NotificationViewHierarchyManager.class);
         Dependency.get(InitController.class).executePostInitTasks();
-        entryManager.setUpWithPresenter(mPresenter, mListContainer, mEntryManagerCallback,
-                mHeadsUpManager);
+        entryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager);
+        entryManager.addNotificationEntryListener(mEntryListener);
         gutsManager.setUpWithPresenter(mPresenter, mListContainer,
                 mCheckSaveListener, mOnSettingsClickListener);
         notificationLogger.setUpWithContainer(mListContainer);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index 65c04fe..c880172 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -31,8 +31,9 @@
 
 import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -85,7 +86,7 @@
 
     @Test
     public void testNotificationUpdateCallsUpdateNotification() {
-        when(mNotificationData.get(mSbn.getKey())).thenReturn(new NotificationData.Entry(mSbn));
+        when(mNotificationData.get(mSbn.getKey())).thenReturn(new NotificationEntry(mSbn));
         mListener.onNotificationPosted(mSbn, mRanking);
         TestableLooper.get(this).processAllMessages();
         verify(mEntryManager).updateNotification(mSbn, mRanking);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index f168a49..d7ca5b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar;
 
-import static android.content.Intent.ACTION_DEVICE_LOCKED_CHANGED;
 import static android.content.Intent.ACTION_USER_SWITCHED;
 
 import static junit.framework.Assert.assertFalse;
@@ -42,8 +41,8 @@
 
 import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index bee931f..c159516 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -20,14 +20,14 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
-import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationRemoteInputManager.RemoteInputActiveExtender;
 import com.android.systemui.statusbar.NotificationRemoteInputManager.RemoteInputHistoryExtender;
 import com.android.systemui.statusbar.NotificationRemoteInputManager.SmartReplyHistoryExtender;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.phone.ShadeController;
 
 import com.google.android.collect.Sets;
 
@@ -37,6 +37,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import dagger.Lazy;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -58,7 +60,7 @@
 
     private TestableNotificationRemoteInputManager mRemoteInputManager;
     private StatusBarNotification mSbn;
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
     private RemoteInputHistoryExtender mRemoteInputHistoryExtender;
     private SmartReplyHistoryExtender mSmartReplyHistoryExtender;
     private RemoteInputActiveExtender mRemoteInputActiveExtender;
@@ -66,17 +68,14 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
-        mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
-                mLockscreenUserManager);
-        mDependency.injectTestDependency(SmartReplyController.class, mSmartReplyController);
-        mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
-                Handler.createAsync(Looper.myLooper()));
 
-        mRemoteInputManager = new TestableNotificationRemoteInputManager(mContext);
+        mRemoteInputManager = new TestableNotificationRemoteInputManager(mContext,
+                mLockscreenUserManager, mSmartReplyController, mEntryManager,
+                () -> mock(ShadeController.class),
+                Handler.createAsync(Looper.myLooper()));
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
                 0, new Notification(), UserHandle.CURRENT, null, 0);
-        mEntry = new NotificationData.Entry(mSbn);
+        mEntry = new NotificationEntry(mSbn);
         mEntry.setRow(mRow);
 
         mRemoteInputManager.setUpWithPresenterForTest(mCallback,
@@ -90,7 +89,7 @@
     @Test
     public void testPerformOnRemoveNotification() {
         when(mController.isRemoteInputActive(mEntry)).thenReturn(true);
-        mRemoteInputManager.onPerformRemoveNotification(mSbn, mEntry);
+        mRemoteInputManager.onPerformRemoveNotification(mEntry, mSbn.getKey());
 
         verify(mController).removeRemoteInput(mEntry, null);
     }
@@ -171,7 +170,7 @@
         // Setup a notification entry with 1 remote input.
         StatusBarNotification newSbn =
                 mRemoteInputManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", false);
-        NotificationData.Entry entry = new NotificationData.Entry(newSbn);
+        NotificationEntry entry = new NotificationEntry(newSbn);
 
         // Try rebuilding to add another reply.
         newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInput(entry, "Reply 2", true);
@@ -196,8 +195,15 @@
 
     private class TestableNotificationRemoteInputManager extends NotificationRemoteInputManager {
 
-        public TestableNotificationRemoteInputManager(Context context) {
-            super(context);
+
+        TestableNotificationRemoteInputManager(Context context,
+                NotificationLockscreenUserManager lockscreenUserManager,
+                SmartReplyController smartReplyController,
+                NotificationEntryManager notificationEntryManager,
+                Lazy<ShadeController> shadeController,
+                Handler mainHandler) {
+            super(context, lockscreenUserManager, smartReplyController, notificationEntryManager,
+                    shadeController, mainHandler);
         }
 
         public void setUpWithPresenterForTest(Callback callback,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index fb5e875..529da82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -32,7 +32,7 @@
 import android.widget.RemoteViews;
 
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.row.NotificationInflaterTest;
@@ -238,7 +238,7 @@
                 mUser,
                 null /* overrideGroupKey */,
                 System.currentTimeMillis());
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        NotificationEntry entry = new NotificationEntry(sbn);
         entry.setRow(row);
         entry.createIcons(mContext, sbn);
         entry.channel = new NotificationChannel(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 520a927..bf91305 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -20,6 +20,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -35,11 +36,12 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.InitController;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -75,7 +77,7 @@
     @Mock private ShadeController mShadeController;
 
     private NotificationViewHierarchyManager mViewHierarchyManager;
-    private NotificationTestHelper mHelper = new NotificationTestHelper(mContext);
+    private NotificationTestHelper mHelper;
 
     @Before
     public void setUp() {
@@ -88,16 +90,21 @@
         mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
         mDependency.injectTestDependency(ShadeController.class, mShadeController);
 
+        mHelper = new NotificationTestHelper(mContext);
+
         when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
 
-        mViewHierarchyManager = new NotificationViewHierarchyManager(mContext);
+        mViewHierarchyManager = new NotificationViewHierarchyManager(mContext,
+                mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
+                mock(StatusBarStateController.class), mEntryManager, mock(BubbleController.class),
+                () -> mShadeController);
         Dependency.get(InitController.class).executePostInitTasks();
         mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
     }
 
-    private NotificationData.Entry createEntry() throws Exception {
+    private NotificationEntry createEntry() throws Exception {
         ExpandableNotificationRow row = mHelper.createRow();
-        NotificationData.Entry entry = new NotificationData.Entry(row.getStatusBarNotification());
+        NotificationEntry entry = new NotificationEntry(row.getStatusBarNotification());
         entry.setRow(row);
         return entry;
     }
@@ -106,9 +113,9 @@
     public void testNotificationsBecomingBundled() throws Exception {
         // Tests 3 top level notifications becoming a single bundled notification with |entry0| as
         // the summary.
-        NotificationData.Entry entry0 = createEntry();
-        NotificationData.Entry entry1 = createEntry();
-        NotificationData.Entry entry2 = createEntry();
+        NotificationEntry entry0 = createEntry();
+        NotificationEntry entry1 = createEntry();
+        NotificationEntry entry2 = createEntry();
 
         // Set up the prior state to look like three top level notifications.
         mListContainer.addContainerView(entry0.getRow());
@@ -135,9 +142,9 @@
     @Test
     public void testNotificationsBecomingUnbundled() throws Exception {
         // Tests a bundled notification becoming three top level notifications.
-        NotificationData.Entry entry0 = createEntry();
-        NotificationData.Entry entry1 = createEntry();
-        NotificationData.Entry entry2 = createEntry();
+        NotificationEntry entry0 = createEntry();
+        NotificationEntry entry1 = createEntry();
+        NotificationEntry entry2 = createEntry();
         entry0.getRow().addChildNotification(entry1.getRow());
         entry0.getRow().addChildNotification(entry2.getRow());
 
@@ -166,8 +173,8 @@
     @Test
     public void testNotificationsBecomingSuppressed() throws Exception {
         // Tests two top level notifications becoming a suppressed summary and a child.
-        NotificationData.Entry entry0 = createEntry();
-        NotificationData.Entry entry1 = createEntry();
+        NotificationEntry entry0 = createEntry();
+        NotificationEntry entry1 = createEntry();
         entry0.getRow().addChildNotification(entry1.getRow());
 
         // Set up the prior state to look like a top level notification.
@@ -192,7 +199,7 @@
 
     @Test
     public void testUpdateNotificationViews_appOps() throws Exception {
-        NotificationData.Entry entry0 = createEntry();
+        NotificationEntry entry0 = createEntry();
         entry0.setRow(spy(entry0.getRow()));
         when(mNotificationData.getActiveNotifications()).thenReturn(
                 Lists.newArrayList(entry0));
@@ -257,10 +264,7 @@
         public void setMaxDisplayedNotifications(int maxNotifications) {}
 
         @Override
-        public void snapViewIfNeeded(Entry entry) {}
-
-        @Override
-        public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
+        public ViewGroup getViewParentForNotification(NotificationEntry entry) {
             return null;
         }
 
@@ -276,10 +280,10 @@
         }
 
         @Override
-        public void cleanUpViewStateForEntry(Entry entry) { }
+        public void cleanUpViewStateForEntry(NotificationEntry entry) { }
 
         @Override
-        public boolean isInVisibleLocation(Entry entry) {
+        public boolean isInVisibleLocation(NotificationEntry entry) {
             return true;
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index 8d52ccd..6cca434 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -19,10 +19,13 @@
 
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
 import android.app.ActivityManager;
 import android.app.Notification;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
@@ -33,8 +36,9 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.phone.ShadeController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -51,9 +55,10 @@
     private static final String TEST_CHOICE_TEXT = "A Reply";
     private static final int TEST_CHOICE_INDEX = 2;
     private static final int TEST_CHOICE_COUNT = 4;
+    private static final int TEST_ACTION_COUNT = 3;
 
     private Notification mNotification;
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
     private SmartReplyController mSmartReplyController;
     private NotificationRemoteInputManager mRemoteInputManager;
 
@@ -69,13 +74,16 @@
         MockitoAnnotations.initMocks(this);
         mDependency.injectTestDependency(NotificationEntryManager.class,
                 mNotificationEntryManager);
-        mDependency.injectTestDependency(IStatusBarService.class, mIStatusBarService);
 
-        mSmartReplyController = new SmartReplyController();
+        mSmartReplyController = new SmartReplyController(mNotificationEntryManager,
+                mIStatusBarService);
         mDependency.injectTestDependency(SmartReplyController.class,
                 mSmartReplyController);
 
-        mRemoteInputManager = new NotificationRemoteInputManager(mContext);
+        mRemoteInputManager = new NotificationRemoteInputManager(mContext,
+                mock(NotificationLockscreenUserManager.class), mSmartReplyController,
+                mNotificationEntryManager, () -> mock(ShadeController.class),
+                Handler.createAsync(Looper.myLooper()));
         mRemoteInputManager.setUpWithCallback(mCallback, mDelegate);
         mNotification = new Notification.Builder(mContext, "")
                 .setSmallIcon(R.drawable.ic_person)
@@ -84,7 +92,7 @@
 
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
                 0, mNotification, new UserHandle(ActivityManager.getCurrentUser()), null, 0);
-        mEntry = new NotificationData.Entry(mSbn);
+        mEntry = new NotificationEntry(mSbn);
     }
 
     @Test
@@ -117,12 +125,14 @@
     }
 
     @Test
-    public void testShowSmartReply_logsToStatusBar() throws RemoteException {
-        mSmartReplyController.smartRepliesAdded(mEntry, TEST_CHOICE_COUNT);
+    public void testShowSmartSuggestions_logsToStatusBar() throws RemoteException {
+        final boolean generatedByAsssistant = true;
+        mSmartReplyController.smartSuggestionsAdded(mEntry, TEST_CHOICE_COUNT, TEST_ACTION_COUNT,
+                generatedByAsssistant);
 
         // Check we log the result to the status bar service.
-        verify(mIStatusBarService).onNotificationSmartRepliesAdded(mSbn.getKey(),
-                TEST_CHOICE_COUNT);
+        verify(mIStatusBarService).onNotificationSmartSuggestionsAdded(mSbn.getKey(),
+                TEST_CHOICE_COUNT, TEST_ACTION_COUNT, generatedByAsssistant);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 8706e21..4d22536 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -52,7 +52,6 @@
 import android.widget.FrameLayout;
 
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.Dependency;
 import com.android.systemui.ForegroundServiceController;
 import com.android.systemui.InitController;
@@ -61,13 +60,13 @@
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.NotificationInflater;
@@ -103,11 +102,13 @@
     @Mock private KeyguardEnvironment mEnvironment;
     @Mock private ExpandableNotificationRow mRow;
     @Mock private NotificationListContainer mListContainer;
-    @Mock private NotificationEntryManager.Callback mCallback;
+    @Mock
+    private NotificationEntryListener mEntryListener;
+    @Mock
+    private NotificationRowBinder.BindRowCallback mBindCallback;
     @Mock private HeadsUpManager mHeadsUpManager;
     @Mock private NotificationListenerService.RankingMap mRankingMap;
     @Mock private RemoteInputController mRemoteInputController;
-    @Mock private IStatusBarService mBarService;
 
     // Dependency mocks:
     @Mock private ForegroundServiceController mForegroundServiceController;
@@ -115,15 +116,15 @@
     @Mock private NotificationGroupManager mGroupManager;
     @Mock private NotificationGutsManager mGutsManager;
     @Mock private NotificationRemoteInputManager mRemoteInputManager;
-    @Mock private NotificationMediaManager mMediaManager;
     @Mock private NotificationListener mNotificationListener;
     @Mock private DeviceProvisionedController mDeviceProvisionedController;
     @Mock private VisualStabilityManager mVisualStabilityManager;
     @Mock private MetricsLogger mMetricsLogger;
     @Mock private SmartReplyController mSmartReplyController;
     @Mock private RowInflaterTask mAsyncInflationTask;
+    @Mock private NotificationRowBinder mMockedRowBinder;
 
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
     private StatusBarNotification mSbn;
     private TestableNotificationEntryManager mEntryManager;
     private CountDownLatch mCountDownLatch;
@@ -131,15 +132,13 @@
     private class TestableNotificationEntryManager extends NotificationEntryManager {
         private final CountDownLatch mCountDownLatch;
 
-        public TestableNotificationEntryManager(Context context, IStatusBarService barService) {
+        TestableNotificationEntryManager(Context context) {
             super(context);
-            mBarService = barService;
             mCountDownLatch = new CountDownLatch(1);
-            mUseHeadsUp = true;
         }
 
         @Override
-        public void onAsyncInflationFinished(NotificationData.Entry entry,
+        public void onAsyncInflationFinished(NotificationEntry entry,
                 @NotificationInflater.InflationFlag int inflatedFlags) {
             super.onAsyncInflationFinished(entry, inflatedFlags);
 
@@ -167,7 +166,7 @@
                     0,
                     NotificationManager.IMPORTANCE_DEFAULT,
                     null, null,
-                    null, null, null, true, sentiment, false, false, false, null, null);
+                    null, null, null, true, sentiment, false, -1, false, null, null);
             return true;
         }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
     }
@@ -185,7 +184,7 @@
                     NotificationManager.IMPORTANCE_DEFAULT,
                     null, null,
                     null, null, null, true,
-                    NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL, false, false,
+                    NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
                     false, smartActions, null);
             return true;
         }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
@@ -202,7 +201,6 @@
         mDependency.injectTestDependency(NotificationGroupManager.class, mGroupManager);
         mDependency.injectTestDependency(NotificationGutsManager.class, mGutsManager);
         mDependency.injectTestDependency(NotificationRemoteInputManager.class, mRemoteInputManager);
-        mDependency.injectTestDependency(NotificationMediaManager.class, mMediaManager);
         mDependency.injectTestDependency(NotificationListener.class, mNotificationListener);
         mDependency.injectTestDependency(DeviceProvisionedController.class,
                 mDeviceProvisionedController);
@@ -225,12 +223,18 @@
                 .setContentText("Text");
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
                 0, n.build(), new UserHandle(ActivityManager.getCurrentUser()), null, 0);
-        mEntry = new NotificationData.Entry(mSbn);
+        mEntry = new NotificationEntry(mSbn);
         mEntry.expandedIcon = mock(StatusBarIconView.class);
 
-        mEntryManager = new TestableNotificationEntryManager(mContext, mBarService);
+        mEntryManager = new TestableNotificationEntryManager(mContext);
         Dependency.get(InitController.class).executePostInitTasks();
-        mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mCallback, mHeadsUpManager);
+        mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager);
+        mEntryManager.addNotificationEntryListener(mEntryListener);
+
+        NotificationRowBinder notificationRowBinder = Dependency.get(NotificationRowBinder.class);
+        notificationRowBinder.setUpWithPresenter(
+                mPresenter, mListContainer, mHeadsUpManager, mEntryManager, mBindCallback);
+        notificationRowBinder.setNotificationClicker(mock(NotificationClicker.class));
 
         setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
     }
@@ -243,7 +247,7 @@
         doAnswer(invocation -> {
             mCountDownLatch.countDown();
             return null;
-        }).when(mCallback).onBindRow(any(), any(), any(), any());
+        }).when(mBindCallback).onBindRow(any(), any(), any(), any());
 
         // Post on main thread, otherwise we will be stuck waiting here for the inflation finished
         // callback forever, since it won't execute until the tests ends.
@@ -253,19 +257,17 @@
         assertTrue(mEntryManager.getCountDownLatch().await(10, TimeUnit.SECONDS));
 
         // Check that no inflation error occurred.
-        verify(mBarService, never()).onNotificationError(any(), any(), anyInt(), anyInt(), anyInt(),
-                any(), anyInt());
-        verify(mForegroundServiceController).addNotification(eq(mSbn), anyInt());
+        verify(mEntryListener, never()).onInflationError(any(), any());
 
         // Row inflation:
-        ArgumentCaptor<NotificationData.Entry> entryCaptor = ArgumentCaptor.forClass(
-                NotificationData.Entry.class);
-        verify(mCallback).onBindRow(entryCaptor.capture(), any(), eq(mSbn), any());
-        NotificationData.Entry entry = entryCaptor.getValue();
+        ArgumentCaptor<NotificationEntry> entryCaptor = ArgumentCaptor.forClass(
+                NotificationEntry.class);
+        verify(mBindCallback).onBindRow(entryCaptor.capture(), any(), eq(mSbn), any());
+        NotificationEntry entry = entryCaptor.getValue();
         verify(mRemoteInputManager).bindRow(entry.getRow());
 
         // Row content inflation:
-        verify(mCallback).onNotificationAdded(entry);
+        verify(mEntryListener).onNotificationAdded(entry);
         verify(mPresenter).updateNotificationViews();
 
         assertEquals(mEntryManager.getNotificationData().get(mSbn.getKey()), entry);
@@ -288,12 +290,10 @@
         // Wait for content update.
         assertTrue(mEntryManager.getCountDownLatch().await(10, TimeUnit.SECONDS));
 
-        verify(mBarService, never()).onNotificationError(any(), any(), anyInt(), anyInt(), anyInt(),
-                any(), anyInt());
+        verify(mEntryListener, never()).onInflationError(any(), any());
 
         verify(mPresenter).updateNotificationViews();
-        verify(mForegroundServiceController).updateNotification(eq(mSbn), anyInt());
-        verify(mCallback).onNotificationUpdated(mSbn);
+        verify(mEntryListener).onEntryUpdated(mEntry);
         assertNotNull(mEntry.getRow());
         assertEquals(mEntry.userSentiment,
                 NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
@@ -308,14 +308,12 @@
 
         mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
 
-        verify(mBarService, never()).onNotificationError(any(), any(), anyInt(), anyInt(), anyInt(),
-                any(), anyInt());
+        verify(mEntryListener, never()).onInflationError(any(), any());
 
-        verify(mMediaManager).onNotificationRemoved(mSbn.getKey());
-        verify(mForegroundServiceController).removeNotification(mSbn);
         verify(mListContainer).cleanUpViewStateForEntry(mEntry);
         verify(mPresenter).updateNotificationViews();
-        verify(mCallback).onNotificationRemoved(mSbn.getKey(), mSbn);
+        verify(mEntryListener).onEntryRemoved(
+                mEntry, null, false /* removedByUser */);
         verify(mRow).setRemoved();
 
         assertNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
@@ -339,6 +337,31 @@
 
         assertNotNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
         verify(extender).setShouldManageLifetime(mEntry, true /* shouldManage */);
+        verify(mEntryListener, never()).onEntryRemoved(
+                mEntry, null, false /* removedByUser */);
+    }
+
+    @Test
+    public void testRemoveNotification_onEntryRemoveNotFiredIfEntryDoesntExist() {
+        com.android.systemui.util.Assert.isNotMainThread();
+
+        mEntryManager.removeNotification("not_a_real_key", mRankingMap);
+
+        verify(mEntryListener, never()).onEntryRemoved(
+                mEntry, null, false /* removedByUser */);
+    }
+
+    @Test
+    public void testRemoveNotification_whilePending() throws InterruptedException {
+        com.android.systemui.util.Assert.isNotMainThread();
+
+        mEntryManager.setRowBinder(mMockedRowBinder);
+
+        mEntryManager.addNotification(mSbn, mRankingMap);
+        mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
+
+        verify(mEntryListener, never()).onEntryRemoved(
+                mEntry, null, false /* removedByUser */);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
new file mode 100644
index 0000000..387475f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.app.Notification;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.service.notification.StatusBarNotification;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ShadeController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class NotificationFilterTest extends SysuiTestCase {
+
+    private static final int UID_NORMAL = 123;
+    private static final int UID_ALLOW_DURING_SETUP = 456;
+    private static final String TEST_HIDDEN_NOTIFICATION_KEY = "testHiddenNotificationKey";
+
+    private final StatusBarNotification mMockStatusBarNotification =
+            mock(StatusBarNotification.class);
+
+    @Mock
+    ForegroundServiceController mFsc;
+    @Mock
+    NotificationData.KeyguardEnvironment mEnvironment;
+    private final IPackageManager mMockPackageManager = mock(IPackageManager.class);
+
+    private NotificationFilter mNotificationFilter;
+    private ExpandableNotificationRow mRow;
+
+    @Before
+    public void setUp() throws Exception {
+        com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
+        MockitoAnnotations.initMocks(this);
+        when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL);
+
+        when(mMockPackageManager.checkUidPermission(
+                eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
+                eq(UID_NORMAL)))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mMockPackageManager.checkUidPermission(
+                eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
+                eq(UID_ALLOW_DURING_SETUP)))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        mDependency.injectTestDependency(ForegroundServiceController.class, mFsc);
+        mDependency.injectTestDependency(NotificationGroupManager.class,
+                new NotificationGroupManager());
+        mDependency.injectMockDependency(ShadeController.class);
+        mDependency.injectTestDependency(NotificationData.KeyguardEnvironment.class, mEnvironment);
+        when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
+        when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
+        mRow = new NotificationTestHelper(getContext()).createRow();
+        mNotificationFilter = new NotificationFilter();
+    }
+
+    @Test
+    @UiThreadTest
+    public void testShowNotificationEvenIfUnprovisioned_FalseIfNoExtra() {
+        initStatusBarNotification(false);
+        when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
+
+        assertFalse(
+                NotificationFilter.showNotificationEvenIfUnprovisioned(
+                        mMockPackageManager,
+                        mMockStatusBarNotification));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testShowNotificationEvenIfUnprovisioned_FalseIfNoPermission() {
+        initStatusBarNotification(true);
+
+        assertFalse(
+                NotificationFilter.showNotificationEvenIfUnprovisioned(
+                        mMockPackageManager,
+                        mMockStatusBarNotification));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testShowNotificationEvenIfUnprovisioned_TrueIfHasPermissionAndExtra() {
+        initStatusBarNotification(true);
+        when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
+
+        assertTrue(
+                NotificationFilter.showNotificationEvenIfUnprovisioned(
+                        mMockPackageManager,
+                        mMockStatusBarNotification));
+    }
+
+    @Test
+    public void testSuppressSystemAlertNotification() {
+        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
+        when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
+        StatusBarNotification sbn = mRow.getEntry().notification;
+        Bundle bundle = new Bundle();
+        bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{"something"});
+        sbn.getNotification().extras = bundle;
+
+        assertTrue(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+    }
+
+    @Test
+    public void testDoNotSuppressSystemAlertNotification() {
+        StatusBarNotification sbn = mRow.getEntry().notification;
+        Bundle bundle = new Bundle();
+        bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{"something"});
+        sbn.getNotification().extras = bundle;
+
+        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
+        when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
+
+        assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+
+        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
+        when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
+
+        assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+
+        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
+        when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
+
+        assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+    }
+
+    @Test
+    public void testDoNotSuppressMalformedSystemAlertNotification() {
+        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
+
+        // missing extra
+        assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+
+        StatusBarNotification sbn = mRow.getEntry().notification;
+        Bundle bundle = new Bundle();
+        bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{});
+        sbn.getNotification().extras = bundle;
+
+        // extra missing values
+        assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+    }
+
+    @Test
+    public void testShouldFilterHiddenNotifications() {
+        initStatusBarNotification(false);
+        // setup
+        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
+        when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
+
+        // test should filter out hidden notifications:
+        // hidden
+        NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
+        entry.suspended = true;
+        assertTrue(mNotificationFilter.shouldFilterOut(entry));
+
+        // not hidden
+        entry = new NotificationEntry(mMockStatusBarNotification);
+        entry.suspended = false;
+        assertFalse(mNotificationFilter.shouldFilterOut(entry));
+    }
+
+    private void initStatusBarNotification(boolean allowDuringSetup) {
+        Bundle bundle = new Bundle();
+        bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
+        Notification notification = new Notification.Builder(mContext, "test")
+                .addExtras(bundle)
+                .build();
+        when(mMockStatusBarNotification.getNotification()).thenReturn(notification);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
index f190979..813fc9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
@@ -16,37 +16,42 @@
 
 package com.android.systemui.statusbar.notification;
 
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.service.notification.StatusBarNotification;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static junit.framework.Assert.assertEquals;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class VisualStabilityManagerTest extends SysuiTestCase {
 
-    private VisualStabilityManager mVisualStabilityManager = new VisualStabilityManager();
+    private VisualStabilityManager mVisualStabilityManager = new VisualStabilityManager(
+            mock(NotificationEntryManager.class));
     private VisualStabilityManager.Callback mCallback = mock(VisualStabilityManager.Callback.class);
     private VisibilityLocationProvider mLocationProvider = mock(VisibilityLocationProvider.class);
     private ExpandableNotificationRow mRow = mock(ExpandableNotificationRow.class);
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
 
     @Before
     public void setUp() {
+        mVisualStabilityManager.setUpWithPresenter(mock(NotificationPresenter.class));
         mVisualStabilityManager.setVisibilityLocationProvider(mLocationProvider);
-        mEntry = new NotificationData.Entry(mock(StatusBarNotification.class));
+        mEntry = new NotificationEntry(mock(StatusBarNotification.class));
         mEntry.setRow(mRow);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
similarity index 73%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
index 8e88ed0..193f483 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
@@ -14,7 +14,7 @@
  * limitations under the License
  */
 
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.collection;
 
 import static android.app.AppOpsManager.OP_ACCEPT_HANDOVER;
 import static android.app.AppOpsManager.OP_CAMERA;
@@ -29,8 +29,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -50,7 +48,6 @@
 import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.SnoozeCriterion;
 import android.service.notification.StatusBarNotification;
-import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -62,7 +59,9 @@
 import com.android.systemui.InitController;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
@@ -105,6 +104,7 @@
         com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
         MockitoAnnotations.initMocks(this);
         when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL);
+        when(mMockStatusBarNotification.cloneLight()).thenReturn(mMockStatusBarNotification);
 
         when(mMockPackageManager.checkUidPermission(
                 eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
@@ -129,41 +129,6 @@
     }
 
     @Test
-    @UiThreadTest
-    public void testShowNotificationEvenIfUnprovisioned_FalseIfNoExtra() {
-        initStatusBarNotification(false);
-        when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
-
-        assertFalse(
-                NotificationData.showNotificationEvenIfUnprovisioned(
-                        mMockPackageManager,
-                        mMockStatusBarNotification));
-    }
-
-    @Test
-    @UiThreadTest
-    public void testShowNotificationEvenIfUnprovisioned_FalseIfNoPermission() {
-        initStatusBarNotification(true);
-
-        assertFalse(
-                NotificationData.showNotificationEvenIfUnprovisioned(
-                        mMockPackageManager,
-                        mMockStatusBarNotification));
-    }
-
-    @Test
-    @UiThreadTest
-    public void testShowNotificationEvenIfUnprovisioned_TrueIfHasPermissionAndExtra() {
-        initStatusBarNotification(true);
-        when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
-
-        assertTrue(
-                NotificationData.showNotificationEvenIfUnprovisioned(
-                        mMockPackageManager,
-                        mMockStatusBarNotification));
-    }
-
-    @Test
     public void testChannelSetWhenAdded() {
         mNotificationData.add(mRow.getEntry());
         assertEquals(NOTIFICATION_CHANNEL, mRow.getEntry().channel);
@@ -230,76 +195,6 @@
     }
 
     @Test
-    public void testSuppressSystemAlertNotification() {
-        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
-        when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
-        StatusBarNotification sbn = mRow.getEntry().notification;
-        Bundle bundle = new Bundle();
-        bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {"something"});
-        sbn.getNotification().extras = bundle;
-
-        assertTrue(mNotificationData.shouldFilterOut(mRow.getEntry()));
-    }
-
-    @Test
-    public void testDoNotSuppressSystemAlertNotification() {
-        StatusBarNotification sbn = mRow.getEntry().notification;
-        Bundle bundle = new Bundle();
-        bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {"something"});
-        sbn.getNotification().extras = bundle;
-
-        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
-        when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
-
-        assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
-
-        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
-        when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
-
-        assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
-
-        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
-        when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
-
-        assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
-    }
-
-    @Test
-    public void testDoNotSuppressMalformedSystemAlertNotification() {
-        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
-
-        // missing extra
-        assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
-
-        StatusBarNotification sbn = mRow.getEntry().notification;
-        Bundle bundle = new Bundle();
-        bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {});
-        sbn.getNotification().extras = bundle;
-
-        // extra missing values
-        assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
-    }
-
-    @Test
-    public void testShouldFilterHiddenNotifications() {
-        initStatusBarNotification(false);
-        // setup
-        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
-        when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
-
-        // test should filter out hidden notifications:
-        // hidden
-        when(mMockStatusBarNotification.getKey()).thenReturn(TEST_HIDDEN_NOTIFICATION_KEY);
-        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
-        assertTrue(mNotificationData.shouldFilterOut(entry));
-
-        // not hidden
-        when(mMockStatusBarNotification.getKey()).thenReturn("not hidden");
-        entry = new NotificationData.Entry(mMockStatusBarNotification);
-        assertFalse(mNotificationData.shouldFilterOut(entry));
-    }
-
-    @Test
     public void testGetNotificationsForCurrentUser_shouldFilterNonCurrentUserNotifications()
             throws Exception {
         mNotificationData.add(mRow.getEntry());
@@ -310,7 +205,7 @@
                 mRow.getEntry().notification)).thenReturn(false);
         when(mEnvironment.isNotificationForCurrentProfiles(
                 row2.getEntry().notification)).thenReturn(true);
-        ArrayList<NotificationData.Entry> result =
+        ArrayList<NotificationEntry> result =
                 mNotificationData.getNotificationsForCurrentUser();
 
         assertEquals(result.size(), 1);
@@ -324,10 +219,11 @@
                 TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
         Notification n = mMockStatusBarNotification.getNotification();
         n.flags = Notification.FLAG_FOREGROUND_SERVICE;
-        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+        NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
+        mNotificationData.add(entry);
 
-        assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
-        assertFalse(mNotificationData.shouldSuppressAmbient(entry));
+        assertTrue(entry.isExemptFromDndVisualSuppression());
+        assertFalse(entry.shouldSuppressAmbient());
     }
 
     @Test
@@ -340,10 +236,11 @@
         nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
         n = nb.build();
         when(mMockStatusBarNotification.getNotification()).thenReturn(n);
-        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+        NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
+        mNotificationData.add(entry);
 
-        assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
-        assertFalse(mNotificationData.shouldSuppressAmbient(entry));
+        assertTrue(entry.isExemptFromDndVisualSuppression());
+        assertFalse(entry.shouldSuppressAmbient());
     }
 
     @Test
@@ -351,11 +248,12 @@
         initStatusBarNotification(false);
         when(mMockStatusBarNotification.getKey()).thenReturn(
                 TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
-        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+        NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
         entry.mIsSystemNotification = true;
+        mNotificationData.add(entry);
 
-        assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
-        assertFalse(mNotificationData.shouldSuppressAmbient(entry));
+        assertTrue(entry.isExemptFromDndVisualSuppression());
+        assertFalse(entry.shouldSuppressAmbient());
     }
 
     @Test
@@ -363,33 +261,35 @@
         initStatusBarNotification(false);
         when(mMockStatusBarNotification.getKey()).thenReturn(
                 TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
-        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+        NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
         entry.mIsSystemNotification = true;
+        mNotificationData.add(entry);
+
         when(mMockStatusBarNotification.getNotification()).thenReturn(
                 new Notification.Builder(mContext, "").setCategory(CATEGORY_CALL).build());
 
-        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
-        assertTrue(mNotificationData.shouldSuppressAmbient(entry));
+        assertFalse(entry.isExemptFromDndVisualSuppression());
+        assertTrue(entry.shouldSuppressAmbient());
 
         when(mMockStatusBarNotification.getNotification()).thenReturn(
                 new Notification.Builder(mContext, "").setCategory(CATEGORY_REMINDER).build());
 
-        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+        assertFalse(entry.isExemptFromDndVisualSuppression());
 
         when(mMockStatusBarNotification.getNotification()).thenReturn(
                 new Notification.Builder(mContext, "").setCategory(CATEGORY_ALARM).build());
 
-        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+        assertFalse(entry.isExemptFromDndVisualSuppression());
 
         when(mMockStatusBarNotification.getNotification()).thenReturn(
                 new Notification.Builder(mContext, "").setCategory(CATEGORY_EVENT).build());
 
-        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+        assertFalse(entry.isExemptFromDndVisualSuppression());
 
         when(mMockStatusBarNotification.getNotification()).thenReturn(
                 new Notification.Builder(mContext, "").setCategory(CATEGORY_MESSAGE).build());
 
-        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+        assertFalse(entry.isExemptFromDndVisualSuppression());
     }
 
     @Test
@@ -415,8 +315,8 @@
         snoozeCriterions.add(snoozeCriterion);
         when(ranking.getSnoozeCriteria()).thenReturn(snoozeCriterions);
 
-        NotificationData.Entry entry =
-                new NotificationData.Entry(mMockStatusBarNotification, ranking);
+        NotificationEntry entry =
+                new NotificationEntry(mMockStatusBarNotification, ranking);
 
         assertEquals(systemGeneratedSmartActions, entry.systemGeneratedSmartActions);
         assertEquals(NOTIFICATION_CHANNEL, entry.channel);
@@ -445,7 +345,7 @@
         StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
                 notification, mContext.getUser(), "", 0);
 
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        NotificationEntry entry = new NotificationEntry(sbn);
         entry.setHasSentReply();
 
         assertTrue(entry.isLastMessageFromReply());
@@ -475,14 +375,14 @@
                         outRanking.getImportance(), outRanking.getImportanceExplanation(),
                         outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
                         outRanking.canShowBadge(), outRanking.getUserSentiment(), true,
-                        false, false, null, null);
+                        -1, false, null, null);
             } else if (key.equals(TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY)) {
                 outRanking.populate(key, outRanking.getRank(),
                         outRanking.matchesInterruptionFilter(),
                         outRanking.getVisibilityOverride(), 255,
                         outRanking.getImportance(), outRanking.getImportanceExplanation(),
                         outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
-                        outRanking.canShowBadge(), outRanking.getUserSentiment(), true, false,
+                        outRanking.canShowBadge(), outRanking.getUserSentiment(), true, -1,
                         false, null, null);
             } else {
                 outRanking.populate(key, outRanking.getRank(),
@@ -490,7 +390,7 @@
                         outRanking.getVisibilityOverride(), outRanking.getSuppressedVisualEffects(),
                         outRanking.getImportance(), outRanking.getImportanceExplanation(),
                         outRanking.getOverrideGroupKey(), NOTIFICATION_CHANNEL, null, null,
-                        outRanking.canShowBadge(), outRanking.getUserSentiment(), false, false,
+                        outRanking.canShowBadge(), outRanking.getUserSentiment(), false, -1,
                         false, null, null);
             }
             return true;
@@ -502,8 +402,7 @@
                 Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
                 title,
                 PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0))
-                        .setSemanticAction(
-                                Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION)
+                        .setContextual(true)
                         .build();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index 512acd0..6472589 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -27,7 +28,6 @@
 import android.app.Notification;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.RemoteException;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.support.test.filters.SmallTest;
@@ -36,11 +36,15 @@
 
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.UiOffloadThread;
 import com.android.systemui.statusbar.NotificationListener;
-import com.android.systemui.statusbar.NotificationPresenter;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 
@@ -49,10 +53,11 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
 
 import java.util.concurrent.ConcurrentLinkedQueue;
 
@@ -63,7 +68,6 @@
     private static final String TEST_PACKAGE_NAME = "test";
     private static final int TEST_UID = 0;
 
-    @Mock private NotificationPresenter mPresenter;
     @Mock private NotificationListContainer mListContainer;
     @Mock private IStatusBarService mBarService;
     @Mock private NotificationData mNotificationData;
@@ -72,27 +76,32 @@
     // Dependency mocks:
     @Mock private NotificationEntryManager mEntryManager;
     @Mock private NotificationListener mListener;
+    @Captor private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
 
-    private NotificationData.Entry mEntry;
-    private StatusBarNotification mSbn;
+    private NotificationEntry mEntry;
     private TestableNotificationLogger mLogger;
+    private NotificationEntryListener mNotificationEntryListener;
     private ConcurrentLinkedQueue<AssertionError> mErrorQueue = new ConcurrentLinkedQueue<>();
 
     @Before
-    public void setUp() throws RemoteException {
+    public void setUp() {
         MockitoAnnotations.initMocks(this);
         mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
         mDependency.injectTestDependency(NotificationListener.class, mListener);
 
         when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
 
-        mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
+        StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
+                0, null, TEST_UID,
                 0, new Notification(), UserHandle.CURRENT, null, 0);
-        mEntry = new NotificationData.Entry(mSbn);
+        mEntry = new NotificationEntry(sbn);
         mEntry.setRow(mRow);
 
-        mLogger = new TestableNotificationLogger(mBarService);
+        mLogger = new TestableNotificationLogger(mListener, Dependency.get(UiOffloadThread.class),
+                mEntryManager, mock(StatusBarStateController.class), mBarService);
         mLogger.setUpWithContainer(mListContainer);
+        verify(mEntryManager).addNotificationEntryListener(mEntryListenerCaptor.capture());
+        mNotificationEntryListener = mEntryListenerCaptor.getValue();
     }
 
     @Test
@@ -101,7 +110,7 @@
                 NotificationVisibility.obtain(mEntry.key, 0, 1, true)
         };
         NotificationVisibility[] noLongerVisibleKeys = {};
-        doAnswer((Answer) invocation -> {
+        doAnswer(invocation -> {
                     try {
                         assertArrayEquals(newlyVisibleKeys,
                                 (NotificationVisibility[]) invocation.getArguments()[0]);
@@ -153,19 +162,20 @@
 
     private class TestableNotificationLogger extends NotificationLogger {
 
-        public TestableNotificationLogger(IStatusBarService barService) {
+        TestableNotificationLogger(NotificationListener notificationListener,
+                UiOffloadThread uiOffloadThread,
+                NotificationEntryManager entryManager,
+                StatusBarStateController statusBarStateController,
+                IStatusBarService barService) {
+            super(notificationListener, uiOffloadThread, entryManager, statusBarStateController);
             mBarService = barService;
             // Make this on the current thread so we can wait for it during tests.
             mHandler = Handler.createAsync(Looper.myLooper());
         }
 
-        public OnChildLocationsChangedListener
+        OnChildLocationsChangedListener
                 getChildLocationsChangedListenerForTest() {
             return mNotificationLocationsChangedListener;
         }
-
-        public Handler getHandlerForTest() {
-            return mHandler;
-        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index f0fa788..d94bf84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -47,7 +47,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.policy.SmartReplyConstants;
 
 import org.junit.Before;
@@ -73,7 +73,7 @@
     StatusBarNotification mStatusBarNotification;
     @Mock
     Notification mNotification;
-    NotificationData.Entry mEntry;
+    NotificationEntry mEntry;
     @Mock
     RemoteInput mRemoteInput;
     @Mock
@@ -107,7 +107,7 @@
         // Smart replies and actions
         when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(true);
         when(mStatusBarNotification.getNotification()).thenReturn(mNotification);
-        mEntry = new NotificationData.Entry(mStatusBarNotification);
+        mEntry = new NotificationEntry(mStatusBarNotification);
         when(mSmartReplyConstants.isEnabled()).thenReturn(true);
         mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
     }
@@ -159,28 +159,37 @@
         verify(mockHeadsUp, times(1)).showAppOpsIcons(any());
     }
 
-    private void setupAppGeneratedReplies(CharSequence[] smartReplyTitles) {
-        Notification.Action freeFormAction =
-                new Notification.Action.Builder(null, "Freeform Test Action", null).build();
-        setupAppGeneratedReplies(smartReplyTitles, freeFormAction);
+    private void setupAppGeneratedReplies(CharSequence[] smartReplies) {
+        setupAppGeneratedReplies(smartReplies, true /* allowSystemGeneratedReplies */);
     }
 
     private void setupAppGeneratedReplies(
-            CharSequence[] smartReplyTitles,
-            Notification.Action freeFormRemoteInputAction) {
+            CharSequence[] smartReplies, boolean allowSystemGeneratedReplies) {
         PendingIntent pendingIntent =
                 PendingIntent.getBroadcast(mContext, 0, new Intent(TEST_ACTION), 0);
         Notification.Action action =
                 new Notification.Action.Builder(null, "Test Action", pendingIntent).build();
-        when(mRemoteInput.getChoices()).thenReturn(smartReplyTitles);
+        when(mRemoteInput.getChoices()).thenReturn(smartReplies);
         Pair<RemoteInput, Notification.Action> remoteInputActionPair =
                 Pair.create(mRemoteInput, action);
         when(mNotification.findRemoteInputActionPair(false)).thenReturn(remoteInputActionPair);
 
+        Notification.Action freeFormRemoteInputAction =
+                createActionBuilder("Freeform Test Action")
+                .setAllowGeneratedReplies(allowSystemGeneratedReplies)
+                .build();
         Pair<RemoteInput, Notification.Action> freeFormRemoteInputActionPair =
                 Pair.create(mFreeFormRemoteInput, freeFormRemoteInputAction);
         when(mNotification.findRemoteInputActionPair(true)).thenReturn(
                 freeFormRemoteInputActionPair);
+
+        when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false);
+    }
+
+    private void setupAppGeneratedSuggestions(
+            CharSequence[] smartReplies, List<Notification.Action> smartActions) {
+        setupAppGeneratedReplies(smartReplies);
+        when(mNotification.getContextualActions()).thenReturn(smartActions);
     }
 
     @Test
@@ -198,7 +207,6 @@
     public void chooseSmartRepliesAndActions_appGeneratedSmartReplies() {
         CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"};
         setupAppGeneratedReplies(smartReplies);
-        when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false);
 
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
@@ -211,12 +219,9 @@
     @Test
     public void chooseSmartRepliesAndActions_appGeneratedSmartRepliesAndActions() {
         CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"};
-        setupAppGeneratedReplies(smartReplies);
-        when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false);
-
         List<Notification.Action> smartActions =
                 createActions(new String[] {"Test Action 1", "Test Action 2"});
-        when(mNotification.getContextualActions()).thenReturn(smartActions);
+        setupAppGeneratedSuggestions(smartReplies, smartActions);
 
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
@@ -229,15 +234,11 @@
 
     @Test
     public void chooseSmartRepliesAndActions_sysGeneratedSmartReplies() {
-        Notification.Action freeFormAction = createActionBuilder("Freeform Action")
-                .setAllowGeneratedReplies(true)
-                .build();
         // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
         // replies.
-        setupAppGeneratedReplies(null, freeFormAction);
+        setupAppGeneratedReplies(null /* smartReplies */);
 
-        mEntry.smartReplies =
-                new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+        mEntry.smartReplies = new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
 
@@ -248,12 +249,9 @@
 
     @Test
     public void chooseSmartRepliesAndActions_noSysGeneratedSmartRepliesIfNotAllowed() {
-        Notification.Action freeFormAction = createActionBuilder("Freeform Action")
-                .setAllowGeneratedReplies(false)
-                .build();
         // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
         // replies.
-        setupAppGeneratedReplies(null, freeFormAction);
+        setupAppGeneratedReplies(null /* smartReplies */, false /* allowSystemGeneratedReplies */);
 
         mEntry.smartReplies =
                 new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
@@ -268,7 +266,7 @@
     public void chooseSmartRepliesAndActions_sysGeneratedSmartActions() {
         // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
         // actions.
-        setupAppGeneratedReplies(null);
+        setupAppGeneratedReplies(null /* smartReplies */);
 
         mEntry.systemGeneratedSmartActions =
                 createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
@@ -283,18 +281,12 @@
 
     @Test
     public void chooseSmartRepliesAndActions_appGenPreferredOverSysGen() {
-        Notification.Action freeFormAction = createActionBuilder("Freeform Action")
-                .setAllowGeneratedReplies(true)
-                .build();
         CharSequence[] appGenSmartReplies = new String[] {"Reply1", "Reply2"};
         // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
         // replies.
-        setupAppGeneratedReplies(appGenSmartReplies, freeFormAction);
-        when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false);
-
         List<Notification.Action> appGenSmartActions =
                 createActions(new String[] {"Test Action 1", "Test Action 2"});
-        when(mNotification.getContextualActions()).thenReturn(appGenSmartActions);
+        setupAppGeneratedSuggestions(appGenSmartReplies, appGenSmartActions);
 
         mEntry.smartReplies = new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
         mEntry.systemGeneratedSmartActions =
@@ -313,12 +305,12 @@
     public void chooseSmartRepliesAndActions_disallowSysGenSmartActions() {
         // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
         // actions.
-        setupAppGeneratedReplies(null);
-
+        setupAppGeneratedReplies(null /* smartReplies */, false /* allowSystemGeneratedReplies */);
         when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(false);
-
+        mEntry.smartReplies = new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
         mEntry.systemGeneratedSmartActions =
                 createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index ad43bea..0899c73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -60,7 +60,7 @@
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -183,8 +183,8 @@
         when(row.getGuts()).thenReturn(guts);
         doNothing().when(row).inflateGuts();
 
-        NotificationData.Entry realEntry = realRow.getEntry();
-        NotificationData.Entry entry = spy(realEntry);
+        NotificationEntry realEntry = realRow.getEntry();
+        NotificationEntry entry = spy(realEntry);
 
         when(entry.getRow()).thenReturn(row);
         when(entry.getGuts()).thenReturn(guts);
@@ -443,7 +443,7 @@
         NotificationGuts guts = new NotificationGuts(mContext);
         ExpandableNotificationRow row = spy(createTestNotificationRow());
         doReturn(guts).when(row).getGuts();
-        NotificationData.Entry entry = row.getEntry();
+        NotificationEntry entry = row.getEntry();
         entry.setRow(row);
         mGutsManager.setExposedGuts(guts);
 
@@ -452,7 +452,7 @@
 
     @Test
     public void testSetShouldManageLifetime_setShouldManage() {
-        NotificationData.Entry entry = createTestNotificationRow().getEntry();
+        NotificationEntry entry = createTestNotificationRow().getEntry();
         mGutsManager.setShouldManageLifetime(entry, true /* shouldManage */);
 
         assertTrue(entry.key.equals(mGutsManager.mKeyToRemoveOnGutsClosed));
@@ -460,7 +460,7 @@
 
     @Test
     public void testSetShouldManageLifetime_setShouldNotManage() {
-        NotificationData.Entry entry = createTestNotificationRow().getEntry();
+        NotificationEntry entry = createTestNotificationRow().getEntry();
         mGutsManager.mKeyToRemoveOnGutsClosed = entry.key;
         mGutsManager.setShouldManageLifetime(entry, false /* shouldManage */);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
index d6b706d..648df3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
@@ -48,7 +48,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.InflationTask;
 import com.android.systemui.statusbar.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -88,7 +88,7 @@
             }
 
             @Override
-            public void onAsyncInflationFinished(NotificationData.Entry entry,
+            public void onAsyncInflationFinished(NotificationEntry entry,
                     @NotificationInflater.InflationFlag int inflatedFlags) {
             }
         });
@@ -174,7 +174,7 @@
                     }
 
                     @Override
-                    public void onAsyncInflationFinished(NotificationData.Entry entry,
+                    public void onAsyncInflationFinished(NotificationEntry entry,
                             @NotificationInflater.InflationFlag int inflatedFlags) {
                         countDownLatch.countDown();
                     }
@@ -256,7 +256,7 @@
             }
 
             @Override
-            public void onAsyncInflationFinished(NotificationData.Entry entry,
+            public void onAsyncInflationFinished(NotificationEntry entry,
                     @NotificationInflater.InflationFlag int inflatedFlags) {
                 if (expectingException) {
                     exceptionHolder.setException(new RuntimeException(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index d28f017..ecb0cf8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -32,6 +32,7 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
@@ -44,6 +45,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.INotificationManager;
@@ -54,6 +56,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
+import android.metrics.LogMaker;
 import android.os.IBinder;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -69,6 +72,7 @@
 import android.widget.TextView;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
@@ -79,6 +83,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -177,6 +182,29 @@
                 () -> VISIBLE == mNotificationInfo.findViewById(R.id.prompt).getVisibility());
     }
 
+    class ImportanceChangeLogMaker implements ArgumentMatcher<LogMaker> {
+        private static final int CATEGORY = MetricsProto.MetricsEvent.ACTION_SAVE_IMPORTANCE;
+        private int mType, mSubtype;
+
+        ImportanceChangeLogMaker(int type, int subtype) {
+            mType = type;
+            mSubtype = subtype;
+        }
+        public boolean matches(LogMaker l) {
+            return (l.getCategory() == CATEGORY)
+                    && (l.getType() == mType)
+                    && (l.getSubtype() == mSubtype);
+        }
+
+        public String toString() {
+            return String.format("LogMaker(%d, %d, %d)", CATEGORY, mType, mSubtype);
+        }
+    }
+
+    private LogMaker importanceChangeLog(int type, int subtype) {
+        return argThat(new ImportanceChangeLogMaker(type, subtype));
+    }
+
     @Test
     public void testBindNotification_SetsTextApplicationName() throws Exception {
         when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
@@ -475,7 +503,7 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
                 IMPORTANCE_DEFAULT);
         mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
-        verify(mMetricsLogger, times(0)).count(anyString(), anyInt());
+        verifyZeroInteractions(mMetricsLogger);
     }
 
     @Test
@@ -484,7 +512,7 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, true,
                 true, true, IMPORTANCE_DEFAULT);
         mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
-        verify(mMetricsLogger, times(1)).count(anyString(), anyInt());
+        verify(mMetricsLogger).count(eq("HowCanNotifsBeRealIfAppsArent"), eq(1));
     }
 
     @Test
@@ -827,6 +855,9 @@
         waitForUndoButton();
         mNotificationInfo.handleCloseControls(true, false);
 
+        verify(mMetricsLogger).write(importanceChangeLog(
+                MetricsProto.MetricsEvent.TYPE_ACTION, IMPORTANCE_NONE - IMPORTANCE_LOW));
+
         mTestableLooper.processAllMessages();
         ArgumentCaptor<NotificationChannel> updated =
                 ArgumentCaptor.forClass(NotificationChannel.class);
@@ -860,6 +891,9 @@
         waitForUndoButton();
         mNotificationInfo.handleCloseControls(true, false);
 
+        verify(mMetricsLogger).write(importanceChangeLog(
+                MetricsProto.MetricsEvent.TYPE_ACTION, IMPORTANCE_NONE - IMPORTANCE_LOW));
+
         mTestableLooper.processAllMessages();
         ArgumentCaptor<NotificationChannel> updated =
                 ArgumentCaptor.forClass(NotificationChannel.class);
@@ -936,15 +970,14 @@
         waitForUndoButton();
         mNotificationInfo.findViewById(R.id.undo).performClick();
         waitForStopButton();
-        mNotificationInfo.handleCloseControls(true, false);
+        // mNotificationInfo.handleCloseControls doesn't get called by this interaction.
+
+        verify(mMetricsLogger).write(importanceChangeLog(
+                MetricsProto.MetricsEvent.TYPE_DISMISS, IMPORTANCE_NONE - IMPORTANCE_LOW));
 
         mTestableLooper.processAllMessages();
-        ArgumentCaptor<NotificationChannel> updated =
-                ArgumentCaptor.forClass(NotificationChannel.class);
-        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
-                anyString(), eq(TEST_UID), updated.capture());
-        assertTrue(0 != (mNotificationChannel.getUserLockedFields() & USER_LOCKED_IMPORTANCE));
-        assertEquals(IMPORTANCE_LOW, mNotificationChannel.getImportance());
+        verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
+                anyString(), eq(TEST_UID), any());
     }
 
     @Test
@@ -958,15 +991,11 @@
         waitForUndoButton();
         mNotificationInfo.findViewById(R.id.undo).performClick();
         waitForStopButton();
-        mNotificationInfo.handleCloseControls(true, false);
+        // mNotificationInfo.handleCloseControls doesn't get called by this code path
 
         mTestableLooper.processAllMessages();
-        ArgumentCaptor<NotificationChannel> updated =
-                ArgumentCaptor.forClass(NotificationChannel.class);
-        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
-                anyString(), eq(TEST_UID), updated.capture());
-        assertTrue(0 != (mNotificationChannel.getUserLockedFields() & USER_LOCKED_IMPORTANCE));
-        assertEquals(IMPORTANCE_LOW, mNotificationChannel.getImportance());
+        verify(mMockINotificationManager, times(0)).updateNotificationChannelForPackage(
+                anyString(), eq(TEST_UID), any());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
index e4d0196..04d7509 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -35,7 +35,7 @@
 import android.view.ViewGroup;
 
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
 
 import org.junit.Before;
@@ -54,7 +54,7 @@
     public void setup() {
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
         mRow = mock(ExpandableNotificationRow.class);
-        NotificationData.Entry entry = new NotificationData.Entry(
+        NotificationEntry entry = new NotificationEntry(
                 mock(StatusBarNotification.class));
         entry.channel = mock(NotificationChannel.class);
         when(mRow.getEntry()).thenReturn(entry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index e65e806..214404c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -35,12 +35,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.os.Handler;
-import android.os.IPowerManager;
-import android.os.Looper;
-import android.os.PowerManager;
 import android.provider.Settings;
-import android.service.dreams.IDreamManager;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -58,9 +53,9 @@
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.FooterView;
 import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
@@ -104,8 +99,6 @@
     @Mock private NotificationData mNotificationData;
     @Mock private NotificationRemoteInputManager mRemoteInputManager;
     @Mock private RemoteInputController mRemoteInputController;
-    @Mock private IDreamManager mDreamManager;
-    private PowerManager mPowerManager;
     private TestableNotificationEntryManager mEntryManager;
     private int mOriginalInterruptionModelSetting;
 
@@ -122,20 +115,16 @@
         mDependency.injectMockDependency(ShadeController.class);
         when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
 
-        IPowerManager powerManagerService = mock(IPowerManager.class);
-        mPowerManager = new PowerManager(mContext, powerManagerService,
-                Handler.createAsync(Looper.myLooper()));
-
-        mEntryManager = new TestableNotificationEntryManager(mDreamManager, mPowerManager,
-                mContext);
+        mEntryManager = new TestableNotificationEntryManager(mContext);
         mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
         Dependency.get(InitController.class).executePostInitTasks();
-        mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, null, mHeadsUpManager,
+        mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, mHeadsUpManager,
                 mNotificationData);
 
 
         NotificationShelf notificationShelf = mock(NotificationShelf.class);
-        mStackScroller = spy(new NotificationStackScrollLayout(getContext()));
+        mStackScroller = spy(new NotificationStackScrollLayout(getContext(), null,
+                true /* allowLongPress */));
         mStackScroller.setShelf(notificationShelf);
         mStackScroller.setStatusBar(mBar);
         mStackScroller.setScrimController(mock(ScrimController.class));
@@ -276,8 +265,8 @@
     @Test
     public void testUpdateFooter_remoteInput() {
         setBarStateForTest(StatusBarState.SHADE);
-        ArrayList<Entry> entries = new ArrayList<>();
-        entries.add(mock(Entry.class));
+        ArrayList<NotificationEntry> entries = new ArrayList<>();
+        entries.add(mock(NotificationEntry.class));
         when(mNotificationData.getActiveNotifications()).thenReturn(entries);
 
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
@@ -293,8 +282,8 @@
     @Test
     public void testUpdateFooter_oneClearableNotification() {
         setBarStateForTest(StatusBarState.SHADE);
-        ArrayList<Entry> entries = new ArrayList<>();
-        entries.add(mock(Entry.class));
+        ArrayList<NotificationEntry> entries = new ArrayList<>();
+        entries.add(mock(NotificationEntry.class));
         when(mNotificationData.getActiveNotifications()).thenReturn(entries);
 
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
@@ -309,8 +298,8 @@
     @Test
     public void testUpdateFooter_oneNonClearableNotification() {
         setBarStateForTest(StatusBarState.SHADE);
-        ArrayList<Entry> entries = new ArrayList<>();
-        entries.add(mock(Entry.class));
+        ArrayList<NotificationEntry> entries = new ArrayList<>();
+        entries.add(mock(NotificationEntry.class));
         when(mEntryManager.getNotificationData().getActiveNotifications()).thenReturn(entries);
         assertTrue(mEntryManager.getNotificationData().getActiveNotifications().size() != 0);
 
@@ -325,7 +314,7 @@
 
         // add notification
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
-        NotificationData.Entry entry = mock(NotificationData.Entry.class);
+        NotificationEntry entry = mock(NotificationEntry.class);
         when(row.getEntry()).thenReturn(entry);
         when(entry.isClearable()).thenReturn(true);
         mStackScroller.addContainerView(row);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 1481aef..c0f7f0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
@@ -30,6 +31,8 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.qs.AutoAddTracker;
 import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.HotspotController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -51,7 +54,11 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         mAutoTileManager = new AutoTileManager(mContext, mAutoAddTracker, mQsTileHost,
-                Handler.createAsync(TestableLooper.get(this).getLooper()));
+                Handler.createAsync(TestableLooper.get(this).getLooper()),
+                mock(HotspotController.class),
+                mock(DataSaverController.class),
+                mock(ManagedProfileController.class),
+                mock(ColorDisplayController.class));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index d99e46d..add8838 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -14,6 +14,8 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.atLeast;
@@ -74,7 +76,7 @@
 
         CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
         fragment.initNotificationIconArea(mMockNotificiationAreaController);
-        fragment.disable(0, 0, false);
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
         assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
                 .getVisibility());
@@ -89,12 +91,12 @@
 
         CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
         fragment.initNotificationIconArea(mMockNotificiationAreaController);
-        fragment.disable(StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
+        fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
 
         assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
                 .getVisibility());
 
-        fragment.disable(0, 0, false);
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
         assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
                 .getVisibility());
@@ -107,11 +109,11 @@
 
         CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
         fragment.initNotificationIconArea(mMockNotificiationAreaController);
-        fragment.disable(StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
+        fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
 
         Mockito.verify(mNotificationAreaInner).setVisibility(eq(View.INVISIBLE));
 
-        fragment.disable(0, 0, false);
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
         Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
     }
@@ -123,11 +125,11 @@
 
         CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
         fragment.initNotificationIconArea(mMockNotificiationAreaController);
-        fragment.disable(StatusBarManager.DISABLE_CLOCK, 0, false);
+        fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_CLOCK, 0, false);
 
         assertEquals(View.GONE, mFragment.getView().findViewById(R.id.clock).getVisibility());
 
-        fragment.disable(0, 0, false);
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
         assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock).getVisibility());
     }
@@ -139,7 +141,7 @@
 
         CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
         fragment.initNotificationIconArea(mMockNotificiationAreaController);
-        fragment.disable(StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
+        fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
 
         Mockito.verify(mNotificationAreaInner).setVisibility(eq(View.INVISIBLE));
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index 4f6329c..38d9ae7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -30,11 +30,11 @@
 import android.widget.TextView;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.statusbar.HeadsUpStatusBarView;
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 
 import org.junit.Assert;
 import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 44deb10..9ceb325 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -16,15 +16,20 @@
 
 package com.android.systemui.statusbar.phone;
 
-import android.view.View;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.when;
+
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.view.View;
 
 import com.android.systemui.statusbar.AlertingNotificationManager;
 import com.android.systemui.statusbar.AlertingNotificationManagerTest;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -34,11 +39,6 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.assertFalse;
-
-import static org.mockito.Mockito.when;
-
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -96,7 +96,7 @@
 
     @Test
     public void testCanRemoveImmediately_notTopEntry() {
-        NotificationData.Entry laterEntry = new NotificationData.Entry(createNewNotification(1));
+        NotificationEntry laterEntry = new NotificationEntry(createNewNotification(1));
         laterEntry.setRow(mRow);
         mHeadsUpManager.showNotification(mEntry);
         mHeadsUpManager.showNotification(laterEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
index 9e2db91..babd452 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
@@ -14,10 +14,13 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static org.junit.Assert.assertNotNull;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.app.Fragment;
 import android.content.Context;
+import android.os.Bundle;
 import android.os.Looper;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -27,13 +30,16 @@
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.Dependency;
 import com.android.systemui.SysuiBaseFragmentTest;
+import com.android.systemui.assist.AssistManager;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -44,6 +50,23 @@
 @SmallTest
 public class NavigationBarFragmentTest extends SysuiBaseFragmentTest {
 
+    private OverviewProxyService mOverviewProxyService =
+            mDependency.injectMockDependency(OverviewProxyService.class);
+    private AccessibilityManagerWrapper mAccessibilityWrapper =
+            new AccessibilityManagerWrapper(mContext) {
+                Tracker mTracker = mLeakCheck.getTracker("accessibility_manager");
+
+                @Override
+                public void addCallback(AccessibilityServicesStateChangeListener listener) {
+                    mTracker.getLeakInfo(listener).addAllocation(new Throwable());
+                }
+
+                @Override
+                public void removeCallback(AccessibilityServicesStateChangeListener listener) {
+                    mTracker.getLeakInfo(listener).clearAllocations();
+                }
+            };
+
     public NavigationBarFragmentTest() {
         super(NavigationBarFragment.class);
     }
@@ -54,32 +77,19 @@
 
     @Before
     public void setup() {
-        mDependency.injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
         mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
         mSysuiContext.putComponent(StatusBar.class, mock(StatusBar.class));
         mSysuiContext.putComponent(Recents.class, mock(Recents.class));
         mSysuiContext.putComponent(Divider.class, mock(Divider.class));
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
-        mDependency.injectMockDependency(OverviewProxyService.class);
         WindowManager windowManager = mock(WindowManager.class);
         Display defaultDisplay = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
         when(windowManager.getDefaultDisplay()).thenReturn(
                 defaultDisplay);
         mContext.addMockSystemService(Context.WINDOW_SERVICE, windowManager);
 
-        Tracker tracker = mLeakCheck.getTracker("accessibility_manager");
-        AccessibilityManagerWrapper wrapper = new AccessibilityManagerWrapper(mContext) {
-            @Override
-            public void addCallback(AccessibilityServicesStateChangeListener listener) {
-                tracker.getLeakInfo(listener).addAllocation(new Throwable());
-            }
-
-            @Override
-            public void removeCallback(AccessibilityServicesStateChangeListener listener) {
-                tracker.getLeakInfo(listener).clearAllocations();
-            }
-        };
-        mDependency.injectTestDependency(AccessibilityManagerWrapper.class, wrapper);
+        mDependency.injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
+        mDependency.injectTestDependency(AccessibilityManagerWrapper.class, mAccessibilityWrapper);
     }
 
     @Test
@@ -91,4 +101,15 @@
         navigationBarFragment.onHomeLongClick(navigationBarFragment.getView());
     }
 
+    @Override
+    protected Fragment instantiate(Context context, String className, Bundle arguments) {
+        DeviceProvisionedController deviceProvisionedController =
+                mock(DeviceProvisionedController.class);
+        assertNotNull(mAccessibilityWrapper);
+        return new NavigationBarFragment(mAccessibilityWrapper,
+                deviceProvisionedController,
+                new MetricsLogger(),
+                new AssistManager(deviceProvisionedController, mContext),
+                mOverviewProxyService);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java
index 76f57f0..40d5415 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java
@@ -67,5 +67,4 @@
 
         assertTrue(mTransitions.isLightsOut(BarTransitions.MODE_LIGHTS_OUT));
     }
-
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index c3bc511..cefd4ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -32,14 +32,18 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.AmbientPulseManager;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
@@ -49,14 +53,17 @@
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
-    @Rule
-    public MockitoRule rule = MockitoJUnit.rule();
+    @Rule public MockitoRule rule = MockitoJUnit.rule();
 
     private NotificationGroupAlertTransferHelper mGroupAlertTransferHelper;
     private NotificationGroupManager mGroupManager;
     private AmbientPulseManager mAmbientPulseManager;
     private HeadsUpManager mHeadsUpManager;
-    private final HashMap<String, Entry> mPendingEntries = new HashMap<>();
+    @Mock private NotificationEntryManager mNotificationEntryManager;
+    @Captor
+    private ArgumentCaptor<NotificationEntryListener> mListenerCaptor;
+    private NotificationEntryListener mNotificationEntryListener;
+    private final HashMap<String, NotificationEntry> mPendingEntries = new HashMap<>();
     private final NotificationGroupTestHelper mGroupTestHelper =
             new NotificationGroupTestHelper(mContext);
 
@@ -67,24 +74,28 @@
         mDependency.injectTestDependency(AmbientPulseManager.class, mAmbientPulseManager);
         mHeadsUpManager = new HeadsUpManager(mContext) {};
 
+        when(mNotificationEntryManager.getPendingNotificationsIterator())
+                .thenReturn(mPendingEntries.values());
+
         mGroupManager = new NotificationGroupManager();
         mDependency.injectTestDependency(NotificationGroupManager.class, mGroupManager);
         mGroupManager.setHeadsUpManager(mHeadsUpManager);
 
         mGroupAlertTransferHelper = new NotificationGroupAlertTransferHelper();
         mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager);
-        mGroupAlertTransferHelper.setPendingEntries(mPendingEntries);
 
-        mGroupManager.addOnGroupChangeListener(mGroupAlertTransferHelper);
+        mGroupAlertTransferHelper.bind(mNotificationEntryManager, mGroupManager);
+        verify(mNotificationEntryManager).addNotificationEntryListener(mListenerCaptor.capture());
+        mNotificationEntryListener = mListenerCaptor.getValue();
         mHeadsUpManager.addListener(mGroupAlertTransferHelper);
         mAmbientPulseManager.addListener(mGroupAlertTransferHelper);
     }
 
     @Test
     public void testSuppressedSummaryHeadsUpTransfersToChild() {
-        Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mHeadsUpManager.showNotification(summaryEntry);
-        Entry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
 
         // Summary will be suppressed because there is only one child.
         mGroupManager.onEntryAdded(summaryEntry);
@@ -97,11 +108,11 @@
 
     @Test
     public void testSuppressedSummaryHeadsUpTransfersToChildButBackAgain() {
-        NotificationData.Entry summaryEntry =
+        NotificationEntry summaryEntry =
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry =
+        NotificationEntry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry2 =
+        NotificationEntry childEntry2 =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         mHeadsUpManager.showNotification(summaryEntry);
         // Trigger a transfer of alert state from summary to child.
@@ -110,7 +121,7 @@
 
         // Add second child notification so that summary is no longer suppressed.
         mPendingEntries.put(childEntry2.key, childEntry2);
-        mGroupAlertTransferHelper.onPendingEntryAdded(childEntry2);
+        mNotificationEntryListener.onPendingEntryAdded(childEntry2);
         mGroupManager.onEntryAdded(childEntry2);
 
         // The alert state should transfer back to the summary as there is now more than one
@@ -121,11 +132,11 @@
 
     @Test
     public void testSuppressedSummaryHeadsUpDoesntTransferBackOnDozingChanged() {
-        NotificationData.Entry summaryEntry =
+        NotificationEntry summaryEntry =
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry =
+        NotificationEntry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry2 =
+        NotificationEntry childEntry2 =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         mHeadsUpManager.showNotification(summaryEntry);
         // Trigger a transfer of alert state from summary to child.
@@ -137,7 +148,7 @@
 
         // Add second child notification so that summary is no longer suppressed.
         mPendingEntries.put(childEntry2.key, childEntry2);
-        mGroupAlertTransferHelper.onPendingEntryAdded(childEntry2);
+        mNotificationEntryListener.onPendingEntryAdded(childEntry2);
         mGroupManager.onEntryAdded(childEntry2);
 
         // Dozing changed so no reason to re-alert summary.
@@ -146,9 +157,9 @@
 
     @Test
     public void testSuppressedSummaryHeadsUpTransferDoesNotAlertChildIfUninflated() {
-        Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mHeadsUpManager.showNotification(summaryEntry);
-        Entry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
             .thenReturn(false);
 
@@ -164,9 +175,9 @@
 
     @Test
     public void testSuppressedSummaryHeadsUpTransferAlertsChildOnInflation() {
-        Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mHeadsUpManager.showNotification(summaryEntry);
-        Entry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
             .thenReturn(false);
 
@@ -175,7 +186,7 @@
 
         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
             .thenReturn(true);
-        mGroupAlertTransferHelper.onInflationFinished(childEntry);
+        mNotificationEntryListener.onEntryReinflated(childEntry);
 
         // Alert is immediately removed from summary, and we show child as its content is inflated.
         assertFalse(mHeadsUpManager.isAlerting(summaryEntry.key));
@@ -184,13 +195,13 @@
 
     @Test
     public void testSuppressedSummaryHeadsUpTransferBackAbortsChildInflation() {
-        NotificationData.Entry summaryEntry =
+        NotificationEntry summaryEntry =
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry =
+        NotificationEntry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
             .thenReturn(false);
-        NotificationData.Entry childEntry2 =
+        NotificationEntry childEntry2 =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         mHeadsUpManager.showNotification(summaryEntry);
         // Trigger a transfer of alert state from summary to child.
@@ -199,13 +210,13 @@
 
         // Add second child notification so that summary is no longer suppressed.
         mPendingEntries.put(childEntry2.key, childEntry2);
-        mGroupAlertTransferHelper.onPendingEntryAdded(childEntry2);
+        mNotificationEntryListener.onPendingEntryAdded(childEntry2);
         mGroupManager.onEntryAdded(childEntry2);
 
         // Child entry finishes its inflation.
         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
             .thenReturn(true);
-        mGroupAlertTransferHelper.onInflationFinished(childEntry);
+        mNotificationEntryListener.onEntryReinflated(childEntry);
 
         verify(childEntry.getRow(), times(1)).freeContentViewWhenSafe(mHeadsUpManager
             .getContentFlag());
@@ -214,9 +225,9 @@
 
     @Test
     public void testCleanUpPendingAlertInfo() {
-        NotificationData.Entry summaryEntry =
+        NotificationEntry summaryEntry =
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry =
+        NotificationEntry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
             .thenReturn(false);
@@ -225,16 +236,16 @@
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
 
-        mGroupAlertTransferHelper.cleanUpPendingAlertInfo(childEntry.key);
+        mNotificationEntryListener.onEntryRemoved(childEntry, null, false);
 
         assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
     }
 
     @Test
     public void testUpdateGroupChangeDoesNotTransfer() {
-        NotificationData.Entry summaryEntry =
+        NotificationEntry summaryEntry =
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry =
+        NotificationEntry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
             .thenReturn(false);
@@ -255,9 +266,9 @@
 
     @Test
     public void testUpdateChildToSummaryDoesNotTransfer() {
-        NotificationData.Entry summaryEntry =
+        NotificationEntry summaryEntry =
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry =
+        NotificationEntry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
             .thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
index b0bd1fb..ecb3e4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
@@ -29,7 +29,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.AmbientPulseManager;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import org.junit.Before;
@@ -69,8 +69,8 @@
 
     @Test
     public void testIsOnlyChildInGroup() {
-        NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
-        NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
 
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
@@ -80,8 +80,8 @@
 
     @Test
     public void testIsChildInGroupWithSummary() {
-        NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
-        NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
 
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
@@ -92,8 +92,8 @@
 
     @Test
     public void testIsSummaryOfGroupWithChildren() {
-        NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
-        NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
 
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
@@ -105,8 +105,8 @@
 
     @Test
     public void testRemoveChildFromGroupWithSummary() {
-        NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
-        NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
         mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
@@ -118,8 +118,8 @@
 
     @Test
     public void testRemoveSummaryFromGroupWithSummary() {
-        NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
-        NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
         mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
@@ -132,8 +132,8 @@
 
     @Test
     public void testHeadsUpEntryIsIsolated() {
-        NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
-        NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
         mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
@@ -149,8 +149,8 @@
 
     @Test
     public void testAmbientPulseEntryIsIsolated() {
-        NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
-        NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
         mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
index 7ad68eb..e6b9778 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -27,7 +27,7 @@
 import android.service.notification.StatusBarNotification;
 
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 /**
@@ -44,23 +44,23 @@
         mContext = context;
     }
 
-    public NotificationData.Entry createSummaryNotification() {
+    public NotificationEntry createSummaryNotification() {
         return createSummaryNotification(Notification.GROUP_ALERT_ALL);
     }
 
-    public NotificationData.Entry createSummaryNotification(int groupAlertBehavior) {
+    public NotificationEntry createSummaryNotification(int groupAlertBehavior) {
         return createEntry(true, groupAlertBehavior);
     }
 
-    public NotificationData.Entry createChildNotification() {
+    public NotificationEntry createChildNotification() {
         return createChildNotification(Notification.GROUP_ALERT_ALL);
     }
 
-    public NotificationData.Entry createChildNotification(int groupAlertBehavior) {
+    public NotificationEntry createChildNotification(int groupAlertBehavior) {
         return createEntry(false, groupAlertBehavior);
     }
 
-    public NotificationData.Entry createEntry(boolean isSummary, int groupAlertBehavior) {
+    public NotificationEntry createEntry(boolean isSummary, int groupAlertBehavior) {
         Notification notif = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentTitle("Title")
                 .setSmallIcon(R.drawable.ic_person)
@@ -79,7 +79,7 @@
                 new UserHandle(ActivityManager.getCurrentUser()),
                 null /* overrideGroupKey */,
                 0 /* postTime */);
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        NotificationEntry entry = new NotificationEntry(sbn);
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
         entry.setRow(row);
         when(row.getEntry()).thenReturn(entry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 0bc304e..146c5d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -220,9 +220,9 @@
         mScrimController.transitionTo(ScrimState.PULSING);
         mScrimController.finishAnimationsImmediately();
         // Front scrim should be transparent
-        // Back scrim should be visible with tint
+        // Back scrim should be semi-transparent so the user can see the wallpaper
         // Pulse callback should have been invoked
-        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
         assertScrimTint(mScrimBehind, true /* tinted */);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
index 72b0156..4f95bc5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
@@ -14,17 +14,14 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
-
 import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON;
 import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE;
 import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
 
-import android.graphics.Rect;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.mock;
+
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
@@ -32,15 +29,15 @@
 import android.widget.LinearLayout;
 
 import com.android.internal.statusbar.StatusBarIcon;
-import com.android.systemui.statusbar.StatusIconDisplayable;
+import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.StatusBarMobileView;
 import com.android.systemui.statusbar.StatusBarWifiView;
+import com.android.systemui.statusbar.StatusIconDisplayable;
 import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
 import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
 
 import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 02f8949..12cb9957 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -14,6 +14,8 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static org.junit.Assert.assertFalse;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -37,7 +39,8 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 
@@ -60,7 +63,7 @@
     public void setup() {
         mMetricsLogger = new FakeMetricsLogger();
         mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
-        mCommandQueue = new CommandQueue();
+        mCommandQueue = new CommandQueue(mContext);
         mContext.putComponent(CommandQueue.class, mCommandQueue);
         mDependency.injectTestDependency(ShadeController.class, mShadeController);
 
@@ -70,7 +73,8 @@
                 mock(NotificationPanelView.class), mock(HeadsUpManagerPhone.class),
                 statusBarWindowView, mock(NotificationListContainerViewGroup.class),
                 mock(DozeScrimController.class), mock(ScrimController.class),
-                mock(ActivityLaunchAnimator.class), mock(StatusBarKeyguardViewManager.class));
+                mock(ActivityLaunchAnimator.class), mock(StatusBarKeyguardViewManager.class),
+                mock(NotificationAlertingManager.class));
     }
 
     @Test
@@ -78,8 +82,9 @@
         Notification n = new Notification.Builder(getContext(), "a").build();
         StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
                 UserHandle.of(0), null, 0);
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
-        mCommandQueue.disable(StatusBarManager.DISABLE_EXPAND, 0, false /* animate */);
+        NotificationEntry entry = new NotificationEntry(sbn);
+        mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0,
+                false /* animate */);
         TestableLooper.get(this).processAllMessages();
 
         assertFalse("The panel shouldn't allow heads up while disabled",
@@ -91,8 +96,9 @@
         Notification n = new Notification.Builder(getContext(), "a").build();
         StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
                 UserHandle.of(0), null, 0);
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
-        mCommandQueue.disable(0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */);
+        NotificationEntry entry = new NotificationEntry(sbn);
+        mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
+                false /* animate */);
         TestableLooper.get(this).processAllMessages();
 
         assertFalse("The panel shouldn't allow heads up while notitifcation shade disabled",
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index c207fef..0d4046b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -17,6 +17,8 @@
 package com.android.systemui.statusbar.phone;
 
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
@@ -66,7 +68,9 @@
 import com.android.systemui.ForegroundServiceController;
 import com.android.systemui.InitController;
 import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.UiOffloadThread;
 import com.android.systemui.appops.AppOpsController;
 import com.android.systemui.appops.AppOpsControllerImpl;
 import com.android.systemui.assist.AssistManager;
@@ -79,6 +83,7 @@
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.NavigationBarController;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationMediaManager;
@@ -89,10 +94,14 @@
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -126,9 +135,11 @@
     @Mock private IDreamManager mDreamManager;
     @Mock private ScrimController mScrimController;
     @Mock private DozeScrimController mDozeScrimController;
-    @Mock private ArrayList<Entry> mNotificationList;
+    @Mock private ArrayList<NotificationEntry> mNotificationList;
     @Mock private BiometricUnlockController mBiometricUnlockController;
     @Mock private NotificationData mNotificationData;
+    @Mock
+    private NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor;
 
     // Mock dependencies:
     @Mock private NotificationViewHierarchyManager mViewHierarchyManager;
@@ -141,13 +152,19 @@
     @Mock private StatusBarStateController mStatusBarStateController;
     @Mock private DeviceProvisionedController mDeviceProvisionedController;
     @Mock private NotificationPresenter mNotificationPresenter;
-    @Mock private NotificationEntryManager.Callback mCallback;
+    @Mock
+    private NotificationEntryListener mEntryListener;
     @Mock private BubbleController mBubbleController;
+    @Mock
+    private NotificationFilter mNotificationFilter;
+    @Mock
+    private NotificationAlertingManager mNotificationAlertingManager;
 
     private TestableStatusBar mStatusBar;
     private FakeMetricsLogger mMetricsLogger;
     private PowerManager mPowerManager;
     private TestableNotificationEntryManager mEntryManager;
+    private TestableNotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
     private NotificationLogger mNotificationLogger;
     private CommandQueue mCommandQueue;
 
@@ -168,18 +185,31 @@
         mDependency.injectTestDependency(DeviceProvisionedController.class,
                 mDeviceProvisionedController);
         mDependency.injectMockDependency(BubbleController.class);
+        mDependency.injectTestDependency(NotificationFilter.class, mNotificationFilter);
+        mDependency.injectTestDependency(NotificationAlertingManager.class,
+                mNotificationAlertingManager);
+
+        IPowerManager powerManagerService = mock(IPowerManager.class);
+        mPowerManager = new PowerManager(mContext, powerManagerService,
+                Handler.createAsync(Looper.myLooper()));
+
+        mNotificationInterruptionStateProvider =
+                new TestableNotificationInterruptionStateProvider(mContext, mPowerManager,
+                        mDreamManager);
+        mDependency.injectTestDependency(NotificationInterruptionStateProvider.class,
+                mNotificationInterruptionStateProvider);
+        mDependency.injectMockDependency(NavigationBarController.class);
 
         mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
         mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
 
         mMetricsLogger = new FakeMetricsLogger();
         mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
+        mEntryManager = new TestableNotificationEntryManager(mContext);
+        mNotificationLogger = new NotificationLogger(mNotificationListener,
+                Dependency.get(UiOffloadThread.class), mEntryManager, mStatusBarStateController);
         mDependency.injectTestDependency(NotificationLogger.class, mNotificationLogger);
-        mNotificationLogger = new NotificationLogger();
-
-        IPowerManager powerManagerService = mock(IPowerManager.class);
-        mPowerManager = new PowerManager(mContext, powerManagerService,
-                Handler.createAsync(Looper.myLooper()));
+        DozeLog.traceDozing(mContext, false /* dozing */);
 
         mCommandQueue = mock(CommandQueue.class);
         when(mCommandQueue.asBinder()).thenReturn(new Binder());
@@ -204,7 +234,9 @@
             return null;
         }).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any());
 
-        mEntryManager = new TestableNotificationEntryManager(mDreamManager, mPowerManager, mContext);
+        mNotificationInterruptionStateProvider.setUpWithPresenter(mNotificationPresenter,
+                mHeadsUpManager, mHeadsUpSuppressor);
+
         when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
         mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache,
                 mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
@@ -216,13 +248,18 @@
                 mock(StatusBarWindowController.class), mock(NotificationIconAreaController.class),
                 mDozeScrimController, mock(NotificationShelf.class),
                 mLockscreenUserManager, mCommandQueue, mNotificationPresenter,
-                mock(BubbleController.class));
+                mock(BubbleController.class), mock(NavigationBarController.class),
+                mock(AutoHideController.class));
         mStatusBar.mContext = mContext;
         mStatusBar.mComponents = mContext.getComponents();
+        SystemUIFactory.getInstance().getRootComponent()
+                .getStatusBarInjector()
+                .createStatusBar(mStatusBar);
         mStatusBar.putComponent(StatusBar.class, mStatusBar);
         Dependency.get(InitController.class).executePostInitTasks();
         mEntryManager.setUpForTest(mock(NotificationPresenter.class), mStackScroller,
-                mCallback, mHeadsUpManager, mNotificationData);
+                mHeadsUpManager, mNotificationData);
+        mEntryManager.addNotificationEntryListener(mEntryListener);
         mNotificationLogger.setUpWithContainer(mStackScroller);
 
         TestableLooper.get(this).setMessageHandler(m -> {
@@ -361,11 +398,9 @@
     public void testShouldHeadsUp_nonSuppressedGroupSummary() throws Exception {
         when(mPowerManager.isScreenOn()).thenReturn(true);
         when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
-        when(mNotificationData.shouldSuppressStatusBar(any())).thenReturn(false);
-        when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+        when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
         when(mDreamManager.isDreaming()).thenReturn(false);
-        when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
-        when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
+        when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
 
         Notification n = new Notification.Builder(getContext(), "a")
                 .setGroup("a")
@@ -374,20 +409,19 @@
                 .build();
         StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
                 UserHandle.of(0), null, 0);
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        NotificationEntry entry = new NotificationEntry(sbn);
+        entry.importance = IMPORTANCE_HIGH;
 
-        assertTrue(mEntryManager.shouldHeadsUp(entry));
+        assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
     }
 
     @Test
     public void testShouldHeadsUp_suppressedGroupSummary() throws Exception {
         when(mPowerManager.isScreenOn()).thenReturn(true);
         when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
-        when(mNotificationData.shouldSuppressStatusBar(any())).thenReturn(false);
-        when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+        when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
         when(mDreamManager.isDreaming()).thenReturn(false);
-        when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
-        when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
+        when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
 
         Notification n = new Notification.Builder(getContext(), "a")
                 .setGroup("a")
@@ -396,47 +430,45 @@
                 .build();
         StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
                 UserHandle.of(0), null, 0);
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        NotificationEntry entry = new NotificationEntry(sbn);
+        entry.importance = IMPORTANCE_HIGH;
 
-        assertFalse(mEntryManager.shouldHeadsUp(entry));
+        assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
     }
 
     @Test
     public void testShouldHeadsUp_suppressedHeadsUp() throws Exception {
         when(mPowerManager.isScreenOn()).thenReturn(true);
         when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
-        when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+        when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
         when(mDreamManager.isDreaming()).thenReturn(false);
-        when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
-        when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
-
-        when(mNotificationData.shouldSuppressPeek(any())).thenReturn(true);
+        when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
 
         Notification n = new Notification.Builder(getContext(), "a").build();
         StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
                 UserHandle.of(0), null, 0);
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        NotificationEntry entry = new NotificationEntry(sbn);
+        entry.suppressedVisualEffects = SUPPRESSED_EFFECT_PEEK;
+        entry.importance = IMPORTANCE_HIGH;
 
-        assertFalse(mEntryManager.shouldHeadsUp(entry));
+        assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
     }
 
     @Test
     public void testShouldHeadsUp_noSuppressedHeadsUp() throws Exception {
         when(mPowerManager.isScreenOn()).thenReturn(true);
         when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
-        when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+        when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
         when(mDreamManager.isDreaming()).thenReturn(false);
-        when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
-        when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
-
-        when(mNotificationData.shouldSuppressPeek(any())).thenReturn(false);
+        when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
 
         Notification n = new Notification.Builder(getContext(), "a").build();
         StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
                 UserHandle.of(0), null, 0);
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        NotificationEntry entry = new NotificationEntry(sbn);
+        entry.importance = IMPORTANCE_HIGH;
 
-        assertTrue(mEntryManager.shouldHeadsUp(entry));
+        assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
     }
 
     @Test
@@ -516,7 +548,7 @@
         when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
 
         when(mCommandQueue.panelsEnabled()).thenReturn(false);
-        mStatusBar.disable(StatusBarManager.DISABLE_NONE,
+        mStatusBar.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
                 StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false);
         verify(mNotificationPanelView).setQsExpansionEnabled(false);
         mStatusBar.animateExpandNotificationsPanel();
@@ -525,7 +557,8 @@
         verify(mNotificationPanelView, never()).expand(anyBoolean());
 
         when(mCommandQueue.panelsEnabled()).thenReturn(true);
-        mStatusBar.disable(StatusBarManager.DISABLE_NONE, StatusBarManager.DISABLE2_NONE, false);
+        mStatusBar.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
+                StatusBarManager.DISABLE2_NONE, false);
         verify(mNotificationPanelView).setQsExpansionEnabled(true);
         mStatusBar.animateExpandNotificationsPanel();
         verify(mNotificationPanelView).expandWithoutQs();
@@ -667,7 +700,9 @@
                 NotificationLockscreenUserManager notificationLockscreenUserManager,
                 CommandQueue commandQueue,
                 NotificationPresenter notificationPresenter,
-                BubbleController bubbleController) {
+                BubbleController bubbleController,
+                NavigationBarController navBarController,
+                AutoHideController autoHideController) {
             mStatusBarKeyguardViewManager = man;
             mUnlockMethodCache = unlock;
             mKeyguardIndicationController = key;
@@ -698,6 +733,8 @@
             mPresenter = notificationPresenter;
             mGestureWakeLock = mock(PowerManager.WakeLock.class);
             mBubbleController = bubbleController;
+            mNavigationBarController = navBarController;
+            mAutoHideController = autoHideController;
         }
 
         private WakefulnessLifecycle createAwakeWakefulnessLifecycle() {
@@ -724,20 +761,27 @@
 
     public static class TestableNotificationEntryManager extends NotificationEntryManager {
 
-        public TestableNotificationEntryManager(IDreamManager dreamManager,
-                PowerManager powerManager, Context context) {
+        public TestableNotificationEntryManager(Context context) {
             super(context);
-            mDreamManager = dreamManager;
-            mPowerManager = powerManager;
         }
 
         public void setUpForTest(NotificationPresenter presenter,
                 NotificationListContainer listContainer,
-                Callback callback,
                 HeadsUpManagerPhone headsUpManager,
                 NotificationData notificationData) {
-            super.setUpWithPresenter(presenter, listContainer, callback, headsUpManager);
+            super.setUpWithPresenter(presenter, listContainer, headsUpManager);
             mNotificationData = notificationData;
+        }
+    }
+
+    public static class TestableNotificationInterruptionStateProvider extends
+            NotificationInterruptionStateProvider {
+
+        public TestableNotificationInterruptionStateProvider(
+                Context context,
+                PowerManager powerManager,
+                IDreamManager dreamManager) {
+            super(context, powerManager, dreamManager);
             mUseHeadsUp = true;
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
index d54c295..e626d08 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.content.Intent;
@@ -26,6 +27,7 @@
 import android.testing.TestableLooper;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.power.EnhancedEstimates;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -47,7 +49,8 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mBatteryController = new BatteryControllerImpl(getContext(), mPowerManager);
+        mBatteryController = new BatteryControllerImpl(getContext(), mock(EnhancedEstimates.class),
+                mPowerManager);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
index d246350..b65b0f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
@@ -74,7 +74,8 @@
                 .thenReturn(mock(LocalBluetoothProfileManager.class));
 
         mBluetoothControllerImpl = new BluetoothControllerImpl(mContext,
-                mTestableLooper.getLooper());
+                mTestableLooper.getLooper(),
+                mMockBluetoothManager);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
index 1cceefa..2553ac1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
@@ -28,7 +28,6 @@
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
-import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.OverlayPlugin;
 import com.android.systemui.plugins.Plugin;
@@ -39,6 +38,7 @@
 import com.android.systemui.statusbar.policy.ExtensionController.TunerFactory;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
+import com.android.systemui.util.leak.LeakDetector;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -62,7 +62,8 @@
         mPluginManager = mDependency.injectMockDependency(PluginManager.class);
         mTunerService = mDependency.injectMockDependency(TunerService.class);
         mConfigurationController = mDependency.injectMockDependency(ConfigurationController.class);
-        mExtensionController = Dependency.get(ExtensionController.class);
+        mExtensionController = new ExtensionControllerImpl(mContext,
+                mock(LeakDetector.class), mPluginManager, mTunerService, mConfigurationController);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 35f0dba..fdbf090 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -212,6 +212,11 @@
             NetworkCapabilities.TRANSPORT_CELLULAR, true, true);
     }
 
+    public void setupDefaultNr5GIconConfiguration() {
+        NetworkControllerImpl.Config.add5GIconMapping("connected_mmwave:5g_plus", mConfig);
+        NetworkControllerImpl.Config.add5GIconMapping("connected:5g", mConfig);
+    }
+
     public void setConnectivityViaBroadcast(
         int networkType, boolean validated, boolean isConnected) {
         setConnectivityCommon(networkType, validated, isConnected);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index d42940a..2baea1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -1,11 +1,14 @@
 package com.android.systemui.statusbar.policy;
 
 import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.net.NetworkCapabilities;
 import android.os.Looper;
+import android.telephony.NetworkRegistrationState;
+import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -16,6 +19,7 @@
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mockito;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -141,6 +145,47 @@
     }
 
     @Test
+    public void testNr5GIcon_NrConnectedWithoutMMWave_show5GIcon() {
+        setupDefaultNr5GIconConfiguration();
+        setupDefaultSignal();
+        updateDataConnectionState(TelephonyManager.DATA_CONNECTED,
+                TelephonyManager.NETWORK_TYPE_LTE);
+        ServiceState ss = Mockito.mock(ServiceState.class);
+        doReturn(NetworkRegistrationState.NR_STATUS_CONNECTED).when(ss).getNrStatus();
+        doReturn(ServiceState.FREQUENCY_RANGE_HIGH).when(ss).getNrFrequencyRange();
+        mPhoneStateListener.onServiceStateChanged(ss);
+
+        verifyDataIndicators(TelephonyIcons.ICON_5G);
+    }
+
+    @Test
+    public void testNr5GIcon_NrConnectedWithMMWave_show5GPlusIcon() {
+        setupDefaultNr5GIconConfiguration();
+        setupDefaultSignal();
+        updateDataConnectionState(TelephonyManager.DATA_CONNECTED,
+                TelephonyManager.NETWORK_TYPE_LTE);
+        ServiceState ss = Mockito.mock(ServiceState.class);
+        doReturn(NetworkRegistrationState.NR_STATUS_CONNECTED).when(ss).getNrStatus();
+        doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(ss).getNrFrequencyRange();
+        mPhoneStateListener.onServiceStateChanged(ss);
+
+        verifyDataIndicators(TelephonyIcons.ICON_5G_PLUS);
+    }
+
+    @Test
+    public void testNr5GIcon_NrRestricted_showLteIcon() {
+        setupDefaultNr5GIconConfiguration();
+        setupDefaultSignal();
+        updateDataConnectionState(TelephonyManager.DATA_CONNECTED,
+                TelephonyManager.NETWORK_TYPE_LTE);
+        ServiceState ss = Mockito.mock(ServiceState.class);
+        doReturn(NetworkRegistrationState.NR_STATUS_RESTRICTED).when(ss).getNrStatus();
+        mPhoneStateListener.onServiceStateChanged(mServiceState);
+
+        verifyDataIndicators(TelephonyIcons.ICON_LTE);
+    }
+
+    @Test
     public void testDataDisabledIcon_UserNotSetup() {
         setupNetworkController();
         when(mMockTm.getDataEnabled(mSubId)).thenReturn(false);
@@ -222,5 +267,4 @@
                 true, DEFAULT_QS_SIGNAL_STRENGTH, dataIcon, false,
                 false);
     }
-
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java
index 3b47eae..8b8e3f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java
@@ -14,11 +14,11 @@
 
 package com.android.systemui.statusbar.policy;
 
-import static com.google.common.truth.Truth.assertThat;
-
 import static junit.framework.TestCase.assertTrue;
+
 import static org.junit.Assert.assertFalse;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -29,6 +29,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -49,7 +50,8 @@
         mCommandQueue = mock(CommandQueue.class);
         mContext.putComponent(CommandQueue.class, mCommandQueue);
 
-        mRemoteInputQuickSettingsDisabler = new RemoteInputQuickSettingsDisabler(mContext);
+        mRemoteInputQuickSettingsDisabler = new RemoteInputQuickSettingsDisabler(mContext,
+                mock(ConfigurationController.class));
     }
 
     @Test
@@ -57,7 +59,7 @@
         mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.TRUE);
         mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.FALSE);
         assertFalse(mRemoteInputQuickSettingsDisabler.mRemoteInputActive);
-        verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyBoolean());
+        verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyInt(), anyBoolean());
     }
 
     @Test
@@ -65,7 +67,7 @@
         mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.FALSE);
         mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.TRUE);
         assertTrue(mRemoteInputQuickSettingsDisabler.mRemoteInputActive);
-        verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyBoolean());
+        verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyInt(), anyBoolean());
     }
 
     @Test
@@ -76,7 +78,7 @@
         c.orientation = Configuration.ORIENTATION_LANDSCAPE;
         mRemoteInputQuickSettingsDisabler.onConfigChanged(c);
         assertTrue(mRemoteInputQuickSettingsDisabler.misLandscape);
-        verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyBoolean());
+        verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyInt(), anyBoolean());
     }
 
     @Test
@@ -87,7 +89,7 @@
         c.orientation = Configuration.ORIENTATION_PORTRAIT;
         mRemoteInputQuickSettingsDisabler.onConfigChanged(c);
         assertFalse(mRemoteInputQuickSettingsDisabler.misLandscape);
-        verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyBoolean());
+        verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyInt(), anyBoolean());
     }
 
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
index f76de5a..3ac42de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
@@ -39,6 +39,8 @@
 import android.net.ConnectivityManager.NetworkCallback;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.UserManager;
 import android.security.IKeyChainService;
 import android.support.test.runner.AndroidJUnit4;
@@ -98,7 +100,10 @@
         // Wait for callbacks from 1) the CACertLoader and 2) the onUserSwitched() function in the
         // constructor of mSecurityController
         mStateChangedLatch = new CountDownLatch(2);
-        mSecurityController = new SecurityControllerImpl(mContext, this);
+        // TODO: Migrate this test to TestableLooper and use a handler attached
+        // to that.
+        mSecurityController = new SecurityControllerImpl(mContext,
+                new Handler(Looper.getMainLooper()), this);
     }
 
     @After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index c5bac92..fdc8ef1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -50,7 +50,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.statusbar.SmartReplyController;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.statusbar.phone.ShadeController;
 
@@ -94,7 +94,7 @@
     private int mSpacing;
 
     @Mock private SmartReplyController mLogger;
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
     private Notification mNotification;
 
     @Mock ActivityStarter mActivityStarter;
@@ -127,7 +127,7 @@
         StatusBarNotification sbn = mock(StatusBarNotification.class);
         when(sbn.getNotification()).thenReturn(mNotification);
         when(sbn.getKey()).thenReturn(TEST_NOTIFICATION_KEY);
-        mEntry = new NotificationData.Entry(sbn);
+        mEntry = new NotificationEntry(sbn);
 
         mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
index 7437e834..8f5f072 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
@@ -89,20 +89,22 @@
 
     @Test
     public void testAreNotificationsHiddenInShade_zenOnShadeNotSuppressed() {
-        mConfig.suppressedVisualEffects =
-                NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+        NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0,
+                NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR);
+        when(mNm.getConsolidatedNotificationPolicy()).thenReturn(policy);
+        mController.updateConsolidatedNotificationPolicy();
         mController.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
-        mController.updateZenModeConfig();
 
         assertFalse(mController.areNotificationsHiddenInShade());
     }
 
     @Test
     public void testAreNotificationsHiddenInShade_zenOnShadeSuppressed() {
-        mConfig.suppressedVisualEffects =
-                NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
+        NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0,
+                NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST);
+        when(mNm.getConsolidatedNotificationPolicy()).thenReturn(policy);
+        mController.updateConsolidatedNotificationPolicy();
         mController.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
-        mController.updateZenModeConfig();
 
         assertTrue(mController.areNotificationsHiddenInShade());
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/tuner/TunablePaddingTest.java b/packages/SystemUI/tests/src/com/android/systemui/tuner/TunablePaddingTest.java
index 3bfefe7..1e27915 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/tuner/TunablePaddingTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/tuner/TunablePaddingTest.java
@@ -48,7 +48,9 @@
         mView = mock(View.class);
         when(mView.getContext()).thenReturn(mContext);
 
-        mTunerService = mDependency.injectMockDependency(TunerService.class);
+        mTunerService = mock(TunerService.class);
+        mDependency.injectTestDependency(TunablePadding.TunablePaddingService.class,
+                new TunablePadding.TunablePaddingService(mTunerService));
         Tracker tracker = mLeakCheck.getTracker("tuner");
         doAnswer(invocation -> {
             tracker.getLeakInfo(invocation.getArguments()[0]).addAllocation(new Throwable());
@@ -118,4 +120,4 @@
 
         mTunablePadding.destroy();
     }
-}
\ No newline at end of file
+}
diff --git a/packages/overlays/AccentColorBlackOverlay/Android.mk b/packages/overlays/AccentColorBlackOverlay/Android.mk
index d316fbd..b81ae5bb 100644
--- a/packages/overlays/AccentColorBlackOverlay/Android.mk
+++ b/packages/overlays/AccentColorBlackOverlay/Android.mk
@@ -19,6 +19,7 @@
 
 LOCAL_RRO_THEME := AccentColorBlack
 LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
diff --git a/packages/overlays/AccentColorBlackOverlay/res/values/strings.xml b/packages/overlays/AccentColorBlackOverlay/res/values/strings.xml
index baf09b1..da10361 100644
--- a/packages/overlays/AccentColorBlackOverlay/res/values/strings.xml
+++ b/packages/overlays/AccentColorBlackOverlay/res/values/strings.xml
@@ -18,6 +18,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Black accent color name application label. [CHAR LIMIT=50] -->
-    <string name="accent_color_black_overlay" translatable="false">Black Accent Color</string>
+    <string name="accent_color_black_overlay" translatable="false">Black</string>
 </resources>
 
diff --git a/packages/overlays/AccentColorGreenOverlay/Android.mk b/packages/overlays/AccentColorGreenOverlay/Android.mk
index afc4287..db92157 100644
--- a/packages/overlays/AccentColorGreenOverlay/Android.mk
+++ b/packages/overlays/AccentColorGreenOverlay/Android.mk
@@ -19,6 +19,7 @@
 
 LOCAL_RRO_THEME := AccentColorGreen
 LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
diff --git a/packages/overlays/AccentColorGreenOverlay/res/values/strings.xml b/packages/overlays/AccentColorGreenOverlay/res/values/strings.xml
index 4de344c..623a1da 100644
--- a/packages/overlays/AccentColorGreenOverlay/res/values/strings.xml
+++ b/packages/overlays/AccentColorGreenOverlay/res/values/strings.xml
@@ -18,6 +18,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Green accent color name application label. [CHAR LIMIT=50] -->
-    <string name="accent_color_green_overlay" translatable="false">Green Accent Color</string>
+    <string name="accent_color_green_overlay" translatable="false">Green</string>
 </resources>
 
diff --git a/packages/overlays/AccentColorPurpleOverlay/Android.mk b/packages/overlays/AccentColorPurpleOverlay/Android.mk
index 3366169..d7dc497 100644
--- a/packages/overlays/AccentColorPurpleOverlay/Android.mk
+++ b/packages/overlays/AccentColorPurpleOverlay/Android.mk
@@ -19,6 +19,7 @@
 
 LOCAL_RRO_THEME := AccentColorPurple
 LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
diff --git a/packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml b/packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml
index d1eb95a..d1c7168 100644
--- a/packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml
+++ b/packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml
@@ -18,6 +18,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Purple accent color name application label. [CHAR LIMIT=50] -->
-    <string name="accent_color_purple_overlay" translatable="false">Purple Accent Color</string>
+    <string name="accent_color_purple_overlay" translatable="false">Purple</string>
 </resources>
 
diff --git a/packages/overlays/CleanSpec.mk b/packages/overlays/CleanSpec.mk
new file mode 100644
index 0000000..16fbaa20
--- /dev/null
+++ b/packages/overlays/CleanSpec.mk
@@ -0,0 +1,53 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list.  These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list.  E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/AccentColor*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/DisplayCutout*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/IconShape*)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk
index 74c43b4..bf2b631 100644
--- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk
@@ -4,6 +4,8 @@
 LOCAL_RRO_THEME := DisplayCutoutEmulationCorner
 LOCAL_CERTIFICATE := platform
 
+LOCAL_PRODUCT_MODULE := true
+
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk
index d83b30a..7042906 100644
--- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk
@@ -4,6 +4,8 @@
 LOCAL_RRO_THEME := DisplayCutoutEmulationDouble
 LOCAL_CERTIFICATE := platform
 
+LOCAL_PRODUCT_MODULE := true
+
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
index f5afad2..ae69e11 100644
--- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
@@ -4,6 +4,8 @@
 LOCAL_RRO_THEME := DisplayCutoutEmulationNarrow
 LOCAL_CERTIFICATE := platform
 
+LOCAL_PRODUCT_MODULE := true
+
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
index f1f8c27..7dcadfb 100644
--- a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
@@ -4,6 +4,8 @@
 LOCAL_RRO_THEME := DisplayCutoutEmulationTall
 LOCAL_CERTIFICATE := platform
 
+LOCAL_PRODUCT_MODULE := true
+
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
index d149d8e..3f7be73 100644
--- a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
@@ -4,6 +4,8 @@
 LOCAL_RRO_THEME := DisplayCutoutEmulationWide
 LOCAL_CERTIFICATE := platform
 
+LOCAL_PRODUCT_MODULE := true
+
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/packages/overlays/FontArbutusSourceOverlay/Android.mk b/packages/overlays/FontArbutusSourceOverlay/Android.mk
new file mode 100644
index 0000000..23aee55
--- /dev/null
+++ b/packages/overlays/FontArbutusSourceOverlay/Android.mk
@@ -0,0 +1,31 @@
+#
+#  Copyright 2018, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := FontArbutusSource
+LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := FontArbutusSourceOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/FontArbutusSourceOverlay/AndroidManifest.xml b/packages/overlays/FontArbutusSourceOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..46c06b9
--- /dev/null
+++ b/packages/overlays/FontArbutusSourceOverlay/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<!--
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.theme.font.arbutussource"
+    android:versionCode="1"
+    android:versionName="1.0">
+    <overlay android:targetPackage="android"
+        android:category="android.theme.customization.font"
+        android:priority="1"/>
+
+    <application android:label="@string/font_arbutus_source_overlay" android:hasCode="false">
+        <meta-data
+            android:name="android.theme.customization.REQUIRED_SYSTEM_FONTS"
+            android:value="arbutus-slab,source-sans-pro,source-sans-pro-medium" />
+    </application>
+</manifest>
diff --git a/packages/overlays/FontArbutusSourceOverlay/res/values/config.xml b/packages/overlays/FontArbutusSourceOverlay/res/values/config.xml
new file mode 100644
index 0000000..165aab2
--- /dev/null
+++ b/packages/overlays/FontArbutusSourceOverlay/res/values/config.xml
@@ -0,0 +1,28 @@
+<!--
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+     <!-- Name of a font family to use for body text. -->
+    <string name="config_bodyFontFamily" translatable="false">source-sans-pro</string>
+    <!-- Name of a font family to use for medium body text. -->
+    <string name="config_bodyFontFamilyMedium" translatable="false">source-sans-pro-medium</string>
+    <!-- Name of a font family to use for headlines. If empty, falls back to platform default -->
+    <string name="config_headlineFontFamily" translatable="false">arbutus-slab</string>
+    <!-- Name of the font family used for system surfaces where the font should use medium weight -->
+    <string name="config_headlineFontFamilyMedium" translatable="false">arbutus-slab</string>
+</resources>
+
diff --git a/core/java/android/service/contentcapture/InteractionContext.aidl b/packages/overlays/FontArbutusSourceOverlay/res/values/strings.xml
similarity index 69%
copy from core/java/android/service/contentcapture/InteractionContext.aidl
copy to packages/overlays/FontArbutusSourceOverlay/res/values/strings.xml
index 982e095..d80eb20 100644
--- a/core/java/android/service/contentcapture/InteractionContext.aidl
+++ b/packages/overlays/FontArbutusSourceOverlay/res/values/strings.xml
@@ -1,3 +1,4 @@
+<!--
 /**
  * Copyright (c) 2018, The Android Open Source Project
  *
@@ -13,7 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package android.service.contentcapture;
-
-parcelable InteractionContext;
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Headline / Body font Arbutus Slab / Source Sans Pro overlay -->
+    <string name="font_arbutus_source_overlay" translatable="false">Arbutus Slab / Source Sans Pro</string>
+</resources>
diff --git a/packages/overlays/FontArvoLatoOverlay/Android.mk b/packages/overlays/FontArvoLatoOverlay/Android.mk
new file mode 100644
index 0000000..3433ecf
--- /dev/null
+++ b/packages/overlays/FontArvoLatoOverlay/Android.mk
@@ -0,0 +1,31 @@
+#
+#  Copyright 2018, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := FontArvoLato
+LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := FontArvoLatoOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/FontArvoLatoOverlay/AndroidManifest.xml b/packages/overlays/FontArvoLatoOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..3f2e5de
--- /dev/null
+++ b/packages/overlays/FontArvoLatoOverlay/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<!--
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.theme.font.arvolato"
+    android:versionCode="1"
+    android:versionName="1.0">
+    <overlay android:targetPackage="android"
+        android:category="android.theme.customization.font"
+        android:priority="1"/>
+
+    <application android:label="@string/font_arvo_lato_overlay" android:hasCode="false">
+        <meta-data
+            android:name="android.theme.customization.REQUIRED_SYSTEM_FONTS"
+            android:value="arvo,arvo-medium,lato,lato-medium" />
+    </application>
+</manifest>
diff --git a/packages/overlays/FontArvoLatoOverlay/res/values/config.xml b/packages/overlays/FontArvoLatoOverlay/res/values/config.xml
new file mode 100644
index 0000000..229c578
--- /dev/null
+++ b/packages/overlays/FontArvoLatoOverlay/res/values/config.xml
@@ -0,0 +1,28 @@
+<!--
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+     <!-- Name of a font family to use for body text. -->
+    <string name="config_bodyFontFamily" translatable="false">lato</string>
+    <!-- Name of a font family to use for medium body text. -->
+    <string name="config_bodyFontFamilyMedium" translatable="false">lato-medium</string>
+    <!-- Name of a font family to use for headlines. If empty, falls back to platform default -->
+    <string name="config_headlineFontFamily" translatable="false">arvo</string>
+    <!-- Name of the font family used for system surfaces where the font should use medium weight -->
+    <string name="config_headlineFontFamilyMedium" translatable="false">arvo-medium</string>
+</resources>
+
diff --git a/core/java/android/service/contentcapture/InteractionContext.aidl b/packages/overlays/FontArvoLatoOverlay/res/values/strings.xml
similarity index 72%
copy from core/java/android/service/contentcapture/InteractionContext.aidl
copy to packages/overlays/FontArvoLatoOverlay/res/values/strings.xml
index 982e095..9ea097f 100644
--- a/core/java/android/service/contentcapture/InteractionContext.aidl
+++ b/packages/overlays/FontArvoLatoOverlay/res/values/strings.xml
@@ -1,3 +1,4 @@
+<!--
 /**
  * Copyright (c) 2018, The Android Open Source Project
  *
@@ -13,7 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package android.service.contentcapture;
-
-parcelable InteractionContext;
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Headline / Body font Arvo / Lato overlay -->
+    <string name="font_arvo_lato_overlay" translatable="false">Arvo / Lato</string>
+</resources>
diff --git a/packages/overlays/FontRubikRubikOverlay/Android.mk b/packages/overlays/FontRubikRubikOverlay/Android.mk
new file mode 100644
index 0000000..21d617e
--- /dev/null
+++ b/packages/overlays/FontRubikRubikOverlay/Android.mk
@@ -0,0 +1,31 @@
+#
+#  Copyright 2018, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := FontRubikRubik
+LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := FontRubikRubikOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/FontRubikRubikOverlay/AndroidManifest.xml b/packages/overlays/FontRubikRubikOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..1f28d46
--- /dev/null
+++ b/packages/overlays/FontRubikRubikOverlay/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<!--
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.theme.font.rubikrubik"
+    android:versionCode="1"
+    android:versionName="1.0">
+    <overlay android:targetPackage="android"
+        android:category="android.theme.customization.font"
+        android:priority="1"/>
+
+    <application android:label="@string/font_rubik_rubik_overlay" android:hasCode="false">
+        <meta-data
+            android:name="android.theme.customization.REQUIRED_SYSTEM_FONTS"
+            android:value="rubik,rubik-medium" />
+    </application>
+</manifest>
diff --git a/packages/overlays/FontRubikRubikOverlay/res/values/config.xml b/packages/overlays/FontRubikRubikOverlay/res/values/config.xml
new file mode 100644
index 0000000..4f90e29
--- /dev/null
+++ b/packages/overlays/FontRubikRubikOverlay/res/values/config.xml
@@ -0,0 +1,28 @@
+<!--
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+     <!-- Name of a font family to use for body text. -->
+    <string name="config_bodyFontFamily" translatable="false">rubik</string>
+    <!-- Name of a font family to use for medium body text. -->
+    <string name="config_bodyFontFamilyMedium" translatable="false">rubik-medium</string>
+    <!-- Name of a font family to use for headlines. If empty, falls back to platform default -->
+    <string name="config_headlineFontFamily" translatable="false">rubik</string>
+    <!-- Name of the font family used for system surfaces where the font should use medium weight -->
+    <string name="config_headlineFontFamilyMedium" translatable="false">rubik-medium</string>
+</resources>
+
diff --git a/core/java/android/service/contentcapture/InteractionContext.aidl b/packages/overlays/FontRubikRubikOverlay/res/values/strings.xml
similarity index 72%
copy from core/java/android/service/contentcapture/InteractionContext.aidl
copy to packages/overlays/FontRubikRubikOverlay/res/values/strings.xml
index 982e095..4bac7da 100644
--- a/core/java/android/service/contentcapture/InteractionContext.aidl
+++ b/packages/overlays/FontRubikRubikOverlay/res/values/strings.xml
@@ -1,3 +1,4 @@
+<!--
 /**
  * Copyright (c) 2018, The Android Open Source Project
  *
@@ -13,7 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package android.service.contentcapture;
-
-parcelable InteractionContext;
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Headline / Body font Rubik overlay -->
+    <string name="font_rubik_rubik_overlay" translatable="false">Rubik / Rubik</string>
+</resources>
diff --git a/packages/overlays/IconShapeRoundedRectOverlay/Android.mk b/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
index a734a6b..08428d1 100644
--- a/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
+++ b/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
@@ -19,6 +19,7 @@
 
 LOCAL_RRO_THEME := IconShapeRoundedRect
 LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
diff --git a/packages/overlays/IconShapeRoundedRectOverlay/res/values/config.xml b/packages/overlays/IconShapeRoundedRectOverlay/res/values/config.xml
index f024615..138c283 100644
--- a/packages/overlays/IconShapeRoundedRectOverlay/res/values/config.xml
+++ b/packages/overlays/IconShapeRoundedRectOverlay/res/values/config.xml
@@ -18,7 +18,7 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
-    <string name="config_icon_mask" translatable="false">"M50,0L92,0C96.42,0 100,4.58 100 8L100,92C100, 96.42 96.42 100 92 100L8 100C4.58, 100 0 96.42 0 92L0 8 C 0 4.42 4.42 0 8 0L50 0Z"</string>
+    <string name="config_icon_mask" translatable="false">"M50,0L88,0 C94.4,0 100,5.4 100 12 L100,88 C100,94.6 94.6 100 88 100 L12,100 C5.4,100 0,94.6 0,88 L0 12 C0 5.4 5.4 0 12 0 L50,0 Z"</string>
     <!-- Flag indicating whether round icons should be parsed from the application manifest. -->
     <bool name="config_useRoundIcon">false</bool>
 
diff --git a/packages/overlays/IconShapeRoundedRectOverlay/res/values/strings.xml b/packages/overlays/IconShapeRoundedRectOverlay/res/values/strings.xml
index dc5c196..3c4c24d 100644
--- a/packages/overlays/IconShapeRoundedRectOverlay/res/values/strings.xml
+++ b/packages/overlays/IconShapeRoundedRectOverlay/res/values/strings.xml
@@ -18,6 +18,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Rounded corner rectangle overlay -->
-    <string name="icon_shape_roundedrect_overlay" translatable="false">RoundedRectangle Icons</string>
+    <string name="icon_shape_roundedrect_overlay" translatable="false">Rounded Rectangle</string>
 
 </resources>
diff --git a/packages/overlays/IconShapeSquareOverlay/Android.mk b/packages/overlays/IconShapeSquareOverlay/Android.mk
index 217da9f..ceb745a 100644
--- a/packages/overlays/IconShapeSquareOverlay/Android.mk
+++ b/packages/overlays/IconShapeSquareOverlay/Android.mk
@@ -19,6 +19,7 @@
 
 LOCAL_RRO_THEME := IconShapeSquare
 LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
diff --git a/packages/overlays/IconShapeSquareOverlay/res/values/strings.xml b/packages/overlays/IconShapeSquareOverlay/res/values/strings.xml
index 4fd39ff26..5772165 100644
--- a/packages/overlays/IconShapeSquareOverlay/res/values/strings.xml
+++ b/packages/overlays/IconShapeSquareOverlay/res/values/strings.xml
@@ -18,6 +18,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Square icon overlay -->
-    <string name="icon_shape_square_overlay" translatable="false">Square Icons</string>
+    <string name="icon_shape_square_overlay" translatable="false">Square</string>
 
 </resources>
diff --git a/packages/overlays/IconShapeSquircleOverlay/Android.mk b/packages/overlays/IconShapeSquircleOverlay/Android.mk
index fd3bfa0..34edc3b 100644
--- a/packages/overlays/IconShapeSquircleOverlay/Android.mk
+++ b/packages/overlays/IconShapeSquircleOverlay/Android.mk
@@ -19,6 +19,7 @@
 
 LOCAL_RRO_THEME := IconShapeSquircle
 LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
diff --git a/packages/overlays/IconShapeSquircleOverlay/res/values/strings.xml b/packages/overlays/IconShapeSquircleOverlay/res/values/strings.xml
index b7c001c..028eccb 100644
--- a/packages/overlays/IconShapeSquircleOverlay/res/values/strings.xml
+++ b/packages/overlays/IconShapeSquircleOverlay/res/values/strings.xml
@@ -18,6 +18,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Squircle icon shape overlay -->
-    <string name="icon_shape_squircle_overlay" translatable="false">Square Icons</string>
+    <string name="icon_shape_squircle_overlay" translatable="false">Squircle</string>
 
 </resources>
diff --git a/packages/overlays/IconShapeTeardropOverlay/Android.mk b/packages/overlays/IconShapeTeardropOverlay/Android.mk
index ea43423..834a1c3 100644
--- a/packages/overlays/IconShapeTeardropOverlay/Android.mk
+++ b/packages/overlays/IconShapeTeardropOverlay/Android.mk
@@ -19,6 +19,7 @@
 
 LOCAL_RRO_THEME := IconShapeTeardrop
 LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
diff --git a/packages/overlays/IconShapeTeardropOverlay/res/values/config.xml b/packages/overlays/IconShapeTeardropOverlay/res/values/config.xml
index 43ad04d..818e696 100644
--- a/packages/overlays/IconShapeTeardropOverlay/res/values/config.xml
+++ b/packages/overlays/IconShapeTeardropOverlay/res/values/config.xml
@@ -18,7 +18,7 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
-    <string name="config_icon_mask" translatable="false">"M50,0A50,50,0,0 1 100,50 L100,85 A15,15,0,0 1 85,100 L50,100 A50,50,0,0 1 50,0z"</string>
+    <string name="config_icon_mask" translatable="false">"M50,0 C77.6,0 100,22.4 100,50 L100,88 C100,94.6 94.6,100 88,100 L50,100 C22.4 100 0 77.6 0 50C0 22.4 22.4 0 50 0 Z"</string>
     <!-- Flag indicating whether round icons should be parsed from the application manifest. -->
     <bool name="config_useRoundIcon">false</bool>
 
diff --git a/packages/overlays/IconShapeTeardropOverlay/res/values/strings.xml b/packages/overlays/IconShapeTeardropOverlay/res/values/strings.xml
index d946ee8..db9fa98 100644
--- a/packages/overlays/IconShapeTeardropOverlay/res/values/strings.xml
+++ b/packages/overlays/IconShapeTeardropOverlay/res/values/strings.xml
@@ -18,6 +18,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Teardrop icon overlay -->
-    <string name="icon_shape_teardrop_overlay" translatable="false">Teardrop Icons</string>
+    <string name="icon_shape_teardrop_overlay" translatable="false">Teardrop</string>
 
 </resources>
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index eb0090b..2b399de 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -3684,7 +3684,7 @@
     ACTION_SETTINGS_TILE_CLICK = 830;
 
     // OPEN: Notification unsnoozed. CLOSE: Notification snoozed. UPDATE: snoozed notification
-    // updated
+    // updated. TYPE_DISMISS: snoozing canceled due to data being cleared on package
     // CATEGORY: NOTIFICATION
     // OS: O
     NOTIFICATION_SNOOZED = 831;
@@ -6642,6 +6642,99 @@
     // OS: Q
     SETTINGS_FINANCIAL_APPS_SMS_ACCESS = 1597;
 
+    // OPEN: QS Sensor Privacy Mode tile shown
+    // ACTION: QS Sensor Privacy Mode tile tapped
+    // SUBTYPE: 0 is off, 1 is on
+    // CATEGORY: QUICK_SETTINGS
+    // OS: Q
+    QS_SENSOR_PRIVACY = 1598;
+
+    // Tagged data for SMART_REPLY_VISIBLE. Count of number of smart actions.
+    // OS: Q
+    NOTIFICATION_SMART_ACTION_COUNT = 1599;
+
+    // Tagged data for SMART_REPLY_VISIBLE and NOTIFICATION_ITEM_ACTION.
+    // Whether the notification has notification-assistant generated
+    // actions/replies.
+    // OS: Q
+    NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED = 1600;
+
+    // Tagged data for NOTIFICATION_ITEM_ACTION. Whether the action is a smart
+    // action.
+    // OS: Q
+    NOTIFICATION_ACTION_IS_SMART = 1601;
+
+    // FIELD: true if the associated ACTION_ZEN_ALLOW_* or ACTION_ZEN_BLOCK_* allows/blocks
+    // the effect/sound when DND is on.  false if set to disallow/show.
+    // OS: Q
+    FIELD_ZEN_TOGGLE_EXCEPTION = 1602;
+
+    // FIELD: rule id an ACTION_ZEN_ALLOW_* or ACTION_ZEN_BLOCK_* is associated with
+    // OS: Q
+    FIELD_ZEN_RULE_ID = 1603;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_SETTINGS = 1604;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior > Custom
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_SOUND_SETTINGS = 1605;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior > Use default Do Not Disturb behavior
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_DEFAULT_SETTINGS = 1606;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior > Custom
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_CUSTOM_SETTINGS = 1607;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior > Use default Do Not Disturb behavior
+    // > Notification restriction
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_NOTIFICATION_RESTRICTIONS = 1608;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior > Use default Do Not Disturb behavior
+    // > Notification restriction > Custom
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_VIS_EFFECTS = 1609;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior > Use default Do Not Disturb behavior
+    // > Notification restriction > Custom > Allow messages
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_MESSAGES = 1610;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+    // > Do Not Disturb behavior > Use default Do Not Disturb behavior
+    // > Notification restriction > Custom > Allow calls
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_RULE_CALLS = 1611;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Click footer link if custom settings applied
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ZEN_CUSTOM_SETTINGS_DIALOG = 1612;
+
+    // OPEN: Settings > Developer Options > Game Update Packages
+    // CATEGORY: SETTINGS
+    // OS: Q
+    SETTINGS_GUP_DASHBOARD = 1613;
+
     // ---- End Q Constants, all Q constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/services/Android.bp b/services/Android.bp
index 58a0997..01734f4 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -28,6 +28,7 @@
         "services.net",
         "services.print",
         "services.restrictions",
+        "services.startop",
         "services.usage",
         "services.usb",
         "services.voiceinteraction",
diff --git a/services/accessibility/TEST_MAPPING b/services/accessibility/TEST_MAPPING
index 320924c..d90c3bd 100644
--- a/services/accessibility/TEST_MAPPING
+++ b/services/accessibility/TEST_MAPPING
@@ -7,7 +7,7 @@
           "include-annotation": "android.platform.test.annotations.Presubmit"
         },
         {
-          "exclude-annotation": "android.support.test.filters.FlakyTest"
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
     },
@@ -18,7 +18,7 @@
           "include-annotation": "android.platform.test.annotations.Presubmit"
         },
         {
-          "exclude-annotation": "android.support.test.filters.FlakyTest"
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
     },
@@ -29,7 +29,7 @@
           "include-annotation": "android.platform.test.annotations.Presubmit"
         },
         {
-          "exclude-annotation": "android.support.test.filters.FlakyTest"
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
     },
@@ -40,7 +40,7 @@
           "include-filter": "com.android.server.accessibility"
         },
         {
-          "exclude-annotation": "android.support.test.filters.FlakyTest"
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
     },
@@ -51,7 +51,7 @@
           "include-filter": "com.android.internal.accessibility"
         },
         {
-          "exclude-annotation": "android.support.test.filters.FlakyTest"
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
     },
@@ -62,7 +62,7 @@
           "include-filter": "android.view.accessibility"
         },
         {
-          "exclude-annotation": "android.support.test.filters.FlakyTest"
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
     }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 612c929..1a6bee9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -74,6 +74,7 @@
 import com.android.server.LocalServices;
 import com.android.server.autofill.ui.AutoFillUI;
 import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.SecureSettingsServiceNameResolver;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -182,7 +183,9 @@
     private int mSupportedSmartSuggestionModes;
 
     public AutofillManagerService(Context context) {
-        super(context, UserManager.DISALLOW_AUTOFILL);
+        super(context,
+                new SecureSettingsServiceNameResolver(context, Settings.Secure.AUTOFILL_SERVICE),
+                UserManager.DISALLOW_AUTOFILL);
         mUi = new AutoFillUI(ActivityThread.currentActivityThread().getSystemUiContext());
         mAm = LocalServices.getService(ActivityManagerInternal.class);
 
@@ -523,7 +526,7 @@
         synchronized (mLock) {
             final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
             if (service != null) {
-                service.mAugmentedAutofillResolver.setTemporaryServiceLocked(serviceName,
+                service.mAugmentedAutofillResolver.setTemporaryService(userId, serviceName,
                         durationMs);
             }
         }
@@ -535,7 +538,7 @@
         synchronized (mLock) {
             final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
             if (service != null) {
-                service.mAugmentedAutofillResolver.resetTemporaryServiceLocked();
+                service.mAugmentedAutofillResolver.resetTemporaryService(userId);
             }
         }
     }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index fa62ef8..4b7efe1 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -77,7 +77,6 @@
 import com.android.server.autofill.ui.AutoFillUI;
 import com.android.server.infra.AbstractPerUserSystemService;
 import com.android.server.infra.FrameworkResourcesServiceNameResolver;
-import com.android.server.infra.SecureSettingsServiceNameResolver;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -170,8 +169,7 @@
     AutofillManagerServiceImpl(AutofillManagerService master, Object lock, LocalLog requestsHistory,
             LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
             AutofillCompatState autofillCompatState, boolean disabled) {
-        super(master, new SecureSettingsServiceNameResolver(master.getContext(), userId,
-                Settings.Secure.AUTOFILL_SERVICE), lock, userId);
+        super(master, lock, userId);
 
         mRequestsHistory = requestsHistory;
         mUiLatencyHistory = uiLatencyHistory;
@@ -181,9 +179,9 @@
         mAutofillCompatState = autofillCompatState;
 
         mAugmentedAutofillResolver = new FrameworkResourcesServiceNameResolver(master.getContext(),
-                userId, lock, com.android.internal.R.string.config_defaultAugmentedAutofillService);
+                com.android.internal.R.string.config_defaultAugmentedAutofillService);
         mAugmentedAutofillResolver.setOnTemporaryServiceNameChangedCallback(
-                () -> updateRemoteAugmentedAutofillService());
+                (u, s) -> updateRemoteAugmentedAutofillService());
 
         updateLocked(disabled);
     }
@@ -207,7 +205,7 @@
     }
 
     @Override // from PerUserSystemService
-    protected ServiceInfo newServiceInfo(@NonNull ComponentName serviceComponent)
+    protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
             throws NameNotFoundException {
         mInfo = new AutofillServiceInfo(getContext(), serviceComponent, mUserId);
         return mInfo.getServiceInfo();
@@ -873,7 +871,7 @@
         pw.print(prefix); pw.print("Default component: "); pw.println(getContext()
                 .getString(R.string.config_defaultAutofillService));
         pw.print(prefix); pw.print("mAugmentedAutofillNamer: ");
-        mAugmentedAutofillResolver.dumpShortLocked(pw); pw.println();
+        mAugmentedAutofillResolver.dumpShort(pw); pw.println();
         if (mRemoteAugmentedAutofillService != null) {
             pw.print(prefix); pw.println("RemoteAugmentedAutofillService: ");
             mRemoteAugmentedAutofillService.dump(prefix2, pw);
@@ -1022,7 +1020,7 @@
     @GuardedBy("mLock")
     @Nullable RemoteAugmentedAutofillService getRemoteAugmentedAutofillServiceLocked() {
         if (mRemoteAugmentedAutofillService == null) {
-            final String serviceName = mAugmentedAutofillResolver.getServiceNameLocked();
+            final String serviceName = mAugmentedAutofillResolver.getServiceName(mUserId);
             if (serviceName == null) {
                 if (mMaster.verbose) {
                     Slog.v(TAG, "getRemoteAugmentedAutofillServiceLocked(): not set");
@@ -1030,7 +1028,7 @@
                 return null;
             }
             final ComponentName componentName = RemoteAugmentedAutofillService.getComponentName(
-                    serviceName, mUserId, mAugmentedAutofillResolver.isTemporaryLocked());
+                    serviceName, mUserId, mAugmentedAutofillResolver.isTemporary(mUserId));
             if (componentName == null) return null;
             if (sVerbose) {
                 Slog.v(TAG, "getRemoteAugmentedAutofillServiceLocked(): " + componentName);
@@ -1053,7 +1051,7 @@
      * Called when the {@link #mAugmentedAutofillResolver} changed (among other places).
      */
     private void updateRemoteAugmentedAutofillService() {
-        final String serviceName = mAugmentedAutofillResolver.getServiceNameLocked();
+        final String serviceName = mAugmentedAutofillResolver.getServiceName(mUserId);
         if (serviceName == null) {
             if (sVerbose) Slog.v(TAG, "updateRemoteAugmentedAutofillService(): time's up!");
             if (mRemoteAugmentedAutofillService != null) {
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index fc7265d..239a386 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -38,8 +38,8 @@
 import android.view.autofill.AutofillValue;
 import android.view.autofill.IAutoFillManagerClient;
 
+import com.android.internal.infra.AbstractSinglePendingRequestRemoteService;
 import com.android.internal.os.IResultReceiver;
-import com.android.server.infra.AbstractSinglePendingRequestRemoteService;
 
 final class RemoteAugmentedAutofillService
         extends AbstractSinglePendingRequestRemoteService<RemoteAugmentedAutofillService,
@@ -47,8 +47,6 @@
 
     private static final String TAG = RemoteAugmentedAutofillService.class.getSimpleName();
 
-    // TODO(b/117779333): changed it so it's permanentely bound
-    private static final long TIMEOUT_IDLE_BIND_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
     private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
 
     RemoteAugmentedAutofillService(Context context, ComponentName serviceName,
@@ -90,7 +88,7 @@
 
     @Override // from AbstractRemoteService
     protected long getTimeoutIdleBindMillis() {
-        return TIMEOUT_IDLE_BIND_MILLIS;
+        return PERMANENT_BOUND_TIMEOUT_MS;
     }
 
     @Override // from AbstractRemoteService
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 417ea9c..34fe5d9 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -39,7 +39,7 @@
 import android.text.format.DateUtils;
 import android.util.Slog;
 
-import com.android.server.infra.AbstractSinglePendingRequestRemoteService;
+import com.android.internal.infra.AbstractSinglePendingRequestRemoteService;
 
 final class RemoteFillService
         extends AbstractSinglePendingRequestRemoteService<RemoteFillService, IAutoFillService> {
@@ -77,7 +77,7 @@
         try {
             mService.onConnectedStateChanged(state);
         } catch (Exception e) {
-            Slog.w(mTag, "Exception calling onConnectedStateChanged(): " + e);
+            Slog.w(mTag, "Exception calling onConnectedStateChanged(" + state + "): " + e);
         }
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 0e9b407..0348f2b 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -64,9 +64,11 @@
 import android.os.SystemClock;
 import android.service.autofill.AutofillFieldClassificationService.Scores;
 import android.service.autofill.AutofillService;
+import android.service.autofill.CompositeUserData;
 import android.service.autofill.Dataset;
 import android.service.autofill.FieldClassification;
 import android.service.autofill.FieldClassification.Match;
+import android.service.autofill.FieldClassificationUserData;
 import android.service.autofill.FillContext;
 import android.service.autofill.FillRequest;
 import android.service.autofill.FillResponse;
@@ -1237,11 +1239,16 @@
             return;
         }
 
+        // Merge UserData if necessary.
+        // Fields in packageUserData will override corresponding fields in genericUserData.
+        final UserData genericUserData = mService.getUserData();
         final UserData packageUserData = lastResponse.getUserData();
-
-        final UserData userData;
-        if (packageUserData != null) {
-            // Replace default userData
+        final FieldClassificationUserData userData;
+        if (packageUserData == null && genericUserData == null) {
+            userData = null;
+        } else if (packageUserData != null && genericUserData != null) {
+            userData = new CompositeUserData(genericUserData, packageUserData);
+        } else if (packageUserData != null) {
             userData = packageUserData;
         } else {
             userData = mService.getUserData();
@@ -1396,7 +1403,8 @@
             @NonNull ArrayList<String> changedDatasetIds,
             @NonNull ArrayList<AutofillId> manuallyFilledFieldIds,
             @NonNull ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
-            @NonNull UserData userData, @NonNull Collection<ViewState> viewStates) {
+            @NonNull FieldClassificationUserData userData,
+            @NonNull Collection<ViewState> viewStates) {
 
         final String[] userValues = userData.getValues();
         final String[] categoryIds = userData.getCategoryIds();
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index a917ced..12db4f3 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -16,10 +16,13 @@
 
 package com.android.server.backup;
 
+import static com.android.internal.util.Preconditions.checkNotNull;
+
 import android.Manifest;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
+import android.app.backup.BackupManager;
 import android.app.backup.IBackupManagerMonitor;
 import android.app.backup.IBackupObserver;
 import android.app.backup.IFullBackupRestoreObserver;
@@ -33,24 +36,19 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.Binder;
-import android.os.Environment;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.util.Slog;
+import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.SystemConfig;
 import com.android.server.SystemService;
 
-import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.Collections;
 import java.util.Set;
@@ -68,10 +66,6 @@
     public static final boolean MORE_DEBUG = false;
     public static final boolean DEBUG_SCHEDULING = true;
 
-    // File containing backup-enabled state. Contains a single byte to denote enabled status.
-    // Nonzero is enabled; file missing or a zero byte is disabled.
-    private static final String BACKUP_ENABLE_FILE = "backup_enabled";
-
     // The published binder is a singleton Trampoline object that calls through to the proper code.
     // This indirection lets us turn down the heavy implementation object on the fly without
     // disturbing binders that have been cached elsewhere in the system.
@@ -83,22 +77,27 @@
     }
 
     private final Context mContext;
-    private UserBackupManagerService mUserBackupManagerService;
+    private final Trampoline mTrampoline;
+    private final HandlerThread mBackupThread;
+
+    // Keeps track of all unlocked users registered with this service. Indexed by user id.
+    private final SparseArray<UserBackupManagerService> mServiceUsers = new SparseArray<>();
+
+    private Set<ComponentName> mTransportWhitelist;
 
     /** Instantiate a new instance of {@link BackupManagerService}. */
     public BackupManagerService(
             Context context, Trampoline trampoline, HandlerThread backupThread) {
-        // Set up our transport options and initialize the default transport
-        SystemConfig systemConfig = SystemConfig.getInstance();
-        Set<ComponentName> transportWhitelist = systemConfig.getBackupTransportWhitelist();
-        if (transportWhitelist == null) {
-            transportWhitelist = Collections.emptySet();
-        }
+        mContext = checkNotNull(context);
+        mTrampoline = checkNotNull(trampoline);
+        mBackupThread = checkNotNull(backupThread);
 
-        mContext = context;
-        mUserBackupManagerService =
-                UserBackupManagerService.createAndInitializeService(
-                        context, trampoline, backupThread, transportWhitelist);
+        // Set up our transport options.
+        SystemConfig systemConfig = SystemConfig.getInstance();
+        mTransportWhitelist = systemConfig.getBackupTransportWhitelist();
+        if (mTransportWhitelist == null) {
+            mTransportWhitelist = Collections.emptySet();
+        }
     }
 
     /**
@@ -108,40 +107,76 @@
      * @param userId User id on which the backup operation is being requested.
      * @param message A message to include in the exception if it is thrown.
      */
-    private void enforceCallingPermissionOnUserId(int userId, String message) {
+    private void enforceCallingPermissionOnUserId(@UserIdInt int userId, String message) {
         if (Binder.getCallingUserHandle().getIdentifier() != userId) {
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
         }
     }
 
-    // TODO(b/118520567): Remove when tests are modified to use per-user instance.
-    @VisibleForTesting
-    void setUserBackupManagerService(UserBackupManagerService userBackupManagerService) {
-        mUserBackupManagerService = userBackupManagerService;
-    }
-
-    /**
-     * Called through Trampoline from {@link Lifecycle#onUnlockUser(int)}. We run the heavy work on
-     * a background thread to keep the unlock time down.
-     */
-    public void unlockSystemUser() {
-        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
-        try {
-            sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM));
-        } catch (RemoteException e) {
-            // can't happen; it's a local object
-        }
-        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-    }
+    // ---------------------------------------------
+    // USER LIFECYCLE CALLBACKS
+    // ---------------------------------------------
 
     /**
      * Starts the backup service for user {@code userId} by creating a new instance of {@link
      * UserBackupManagerService} and registering it with this service.
      */
-    // TODO(b/120212806): Add UserBackupManagerService initialization logic.
-    void startServiceForUser(int userId) {
-        // Intentionally empty.
+    @VisibleForTesting
+    protected void startServiceForUser(int userId) {
+        UserBackupManagerService userBackupManagerService =
+                UserBackupManagerService.createAndInitializeService(
+                        userId, mContext, mTrampoline, mTransportWhitelist);
+        startServiceForUser(userId, userBackupManagerService);
+    }
+
+    /**
+     * Starts the backup service for user {@code userId} by registering its instance of {@link
+     * UserBackupManagerService} with this service and setting enabled state.
+     */
+    void startServiceForUser(int userId, UserBackupManagerService userBackupManagerService) {
+        mServiceUsers.put(userId, userBackupManagerService);
+
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
+        userBackupManagerService.initializeBackupEnableState();
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+    }
+
+    /** Stops the backup service for user {@code userId} when the user is stopped. */
+    @VisibleForTesting
+    protected void stopServiceForUser(int userId) {
+        UserBackupManagerService userBackupManagerService = mServiceUsers.removeReturnOld(userId);
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.tearDownService();
+        }
+    }
+
+    SparseArray<UserBackupManagerService> getServiceUsers() {
+        return mServiceUsers;
+    }
+
+    /**
+     * Returns the {@link UserBackupManagerService} instance for the specified user {@code userId}.
+     * If the user is not registered with the service (either the user is locked or not eligible for
+     * the backup service) then return {@code null}.
+     *
+     * @param userId The id of the user to retrieve its instance of {@link
+     *     UserBackupManagerService}.
+     * @param caller A {@link String} identifying the caller for logging purposes.
+     * @throws SecurityException if {@code userId} is different from the calling user id and the
+     *     caller does NOT have the android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     */
+    @Nullable
+    @VisibleForTesting
+    UserBackupManagerService getServiceForUserIfCallerHasPermission(
+            @UserIdInt int userId, String caller) {
+        enforceCallingPermissionOnUserId(userId, caller);
+        UserBackupManagerService userBackupManagerService = mServiceUsers.get(userId);
+        if (userBackupManagerService == null) {
+            Slog.w(TAG, "Called " + caller + " for unknown user: " + userId);
+        }
+        return userBackupManagerService;
     }
 
     /*
@@ -149,7 +184,7 @@
      * They delegate to the appropriate per-user instance of UserBackupManagerService to perform the
      * action on the passed in user. Currently this is a straight redirection (see TODO).
      */
-    // TODO (b/118520567): Take in user id and call per-user instance of UserBackupManagerService.
+    // TODO (b/118520567): Stop hardcoding system user when we pass in user id as a parameter
 
     // ---------------------------------------------
     // BACKUP AGENT OPERATIONS
@@ -160,32 +195,52 @@
      * backup for their app {@code packageName}. Only used for apps participating in key-value
      * backup.
      */
-    public void dataChanged(String packageName) {
-        mUserBackupManagerService.dataChanged(packageName);
+    public void dataChanged(@UserIdInt int userId, String packageName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "dataChanged()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.dataChanged(packageName);
+        }
     }
 
     /**
      * Callback: a requested backup agent has been instantiated. This should only be called from the
      * {@link ActivityManager}.
      */
-    public void agentConnected(String packageName, IBinder agentBinder) {
-        mUserBackupManagerService.agentConnected(packageName, agentBinder);
+    public void agentConnected(@UserIdInt int userId, String packageName, IBinder agentBinder) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "agentConnected()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.agentConnected(packageName, agentBinder);
+        }
     }
 
     /**
      * Callback: a backup agent has failed to come up, or has unexpectedly quit. This should only be
      * called from the {@link ActivityManager}.
      */
-    public void agentDisconnected(String packageName) {
-        mUserBackupManagerService.agentDisconnected(packageName);
+    public void agentDisconnected(@UserIdInt int userId, String packageName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "agentDisconnected()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.agentDisconnected(packageName);
+        }
     }
 
     /**
      * Used by a currently-active backup agent to notify the service that it has completed its given
      * outstanding asynchronous backup/restore operation.
      */
-    public void opComplete(int token, long result) {
-        mUserBackupManagerService.opComplete(token, result);
+    public void opComplete(@UserIdInt int userId, int token, long result) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "opComplete()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.opComplete(token, result);
+        }
     }
 
     // ---------------------------------------------
@@ -193,44 +248,87 @@
     // ---------------------------------------------
 
     /** Run an initialize operation for the given transports {@code transportNames}. */
-    public void initializeTransports(String[] transportNames, IBackupObserver observer) {
-        mUserBackupManagerService.initializeTransports(transportNames, observer);
+    public void initializeTransports(
+            @UserIdInt int userId, String[] transportNames, IBackupObserver observer) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "initializeTransports()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.initializeTransports(transportNames, observer);
+        }
     }
 
     /**
      * Clear the given package {@code packageName}'s backup data from the transport {@code
      * transportName}.
      */
-    public void clearBackupData(String transportName, String packageName) {
-        mUserBackupManagerService.clearBackupData(transportName, packageName);
+    public void clearBackupData(@UserIdInt int userId, String transportName, String packageName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "clearBackupData()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.clearBackupData(transportName, packageName);
+        }
     }
 
     /** Return the name of the currently active transport. */
-    public String getCurrentTransport() {
-        return mUserBackupManagerService.getCurrentTransport();
+    @Nullable
+    public String getCurrentTransport(@UserIdInt int userId) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "getCurrentTransport()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.getCurrentTransport();
     }
 
     /**
      * Returns the {@link ComponentName} of the host service of the selected transport or {@code
      * null} if no transport selected or if the transport selected is not registered.
      */
-    public ComponentName getCurrentTransportComponent() {
-        return mUserBackupManagerService.getCurrentTransportComponent();
+    @Nullable
+    public ComponentName getCurrentTransportComponent(@UserIdInt int userId) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "getCurrentTransportComponent()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.getCurrentTransportComponent();
     }
 
     /** Report all known, available backup transports by name. */
-    public String[] listAllTransports() {
-        return mUserBackupManagerService.listAllTransports();
+    @Nullable
+    public String[] listAllTransports(@UserIdInt int userId) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "listAllTransports()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.listAllTransports();
     }
 
     /** Report all known, available backup transports by {@link ComponentName}. */
-    public ComponentName[] listAllTransportComponents() {
-        return mUserBackupManagerService.listAllTransportComponents();
+    @Nullable
+    public ComponentName[] listAllTransportComponents(@UserIdInt int userId) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "listAllTransportComponents()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.listAllTransportComponents();
     }
 
     /** Report all system whitelisted transports. */
+    @Nullable
     public String[] getTransportWhitelist() {
-        return mUserBackupManagerService.getTransportWhitelist();
+        // No permission check, intentionally.
+        String[] whitelistedTransports = new String[mTransportWhitelist.size()];
+        int i = 0;
+        for (ComponentName component : mTransportWhitelist) {
+            whitelistedTransports[i] = component.flattenToShortString();
+            i++;
+        }
+        return whitelistedTransports;
     }
 
     /**
@@ -257,19 +355,25 @@
      *     {@code transportComponent} or if the caller does NOT have BACKUP permission.
      */
     public void updateTransportAttributes(
+            @UserIdInt int userId,
             ComponentName transportComponent,
             String name,
             @Nullable Intent configurationIntent,
             String currentDestinationString,
             @Nullable Intent dataManagementIntent,
             String dataManagementLabel) {
-        mUserBackupManagerService.updateTransportAttributes(
-                transportComponent,
-                name,
-                configurationIntent,
-                currentDestinationString,
-                dataManagementIntent,
-                dataManagementLabel);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "updateTransportAttributes()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.updateTransportAttributes(
+                    transportComponent,
+                    name,
+                    configurationIntent,
+                    currentDestinationString,
+                    dataManagementIntent,
+                    dataManagementLabel);
+        }
     }
 
     /**
@@ -280,8 +384,13 @@
      */
     @Deprecated
     @Nullable
-    public String selectBackupTransport(String transportName) {
-        return mUserBackupManagerService.selectBackupTransport(transportName);
+    public String selectBackupTransport(@UserIdInt int userId, String transportName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "selectBackupTransport()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.selectBackupTransport(transportName);
     }
 
     /**
@@ -289,8 +398,15 @@
      * with the result upon completion.
      */
     public void selectBackupTransportAsync(
-            ComponentName transportComponent, ISelectBackupTransportCallback listener) {
-        mUserBackupManagerService.selectBackupTransportAsync(transportComponent, listener);
+            @UserIdInt int userId,
+            ComponentName transportComponent,
+            ISelectBackupTransportCallback listener) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "selectBackupTransportAsync()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.selectBackupTransportAsync(transportComponent, listener);
+        }
     }
 
     /**
@@ -298,8 +414,14 @@
      * available transports, or if the transport does not supply any configuration UI, the method
      * returns {@code null}.
      */
-    public Intent getConfigurationIntent(String transportName) {
-        return mUserBackupManagerService.getConfigurationIntent(transportName);
+    @Nullable
+    public Intent getConfigurationIntent(@UserIdInt int userId, String transportName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "getConfigurationIntent()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.getConfigurationIntent(transportName);
     }
 
     /**
@@ -311,21 +433,39 @@
      * @param transportName The name of the registered transport.
      * @return The current destination string or null if the transport is not registered.
      */
-    public String getDestinationString(String transportName) {
-        return mUserBackupManagerService.getDestinationString(transportName);
+    @Nullable
+    public String getDestinationString(@UserIdInt int userId, String transportName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "getDestinationString()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.getDestinationString(transportName);
     }
 
     /** Supply the manage-data intent for the given transport. */
-    public Intent getDataManagementIntent(String transportName) {
-        return mUserBackupManagerService.getDataManagementIntent(transportName);
+    @Nullable
+    public Intent getDataManagementIntent(@UserIdInt int userId, String transportName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "getDataManagementIntent()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.getDataManagementIntent(transportName);
     }
 
     /**
      * Supply the menu label for affordances that fire the manage-data intent for the given
      * transport.
      */
-    public String getDataManagementLabel(String transportName) {
-        return mUserBackupManagerService.getDataManagementLabel(transportName);
+    @Nullable
+    public String getDataManagementLabel(@UserIdInt int userId, String transportName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "getDataManagementLabel()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.getDataManagementLabel(transportName);
     }
 
     // ---------------------------------------------
@@ -334,26 +474,32 @@
 
     /** Enable/disable the backup service. This is user-configurable via backup settings. */
     public void setBackupEnabled(@UserIdInt int userId, boolean enable) {
-        enforceCallingPermissionOnUserId(userId, "setBackupEnabled");
-        mUserBackupManagerService.setBackupEnabled(enable);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "setBackupEnabled()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.setBackupEnabled(enable);
+        }
     }
 
     /** Enable/disable automatic restore of app data at install time. */
-    public void setAutoRestore(boolean autoRestore) {
-        mUserBackupManagerService.setAutoRestore(autoRestore);
-    }
+    public void setAutoRestore(@UserIdInt int userId, boolean autoRestore) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "setAutoRestore()");
 
-    /** Mark the backup service as having been provisioned (device has gone through SUW). */
-    public void setBackupProvisioned(boolean provisioned) {
-        mUserBackupManagerService.setBackupProvisioned(provisioned);
+        if (userBackupManagerService != null) {
+            userBackupManagerService.setAutoRestore(autoRestore);
+        }
     }
 
     /**
      * Return {@code true} if the backup mechanism is currently enabled, else returns {@code false}.
      */
     public boolean isBackupEnabled(@UserIdInt int userId) {
-        enforceCallingPermissionOnUserId(userId, "isBackupEnabled");
-        return mUserBackupManagerService.isBackupEnabled();
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "isBackupEnabled()");
+
+        return userBackupManagerService != null && userBackupManagerService.isBackupEnabled();
     }
 
     // ---------------------------------------------
@@ -361,15 +507,25 @@
     // ---------------------------------------------
 
     /** Checks if the given package {@code packageName} is eligible for backup. */
-    public boolean isAppEligibleForBackup(String packageName) {
-        return mUserBackupManagerService.isAppEligibleForBackup(packageName);
+    public boolean isAppEligibleForBackup(@UserIdInt int userId, String packageName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "isAppEligibleForBackup()");
+
+        return userBackupManagerService != null
+                && userBackupManagerService.isAppEligibleForBackup(packageName);
     }
 
     /**
      * Returns from the inputted packages {@code packages}, the ones that are eligible for backup.
      */
-    public String[] filterAppsEligibleForBackup(String[] packages) {
-        return mUserBackupManagerService.filterAppsEligibleForBackup(packages);
+    @Nullable
+    public String[] filterAppsEligibleForBackup(@UserIdInt int userId, String[] packages) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "filterAppsEligibleForBackup()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.filterAppsEligibleForBackup(packages);
     }
 
     /**
@@ -377,8 +533,12 @@
      * they have pending updates.
      */
     public void backupNow(@UserIdInt int userId) {
-        enforceCallingPermissionOnUserId(userId, "backupNow");
-        mUserBackupManagerService.backupNow();
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "backupNow()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.backupNow();
+        }
     }
 
     /**
@@ -391,14 +551,22 @@
             IBackupObserver observer,
             IBackupManagerMonitor monitor,
             int flags) {
-        enforceCallingPermissionOnUserId(userId, "requestBackup");
-        return mUserBackupManagerService.requestBackup(packages, observer, monitor, flags);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "requestBackup()");
+
+        return userBackupManagerService == null
+                ? BackupManager.ERROR_BACKUP_NOT_ALLOWED
+                : userBackupManagerService.requestBackup(packages, observer, monitor, flags);
     }
 
     /** Cancel all running backup operations. */
     public void cancelBackups(@UserIdInt int userId) {
-        enforceCallingPermissionOnUserId(userId, "cancelBackups");
-        mUserBackupManagerService.cancelBackups();
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "cancelBackups()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.cancelBackups();
+        }
     }
 
     /**
@@ -410,7 +578,11 @@
      *     return value to the callback {@link JobService#onStartJob(JobParameters)}.
      */
     public boolean beginFullBackup(FullBackupJob scheduledJob) {
-        return mUserBackupManagerService.beginFullBackup(scheduledJob);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "beginFullBackup()");
+
+        return userBackupManagerService != null
+                && userBackupManagerService.beginFullBackup(scheduledJob);
     }
 
     /**
@@ -418,14 +590,24 @@
      * longer met for running the full backup job.
      */
     public void endFullBackup() {
-        mUserBackupManagerService.endFullBackup();
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "endFullBackup()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.endFullBackup();
+        }
     }
 
     /**
      * Run a full backup pass for the given packages {@code packageNames}. Used by 'adb shell bmgr'.
      */
-    public void fullTransportBackup(String[] packageNames) {
-        mUserBackupManagerService.fullTransportBackup(packageNames);
+    public void fullTransportBackup(@UserIdInt int userId, String[] packageNames) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "fullTransportBackup()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.fullTransportBackup(packageNames);
+        }
     }
 
     // ---------------------------------------------
@@ -436,24 +618,41 @@
      * Used to run a restore pass for an application that is being installed. This should only be
      * called from the {@link PackageManager}.
      */
-    public void restoreAtInstall(String packageName, int token) {
-        mUserBackupManagerService.restoreAtInstall(packageName, token);
+    public void restoreAtInstall(@UserIdInt int userId, String packageName, int token) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "restoreAtInstall()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.restoreAtInstall(packageName, token);
+        }
     }
 
     /**
      * Begin a restore for the specified package {@code packageName} using the specified transport
      * {@code transportName}.
      */
-    public IRestoreSession beginRestoreSession(String packageName, String transportName) {
-        return mUserBackupManagerService.beginRestoreSession(packageName, transportName);
+    @Nullable
+    public IRestoreSession beginRestoreSession(
+            @UserIdInt int userId, String packageName, String transportName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "beginRestoreSession()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.beginRestoreSession(packageName, transportName);
     }
 
     /**
      * Get the restore-set token for the best-available restore set for this {@code packageName}:
      * the active set if possible, else the ancestral one. Returns zero if none available.
      */
-    public long getAvailableRestoreToken(String packageName) {
-        return mUserBackupManagerService.getAvailableRestoreToken(packageName);
+    public long getAvailableRestoreToken(@UserIdInt int userId, String packageName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "getAvailableRestoreToken()");
+
+        return userBackupManagerService == null
+                ? 0
+                : userBackupManagerService.getAvailableRestoreToken(packageName);
     }
 
     // ---------------------------------------------
@@ -462,12 +661,21 @@
 
     /** Sets the backup password used when running adb backup. */
     public boolean setBackupPassword(String currentPassword, String newPassword) {
-        return mUserBackupManagerService.setBackupPassword(currentPassword, newPassword);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(
+                        UserHandle.USER_SYSTEM, "setBackupPassword()");
+
+        return userBackupManagerService != null
+                && userBackupManagerService.setBackupPassword(currentPassword, newPassword);
     }
 
     /** Returns {@code true} if adb backup was run with a password, else returns {@code false}. */
     public boolean hasBackupPassword() {
-        return mUserBackupManagerService.hasBackupPassword();
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(
+                        UserHandle.USER_SYSTEM, "hasBackupPassword()");
+
+        return userBackupManagerService != null && userBackupManagerService.hasBackupPassword();
     }
 
     /**
@@ -477,6 +685,7 @@
      * requires on-screen confirmation by the user.
      */
     public void adbBackup(
+            @UserIdInt int userId,
             ParcelFileDescriptor fd,
             boolean includeApks,
             boolean includeObbs,
@@ -487,17 +696,22 @@
             boolean doCompress,
             boolean doKeyValue,
             String[] packageNames) {
-        mUserBackupManagerService.adbBackup(
-                fd,
-                includeApks,
-                includeObbs,
-                includeShared,
-                doWidgets,
-                doAllApps,
-                includeSystem,
-                doCompress,
-                doKeyValue,
-                packageNames);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "adbBackup()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.adbBackup(
+                    fd,
+                    includeApks,
+                    includeObbs,
+                    includeShared,
+                    doWidgets,
+                    doAllApps,
+                    includeSystem,
+                    doCompress,
+                    doKeyValue,
+                    packageNames);
+        }
     }
 
     /**
@@ -505,8 +719,13 @@
      * is synchronous and does not return to the caller until the restore has been completed. It
      * requires on-screen confirmation by the user.
      */
-    public void adbRestore(ParcelFileDescriptor fd) {
-        mUserBackupManagerService.adbRestore(fd);
+    public void adbRestore(@UserIdInt int userId, ParcelFileDescriptor fd) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "adbRestore()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.adbRestore(fd);
+        }
     }
 
     /**
@@ -514,13 +733,19 @@
      * to require a user-facing disclosure about the operation.
      */
     public void acknowledgeAdbBackupOrRestore(
+            @UserIdInt int userId,
             int token,
             boolean allow,
             String currentPassword,
             String encryptionPassword,
             IFullBackupRestoreObserver observer) {
-        mUserBackupManagerService.acknowledgeAdbBackupOrRestore(
-                token, allow, currentPassword, encryptionPassword, observer);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "acknowledgeAdbBackupOrRestore()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.acknowledgeAdbBackupOrRestore(
+                    token, allow, currentPassword, encryptionPassword, observer);
+        }
     }
 
     // ---------------------------------------------
@@ -529,44 +754,11 @@
 
     /** Prints service state for 'dumpsys backup'. */
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        mUserBackupManagerService.dump(fd, pw, args);
-    }
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "dump()");
 
-    private static boolean readBackupEnableState(int userId) {
-        File base = new File(Environment.getDataDirectory(), "backup");
-        File enableFile = new File(base, BACKUP_ENABLE_FILE);
-        if (enableFile.exists()) {
-            try (FileInputStream fin = new FileInputStream(enableFile)) {
-                int state = fin.read();
-                return state != 0;
-            } catch (IOException e) {
-                // can't read the file; fall through to assume disabled
-                Slog.e(TAG, "Cannot read enable state; assuming disabled");
-            }
-        } else {
-            if (DEBUG) {
-                Slog.i(TAG, "isBackupEnabled() => false due to absent settings file");
-            }
-        }
-        return false;
-    }
-
-    static void writeBackupEnableState(boolean enable, int userId) {
-        File base = new File(Environment.getDataDirectory(), "backup");
-        File enableFile = new File(base, BACKUP_ENABLE_FILE);
-        File stage = new File(base, BACKUP_ENABLE_FILE + "-stage");
-        try (FileOutputStream fout = new FileOutputStream(stage)) {
-            fout.write(enable ? 1 : 0);
-            fout.close();
-            stage.renameTo(enableFile);
-            // will be synced immediately by the try-with-resources call to close()
-        } catch (IOException | RuntimeException e) {
-            Slog.e(
-                    TAG,
-                    "Unable to record backup enable state; reverting to disabled: "
-                            + e.getMessage());
-            enableFile.delete();
-            stage.delete();
+        if (userBackupManagerService != null) {
+            userBackupManagerService.dump(fd, pw, args);
         }
     }
 
@@ -585,10 +777,14 @@
         @Override
         public void onUnlockUser(int userId) {
             if (userId == UserHandle.USER_SYSTEM) {
-                sInstance.initializeServiceAndUnlockSystemUser();
-            } else {
-                sInstance.startServiceForUser(userId);
+                sInstance.initializeService();
             }
+            sInstance.unlockUser(userId);
+        }
+
+        @Override
+        public void onStopUser(int userId) {
+            sInstance.stopUser(userId);
         }
     }
 }
diff --git a/services/backup/java/com/android/server/backup/FullBackupJob.java b/services/backup/java/com/android/server/backup/FullBackupJob.java
index 82638b4..5708b1c 100644
--- a/services/backup/java/com/android/server/backup/FullBackupJob.java
+++ b/services/backup/java/com/android/server/backup/FullBackupJob.java
@@ -24,15 +24,12 @@
 import android.content.Context;
 
 public class FullBackupJob extends JobService {
-    private static final String TAG = "FullBackupJob";
-    private static final boolean DEBUG = true;
-
     private static ComponentName sIdleService =
             new ComponentName("android", FullBackupJob.class.getName());
 
     private static final int JOB_ID = 0x5038;
 
-    JobParameters mParams;
+    private JobParameters mParams;
 
     public static void schedule(Context ctx, long minDelay, BackupManagerConstants constants) {
         JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
index bed520e..3184bd8 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
@@ -13,8 +13,6 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
-import com.android.server.backup.restore.PerformAdbRestoreTask;
-
 import libcore.io.IoUtils;
 
 import java.io.File;
@@ -42,11 +40,10 @@
     private final UserBackupManagerService mBackupManagerService;
     private final File mDataDir;
 
-    FileMetadata mInfo;
-    PerformAdbRestoreTask mRestoreTask;
-    ParcelFileDescriptor mInFD;
-    IBackupAgent mAgent;
-    int mToken;
+    private final FileMetadata mInfo;
+    private final ParcelFileDescriptor mInFD;
+    private final IBackupAgent mAgent;
+    private final int mToken;
 
     public KeyValueAdbRestoreEngine(UserBackupManagerService backupManagerService,
             File dataDir, FileMetadata info, ParcelFileDescriptor inFD, IBackupAgent agent,
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index ed6ff9b..79d4a2c 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -116,7 +116,7 @@
         return SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false);
     }
 
-    protected boolean isMultiUserEnabled() {
+    private boolean isMultiUserEnabled() {
         return Settings.Global.getInt(
                 mContext.getContentResolver(),
                 Settings.Global.BACKUP_MULTI_USER_ENABLED,
@@ -145,68 +145,74 @@
         return new BackupManagerService(mContext, this, mHandlerThread);
     }
 
-    /**
-     * Initialize {@link BackupManagerService} if the backup service is not disabled. Only the
-     * system user can initialize the service.
-     */
-    /* package */ void initializeService(int userId) {
-        if (mGlobalDisable) {
-            Slog.i(TAG, "Backup service not supported");
-            return;
-        }
-
-        if (userId != UserHandle.USER_SYSTEM) {
-            Slog.i(TAG, "Cannot initialize backup service for non-system user: " + userId);
-            return;
-        }
-
-        synchronized (mStateLock) {
-            if (!mSuppressFile.exists()) {
-                mService = createBackupManagerService();
-            } else {
-                Slog.i(TAG, "Backup service inactive");
-            }
-        }
+    protected void postToHandler(Runnable runnable) {
+        mHandler.post(runnable);
     }
 
     /**
      * Called from {@link BackupManagerService.Lifecycle} when the system user is unlocked. Attempts
-     * to initialize {@link BackupManagerService} and set backup state for the system user.
+     * to initialize {@link BackupManagerService}. Offloads work onto the handler thread {@link
+     * #mHandlerThread} to keep unlock time low.
      */
-    void initializeServiceAndUnlockSystemUser() {
-        mHandler.post(
+    void initializeService() {
+        postToHandler(
                 () -> {
                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
-                    initializeService(UserHandle.USER_SYSTEM);
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
-                    BackupManagerService service = mService;
-                    if (service != null) {
-                        Slog.i(TAG, "Unlocking system user");
-                        service.unlockSystemUser();
+                    if (mGlobalDisable) {
+                        Slog.i(TAG, "Backup service not supported");
+                        return;
                     }
+
+                    synchronized (mStateLock) {
+                        if (!mSuppressFile.exists()) {
+                            mService = createBackupManagerService();
+                        } else {
+                            Slog.i(TAG, "Backup service inactive");
+                        }
+                    }
+                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                 });
     }
 
     /**
-     * Called from {@link BackupManagerService.Lifecycle} when a non-system user {@code userId} is
-     * unlocked. Starts the backup service for this user if the service supports multi-user.
-     * Offloads work onto the handler thread {@link #mHandlerThread} to keep unlock time low.
+     * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is unlocked.
+     * Starts the backup service for this user if it's the system user or if the service supports
+     * multi-user. Offloads work onto the handler thread {@link #mHandlerThread} to keep unlock time
+     * low.
      */
-    // TODO(b/120212806): Consolidate service start for system and non-system users when system
-    // user-only logic is removed.
-    void startServiceForUser(int userId) {
-        if (!isMultiUserEnabled()) {
+    void unlockUser(int userId) {
+        if (userId != UserHandle.USER_SYSTEM && !isMultiUserEnabled()) {
             Slog.i(TAG, "Multi-user disabled, cannot start service for user: " + userId);
             return;
         }
 
-        mHandler.post(
+        postToHandler(() -> startServiceForUser(userId));
+    }
+
+    private void startServiceForUser(int userId) {
+        BackupManagerService service = mService;
+        if (service != null) {
+            Slog.i(TAG, "Starting service for user: " + userId);
+            service.startServiceForUser(userId);
+        }
+    }
+
+    /**
+     * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is stopped.
+     * Offloads work onto the handler thread {@link #mHandlerThread} to keep stopping time low.
+     */
+    void stopUser(int userId) {
+        if (userId != UserHandle.USER_SYSTEM && !isMultiUserEnabled()) {
+            Slog.i(TAG, "Multi-user disabled, cannot stop service for user: " + userId);
+            return;
+        }
+
+        postToHandler(
                 () -> {
                     BackupManagerService service = mService;
                     if (service != null) {
-                        Slog.i(TAG, "Starting service for user: " + userId);
-                        service.startServiceForUser(userId);
+                        Slog.i(TAG, "Stopping service for user: " + userId);
+                        service.stopServiceForUser(userId);
                     }
                 });
     }
@@ -242,6 +248,7 @@
             if (makeActive) {
                 mService = createBackupManagerService();
                 mSuppressFile.delete();
+                startServiceForUser(userId);
             } else {
                 mService = null;
                 try {
@@ -274,53 +281,81 @@
     }
 
     @Override
-    public void dataChanged(String packageName) throws RemoteException {
+    public void dataChangedForUser(int userId, String packageName) throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
-            svc.dataChanged(packageName);
+            svc.dataChanged(userId, packageName);
         }
     }
 
     @Override
-    public void initializeTransports(String[] transportNames, IBackupObserver observer)
+    public void dataChanged(String packageName) throws RemoteException {
+        dataChangedForUser(binderGetCallingUserId(), packageName);
+    }
+
+    @Override
+    public void initializeTransportsForUser(
+            int userId, String[] transportNames, IBackupObserver observer) throws RemoteException {
+        BackupManagerService svc = mService;
+        if (svc != null) {
+            svc.initializeTransports(userId, transportNames, observer);
+        }
+    }
+
+    @Override
+    public void clearBackupDataForUser(int userId, String transportName, String packageName)
             throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
-            svc.initializeTransports(transportNames, observer);
+            svc.clearBackupData(userId, transportName, packageName);
         }
     }
 
     @Override
     public void clearBackupData(String transportName, String packageName)
             throws RemoteException {
+        clearBackupDataForUser(binderGetCallingUserId(), transportName, packageName);
+    }
+
+    @Override
+    public void agentConnectedForUser(int userId, String packageName, IBinder agent)
+            throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
-            svc.clearBackupData(transportName, packageName);
+            svc.agentConnected(userId, packageName, agent);
         }
     }
 
     @Override
     public void agentConnected(String packageName, IBinder agent) throws RemoteException {
+        agentConnectedForUser(binderGetCallingUserId(), packageName, agent);
+    }
+
+    @Override
+    public void agentDisconnectedForUser(int userId, String packageName) throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
-            svc.agentConnected(packageName, agent);
+            svc.agentDisconnected(userId, packageName);
         }
     }
 
     @Override
     public void agentDisconnected(String packageName) throws RemoteException {
+        agentDisconnectedForUser(binderGetCallingUserId(), packageName);
+    }
+
+    @Override
+    public void restoreAtInstallForUser(int userId, String packageName, int token)
+            throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
-            svc.agentDisconnected(packageName);
+            svc.restoreAtInstall(userId, packageName, token);
         }
     }
 
     @Override
     public void restoreAtInstall(String packageName, int token) throws RemoteException {
-        BackupManagerService svc = mService;
-        if (svc != null) {
-            svc.restoreAtInstall(packageName, token);
-        }
+        restoreAtInstallForUser(binderGetCallingUserId(), packageName, token);
     }
 
     @Override
@@ -338,19 +373,16 @@
     }
 
     @Override
-    public void setAutoRestore(boolean doAutoRestore) throws RemoteException {
+    public void setAutoRestoreForUser(int userId, boolean doAutoRestore) throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
-            svc.setAutoRestore(doAutoRestore);
+            svc.setAutoRestore(userId, doAutoRestore);
         }
     }
 
     @Override
-    public void setBackupProvisioned(boolean isProvisioned) throws RemoteException {
-        BackupManagerService svc = mService;
-        if (svc != null) {
-            svc.setBackupProvisioned(isProvisioned);
-        }
+    public void setAutoRestore(boolean doAutoRestore) throws RemoteException {
+        setAutoRestoreForUser(binderGetCallingUserId(), doAutoRestore);
     }
 
     @Override
@@ -389,31 +421,47 @@
         backupNowForUser(binderGetCallingUserId());
     }
 
-    @Override
-    public void adbBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
-            boolean includeShared, boolean doWidgets, boolean allApps,
-            boolean allIncludesSystem, boolean doCompress, boolean doKeyValue, String[] packageNames)
-                    throws RemoteException {
+    public void adbBackup(@UserIdInt int userId, ParcelFileDescriptor fd,
+            boolean includeApks, boolean includeObbs, boolean includeShared, boolean doWidgets,
+            boolean allApps, boolean allIncludesSystem, boolean doCompress, boolean doKeyValue,
+            String[] packageNames) throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
-            svc.adbBackup(fd, includeApks, includeObbs, includeShared, doWidgets,
+            svc.adbBackup(userId, fd, includeApks, includeObbs, includeShared, doWidgets,
                     allApps, allIncludesSystem, doCompress, doKeyValue, packageNames);
         }
     }
 
     @Override
-    public void fullTransportBackup(String[] packageNames) throws RemoteException {
+    public void fullTransportBackupForUser(int userId, String[] packageNames)
+            throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
-            svc.fullTransportBackup(packageNames);
+            svc.fullTransportBackup(userId, packageNames);
         }
     }
 
     @Override
-    public void adbRestore(ParcelFileDescriptor fd) throws RemoteException {
+    public void adbRestore(@UserIdInt int userId, ParcelFileDescriptor fd) throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
-            svc.adbRestore(fd);
+            svc.adbRestore(userId, fd);
+        }
+    }
+
+    @Override
+    public void acknowledgeFullBackupOrRestoreForUser(
+            int userId,
+            int token,
+            boolean allow,
+            String curPassword,
+            String encryptionPassword,
+            IFullBackupRestoreObserver observer)
+            throws RemoteException {
+        BackupManagerService svc = mService;
+        if (svc != null) {
+            svc.acknowledgeAdbBackupOrRestore(userId, token, allow,
+                    curPassword, encryptionPassword, observer);
         }
     }
 
@@ -422,16 +470,20 @@
             String encryptionPassword, IFullBackupRestoreObserver observer)
                     throws RemoteException {
         BackupManagerService svc = mService;
-        if (svc != null) {
-            svc.acknowledgeAdbBackupOrRestore(token, allow,
-                    curPassword, encryptionPassword, observer);
-        }
+        acknowledgeFullBackupOrRestoreForUser(
+                binderGetCallingUserId(), token, allow, curPassword, encryptionPassword, observer);
+    }
+
+
+    @Override
+    public String getCurrentTransportForUser(int userId) throws RemoteException {
+        BackupManagerService svc = mService;
+        return (svc != null) ? svc.getCurrentTransport(userId) : null;
     }
 
     @Override
     public String getCurrentTransport() throws RemoteException {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.getCurrentTransport() : null;
+        return getCurrentTransportForUser(binderGetCallingUserId());
     }
 
     /**
@@ -440,21 +492,26 @@
      */
     @Override
     @Nullable
-    public ComponentName getCurrentTransportComponent() {
+    public ComponentName getCurrentTransportComponentForUser(int userId) {
         BackupManagerService svc = mService;
-        return (svc != null) ? svc.getCurrentTransportComponent() : null;
+        return (svc != null) ? svc.getCurrentTransportComponent(userId) : null;
+    }
+
+    @Override
+    public String[] listAllTransportsForUser(int userId) throws RemoteException {
+        BackupManagerService svc = mService;
+        return (svc != null) ? svc.listAllTransports(userId) : null;
     }
 
     @Override
     public String[] listAllTransports() throws RemoteException {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.listAllTransports() : null;
+        return listAllTransportsForUser(binderGetCallingUserId());
     }
 
     @Override
-    public ComponentName[] listAllTransportComponents() throws RemoteException {
+    public ComponentName[] listAllTransportComponentsForUser(int userId) throws RemoteException {
         BackupManagerService svc = mService;
-        return (svc != null) ? svc.listAllTransportComponents() : null;
+        return (svc != null) ? svc.listAllTransportComponents(userId) : null;
     }
 
     @Override
@@ -464,7 +521,8 @@
     }
 
     @Override
-    public void updateTransportAttributes(
+    public void updateTransportAttributesForUser(
+            int userId,
             ComponentName transportComponent,
             String name,
             @Nullable Intent configurationIntent,
@@ -474,6 +532,7 @@
         BackupManagerService svc = mService;
         if (svc != null) {
             svc.updateTransportAttributes(
+                    userId,
                     transportComponent,
                     name,
                     configurationIntent,
@@ -484,17 +543,23 @@
     }
 
     @Override
-    public String selectBackupTransport(String transport) throws RemoteException {
+    public String selectBackupTransportForUser(int userId, String transport)
+            throws RemoteException {
         BackupManagerService svc = mService;
-        return (svc != null) ? svc.selectBackupTransport(transport) : null;
+        return (svc != null) ? svc.selectBackupTransport(userId, transport) : null;
     }
 
     @Override
-    public void selectBackupTransportAsync(ComponentName transport,
+    public String selectBackupTransport(String transport) throws RemoteException {
+        return selectBackupTransportForUser(binderGetCallingUserId(), transport);
+    }
+
+    @Override
+    public void selectBackupTransportAsyncForUser(int userId, ComponentName transport,
             ISelectBackupTransportCallback listener) throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
-            svc.selectBackupTransportAsync(transport, listener);
+            svc.selectBackupTransportAsync(userId, transport, listener);
         } else {
             if (listener != null) {
                 try {
@@ -507,60 +572,86 @@
     }
 
     @Override
-    public Intent getConfigurationIntent(String transport) throws RemoteException {
+    public Intent getConfigurationIntentForUser(int userId, String transport)
+            throws RemoteException {
         BackupManagerService svc = mService;
-        return (svc != null) ? svc.getConfigurationIntent(transport) : null;
+        return (svc != null) ? svc.getConfigurationIntent(userId, transport) : null;
+    }
+
+    @Override
+    public Intent getConfigurationIntent(String transport)
+            throws RemoteException {
+        return getConfigurationIntentForUser(binderGetCallingUserId(), transport);
+    }
+
+    @Override
+    public String getDestinationStringForUser(int userId, String transport) throws RemoteException {
+        BackupManagerService svc = mService;
+        return (svc != null) ? svc.getDestinationString(userId, transport) : null;
     }
 
     @Override
     public String getDestinationString(String transport) throws RemoteException {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.getDestinationString(transport) : null;
+        return getDestinationStringForUser(binderGetCallingUserId(), transport);
     }
 
     @Override
-    public Intent getDataManagementIntent(String transport) throws RemoteException {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.getDataManagementIntent(transport) : null;
-    }
-
-    @Override
-    public String getDataManagementLabel(String transport) throws RemoteException {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.getDataManagementLabel(transport) : null;
-    }
-
-    @Override
-    public IRestoreSession beginRestoreSession(String packageName, String transportID)
+    public Intent getDataManagementIntentForUser(int userId, String transport)
             throws RemoteException {
         BackupManagerService svc = mService;
-        return (svc != null) ? svc.beginRestoreSession(packageName, transportID) : null;
+        return (svc != null) ? svc.getDataManagementIntent(userId, transport) : null;
+    }
+
+    @Override
+    public Intent getDataManagementIntent(String transport)
+            throws RemoteException {
+        return getDataManagementIntentForUser(binderGetCallingUserId(), transport);
+    }
+
+    @Override
+    public String getDataManagementLabelForUser(int userId, String transport)
+            throws RemoteException {
+        BackupManagerService svc = mService;
+        return (svc != null) ? svc.getDataManagementLabel(userId, transport) : null;
+    }
+
+    @Override
+    public String getDataManagementLabel(String transport)
+            throws RemoteException {
+        return getDataManagementLabelForUser(binderGetCallingUserId(), transport);
+    }
+
+    @Override
+    public IRestoreSession beginRestoreSessionForUser(
+            int userId, String packageName, String transportID) throws RemoteException {
+        BackupManagerService svc = mService;
+        return (svc != null) ? svc.beginRestoreSession(userId, packageName, transportID) : null;
     }
 
     @Override
     public void opComplete(int token, long result) throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
-            svc.opComplete(token, result);
+            svc.opComplete(binderGetCallingUserId(), token, result);
         }
     }
 
     @Override
-    public long getAvailableRestoreToken(String packageName) {
+    public long getAvailableRestoreTokenForUser(int userId, String packageName) {
         BackupManagerService svc = mService;
-        return (svc != null) ? svc.getAvailableRestoreToken(packageName) : 0;
+        return (svc != null) ? svc.getAvailableRestoreToken(userId, packageName) : 0;
     }
 
     @Override
-    public boolean isAppEligibleForBackup(String packageName) {
+    public boolean isAppEligibleForBackupForUser(int userId, String packageName) {
         BackupManagerService svc = mService;
-        return (svc != null) ? svc.isAppEligibleForBackup(packageName) : false;
+        return (svc != null) ? svc.isAppEligibleForBackup(userId, packageName) : false;
     }
 
     @Override
-    public String[] filterAppsEligibleForBackup(String[] packages) {
+    public String[] filterAppsEligibleForBackupForUser(int userId, String[] packages) {
         BackupManagerService svc = mService;
-        return (svc != null) ? svc.filterAppsEligibleForBackup(packages) : null;
+        return (svc != null) ? svc.filterAppsEligibleForBackup(userId, packages) : null;
     }
 
     @Override
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerFilePersistedSettings.java b/services/backup/java/com/android/server/backup/UserBackupManagerFilePersistedSettings.java
new file mode 100644
index 0000000..6a1de63
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerFilePersistedSettings.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+import static com.android.server.backup.BackupManagerService.DEBUG;
+import static com.android.server.backup.BackupManagerService.TAG;
+
+import android.util.Slog;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/** User settings which are persisted across reboots. */
+final class UserBackupManagerFilePersistedSettings {
+    // File containing backup-enabled state. Contains a single byte to denote enabled status.
+    // Nonzero is enabled; file missing or a zero byte is disabled.
+    private static final String BACKUP_ENABLE_FILE = "backup_enabled";
+
+    static boolean readBackupEnableState(int userId) {
+        return readBackupEnableState(UserBackupManagerFiles.getBaseStateDir(userId));
+    }
+
+    static void writeBackupEnableState(int userId, boolean enable) {
+        writeBackupEnableState(UserBackupManagerFiles.getBaseStateDir(userId), enable);
+    }
+
+    private static boolean readBackupEnableState(File baseDir) {
+        File enableFile = new File(baseDir, BACKUP_ENABLE_FILE);
+        if (enableFile.exists()) {
+            try (FileInputStream fin = new FileInputStream(enableFile)) {
+                int state = fin.read();
+                return state != 0;
+            } catch (IOException e) {
+                // can't read the file; fall through to assume disabled
+                Slog.e(TAG, "Cannot read enable state; assuming disabled");
+            }
+        } else {
+            if (DEBUG) {
+                Slog.i(TAG, "isBackupEnabled() => false due to absent settings file");
+            }
+        }
+        return false;
+    }
+
+    private static void writeBackupEnableState(File baseDir, boolean enable) {
+        File enableFile = new File(baseDir, BACKUP_ENABLE_FILE);
+        File stage = new File(baseDir, BACKUP_ENABLE_FILE + "-stage");
+        try (FileOutputStream fout = new FileOutputStream(stage)) {
+            fout.write(enable ? 1 : 0);
+            fout.close();
+            stage.renameTo(enableFile);
+            // will be synced immediately by the try-with-resources call to close()
+        } catch (IOException | RuntimeException e) {
+            Slog.e(
+                    TAG,
+                    "Unable to record backup enable state; reverting to disabled: "
+                            + e.getMessage());
+            enableFile.delete();
+            stage.delete();
+        }
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
new file mode 100644
index 0000000..aabd41a
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+import android.os.Environment;
+import android.os.UserHandle;
+
+import java.io.File;
+
+/** Directories used for user specific backup/restore persistent state and book-keeping. */
+final class UserBackupManagerFiles {
+    // Name of the directories the service stores bookkeeping data under.
+    private static final String BACKUP_PERSISTENT_DIR = "backup";
+    private static final String BACKUP_STAGING_DIR = "backup_stage";
+
+    private static File getBaseDir(int userId) {
+        return Environment.getDataSystemCeDirectory(userId);
+    }
+
+    static File getBaseStateDir(int userId) {
+        if (userId != UserHandle.USER_SYSTEM) {
+            return new File(getBaseDir(userId), BACKUP_PERSISTENT_DIR);
+        }
+        // TODO (b/120424138) remove if clause above and use same logic for system user.
+        // simultaneously, copy below dir to new system user dir
+        return new File(Environment.getDataDirectory(), BACKUP_PERSISTENT_DIR);
+    }
+
+    static File getDataDir(int userId) {
+        if (userId != UserHandle.USER_SYSTEM) {
+            return new File(getBaseDir(userId), BACKUP_STAGING_DIR);
+        }
+        // TODO (b/120424138) remove if clause above and use same logic for system user. Since this
+        // is a staging dir, we dont need to copy below dir to new system user dir
+        return new File(Environment.getDownloadCacheDirectory(), BACKUP_STAGING_DIR);
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 5220a59..5d44144 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -37,6 +37,7 @@
 import static com.android.server.backup.internal.BackupHandler.MSG_SCHEDULE_BACKUP_PACKAGE;
 
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.AlarmManager;
 import android.app.AppGlobals;
@@ -69,7 +70,6 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
-import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -110,9 +110,9 @@
 import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.internal.Operation;
 import com.android.server.backup.internal.PerformInitializeTask;
-import com.android.server.backup.internal.ProvisionedObserver;
 import com.android.server.backup.internal.RunBackupReceiver;
 import com.android.server.backup.internal.RunInitializeReceiver;
+import com.android.server.backup.internal.SetupObserver;
 import com.android.server.backup.keyvalue.BackupRequest;
 import com.android.server.backup.params.AdbBackupParams;
 import com.android.server.backup.params.AdbParams;
@@ -163,17 +163,9 @@
 
 /** System service that performs backup/restore operations. */
 public class UserBackupManagerService {
-    // File containing backup-enabled state.  Contains a single byte;
-    // nonzero == enabled.  File missing or contains a zero byte == disabled.
-    private static final String BACKUP_ENABLE_FILE = "backup_enabled";
-
     // Persistently track the need to do a full init.
     private static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
 
-    // Name of the directories the service stores bookkeeping data under.
-    private static final String BACKUP_PERSISTENT_DIR = "backup";
-    private static final String BACKUP_STAGING_DIR = "backup_stage";
-
     // System-private key used for backing up an app's widget state.  Must
     // begin with U+FFxx by convention (we reserve all keys starting
     // with U+FF00 or higher for system use).
@@ -216,8 +208,8 @@
 
     public static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
     public static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
-    public static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
-    public static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
+    private static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
+    private static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
 
     // Bookkeeping of in-flight operations. The operation token is the index of the entry in the
     // pending operations list.
@@ -250,28 +242,30 @@
     private static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60;  // one hour
     private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2;  // two hours
 
+    private final @UserIdInt int mUserId;
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
     private final TransportManager mTransportManager;
+    private final HandlerThread mUserBackupThread;
 
-    private Context mContext;
-    private PackageManager mPackageManager;
-    private IPackageManager mPackageManagerBinder;
-    private IActivityManager mActivityManager;
+    private final Context mContext;
+    private final PackageManager mPackageManager;
+    private final IPackageManager mPackageManagerBinder;
+    private final IActivityManager mActivityManager;
     private PowerManager mPowerManager;
-    private AlarmManager mAlarmManager;
-    private IStorageManager mStorageManager;
-    private BackupManagerConstants mConstants;
-    private PowerManager.WakeLock mWakelock;
-    private BackupHandler mBackupHandler;
+    private final AlarmManager mAlarmManager;
+    private final IStorageManager mStorageManager;
+    private final BackupManagerConstants mConstants;
+    private final PowerManager.WakeLock mWakelock;
+    private final BackupHandler mBackupHandler;
 
-    private IBackupManager mBackupManagerBinder;
+    private final IBackupManager mBackupManagerBinder;
 
     private boolean mEnabled;   // access to this is synchronized on 'this'
-    private boolean mProvisioned;
+    private boolean mSetupComplete;
     private boolean mAutoRestore;
 
-    private PendingIntent mRunBackupIntent;
-    private PendingIntent mRunInitIntent;
+    private final PendingIntent mRunBackupIntent;
+    private final PendingIntent mRunInitIntent;
 
     private final ArraySet<String> mPendingInits = new ArraySet<>();  // transport names
 
@@ -279,7 +273,7 @@
     private final SparseArray<HashSet<String>> mBackupParticipants = new SparseArray<>();
 
     // Backups that we haven't started yet.  Keys are package names.
-    private HashMap<String, BackupRequest> mPendingBackups = new HashMap<>();
+    private final HashMap<String, BackupRequest> mPendingBackups = new HashMap<>();
 
     // locking around the pending-backup management
     private final Object mQueueLock = new Object();
@@ -321,9 +315,6 @@
 
     private ActiveRestoreSession mActiveRestoreSession;
 
-    // Watch the device provisioning operation during setup
-    private ContentObserver mProvisionedObserver;
-
     /**
      * mCurrentOperations contains the list of currently active operations.
      *
@@ -349,15 +340,15 @@
     private final SparseArray<Operation> mCurrentOperations = new SparseArray<>();
     private final Object mCurrentOpLock = new Object();
     private final Random mTokenGenerator = new Random();
-    final AtomicInteger mNextToken = new AtomicInteger();
+    private final AtomicInteger mNextToken = new AtomicInteger();
 
     // Where we keep our journal files and other bookkeeping.
-    private File mBaseStateDir;
-    private File mDataDir;
-    private File mJournalDir;
+    private final File mBaseStateDir;
+    private final File mDataDir;
+    private final File mJournalDir;
     @Nullable
     private DataChangedJournal mJournal;
-    private File mFullBackupScheduleFile;
+    private final File mFullBackupScheduleFile;
 
     // Keep a log of all the apps we've ever backed up.
     private ProcessedPackagesJournal mProcessedPackagesJournal;
@@ -371,17 +362,17 @@
      * Creates an instance of {@link UserBackupManagerService} and initializes state for it. This
      * includes setting up the directories where we keep our bookkeeping and transport management.
      *
-     * @see #createAndInitializeService(Context, Trampoline, HandlerThread, File, File,
+     * @see #createAndInitializeService(int, Context, Trampoline, HandlerThread, File, File,
      *     TransportManager)
      */
     static UserBackupManagerService createAndInitializeService(
+            @UserIdInt int userId,
             Context context,
             Trampoline trampoline,
-            HandlerThread backupThread,
             Set<ComponentName> transportWhitelist) {
         String currentTransport =
-                Settings.Secure.getString(
-                        context.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
+                Settings.Secure.getStringForUser(
+                        context.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT, userId);
         if (TextUtils.isEmpty(currentTransport)) {
             currentTransport = null;
         }
@@ -392,21 +383,33 @@
         TransportManager transportManager =
                 new TransportManager(context, transportWhitelist, currentTransport);
 
-        File baseStateDir = new File(Environment.getDataDirectory(), BACKUP_PERSISTENT_DIR);
+        File baseStateDir = UserBackupManagerFiles.getBaseStateDir(userId);
+        File dataDir = UserBackupManagerFiles.getDataDir(userId);
 
-        // This dir on /cache is managed directly in init.rc
-        File dataDir = new File(Environment.getDownloadCacheDirectory(), BACKUP_STAGING_DIR);
+        HandlerThread userBackupThread =
+                new HandlerThread("backup-" + userId, Process.THREAD_PRIORITY_BACKGROUND);
+        userBackupThread.start();
+        if (DEBUG) {
+            Slog.d(TAG, "Started thread " + userBackupThread.getName() + " for user " + userId);
+        }
 
         return createAndInitializeService(
-                context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
+                userId,
+                context,
+                trampoline,
+                userBackupThread,
+                baseStateDir,
+                dataDir,
+                transportManager);
     }
 
     /**
      * Creates an instance of {@link UserBackupManagerService}.
      *
+     * @param userId The user which this service is for.
      * @param context The system server context.
      * @param trampoline A reference to the proxy to {@link BackupManagerService}.
-     * @param backupThread The thread running backup/restore operations for the user.
+     * @param userBackupThread The thread running backup/restore operations for the user.
      * @param baseStateDir The directory we store the user's persistent bookkeeping data.
      * @param dataDir The directory we store the user's temporary staging data.
      * @param transportManager The {@link TransportManager} responsible for handling the user's
@@ -414,23 +417,45 @@
      */
     @VisibleForTesting
     public static UserBackupManagerService createAndInitializeService(
+            @UserIdInt int userId,
             Context context,
             Trampoline trampoline,
-            HandlerThread backupThread,
+            HandlerThread userBackupThread,
             File baseStateDir,
             File dataDir,
             TransportManager transportManager) {
         return new UserBackupManagerService(
-                context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
+                userId,
+                context,
+                trampoline,
+                userBackupThread,
+                baseStateDir,
+                dataDir,
+                transportManager);
+    }
+
+    /**
+     * Returns the value of {@link Settings.Secure#USER_SETUP_COMPLETE} for the specified user
+     * {@code userId} as a {@code boolean}.
+     */
+    public static boolean getSetupCompleteSettingForUser(Context context, int userId) {
+        return Settings.Secure.getIntForUser(
+                context.getContentResolver(),
+                Settings.Secure.USER_SETUP_COMPLETE,
+                0,
+                userId)
+                != 0;
     }
 
     private UserBackupManagerService(
+            @UserIdInt int userId,
             Context context,
             Trampoline parent,
-            HandlerThread backupThread,
+            HandlerThread userBackupThread,
             File baseStateDir,
             File dataDir,
             TransportManager transportManager) {
+        mUserId = userId;
         mContext = checkNotNull(context, "context cannot be null");
         mPackageManager = context.getPackageManager();
         mPackageManagerBinder = AppGlobals.getPackageManager();
@@ -447,21 +472,22 @@
                 BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
         mAgentTimeoutParameters.start();
 
-        // spin up the backup/restore handler thread
-        checkNotNull(backupThread, "backupThread cannot be null");
-        mBackupHandler = new BackupHandler(this, backupThread.getLooper());
+        checkNotNull(userBackupThread, "userBackupThread cannot be null");
+        mUserBackupThread = userBackupThread;
+        mBackupHandler = new BackupHandler(this, userBackupThread.getLooper());
 
         // Set up our bookkeeping
         final ContentResolver resolver = context.getContentResolver();
-        mProvisioned = Settings.Global.getInt(resolver,
-                Settings.Global.DEVICE_PROVISIONED, 0) != 0;
-        mAutoRestore = Settings.Secure.getInt(resolver,
-                Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
+        mSetupComplete = getSetupCompleteSettingForUser(context, userId);
+        mAutoRestore = Settings.Secure.getIntForUser(resolver,
+                Settings.Secure.BACKUP_AUTO_RESTORE, 1, userId) != 0;
 
-        mProvisionedObserver = new ProvisionedObserver(this, mBackupHandler);
+        ContentObserver setupObserver = new SetupObserver(this, mBackupHandler);
         resolver.registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
-                false, mProvisionedObserver);
+                Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE),
+                /* notifyForDescendents */ false,
+                setupObserver,
+                mUserId);
 
         mBaseStateDir = checkNotNull(baseStateDir, "baseStateDir cannot be null");
         mBaseStateDir.mkdirs();
@@ -530,6 +556,20 @@
         mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
     }
 
+    void initializeBackupEnableState() {
+        boolean isEnabled = UserBackupManagerFilePersistedSettings.readBackupEnableState(mUserId);
+        setBackupEnabled(isEnabled);
+    }
+
+    /** Cleans up state when the user of this service is stopped. */
+    void tearDownService() {
+        mUserBackupThread.quit();
+    }
+
+    public @UserIdInt int getUserId() {
+        return mUserId;
+    }
+
     public BackupManagerConstants getConstants() {
         return mConstants;
     }
@@ -542,51 +582,27 @@
         return mContext;
     }
 
-    public void setContext(Context context) {
-        mContext = context;
-    }
-
     public PackageManager getPackageManager() {
         return mPackageManager;
     }
 
-    public void setPackageManager(PackageManager packageManager) {
-        mPackageManager = packageManager;
-    }
-
     public IPackageManager getPackageManagerBinder() {
         return mPackageManagerBinder;
     }
 
-    public void setPackageManagerBinder(IPackageManager packageManagerBinder) {
-        mPackageManagerBinder = packageManagerBinder;
-    }
-
     public IActivityManager getActivityManager() {
         return mActivityManager;
     }
 
-    public void setActivityManager(IActivityManager activityManager) {
-        mActivityManager = activityManager;
-    }
-
     public AlarmManager getAlarmManager() {
         return mAlarmManager;
     }
 
-    public void setAlarmManager(AlarmManager alarmManager) {
-        mAlarmManager = alarmManager;
-    }
-
     @VisibleForTesting
     void setPowerManager(PowerManager powerManager) {
         mPowerManager = powerManager;
     }
 
-    public void setBackupManagerBinder(IBackupManager backupManagerBinder) {
-        mBackupManagerBinder = backupManagerBinder;
-    }
-
     public TransportManager getTransportManager() {
         return mTransportManager;
     }
@@ -599,12 +615,12 @@
         mEnabled = enabled;
     }
 
-    public boolean isProvisioned() {
-        return mProvisioned;
+    public boolean isSetupComplete() {
+        return mSetupComplete;
     }
 
-    public void setProvisioned(boolean provisioned) {
-        mProvisioned = provisioned;
+    public void setSetupComplete(boolean setupComplete) {
+        mSetupComplete = setupComplete;
     }
 
     public PowerManager.WakeLock getWakelock() {
@@ -621,35 +637,18 @@
         mWakelock.setWorkSource(workSource);
     }
 
-    public void setWakelock(PowerManager.WakeLock wakelock) {
-        mWakelock = wakelock;
-    }
-
     public Handler getBackupHandler() {
         return mBackupHandler;
     }
 
-    public void setBackupHandler(BackupHandler backupHandler) {
-        mBackupHandler = backupHandler;
-    }
-
     public PendingIntent getRunInitIntent() {
         return mRunInitIntent;
     }
 
-    public void setRunInitIntent(PendingIntent runInitIntent) {
-        mRunInitIntent = runInitIntent;
-    }
-
     public HashMap<String, BackupRequest> getPendingBackups() {
         return mPendingBackups;
     }
 
-    public void setPendingBackups(
-            HashMap<String, BackupRequest> pendingBackups) {
-        mPendingBackups = pendingBackups;
-    }
-
     public Object getQueueLock() {
         return mQueueLock;
     }
@@ -662,10 +661,6 @@
         mBackupRunning = backupRunning;
     }
 
-    public long getLastBackupPass() {
-        return mLastBackupPass;
-    }
-
     public void setLastBackupPass(long lastBackupPass) {
         mLastBackupPass = lastBackupPass;
     }
@@ -674,10 +669,6 @@
         return mClearDataLock;
     }
 
-    public boolean isClearingData() {
-        return mClearingData;
-    }
-
     public void setClearingData(boolean clearingData) {
         mClearingData = clearingData;
     }
@@ -698,11 +689,6 @@
         return mActiveRestoreSession;
     }
 
-    public void setActiveRestoreSession(
-            ActiveRestoreSession activeRestoreSession) {
-        mActiveRestoreSession = activeRestoreSession;
-    }
-
     public SparseArray<Operation> getCurrentOperations() {
         return mCurrentOperations;
     }
@@ -719,18 +705,10 @@
         return mBaseStateDir;
     }
 
-    public void setBaseStateDir(File baseStateDir) {
-        mBaseStateDir = baseStateDir;
-    }
-
     public File getDataDir() {
         return mDataDir;
     }
 
-    public void setDataDir(File dataDir) {
-        mDataDir = dataDir;
-    }
-
     @Nullable
     public DataChangedJournal getJournal() {
         return mJournal;
@@ -744,18 +722,10 @@
         return mRng;
     }
 
-    public Set<String> getAncestralPackages() {
-        return mAncestralPackages;
-    }
-
     public void setAncestralPackages(Set<String> ancestralPackages) {
         mAncestralPackages = ancestralPackages;
     }
 
-    public long getAncestralToken() {
-        return mAncestralToken;
-    }
-
     public void setAncestralToken(long ancestralToken) {
         mAncestralToken = ancestralToken;
     }
@@ -1565,11 +1535,16 @@
             throw new IllegalArgumentException("No packages are provided for backup");
         }
 
-        if (!mEnabled || !mProvisioned) {
-            Slog.i(TAG, "Backup requested but e=" + mEnabled + " p=" + mProvisioned);
+        if (!mEnabled || !mSetupComplete) {
+            Slog.i(
+                    TAG,
+                    "Backup requested but enabled="
+                            + mEnabled
+                            + " setupComplete="
+                            + mSetupComplete);
             BackupObserverUtils.sendBackupFinished(observer,
                     BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-            final int logTag = mProvisioned
+            final int logTag = mSetupComplete
                     ? BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED
                     : BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;
             monitor = BackupManagerMonitorUtils.monitorEvent(monitor, logTag, null,
@@ -2004,13 +1979,13 @@
         FullBackupEntry entry = null;
         long latency = fullBackupInterval;
 
-        if (!mEnabled || !mProvisioned) {
+        if (!mEnabled || !mSetupComplete) {
             // Backups are globally disabled, so don't proceed.  We also don't reschedule
             // the job driving automatic backups; that job will be scheduled again when
             // the user enables backup.
             if (MORE_DEBUG) {
-                Slog.i(TAG, "beginFullBackup but e=" + mEnabled
-                        + " p=" + mProvisioned + "; ignoring");
+                Slog.i(TAG, "beginFullBackup but enabled=" + mEnabled
+                        + " setupComplete=" + mSetupComplete + "; ignoring");
             }
             return false;
         }
@@ -2411,21 +2386,15 @@
         }
     }
 
-    /** Returns {@code true} if the system user has gone through SUW. */
-    public boolean deviceIsProvisioned() {
-        final ContentResolver resolver = mContext.getContentResolver();
-        return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
-    }
-
     /**
      * Used by 'adb backup' to run a backup pass for packages supplied via the command line, writing
      * the resulting data stream to the supplied {@code fd}. This method is synchronous and does not
      * return to the caller until the backup has been completed. It requires on-screen confirmation
      * by the user.
      */
-    public void adbBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
-            boolean includeShared, boolean doWidgets, boolean doAllApps, boolean includeSystem,
-            boolean compress, boolean doKeyValue, String[] pkgList) {
+    public void adbBackup(ParcelFileDescriptor fd, boolean includeApks,
+            boolean includeObbs, boolean includeShared, boolean doWidgets, boolean doAllApps,
+            boolean includeSystem, boolean compress, boolean doKeyValue, String[] pkgList) {
         mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbBackup");
 
         final int callingUserHandle = UserHandle.getCallingUserId();
@@ -2449,8 +2418,7 @@
 
         long oldId = Binder.clearCallingIdentity();
         try {
-            // Doesn't make sense to do a full backup prior to setup
-            if (!deviceIsProvisioned()) {
+            if (!mSetupComplete) {
                 Slog.i(TAG, "Backup not supported before setup");
                 return;
             }
@@ -2576,9 +2544,7 @@
         long oldId = Binder.clearCallingIdentity();
 
         try {
-            // Check whether the device has been provisioned -- we don't handle
-            // full restores prior to completing the setup process.
-            if (!deviceIsProvisioned()) {
+            if (!mSetupComplete) {
                 Slog.i(TAG, "Full restore not permitted before setup");
                 return;
             }
@@ -2728,13 +2694,12 @@
         try {
             boolean wasEnabled = mEnabled;
             synchronized (this) {
-                // TODO(b/118520567): Clean up writing backup enabled logic.
-                BackupManagerService.writeBackupEnableState(enable, UserHandle.USER_SYSTEM);
+                UserBackupManagerFilePersistedSettings.writeBackupEnableState(mUserId, enable);
                 mEnabled = enable;
             }
 
             synchronized (mQueueLock) {
-                if (enable && !wasEnabled && mProvisioned) {
+                if (enable && !wasEnabled && mSetupComplete) {
                     // if we've just been enabled, start scheduling backup passes
                     KeyValueBackupJob.schedule(mContext, mConstants);
                     scheduleNextFullBackupJob(0);
@@ -2747,7 +2712,7 @@
                     // This also constitutes an opt-out, so we wipe any data for
                     // this device from the backend.  We start that process with
                     // an alarm in order to guarantee wakelock states.
-                    if (wasEnabled && mProvisioned) {
+                    if (wasEnabled && mSetupComplete) {
                         // NOTE: we currently flush every registered transport, not just
                         // the currently-active one.
                         List<String> transportNames = new ArrayList<>();
@@ -2795,8 +2760,8 @@
         final long oldId = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
-                Settings.Secure.putInt(mContext.getContentResolver(),
-                        Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
+                Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                        Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0, mUserId);
                 mAutoRestore = doAutoRestore;
             }
         } finally {
@@ -2804,15 +2769,6 @@
         }
     }
 
-    /** Mark the backup service as having been provisioned. */
-    public void setBackupProvisioned(boolean available) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "setBackupProvisioned");
-        /*
-         * This is now a no-op; provisioning is simply the device's own setup state.
-         */
-    }
-
     /** Report whether the backup mechanism is currently enabled. */
     public boolean isBackupEnabled() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
@@ -2862,19 +2818,6 @@
         return mTransportManager.getRegisteredTransportComponents();
     }
 
-    /** Report all system whitelisted transports. */
-    public String[] getTransportWhitelist() {
-        // No permission check, intentionally.
-        Set<ComponentName> whitelistedComponents = mTransportManager.getTransportWhitelist();
-        String[] whitelistedTransports = new String[whitelistedComponents.size()];
-        int i = 0;
-        for (ComponentName component : whitelistedComponents) {
-            whitelistedTransports[i] = component.flattenToShortString();
-            i++;
-        }
-        return whitelistedTransports;
-    }
-
     /**
      * Update the attributes of the transport identified by {@code transportComponent}. If the
      * specified transport has not been bound at least once (for registration), this call will be
@@ -3031,8 +2974,8 @@
 
     private void updateStateForTransport(String newTransportName) {
         // Publish the name change
-        Settings.Secure.putString(mContext.getContentResolver(),
-                Settings.Secure.BACKUP_TRANSPORT, newTransportName);
+        Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                Settings.Secure.BACKUP_TRANSPORT, newTransportName, mUserId);
 
         // And update our current-dataset bookkeeping
         String callerLogString = "BMS.updateStateForTransport()";
@@ -3147,8 +3090,7 @@
         synchronized (mAgentConnectLock) {
             if (Binder.getCallingUid() == Process.SYSTEM_UID) {
                 Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
-                IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
-                mConnectedAgent = agent;
+                mConnectedAgent = IBackupAgent.Stub.asInterface(agentBinder);
                 mConnecting = false;
             } else {
                 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
@@ -3466,7 +3408,7 @@
     private void dumpInternal(PrintWriter pw) {
         synchronized (mQueueLock) {
             pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
-                    + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
+                    + " / " + (!mSetupComplete ? "not " : "") + "setup complete / "
                     + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
             pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
             if (mBackupRunning) pw.println("Backup currently running");
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index 5e92339..03d4e97 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -24,6 +24,7 @@
 import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
 import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 
+import android.annotation.UserIdInt;
 import android.app.ApplicationThreadConstants;
 import android.app.IBackupAgent;
 import android.app.backup.BackupTransport;
@@ -33,7 +34,6 @@
 import android.content.pm.PackageManager;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.Slog;
 
 import com.android.internal.util.Preconditions;
@@ -66,6 +66,7 @@
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     class FullBackupRunner implements Runnable {
+        private final @UserIdInt int mUserId;
         private final PackageManager mPackageManager;
         private final PackageInfo mPackage;
         private final IBackupAgent mAgent;
@@ -75,19 +76,21 @@
         private final File mFilesDir;
 
         FullBackupRunner(
+                UserBackupManagerService userBackupManagerService,
                 PackageInfo packageInfo,
                 IBackupAgent agent,
                 ParcelFileDescriptor pipe,
                 int token,
                 boolean includeApks)
                 throws IOException {
+            mUserId = userBackupManagerService.getUserId();
             mPackageManager = backupManagerService.getPackageManager();
             mPackage = packageInfo;
             mAgent = agent;
             mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor());
             mToken = token;
             mIncludeApks = includeApks;
-            mFilesDir = new File("/data/system");
+            mFilesDir = userBackupManagerService.getDataDir();
         }
 
         @Override
@@ -114,10 +117,8 @@
                     manifestFile.delete();
 
                     // Write widget data.
-                    // TODO: http://b/22388012
                     byte[] widgetData =
-                            AppWidgetBackupBridge.getWidgetState(
-                                    packageName, UserHandle.USER_SYSTEM);
+                            AppWidgetBackupBridge.getWidgetState(packageName, mUserId);
                     if (widgetData != null && widgetData.length > 0) {
                         File metadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME);
                         appMetadataBackupWriter.backupWidget(
@@ -152,9 +153,12 @@
                         backupManagerService.getBackupManagerBinder(),
                         mTransportFlags);
             } catch (IOException e) {
-                Slog.e(TAG, "Error running full backup for " + mPackage.packageName);
+                Slog.e(TAG, "Error running full backup for " + mPackage.packageName, e);
             } catch (RemoteException e) {
-                Slog.e(TAG, "Remote agent vanished during full backup of " + mPackage.packageName);
+                Slog.e(
+                        TAG,
+                        "Remote agent vanished during full backup of " + mPackage.packageName,
+                        e);
             } finally {
                 try {
                     mPipe.close();
@@ -230,7 +234,13 @@
                 pipes = ParcelFileDescriptor.createPipe();
 
                 FullBackupRunner runner =
-                        new FullBackupRunner(mPkg, mAgent, pipes[1], mOpToken, mIncludeApks);
+                        new FullBackupRunner(
+                                backupManagerService,
+                                mPkg,
+                                mAgent,
+                                pipes[1],
+                                mOpToken,
+                                mIncludeApks);
                 pipes[1].close(); // the runner has dup'd it
                 pipes[1] = null;
                 Thread t = new Thread(runner, "app-data-runner");
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 5b449c5..2ee96d1 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -317,16 +317,16 @@
         int backupRunStatus = BackupManager.SUCCESS;
 
         try {
-            if (!backupManagerService.isEnabled() || !backupManagerService.isProvisioned()) {
+            if (!backupManagerService.isEnabled() || !backupManagerService.isSetupComplete()) {
                 // Backups are globally disabled, so don't proceed.
                 if (DEBUG) {
                     Slog.i(TAG, "full backup requested but enabled=" + backupManagerService
                             .isEnabled()
-                            + " provisioned=" + backupManagerService.isProvisioned()
+                            + " setupComplete=" + backupManagerService.isSetupComplete()
                             + "; ignoring");
                 }
                 int monitoringEvent;
-                if (backupManagerService.isProvisioned()) {
+                if (backupManagerService.isSetupComplete()) {
                     monitoringEvent = BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED;
                 } else {
                     monitoringEvent = BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;
diff --git a/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java b/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java
deleted file mode 100644
index 7e2ac79..0000000
--- a/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2017 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.backup.internal;
-
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.TAG;
-
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.util.Slog;
-
-import com.android.server.backup.KeyValueBackupJob;
-import com.android.server.backup.UserBackupManagerService;
-
-public class ProvisionedObserver extends ContentObserver {
-
-    private UserBackupManagerService backupManagerService;
-
-    public ProvisionedObserver(
-            UserBackupManagerService backupManagerService, Handler handler) {
-        super(handler);
-        this.backupManagerService = backupManagerService;
-    }
-
-    public void onChange(boolean selfChange) {
-        final boolean wasProvisioned = backupManagerService.isProvisioned();
-        final boolean isProvisioned = backupManagerService.deviceIsProvisioned();
-        // latch: never unprovision
-        backupManagerService.setProvisioned(wasProvisioned || isProvisioned);
-        if (MORE_DEBUG) {
-            Slog.d(TAG, "Provisioning change: was=" + wasProvisioned
-                    + " is=" + isProvisioned + " now=" + backupManagerService.isProvisioned());
-        }
-
-        synchronized (backupManagerService.getQueueLock()) {
-            if (backupManagerService.isProvisioned() && !wasProvisioned
-                    && backupManagerService.isEnabled()) {
-                // we're now good to go, so start the backup alarms
-                if (MORE_DEBUG) {
-                    Slog.d(TAG, "Now provisioned, so starting backups");
-                }
-                KeyValueBackupJob.schedule(backupManagerService.getContext(),
-                        backupManagerService.getConstants());
-                backupManagerService.scheduleNextFullBackupJob(0);
-            }
-        }
-    }
-}
diff --git a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java b/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
index 2a5d913..3b87724 100644
--- a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
+++ b/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
@@ -59,7 +59,8 @@
                 } else {
                     // Don't run backups now if we're disabled or not yet
                     // fully set up.
-                    if (backupManagerService.isEnabled() && backupManagerService.isProvisioned()) {
+                    if (backupManagerService.isEnabled()
+                            && backupManagerService.isSetupComplete()) {
                         if (!backupManagerService.isBackupRunning()) {
                             if (DEBUG) {
                                 Slog.v(TAG, "Running a backup pass");
@@ -77,8 +78,8 @@
                             Slog.i(TAG, "Backup time but one already running");
                         }
                     } else {
-                        Slog.w(TAG, "Backup pass but e=" + backupManagerService.isEnabled() + " p="
-                                + backupManagerService.isProvisioned());
+                        Slog.w(TAG, "Backup pass but enabled=" + backupManagerService.isEnabled()
+                                + " setupComplete=" + backupManagerService.isSetupComplete());
                     }
                 }
             }
diff --git a/services/backup/java/com/android/server/backup/internal/SetupObserver.java b/services/backup/java/com/android/server/backup/internal/SetupObserver.java
new file mode 100644
index 0000000..62ae3eb
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/internal/SetupObserver.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.internal;
+
+import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
+import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.getSetupCompleteSettingForUser;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.server.backup.KeyValueBackupJob;
+import com.android.server.backup.UserBackupManagerService;
+
+/**
+ * A {@link ContentObserver} for changes to the setting {@link Settings.Secure#USER_SETUP_COMPLETE}
+ * for a particular user.
+ */
+public class SetupObserver extends ContentObserver {
+    private final UserBackupManagerService mUserBackupManagerService;
+    private final Context mContext;
+    private final int mUserId;
+
+    public SetupObserver(UserBackupManagerService userBackupManagerService, Handler handler) {
+        super(handler);
+        mUserBackupManagerService = userBackupManagerService;
+        mContext = userBackupManagerService.getContext();
+        mUserId = userBackupManagerService.getUserId();
+    }
+
+    /**
+     * Callback that executes when the setting {@link Settings.Secure#USER_SETUP_COMPLETE} changes
+     * for the user {@link #mUserId}. If the user is newly setup and backup is enabled, then we
+     * schedule a key value and full backup job for the user. If the user was previously setup and
+     * now the setting has changed to {@code false}, we don't reset the state as having gone through
+     * setup is a non-reversible action.
+     */
+    public void onChange(boolean selfChange) {
+        boolean previousSetupComplete = mUserBackupManagerService.isSetupComplete();
+        boolean newSetupComplete = getSetupCompleteSettingForUser(mContext, mUserId);
+
+        boolean resolvedSetupComplete = previousSetupComplete || newSetupComplete;
+        mUserBackupManagerService.setSetupComplete(resolvedSetupComplete);
+        if (MORE_DEBUG) {
+            Slog.d(
+                    TAG,
+                    "Setup complete change: was="
+                            + previousSetupComplete
+                            + " new="
+                            + newSetupComplete
+                            + " resolved="
+                            + resolvedSetupComplete);
+        }
+
+        synchronized (mUserBackupManagerService.getQueueLock()) {
+            // Start backup if the user is newly setup and backup is enabled.
+            if (resolvedSetupComplete
+                    && !previousSetupComplete
+                    && mUserBackupManagerService.isEnabled()) {
+                if (MORE_DEBUG) {
+                    Slog.d(TAG, "Setup complete so starting backups");
+                }
+                KeyValueBackupJob.schedule(mContext, mUserBackupManagerService.getConstants());
+                mUserBackupManagerService.scheduleNextFullBackupJob(0);
+            }
+        }
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index 0d26ea5..45a398f 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -678,12 +678,13 @@
      * Returns whether the package is in the list of the packages for which clear app data should
      * be called despite the fact that they have backup agent.
      *
-     * <p>The list is read from {@link Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE}.
+     * <p>The list is read from {@link Settings.Secure#PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE}.
      */
     private boolean shouldForceClearAppDataOnFullRestore(String packageName) {
-        String packageListString = Settings.Secure.getString(
+        String packageListString = Settings.Secure.getStringForUser(
                 mBackupManagerService.getContext().getContentResolver(),
-                Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE);
+                Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE,
+                mBackupManagerService.getUserId());
         if (TextUtils.isEmpty(packageListString)) {
             return false;
         }
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 872fe42..4b24ef9 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -20,11 +20,11 @@
 import static android.content.Context.CONTENT_CAPTURE_MANAGER_SERVICE;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManagerInternal;
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -33,7 +33,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Slog;
-import android.view.contentcapture.ContentCaptureEvent;
 import android.view.contentcapture.IContentCaptureManager;
 
 import com.android.internal.annotations.GuardedBy;
@@ -42,11 +41,11 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
 import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.List;
 
 /**
  * A service used to observe the contents of the screen.
@@ -68,14 +67,16 @@
 
     private final LocalService mLocalService = new LocalService();
 
-    public ContentCaptureManagerService(Context context) {
-        super(context, UserManager.DISALLOW_CONTENT_CAPTURE);
+    public ContentCaptureManagerService(@NonNull Context context) {
+        super(context, new FrameworkResourcesServiceNameResolver(context,
+                com.android.internal.R.string.config_defaultContentCaptureService),
+                UserManager.DISALLOW_CONTENT_CAPTURE);
     }
 
     @Override // from AbstractMasterSystemService
     protected ContentCapturePerUserService newServiceLocked(@UserIdInt int resolvedUserId,
             boolean disabled) {
-        return new ContentCapturePerUserService(this, mLock, resolvedUserId);
+        return new ContentCapturePerUserService(this, mLock, disabled, resolvedUserId);
     }
 
     @Override // from SystemService
@@ -164,8 +165,8 @@
 
         @Override
         public void startSession(@UserIdInt int userId, @NonNull IBinder activityToken,
-                @NonNull ComponentName componentName, @NonNull String sessionId,
-                int flags, @NonNull IResultReceiver result) {
+                @NonNull ComponentName componentName, @NonNull String sessionId, int flags,
+                @NonNull IResultReceiver result) {
             Preconditions.checkNotNull(activityToken);
             Preconditions.checkNotNull(componentName);
             Preconditions.checkNotNull(sessionId);
@@ -174,36 +175,23 @@
             // so we don't pass it on startSession (same for Autofill)
             final int taskId = getAmInternal().getTaskIdForActivity(activityToken, false);
 
-            // TODO(b/111276913): get from AM as well
+            // TODO(b/121260224): get from AM as well
             final int displayId = 0;
 
             synchronized (mLock) {
                 final ContentCapturePerUserService service = getServiceForUserLocked(userId);
                 service.startSessionLocked(activityToken, componentName, taskId, displayId,
-                        sessionId, flags, mAllowInstantService, result);
+                        sessionId, Binder.getCallingUid(), flags, result);
             }
         }
 
         @Override
-        public void sendEvents(@UserIdInt int userId, @NonNull String sessionId,
-                @NonNull List<ContentCaptureEvent> events) {
-            Preconditions.checkNotNull(sessionId);
-            Preconditions.checkNotNull(events);
-
-            synchronized (mLock) {
-                final ContentCapturePerUserService service = getServiceForUserLocked(userId);
-                service.sendEventsLocked(sessionId, events);
-            }
-        }
-
-        @Override
-        public void finishSession(@UserIdInt int userId, @NonNull String sessionId,
-                @Nullable List<ContentCaptureEvent> events) {
+        public void finishSession(@UserIdInt int userId, @NonNull String sessionId) {
             Preconditions.checkNotNull(sessionId);
 
             synchronized (mLock) {
                 final ContentCapturePerUserService service = getServiceForUserLocked(userId);
-                service.finishSessionLocked(sessionId, events);
+                service.finishSessionLocked(sessionId);
             }
         }
 
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index aa171f4..c467935 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -16,13 +16,14 @@
 
 package com.android.server.contentcapture;
 
+import static android.service.contentcapture.ContentCaptureService.setClientState;
+
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
 
 import android.Manifest;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.AppGlobals;
 import android.app.assist.AssistContent;
@@ -34,44 +35,80 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.service.contentcapture.ContentCaptureService;
 import android.service.contentcapture.SnapshotData;
 import android.util.ArrayMap;
 import android.util.Slog;
-import android.view.contentcapture.ContentCaptureEvent;
-import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.ContentCaptureSession;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.IResultReceiver;
+import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks;
 import com.android.server.infra.AbstractPerUserSystemService;
-import com.android.server.infra.FrameworkResourcesServiceNameResolver;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.List;
 
 /**
  * Per-user instance of {@link ContentCaptureManagerService}.
  */
 final class ContentCapturePerUserService
         extends
-        AbstractPerUserSystemService<ContentCapturePerUserService, ContentCaptureManagerService> {
+        AbstractPerUserSystemService<ContentCapturePerUserService, ContentCaptureManagerService>
+        implements ContentCaptureServiceCallbacks {
 
     private static final String TAG = ContentCaptureManagerService.class.getSimpleName();
 
     @GuardedBy("mLock")
-    private final ArrayMap<String, ContentCaptureSession> mSessions =
+    private final ArrayMap<String, ContentCaptureServerSession> mSessions =
             new ArrayMap<>();
 
+    /**
+     * Reference to the remote service.
+     *
+     * <p>It's set in the constructor, but it's also updated when the service's updated in the
+     * master's cache (for example, because a temporary service was set).
+     */
+    @GuardedBy("mLock")
+    private RemoteContentCaptureService mRemoteService;
+
     // TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's
 
-    protected ContentCapturePerUserService(
-            ContentCaptureManagerService master, Object lock, @UserIdInt int userId) {
-        super(master, new FrameworkResourcesServiceNameResolver(master.getContext(), userId, lock,
-                com.android.internal.R.string.config_defaultContentCaptureService), lock, userId);
+    ContentCapturePerUserService(@NonNull ContentCaptureManagerService master,
+            @NonNull Object lock, boolean disabled, @UserIdInt int userId) {
+        super(master, lock, userId);
+
+        updateRemoteServiceLocked(disabled);
+    }
+
+    /**
+     * Updates the reference to the remote service.
+     */
+    private void updateRemoteServiceLocked(boolean disabled) {
+        if (mRemoteService != null) {
+            if (mMaster.debug) Slog.d(TAG, "updateRemoteService(): destroying old remote service");
+            mRemoteService.destroy();
+            mRemoteService = null;
+        }
+
+        // Updates the component name
+        final ComponentName serviceComponentName = updateServiceInfoLocked();
+
+        if (serviceComponentName == null) {
+            Slog.w(TAG, "updateRemoteService(): no service componennt name");
+            return;
+        }
+
+        if (!disabled) {
+            mRemoteService = new RemoteContentCaptureService(
+                  mMaster.getContext(),
+                  ContentCaptureService.SERVICE_INTERFACE, serviceComponentName, mUserId, this,
+                  mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
+        }
     }
 
     @Override // from PerUserSystemService
-    protected ServiceInfo newServiceInfo(@NonNull ComponentName serviceComponent)
+    protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
             throws NameNotFoundException {
 
         int flags = PackageManager.GET_META_DATA;
@@ -106,17 +143,27 @@
     @GuardedBy("mLock")
     protected boolean updateLocked(boolean disabled) {
         destroyLocked();
-        return super.updateLocked(disabled);
+        final boolean disabledStateChanged = super.updateLocked(disabled);
+        updateRemoteServiceLocked(disabled);
+        return disabledStateChanged;
     }
 
-    // TODO(b/111276913): log metrics
+    @Override // from ContentCaptureServiceCallbacks
+    public void onServiceDied(@NonNull RemoteContentCaptureService service) {
+        if (mMaster.debug) Slog.d(TAG, "remote service died: " + service);
+        synchronized (mLock) {
+            removeSelfFromCacheLocked();
+        }
+    }
+
+    // TODO(b/119613670): log metrics
     @GuardedBy("mLock")
     public void startSessionLocked(@NonNull IBinder activityToken,
             @NonNull ComponentName componentName, int taskId, int displayId,
-            @NonNull String sessionId, int flags, boolean bindInstantServiceAllowed,
-            @NonNull IResultReceiver resultReceiver) {
+            @NonNull String sessionId, int uid, int flags,
+            @NonNull IResultReceiver clientReceiver) {
         if (!isEnabledLocked()) {
-            sendToClient(resultReceiver, ContentCaptureManager.STATE_DISABLED);
+            setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED, /* binder=*/ null);
             return;
         }
         final ComponentName serviceComponentName = getServiceComponentName();
@@ -130,81 +177,56 @@
             return;
         }
 
-        ContentCaptureSession session = mSessions.get(sessionId);
-        if (session != null) {
-            if (mMaster.debug) {
-                Slog.d(TAG, "startSession(): reusing session " + sessionId + " for "
-                        + componentName);
-            }
-            // TODO(b/111276913): check if local ids match and decide what to do if they don't
-            // TODO(b/111276913): should we call session.notifySessionStartedLocked() again??
-            // if not, move notifySessionStartedLocked() into session constructor
-            sendToClient(resultReceiver, ContentCaptureManager.STATE_ACTIVE);
+        final ContentCaptureServerSession existingSession = mSessions.get(sessionId);
+        if (existingSession != null) {
+            Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
+                    + ": ignoring because it already exists for " + existingSession.mActivityToken);
+            setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_DUPLICATED_ID,
+                    /* binder=*/ null);
             return;
         }
 
-        session = new ContentCaptureSession(getContext(), mUserId, mLock, activityToken,
-                this, serviceComponentName, componentName, taskId, displayId, sessionId, flags,
-                bindInstantServiceAllowed, mMaster.verbose);
-        if (mMaster.verbose) {
-            Slog.v(TAG, "startSession(): new session for " + componentName + " and id "
-                    + sessionId);
+        if (mRemoteService == null) {
+            updateRemoteServiceLocked(/* disabled= */ false); // already checked for isEnabled
         }
-        mSessions.put(sessionId, session);
-        session.notifySessionStartedLocked();
-        sendToClient(resultReceiver, ContentCaptureManager.STATE_ACTIVE);
+
+        if (mRemoteService == null) {
+            // TODO(b/119613670): log metrics
+            Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
+                    + ": ignoring because service is not set");
+            // TODO(b/111276913): use a new disabled state?
+            setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED,
+                    /* binder=*/ null);
+            return;
+        }
+
+        final ContentCaptureServerSession newSession = new ContentCaptureServerSession(
+                activityToken, this, mRemoteService, componentName, taskId,
+                displayId, sessionId, uid, flags);
+        if (mMaster.verbose) {
+            Slog.v(TAG, "startSession(): new session for "
+                    + ComponentName.flattenToShortString(componentName) + " and id " + sessionId);
+        }
+        mSessions.put(sessionId, newSession);
+        newSession.notifySessionStartedLocked(clientReceiver);
     }
 
-    // TODO(b/111276913): log metrics
+    // TODO(b/119613670): log metrics
     @GuardedBy("mLock")
-    public void finishSessionLocked(@NonNull String sessionId,
-            @Nullable List<ContentCaptureEvent> events) {
+    public void finishSessionLocked(@NonNull String sessionId) {
         if (!isEnabledLocked()) {
             return;
         }
 
-        final ContentCaptureSession session = mSessions.get(sessionId);
+        final ContentCaptureServerSession session = mSessions.get(sessionId);
         if (session == null) {
             if (mMaster.debug) {
                 Slog.d(TAG, "finishSession(): no session with id" + sessionId);
             }
             return;
         }
-        if (events != null && !events.isEmpty()) {
-            // TODO(b/111276913): for now we're sending the events and the onDestroy() in 2 separate
-            // calls because it's not clear yet whether we'll change the manager to send events
-            // to the service directly (i.e., without passing through system server). Once we
-            // decide, we might need to split IContentCaptureManager.onSessionLifecycle() in 2
-            // methods, one for start and another for finish (and passing the events to finish),
-            // otherwise the service might receive the 2 calls out of order.
-            session.sendEventsLocked(events);
-        }
-        if (mMaster.verbose) {
-            Slog.v(TAG, "finishSession(" + (events == null ? 0 : events.size()) + " events): "
-                    + session);
-        }
-        session.removeSelfLocked(true);
-    }
-
-    // TODO(b/111276913): need to figure out why some events are sent before session is started;
-    // probably because ContentCaptureManager is not buffering them until it gets the session back
-    @GuardedBy("mLock")
-    public void sendEventsLocked(@NonNull String sessionId,
-            @NonNull List<ContentCaptureEvent> events) {
-        if (!isEnabledLocked()) {
-            return;
-        }
-        final ContentCaptureSession session = mSessions.get(sessionId);
-        if (session == null) {
-            if (mMaster.verbose) {
-                Slog.v(TAG, "sendEvents(): no session for " + sessionId);
-            }
-            return;
-        }
-        if (mMaster.verbose) {
-            Slog.v(TAG, "sendEvents(): id=" + sessionId + ", events=" + events.size());
-        }
-        session.sendEventsLocked(events);
+        if (mMaster.verbose) Slog.v(TAG, "finishSession(): id=" + sessionId);
+        session.removeSelfLocked(/* notifyRemoteService= */ true);
     }
 
     @GuardedBy("mLock")
@@ -212,7 +234,7 @@
             @NonNull Bundle data) {
         final String id = getSessionId(activityToken);
         if (id != null) {
-            final ContentCaptureSession session = mSessions.get(id);
+            final ContentCaptureServerSession session = mSessions.get(id);
             final Bundle assistData = data.getBundle(ASSIST_KEY_DATA);
             final AssistStructure assistStructure = data.getParcelable(ASSIST_KEY_STRUCTURE);
             final AssistContent assistContent = data.getParcelable(ASSIST_KEY_CONTENT);
@@ -237,9 +259,9 @@
     }
 
     @GuardedBy("mLock")
-    private ContentCaptureSession getSession(@NonNull IBinder activityToken) {
+    private ContentCaptureServerSession getSession(@NonNull IBinder activityToken) {
         for (int i = 0; i < mSessions.size(); i++) {
-            final ContentCaptureSession session = mSessions.valueAt(i);
+            final ContentCaptureServerSession session = mSessions.valueAt(i);
             if (session.mActivityToken.equals(activityToken)) {
                 return session;
             }
@@ -262,7 +284,7 @@
     void destroySessionsLocked() {
         final int numSessions = mSessions.size();
         for (int i = 0; i < numSessions; i++) {
-            final ContentCaptureSession session = mSessions.valueAt(i);
+            final ContentCaptureServerSession session = mSessions.valueAt(i);
             session.destroyLocked(true);
         }
         mSessions.clear();
@@ -272,7 +294,7 @@
     void listSessionsLocked(ArrayList<String> output) {
         final int numSessions = mSessions.size();
         for (int i = 0; i < numSessions; i++) {
-            final ContentCaptureSession session = mSessions.valueAt(i);
+            final ContentCaptureServerSession session = mSessions.valueAt(i);
             output.add(session.toShortString());
         }
     }
@@ -280,15 +302,21 @@
     @Override
     protected void dumpLocked(String prefix, PrintWriter pw) {
         super.dumpLocked(prefix, pw);
+
+        final String prefix2 = prefix + "  ";
+        if (mRemoteService != null) {
+            pw.print(prefix); pw.println("remote service:");
+            mRemoteService.dump(prefix2, pw);
+        }
+
         if (mSessions.isEmpty()) {
             pw.print(prefix); pw.println("no sessions");
         } else {
             final int size = mSessions.size();
             pw.print(prefix); pw.print("number sessions: "); pw.println(size);
-            final String prefix2 = prefix + "  ";
             for (int i = 0; i < size; i++) {
                 pw.print(prefix); pw.print("session@"); pw.println(i);
-                final ContentCaptureSession session = mSessions.valueAt(i);
+                final ContentCaptureServerSession session = mSessions.valueAt(i);
                 session.dumpLocked(prefix2, pw);
             }
         }
@@ -300,19 +328,11 @@
     @GuardedBy("mLock")
     private String getSessionId(@NonNull IBinder activityToken) {
         for (int i = 0; i < mSessions.size(); i++) {
-            ContentCaptureSession session = mSessions.valueAt(i);
+            ContentCaptureServerSession session = mSessions.valueAt(i);
             if (session.isActivitySession(activityToken)) {
                 return mSessions.keyAt(i);
             }
         }
         return null;
     }
-
-    private static void sendToClient(@NonNull IResultReceiver resultReceiver, int value) {
-        try {
-            resultReceiver.send(value, null);
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Error async reporting result to client: " + e);
-        }
-    }
 }
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
new file mode 100644
index 0000000..ebe0083
--- /dev/null
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.contentcapture;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.os.IBinder;
+import android.service.contentcapture.ContentCaptureService;
+import android.service.contentcapture.SnapshotData;
+import android.util.Slog;
+import android.view.contentcapture.ContentCaptureContext;
+import android.view.contentcapture.ContentCaptureSessionId;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+
+final class ContentCaptureServerSession {
+
+    private static final String TAG = ContentCaptureServerSession.class.getSimpleName();
+
+    final IBinder mActivityToken;
+    private final ContentCapturePerUserService mService;
+    private final RemoteContentCaptureService mRemoteService;
+    private final ContentCaptureContext mContentCaptureContext;
+
+    /**
+     * Canonical session id.
+     */
+    private final String mId;
+
+    /**
+     * UID of the app whose contents is being captured.
+     */
+    private final int mUid;
+
+    ContentCaptureServerSession(@NonNull IBinder activityToken,
+            @NonNull ContentCapturePerUserService service,
+            @NonNull RemoteContentCaptureService remoteService,
+            @NonNull ComponentName appComponentName,
+            int taskId, int displayId, @NonNull String sessionId, int uid, int flags) {
+        mActivityToken = activityToken;
+        mService = service;
+        mId = Preconditions.checkNotNull(sessionId);
+        mUid = uid;
+        mRemoteService = remoteService;
+        mContentCaptureContext = new ContentCaptureContext(/* clientContext= */ null,
+                appComponentName, taskId, displayId, flags);
+    }
+
+    /**
+     * Returns whether this session is for the given activity.
+     */
+    boolean isActivitySession(@NonNull IBinder activityToken) {
+        return mActivityToken.equals(activityToken);
+    }
+
+    /**
+     * Notifies the {@link ContentCaptureService} that the service started.
+     */
+    @GuardedBy("mLock")
+    public void notifySessionStartedLocked(@NonNull IResultReceiver clientReceiver) {
+        mRemoteService.onSessionStarted(mContentCaptureContext, mId, mUid, clientReceiver);
+    }
+
+    /**
+     * Notifies the {@link ContentCaptureService} of a snapshot of an activity.
+     */
+    @GuardedBy("mLock")
+    public void sendActivitySnapshotLocked(@NonNull SnapshotData snapshotData) {
+        mRemoteService.onActivitySnapshotRequest(mId, snapshotData);
+    }
+
+    /**
+     * Cleans up the session and removes it from the service.
+     *
+     * @param notifyRemoteService whether it should trigger a {@link
+     * ContentCaptureService#onDestroyContentCaptureSession(ContentCaptureSessionId)}
+     * request.
+     */
+    @GuardedBy("mLock")
+    public void removeSelfLocked(boolean notifyRemoteService) {
+        try {
+            destroyLocked(notifyRemoteService);
+        } finally {
+            mService.removeSessionLocked(mId);
+        }
+    }
+
+    /**
+     * Cleans up the session, but not removes it from the service.
+     *
+     * @param notifyRemoteService whether it should trigger a {@link
+     * ContentCaptureService#onDestroyContentCaptureSession(ContentCaptureSessionId)}
+     * request.
+     */
+    @GuardedBy("mLock")
+    public void destroyLocked(boolean notifyRemoteService) {
+        if (mService.isVerbose()) {
+            Slog.v(TAG, "destroy(notifyRemoteService=" + notifyRemoteService + ")");
+        }
+        // TODO(b/111276913): must call client to set session as FINISHED_BY_SERVER
+        if (notifyRemoteService) {
+            mRemoteService.onSessionFinished(mId);
+        }
+    }
+
+    @GuardedBy("mLock")
+    public void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
+        pw.print(prefix); pw.print("id: ");  pw.print(mId); pw.println();
+        pw.print(prefix); pw.print("uid: ");  pw.print(mUid); pw.println();
+        pw.print(prefix); pw.print("context: ");  mContentCaptureContext.dump(pw); pw.println();
+        pw.print(prefix); pw.print("activity token: "); pw.println(mActivityToken);
+        pw.print(prefix); pw.print("has autofill callback: ");
+    }
+
+    String toShortString() {
+        return mId  + ":" + mActivityToken;
+    }
+
+    @Override
+    public String toString() {
+        return "ContentCaptureSession[id=" + mId + ", act=" + mActivityToken + "]";
+    }
+}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureSession.java
deleted file mode 100644
index a4012d5..0000000
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureSession.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.contentcapture;
-
-import android.annotation.NonNull;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.IBinder;
-import android.service.contentcapture.ContentCaptureService;
-import android.service.contentcapture.InteractionContext;
-import android.service.contentcapture.InteractionSessionId;
-import android.service.contentcapture.SnapshotData;
-import android.util.Slog;
-import android.view.contentcapture.ContentCaptureEvent;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
-import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks;
-
-import java.io.PrintWriter;
-import java.util.List;
-
-final class ContentCaptureSession implements ContentCaptureServiceCallbacks {
-
-    private static final String TAG = "ContentCaptureSession";
-
-    private final Object mLock;
-    final IBinder mActivityToken;
-    private final ContentCapturePerUserService mService;
-    private final RemoteContentCaptureService mRemoteService;
-    private final InteractionContext mInterationContext;
-    private final String mId;
-
-    ContentCaptureSession(@NonNull Context context, int userId, @NonNull Object lock,
-            @NonNull IBinder activityToken, @NonNull ContentCapturePerUserService service,
-            @NonNull ComponentName serviceComponentName, @NonNull ComponentName appComponentName,
-            int taskId, int displayId, @NonNull String sessionId, int flags,
-            boolean bindInstantServiceAllowed, boolean verbose) {
-        mLock = lock;
-        mActivityToken = activityToken;
-        mService = service;
-        mId = Preconditions.checkNotNull(sessionId);
-        mRemoteService = new RemoteContentCaptureService(context,
-                ContentCaptureService.SERVICE_INTERFACE, serviceComponentName, userId, this,
-                bindInstantServiceAllowed, verbose);
-        mInterationContext = new InteractionContext(appComponentName, taskId, displayId, flags);
-    }
-
-    /**
-     * Returns whether this session is for the given activity.
-     */
-    boolean isActivitySession(@NonNull IBinder activityToken) {
-        return mActivityToken.equals(activityToken);
-    }
-
-    /**
-     * Notifies the {@link ContentCaptureService} that the service started.
-     */
-    @GuardedBy("mLock")
-    public void notifySessionStartedLocked() {
-        mRemoteService.onSessionLifecycleRequest(mInterationContext, mId);
-    }
-
-    /**
-     * Notifies the {@link ContentCaptureService} of a batch of events.
-     */
-    public void sendEventsLocked(@NonNull List<ContentCaptureEvent> events) {
-        mRemoteService.onContentCaptureEventsRequest(mId, events);
-    }
-
-    /**
-     * Notifies the {@link ContentCaptureService} of a snapshot of an activity.
-     */
-    @GuardedBy("mLock")
-    public void sendActivitySnapshotLocked(@NonNull SnapshotData snapshotData) {
-        mRemoteService.onActivitySnapshotRequest(mId, snapshotData);
-    }
-
-    /**
-     * Cleans up the session and removes it from the service.
-     *
-     * @param notifyRemoteService whether it should trigger a {@link
-     * ContentCaptureService#onDestroyInteractionSession(InteractionSessionId)}
-     * request.
-     */
-    @GuardedBy("mLock")
-    public void removeSelfLocked(boolean notifyRemoteService) {
-        try {
-            destroyLocked(notifyRemoteService);
-        } finally {
-            mService.removeSessionLocked(mId);
-        }
-    }
-
-    /**
-     * Cleans up the session, but not removes it from the service.
-     *
-     * @param notifyRemoteService whether it should trigger a {@link
-     * ContentCaptureService#onDestroyInteractionSession(InteractionSessionId)}
-     * request.
-     */
-    @GuardedBy("mLock")
-    public void destroyLocked(boolean notifyRemoteService) {
-        if (mService.isVerbose()) {
-            Slog.v(TAG, "destroyLocked(notifyRemoteService=" + notifyRemoteService + ")");
-        }
-        // TODO(b/111276913): must call client to set session as FINISHED_BY_SERVER
-        if (notifyRemoteService) {
-            mRemoteService.onSessionLifecycleRequest(/* context= */ null, mId);
-        }
-    }
-
-    @Override // from RemoteContentCaptureServiceCallbacks
-    public void onServiceDied(@NonNull RemoteContentCaptureService service) {
-        // TODO(b/111276913): implement (remove session from PerUserSession?)
-        if (mService.isDebug()) {
-            Slog.d(TAG, "onServiceDied() for " + mId);
-        }
-        synchronized (mLock) {
-            removeSelfLocked(/* notifyRemoteService= */ false);
-        }
-    }
-
-    @GuardedBy("mLock")
-    public void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
-        pw.print(prefix); pw.print("id: ");  pw.print(mId); pw.println();
-        pw.print(prefix); pw.print("context: ");  mInterationContext.dump(pw); pw.println();
-        pw.print(prefix); pw.print("activity token: "); pw.println(mActivityToken);
-        pw.print(prefix); pw.print("has autofill callback: ");
-    }
-
-    String toShortString() {
-        return mId  + ":" + mActivityToken;
-    }
-
-    @Override
-    public String toString() {
-        return "ContentCaptureSession[id=" + mId + ", act=" + mActivityToken + "]";
-    }
-}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
index 33b6c8d..8241628 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
@@ -20,23 +20,19 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.IBinder;
-import android.service.contentcapture.ContentCaptureEventsRequest;
 import android.service.contentcapture.IContentCaptureService;
-import android.service.contentcapture.InteractionContext;
 import android.service.contentcapture.SnapshotData;
 import android.text.format.DateUtils;
-import android.view.contentcapture.ContentCaptureEvent;
+import android.util.Slog;
+import android.view.contentcapture.ContentCaptureContext;
 
-import com.android.server.infra.AbstractMultiplePendingRequestsRemoteService;
-
-import java.util.List;
+import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
+import com.android.internal.os.IResultReceiver;
 
 final class RemoteContentCaptureService
         extends AbstractMultiplePendingRequestsRemoteService<RemoteContentCaptureService,
         IContentCaptureService> {
 
-    // TODO(b/117779333): changed it so it's permanentely bound
-    private static final long TIMEOUT_IDLE_BIND_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
     private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
 
     RemoteContentCaptureService(Context context, String serviceInterface,
@@ -45,47 +41,59 @@
             boolean verbose) {
         super(context, serviceInterface, componentName, userId, callbacks,
                 bindInstantServiceAllowed, verbose, /* initialCapacity= */ 2);
+
+        // Bind right away, which will trigger a onConnected() on service's
+        scheduleBind();
     }
 
-    @Override // from RemoteService
+    @Override // from AbstractRemoteService
     protected IContentCaptureService getServiceInterface(@NonNull IBinder service) {
         return IContentCaptureService.Stub.asInterface(service);
     }
 
-    // TODO(b/111276913): modify super class to allow permanent binding when value is 0 or negative
-    @Override // from RemoteService
+    @Override // from AbstractRemoteService
     protected long getTimeoutIdleBindMillis() {
         // TODO(b/111276913): read from Settings so it can be changed in the field
-        return TIMEOUT_IDLE_BIND_MILLIS;
+        return PERMANENT_BOUND_TIMEOUT_MS;
     }
 
-    @Override // from RemoteService
+    @Override // from AbstractRemoteService
     protected long getRemoteRequestMillis() {
         // TODO(b/111276913): read from Settings so it can be changed in the field
         return TIMEOUT_REMOTE_REQUEST_MILLIS;
     }
 
-    /**
-     * Called by {@link ContentCaptureSession} to generate a call to the
-     * {@link RemoteContentCaptureService} to indicate the session was created (when {@code context}
-     * is not {@code null} or destroyed (when {@code context} is {@code null}).
-     */
-    public void onSessionLifecycleRequest(@Nullable InteractionContext context,
-            @NonNull String sessionId) {
-        scheduleAsyncRequest((s) -> s.onSessionLifecycle(context, sessionId));
+    @Override // from RemoteService
+    protected void handleOnConnectedStateChanged(boolean state) {
+        if (state && getTimeoutIdleBindMillis() != PERMANENT_BOUND_TIMEOUT_MS) {
+            scheduleUnbind();
+        }
+        try {
+            mService.onConnectedStateChanged(state);
+        } catch (Exception e) {
+            Slog.w(mTag, "Exception calling onConnectedStateChanged(" + state + "): " + e);
+        }
     }
 
     /**
-     * Called by {@link ContentCaptureSession} to send a batch of events to the service.
+     * Called by {@link ContentCaptureServerSession} to generate a call to the
+     * {@link RemoteContentCaptureService} to indicate the session was created.
      */
-    public void onContentCaptureEventsRequest(@NonNull String sessionId,
-            @NonNull List<ContentCaptureEvent> events) {
-        scheduleAsyncRequest((s) -> s.onContentCaptureEventsRequest(sessionId,
-                new ContentCaptureEventsRequest(events)));
+    public void onSessionStarted(@Nullable ContentCaptureContext context,
+            @NonNull String sessionId, int uid, @NonNull IResultReceiver clientReceiver) {
+        scheduleAsyncRequest((s) -> s.onSessionStarted(context, sessionId, uid, clientReceiver));
     }
 
     /**
-     * Called by {@link ContentCaptureSession} to send snapshot data to the service.
+     * Called by {@link ContentCaptureServerSession} to generate a call to the
+     * {@link RemoteContentCaptureService} to indicate the session was finished.
+     */
+    public void onSessionFinished(@NonNull String sessionId) {
+        scheduleAsyncRequest((s) -> s.onSessionFinished(sessionId));
+    }
+
+    /**
+     * Called by {@link ContentCaptureServerSession} to send snapshot data to the service.
      */
     public void onActivitySnapshotRequest(@NonNull String sessionId,
             @NonNull SnapshotData snapshotData) {
@@ -94,8 +102,8 @@
 
     public interface ContentCaptureServiceCallbacks
             extends VultureCallback<RemoteContentCaptureService> {
-        // NOTE: so far we don't need to notify the callback implementation (an inner class on
-        // AutofillManagerServiceImpl) of the request results (success, timeouts, etc..), so this
+        // NOTE: so far we don't need to notify the callback implementation
+        // (ContentCaptureServerSession) of the request results (success, timeouts, etc..), so this
         // callback interface is empty.
     }
 }
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 08034f7..0fa996e 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1123,8 +1123,6 @@
         rescheduleKernelAlarmsLocked();
         updateNextAlarmClockLocked();
 
-        // And send a TIME_TICK right now, since it is important to get the UI updated.
-        mHandler.post(() ->  getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL));
     }
 
     static final class InFlight {
@@ -1298,7 +1296,7 @@
         mInjector.init();
 
         synchronized (mLock) {
-            mHandler = new AlarmHandler(Looper.myLooper());
+            mHandler = new AlarmHandler();
             mConstants = new Constants(mHandler);
 
             mNextWakeup = mNextNonWakeup = 0;
@@ -3050,6 +3048,9 @@
                         mNonInteractiveTime = dur;
                     }
                 }
+                // And send a TIME_TICK right now, since it is important to get the UI updated.
+                mHandler.post(() ->
+                        getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL));
             } else {
                 mNonInteractiveStartTime = nowELAPSED;
             }
@@ -3838,7 +3839,8 @@
         mWakeLock.setWorkSource(null);
     }
 
-    private class AlarmHandler extends Handler {
+    @VisibleForTesting
+    class AlarmHandler extends Handler {
         public static final int ALARM_EVENT = 1;
         public static final int SEND_NEXT_ALARM_CLOCK_CHANGED = 2;
         public static final int LISTENER_TIMEOUT = 3;
@@ -3847,8 +3849,8 @@
         public static final int APP_STANDBY_PAROLE_CHANGED = 6;
         public static final int REMOVE_FOR_STOPPED = 7;
 
-        AlarmHandler(Looper looper) {
-            super(looper);
+        AlarmHandler() {
+            super(Looper.myLooper());
         }
 
         public void postRemoveForStopped(int uid) {
@@ -3961,8 +3963,8 @@
 
             final WorkSource workSource = null; // Let system take blame for time tick events.
             setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0,
-                    0, null, mTimeTickTrigger, null, AlarmManager.FLAG_STANDALONE, workSource,
-                    null, Process.myUid(), "android");
+                    0, null, mTimeTickTrigger, "TIME_TICK", AlarmManager.FLAG_STANDALONE,
+                    workSource, null, Process.myUid(), "android");
 
             // Finally, remember when we set the tick alarm
             synchronized (mLock) {
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 8d912fa..3cdf09e 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -65,7 +65,6 @@
 import android.os.ShellCallback;
 import android.os.ShellCommand;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
@@ -86,6 +85,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IAppOpsActiveCallback;
 import com.android.internal.app.IAppOpsCallback;
+import com.android.internal.app.IAppOpsNotedCallback;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.os.Zygote;
 import com.android.internal.util.ArrayUtils;
@@ -456,6 +456,7 @@
     final ArrayMap<String, ArraySet<ModeCallback>> mPackageModeWatchers = new ArrayMap<>();
     final ArrayMap<IBinder, ModeCallback> mModeWatchers = new ArrayMap<>();
     final ArrayMap<IBinder, SparseArray<ActiveCallback>> mActiveWatchers = new ArrayMap<>();
+    final ArrayMap<IBinder, SparseArray<NotedCallback>> mNotedWatchers = new ArrayMap<>();
     final SparseArray<SparseArray<Restriction>> mAudioRestrictions = new SparseArray<>();
 
     final class ModeCallback implements DeathRecipient {
@@ -475,6 +476,7 @@
             try {
                 mCallback.asBinder().linkToDeath(this, 0);
             } catch (RemoteException e) {
+                /*ignored*/
             }
         }
 
@@ -524,6 +526,7 @@
             try {
                 mCallback.asBinder().linkToDeath(this, 0);
             } catch (RemoteException e) {
+                /*ignored*/
             }
         }
 
@@ -552,6 +555,50 @@
         }
     }
 
+    final class NotedCallback implements DeathRecipient {
+        final IAppOpsNotedCallback mCallback;
+        final int mWatchingUid;
+        final int mCallingUid;
+        final int mCallingPid;
+
+        NotedCallback(IAppOpsNotedCallback callback, int watchingUid, int callingUid,
+                int callingPid) {
+            mCallback = callback;
+            mWatchingUid = watchingUid;
+            mCallingUid = callingUid;
+            mCallingPid = callingPid;
+            try {
+                mCallback.asBinder().linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                /*ignored*/
+            }
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder(128);
+            sb.append("NotedCallback{");
+            sb.append(Integer.toHexString(System.identityHashCode(this)));
+            sb.append(" watchinguid=");
+            UserHandle.formatUid(sb, mWatchingUid);
+            sb.append(" from uid=");
+            UserHandle.formatUid(sb, mCallingUid);
+            sb.append(" pid=");
+            sb.append(mCallingPid);
+            sb.append('}');
+            return sb.toString();
+        }
+
+        void destroy() {
+            mCallback.asBinder().unlinkToDeath(this, 0);
+        }
+
+        @Override
+        public void binderDied() {
+            stopWatchingNoted(mCallback);
+        }
+    }
+
     final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
 
     final class ClientState extends Binder implements DeathRecipient {
@@ -1598,29 +1645,40 @@
     }
 
     @Override
+    public int checkOperationRaw(int code, int uid, String packageName) {
+        return checkOperationInternal(code, uid, packageName, true /*raw*/);
+    }
+
+    @Override
     public int checkOperation(int code, int uid, String packageName) {
+        return checkOperationInternal(code, uid, packageName, false /*raw*/);
+    }
+
+    private int checkOperationInternal(int code, int uid, String packageName, boolean raw) {
         final CheckOpsDelegate delegate;
         synchronized (this) {
             delegate = mCheckOpsDelegate;
         }
         if (delegate == null) {
-            return checkOperationImpl(code, uid, packageName);
+            return checkOperationImpl(code, uid, packageName, raw);
         }
-        return delegate.checkOperation(code, uid, packageName,
+        return delegate.checkOperation(code, uid, packageName, raw,
                     AppOpsService.this::checkOperationImpl);
     }
 
-    private int checkOperationImpl(int code, int uid, String packageName) {
+    private int checkOperationImpl(int code, int uid, String packageName,
+                boolean raw) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
         String resolvedPackageName = resolvePackageName(uid, packageName);
         if (resolvedPackageName == null) {
             return AppOpsManager.MODE_IGNORED;
         }
-        return checkOperationUnchecked(code, uid, resolvedPackageName);
+        return checkOperationUnchecked(code, uid, resolvedPackageName, raw);
     }
 
-    private int checkOperationUnchecked(int code, int uid, String packageName) {
+    private int checkOperationUnchecked(int code, int uid, String packageName,
+                boolean raw) {
         synchronized (this) {
             if (isOpRestrictedLocked(uid, code, packageName)) {
                 return AppOpsManager.MODE_IGNORED;
@@ -1629,7 +1687,8 @@
             UidState uidState = getUidStateLocked(uid, false);
             if (uidState != null && uidState.opModes != null
                     && uidState.opModes.indexOfKey(code) >= 0) {
-                return uidState.opModes.get(code);
+                final int rawMode = uidState.opModes.get(code);
+                return raw ? rawMode : uidState.evalMode(rawMode);
             }
             Op op = getOpLocked(code, uid, packageName, false, true, false);
             if (op == null) {
@@ -1795,12 +1854,16 @@
             final Ops ops = getOpsRawLocked(uid, packageName, true /* edit */,
                     false /* uidMismatchExpected */);
             if (ops == null) {
+                scheduleOpNotedIfNeededLocked(code, uid, packageName,
+                        AppOpsManager.MODE_IGNORED);
                 if (DEBUG) Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
                         + " package " + packageName);
                 return AppOpsManager.MODE_ERRORED;
             }
             final Op op = getOpLocked(ops, code, true);
             if (isOpRestrictedLocked(uid, code, packageName)) {
+                scheduleOpNotedIfNeededLocked(code, uid, packageName,
+                        AppOpsManager.MODE_IGNORED);
                 return AppOpsManager.MODE_IGNORED;
             }
             final UidState uidState = ops.uidState;
@@ -1820,6 +1883,7 @@
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + packageName);
                     op.rejectTime[uidState.state] = System.currentTimeMillis();
+                    scheduleOpNotedIfNeededLocked(code, uid, packageName, uidMode);
                     return uidMode;
                 }
             } else {
@@ -1830,6 +1894,7 @@
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + packageName);
                     op.rejectTime[uidState.state] = System.currentTimeMillis();
+                    scheduleOpNotedIfNeededLocked(op.op, uid, packageName, mode);
                     return mode;
                 }
             }
@@ -1839,6 +1904,8 @@
             op.rejectTime[uidState.state] = 0;
             op.proxyUid = proxyUid;
             op.proxyPackageName = proxyPackageName;
+            scheduleOpNotedIfNeededLocked(code, uid, packageName,
+                    AppOpsManager.MODE_ALLOWED);
             return AppOpsManager.MODE_ALLOWED;
         }
     }
@@ -1886,10 +1953,50 @@
             }
             final int callbackCount = activeCallbacks.size();
             for (int i = 0; i < callbackCount; i++) {
-                // Apps ops are mapped to a singleton
-                if (i == 0) {
-                    activeCallbacks.valueAt(i).destroy();
-                }
+                activeCallbacks.valueAt(i).destroy();
+            }
+        }
+    }
+
+    @Override
+    public void startWatchingNoted(@NonNull int[] ops, @NonNull IAppOpsNotedCallback callback) {
+        int watchedUid = Process.INVALID_UID;
+        final int callingUid = Binder.getCallingUid();
+        final int callingPid = Binder.getCallingPid();
+        if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
+                != PackageManager.PERMISSION_GRANTED) {
+            watchedUid = callingUid;
+        }
+        Preconditions.checkArgument(!ArrayUtils.isEmpty(ops), "Ops cannot be null or empty");
+        Preconditions.checkArrayElementsInRange(ops, 0, AppOpsManager._NUM_OP - 1,
+                "Invalid op code in: " + Arrays.toString(ops));
+        Preconditions.checkNotNull(callback, "Callback cannot be null");
+        synchronized (this) {
+            SparseArray<NotedCallback> callbacks = mNotedWatchers.get(callback.asBinder());
+            if (callbacks == null) {
+                callbacks = new SparseArray<>();
+                mNotedWatchers.put(callback.asBinder(), callbacks);
+            }
+            final NotedCallback notedCallback = new NotedCallback(callback, watchedUid,
+                    callingUid, callingPid);
+            for (int op : ops) {
+                callbacks.put(op, notedCallback);
+            }
+        }
+    }
+
+    @Override
+    public void stopWatchingNoted(IAppOpsNotedCallback callback) {
+        Preconditions.checkNotNull(callback, "Callback cannot be null");
+        synchronized (this) {
+            final SparseArray<NotedCallback> notedCallbacks =
+                    mNotedWatchers.remove(callback.asBinder());
+            if (notedCallbacks == null) {
+                return;
+            }
+            final int callbackCount = notedCallbacks.size();
+            for (int i = 0; i < callbackCount; i++) {
+                notedCallbacks.valueAt(i).destroy();
             }
         }
     }
@@ -2052,6 +2159,51 @@
         }
     }
 
+    private void scheduleOpNotedIfNeededLocked(int code, int uid, String packageName,
+            int result) {
+        ArraySet<NotedCallback> dispatchedCallbacks = null;
+        final int callbackListCount = mNotedWatchers.size();
+        for (int i = 0; i < callbackListCount; i++) {
+            final SparseArray<NotedCallback> callbacks = mNotedWatchers.valueAt(i);
+            final NotedCallback callback = callbacks.get(code);
+            if (callback != null) {
+                if (callback.mWatchingUid >= 0 && callback.mWatchingUid != uid) {
+                    continue;
+                }
+                if (dispatchedCallbacks == null) {
+                    dispatchedCallbacks = new ArraySet<>();
+                }
+                dispatchedCallbacks.add(callback);
+            }
+        }
+        if (dispatchedCallbacks == null) {
+            return;
+        }
+        mHandler.sendMessage(PooledLambda.obtainMessage(
+                AppOpsService::notifyOpChecked,
+                this, dispatchedCallbacks, code, uid, packageName, result));
+    }
+
+    private void notifyOpChecked(ArraySet<NotedCallback> callbacks,
+            int code, int uid, String packageName, int result) {
+        // There are components watching for checks in our process. The callbacks in
+        // these components may require permissions our remote caller does not have.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            final int callbackCount = callbacks.size();
+            for (int i = 0; i < callbackCount; i++) {
+                final NotedCallback callback = callbacks.valueAt(i);
+                try {
+                    callback.mCallback.opNoted(code, uid, packageName, result);
+                } catch (RemoteException e) {
+                    /* do nothing */
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     @Override
     public int permissionToOpCode(String permission) {
         if (permission == null) {
@@ -3463,6 +3615,46 @@
                     pw.println(cb);
                 }
             }
+            if (mNotedWatchers.size() > 0 && dumpMode < 0) {
+                needSep = true;
+                boolean printedHeader = false;
+                for (int i = 0; i < mNotedWatchers.size(); i++) {
+                    final SparseArray<NotedCallback> notedWatchers = mNotedWatchers.valueAt(i);
+                    if (notedWatchers.size() <= 0) {
+                        continue;
+                    }
+                    final NotedCallback cb = notedWatchers.valueAt(0);
+                    if (dumpOp >= 0 && notedWatchers.indexOfKey(dumpOp) < 0) {
+                        continue;
+                    }
+                    if (dumpPackage != null && cb.mWatchingUid >= 0
+                            && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) {
+                        continue;
+                    }
+                    if (!printedHeader) {
+                        pw.println("  All op noted watchers:");
+                        printedHeader = true;
+                    }
+                    pw.print("    ");
+                    pw.print(Integer.toHexString(System.identityHashCode(
+                            mNotedWatchers.keyAt(i))));
+                    pw.println(" ->");
+                    pw.print("        [");
+                    final int opCount = notedWatchers.size();
+                    for (i = 0; i < opCount; i++) {
+                        if (i > 0) {
+                            pw.print(' ');
+                        }
+                        pw.print(AppOpsManager.opToName(notedWatchers.keyAt(i)));
+                        if (i < opCount - 1) {
+                            pw.print(',');
+                        }
+                    }
+                    pw.println("]");
+                    pw.print("        ");
+                    pw.println(cb);
+                }
+            }
             if (mClients.size() > 0 && dumpMode < 0) {
                 needSep = true;
                 boolean printedHeader = false;
@@ -3904,10 +4096,15 @@
 
     private static String[] getPackagesForUid(int uid) {
         String[] packageNames = null;
-        try {
-            packageNames = AppGlobals.getPackageManager().getPackagesForUid(uid);
-        } catch (RemoteException e) {
-            /* ignore - local call */
+
+        // Very early during boot the package manager is not yet or not yet fully started. At this
+        // time there are no packages yet.
+        if (AppGlobals.getPackageManager() != null) {
+            try {
+                packageNames = AppGlobals.getPackageManager().getPackagesForUid(uid);
+            } catch (RemoteException e) {
+                /* ignore - local call */
+            }
         }
         if (packageNames == null) {
             return EmptyArray.STRING;
@@ -4077,9 +4274,8 @@
         }
 
         @Override
-        public void setMode(int code, int uid, @NonNull String packageName, int mode,
-                boolean isPrivileged) {
-            AppOpsService.this.setMode(code, uid, packageName, mode, false, isPrivileged);
+        public void setUidMode(int code, int uid, int mode) {
+            AppOpsService.this.setUidMode(code, uid, mode);
         }
     }
 }
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index a07939e..7bbc543 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -76,6 +76,7 @@
 import java.util.LinkedList;
 import java.util.Locale;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
@@ -1276,7 +1277,11 @@
             if (mService == null) {
                 return;
             }
-            mService.unlinkToDeath(this, 0);
+            try {
+                mService.unlinkToDeath(this, 0);
+            } catch (NoSuchElementException e) {
+                Log.e(TAG, "error unlinking to death", e);
+            }
             mService = null;
             mClassName = null;
 
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 89194e4..66ceae4 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -5714,7 +5714,6 @@
                 // This should never fail.  Specifying an already in use NetID will cause failure.
                 if (networkAgent.isVPN()) {
                     mNMS.createVirtualNetwork(networkAgent.network.netId,
-                            !networkAgent.linkProperties.getDnsServers().isEmpty(),
                             (networkAgent.networkMisc == null ||
                                 !networkAgent.networkMisc.allowBypass));
                 } else {
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 08cb7a2..121a830 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -87,6 +87,10 @@
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
 import com.android.server.am.BatteryStatsService;
+import com.android.server.deviceidle.ConstraintController;
+import com.android.server.deviceidle.DeviceIdleConstraintTracker;
+import com.android.server.deviceidle.IDeviceIdleConstraint;
+import com.android.server.deviceidle.TvConstraintController;
 import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
@@ -104,6 +108,7 @@
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
+import java.util.stream.Collectors;
 
 /**
  * Keeps track of device idleness and drives low power mode based on that.
@@ -296,6 +301,17 @@
     private Location mLastGpsLocation;
     // Current locked state of the screen
     private boolean mScreenLocked;
+    private int mNumBlockingConstraints = 0;
+
+    /**
+     * Constraints are the "handbrakes" that stop the device from moving into a lower state until
+     * every one is released at the same time.
+     *
+     * @see #registerDeviceIdleConstraintInternal(IDeviceIdleConstraint, String, int)
+     */
+    private final ArrayMap<IDeviceIdleConstraint, DeviceIdleConstraintTracker>
+            mConstraints = new ArrayMap<>();
+    private ConstraintController mConstraintController;
 
     /** Device is currently active. */
     @VisibleForTesting
@@ -703,8 +719,7 @@
      * global Settings. Any access to this class or its fields should be done while
      * holding the DeviceIdleController lock.
      */
-    @VisibleForTesting
-    final class Constants extends ContentObserver {
+    public final class Constants extends ContentObserver {
         // Key names stored in the settings value.
         private static final String KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT
                 = "light_after_inactive_to";
@@ -1031,9 +1046,9 @@
                 INACTIVE_TIMEOUT = mParser.getDurationMillis(KEY_INACTIVE_TIMEOUT,
                         !COMPRESS_TIME ? inactiveTimeoutDefault : (inactiveTimeoutDefault / 10));
                 SENSING_TIMEOUT = mParser.getDurationMillis(KEY_SENSING_TIMEOUT,
-                        !DEBUG ? 4 * 60 * 1000L : 60 * 1000L);
+                        !COMPRESS_TIME ? 4 * 60 * 1000L : 60 * 1000L);
                 LOCATING_TIMEOUT = mParser.getDurationMillis(KEY_LOCATING_TIMEOUT,
-                        !DEBUG ? 30 * 1000L : 15 * 1000L);
+                        !COMPRESS_TIME ? 30 * 1000L : 15 * 1000L);
                 LOCATION_ACCURACY = mParser.getFloat(KEY_LOCATION_ACCURACY, 20);
                 MOTION_INACTIVE_TIMEOUT = mParser.getDurationMillis(KEY_MOTION_INACTIVE_TIMEOUT,
                         !COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L);
@@ -1228,6 +1243,7 @@
     private static final int MSG_REPORT_MAINTENANCE_ACTIVITY = 7;
     private static final int MSG_FINISH_IDLE_OP = 8;
     private static final int MSG_REPORT_TEMP_APP_WHITELIST_CHANGED = 9;
+    private static final int MSG_SEND_CONSTRAINT_MONITORING = 10;
 
     final class MyHandler extends Handler {
         MyHandler(Looper looper) {
@@ -1348,6 +1364,15 @@
                     final boolean added = (msg.arg2 == 1);
                     mNetworkPolicyManagerInternal.onTempPowerSaveWhitelistChange(appId, added);
                 } break;
+                case MSG_SEND_CONSTRAINT_MONITORING: {
+                    final IDeviceIdleConstraint constraint = (IDeviceIdleConstraint) msg.obj;
+                    final boolean monitoring = (msg.arg1 == 1);
+                    if (monitoring) {
+                        constraint.startMonitoring();
+                    } else {
+                        constraint.stopMonitoring();
+                    }
+                } break;
             }
         }
     }
@@ -1512,6 +1537,25 @@
     }
 
     public class LocalService {
+        public void onConstraintStateChanged(IDeviceIdleConstraint constraint, boolean active) {
+            synchronized (DeviceIdleController.this) {
+                onConstraintStateChangedLocked(constraint, active);
+            }
+        }
+
+        public void registerDeviceIdleConstraint(IDeviceIdleConstraint constraint, String name,
+                @IDeviceIdleConstraint.MinimumState int minState) {
+            registerDeviceIdleConstraintInternal(constraint, name, minState);
+        }
+
+        public void unregisterDeviceIdleConstraint(IDeviceIdleConstraint constraint) {
+            unregisterDeviceIdleConstraintInternal(constraint);
+        }
+
+        public void exitIdle(String reason) {
+            exitIdleInternal(reason);
+        }
+
         // duration in milliseconds
         public void addPowerSaveTempWhitelistApp(int callingUid, String packageName,
                 long duration, int userId, boolean sync, String reason) {
@@ -1562,6 +1606,7 @@
     static class Injector {
         private final Context mContext;
         private ConnectivityService mConnectivityService;
+        private Constants mConstants;
         private LocationManager mLocationManager;
 
         Injector(Context ctx) {
@@ -1591,7 +1636,10 @@
 
         Constants getConstants(DeviceIdleController controller, Handler handler,
                 ContentResolver resolver) {
-            return controller.new Constants(handler, resolver);
+            if (mConstants == null) {
+                mConstants = controller.new Constants(handler, resolver);
+            }
+            return mConstants;
         }
 
         LocationManager getLocationManager() {
@@ -1608,6 +1656,23 @@
         PowerManager getPowerManager() {
             return mContext.getSystemService(PowerManager.class);
         }
+
+        SensorManager getSensorManager() {
+            return mContext.getSystemService(SensorManager.class);
+        }
+
+        ConstraintController getConstraintController(Handler handler, LocalService localService) {
+            if (mContext.getPackageManager()
+                    .hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY)) {
+                return new TvConstraintController(mContext, handler);
+            }
+            return null;
+        }
+
+        boolean useMotionSensor() {
+            return mContext.getResources().getBoolean(
+                   com.android.internal.R.bool.config_autoPowerModeUseMotionSensor);
+        }
     }
 
     private final Injector mInjector;
@@ -1632,9 +1697,7 @@
         mHandler = mInjector.getHandler(this);
         mAppStateTracker = mInjector.getAppStateTracker(context, FgThread.get().getLooper());
         LocalServices.addService(AppStateTracker.class, mAppStateTracker);
-
-        mUseMotionSensor = context.getResources().getBoolean(
-                com.android.internal.R.bool.config_autoPowerModeUseMotionSensor);
+        mUseMotionSensor = mInjector.useMotionSensor();
     }
 
     public DeviceIdleController(Context context) {
@@ -1734,7 +1797,7 @@
                 mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(
                         ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
                 mNetworkPolicyManagerInternal = getLocalService(NetworkPolicyManagerInternal.class);
-                mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
+                mSensorManager = mInjector.getSensorManager();
 
                 if (mUseMotionSensor) {
                     int sigMotionSensorId = getContext().getResources().getInteger(
@@ -1763,6 +1826,12 @@
                         .setNumUpdates(1);
                 }
 
+                mConstraintController = mInjector.getConstraintController(
+                        mHandler, getLocalService(LocalService.class));
+                if (mConstraintController != null) {
+                    mConstraintController.start();
+                }
+
                 float angleThreshold = getContext().getResources().getInteger(
                         com.android.internal.R.integer.config_autoPowerModeThresholdAngle) / 100f;
                 mAnyMotionDetector = mInjector.getAnyMotionDetector(mHandler, mSensorManager, this,
@@ -1818,6 +1887,99 @@
         }
     }
 
+    @VisibleForTesting
+    boolean hasMotionSensor() {
+        return mUseMotionSensor && mMotionSensor != null;
+    }
+
+    private void registerDeviceIdleConstraintInternal(IDeviceIdleConstraint constraint,
+            final String name, final int type) {
+        final int minState;
+        switch (type) {
+            case IDeviceIdleConstraint.ACTIVE:
+                minState = STATE_ACTIVE;
+                break;
+            case IDeviceIdleConstraint.SENSING_OR_ABOVE:
+                minState = STATE_SENSING;
+                break;
+            default:
+                Slog.wtf(TAG, "Registering device-idle constraint with invalid type: " + type);
+                return;
+        }
+        synchronized (this) {
+            if (mConstraints.containsKey(constraint)) {
+                Slog.e(TAG, "Re-registering device-idle constraint: " + constraint + ".");
+                return;
+            }
+            DeviceIdleConstraintTracker tracker = new DeviceIdleConstraintTracker(name, minState);
+            mConstraints.put(constraint, tracker);
+            updateActiveConstraintsLocked();
+        }
+    }
+
+    private void unregisterDeviceIdleConstraintInternal(IDeviceIdleConstraint constraint) {
+        synchronized (this) {
+            // Artifically force the constraint to inactive to unblock anything waiting for it.
+            onConstraintStateChangedLocked(constraint, /* active= */ false);
+
+            // Let the constraint know that we are not listening to it any more.
+            setConstraintMonitoringLocked(constraint, /* monitoring= */ false);
+            mConstraints.remove(constraint);
+        }
+    }
+
+    @GuardedBy("this")
+    private void onConstraintStateChangedLocked(IDeviceIdleConstraint constraint, boolean active) {
+        DeviceIdleConstraintTracker tracker = mConstraints.get(constraint);
+        if (tracker == null) {
+            Slog.e(TAG, "device-idle constraint " + constraint + " has not been registered.");
+            return;
+        }
+        if (active != tracker.active && tracker.monitoring) {
+            tracker.active = active;
+            mNumBlockingConstraints += (tracker.active ? +1 : -1);
+            if (mNumBlockingConstraints == 0) {
+                if (mState == STATE_ACTIVE) {
+                    becomeInactiveIfAppropriateLocked();
+                } else if (mNextAlarmTime == 0 || mNextAlarmTime < SystemClock.elapsedRealtime()) {
+                    stepIdleStateLocked("s:" + tracker.name);
+                }
+            }
+        }
+    }
+
+    @GuardedBy("this")
+    private void setConstraintMonitoringLocked(IDeviceIdleConstraint constraint, boolean monitor) {
+        DeviceIdleConstraintTracker tracker = mConstraints.get(constraint);
+        if (tracker.monitoring != monitor) {
+            tracker.monitoring = monitor;
+            updateActiveConstraintsLocked();
+            // We send the callback on a separate thread instead of just relying on oneway as
+            // the client could be in the system server with us and cause re-entry problems.
+            mHandler.obtainMessage(MSG_SEND_CONSTRAINT_MONITORING,
+                    /* monitoring= */ monitor ? 1 : 0,
+                    /* <not used>= */ -1,
+                    /* constraint= */ constraint).sendToTarget();
+        }
+    }
+
+    @GuardedBy("this")
+    private void updateActiveConstraintsLocked() {
+        mNumBlockingConstraints = 0;
+        for (int i = 0; i < mConstraints.size(); i++) {
+            final IDeviceIdleConstraint constraint = mConstraints.keyAt(i);
+            final DeviceIdleConstraintTracker tracker = mConstraints.valueAt(i);
+            final boolean monitoring = (tracker.minState == mState);
+            if (monitoring != tracker.monitoring) {
+                setConstraintMonitoringLocked(constraint, monitoring);
+                tracker.active = monitoring;
+            }
+            if (tracker.monitoring && tracker.active) {
+                mNumBlockingConstraints++;
+            }
+        }
+    }
+
     public boolean addPowerSaveWhitelistAppInternal(String name) {
         synchronized (this) {
             try {
@@ -2448,6 +2610,7 @@
         cancelLocatingLocked();
         stopMonitoringMotionLocked();
         mAnyMotionDetector.stop();
+        updateActiveConstraintsLocked();
     }
 
     private void resetLightIdleManagementLocked() {
@@ -2583,40 +2746,50 @@
             return;
         }
 
+        if (mNumBlockingConstraints != 0 && !mForceIdle) {
+            // We have some constraints from other parts of the system server preventing
+            // us from moving to the next state.
+            if (DEBUG) {
+                Slog.i(TAG, "Cannot step idle state. Blocked by: " + mConstraints.values().stream()
+                        .filter(x -> x.active)
+                        .map(x -> x.name)
+                        .collect(Collectors.joining(",")));
+            }
+            return;
+        }
+
         switch (mState) {
             case STATE_INACTIVE:
                 // We have now been inactive long enough, it is time to start looking
                 // for motion and sleep some more while doing so.
                 startMonitoringMotionLocked();
                 scheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT, false);
-                mState = STATE_IDLE_PENDING;
-                if (DEBUG) Slog.d(TAG, "Moved from STATE_INACTIVE to STATE_IDLE_PENDING.");
-                EventLogTags.writeDeviceIdle(mState, reason);
+                moveToStateLocked(STATE_IDLE_PENDING, reason);
                 break;
             case STATE_IDLE_PENDING:
-                mState = STATE_SENSING;
-                if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE_PENDING to STATE_SENSING.");
-                EventLogTags.writeDeviceIdle(mState, reason);
+                moveToStateLocked(STATE_SENSING, reason);
                 cancelLocatingLocked();
                 mLocated = false;
                 mLastGenericLocation = null;
                 mLastGpsLocation = null;
+                updateActiveConstraintsLocked();
 
-                // If we have an accelerometer, wait to find out whether we are moving.
+                // Wait for open constraints and an accelerometer reading before moving on.
                 if (mUseMotionSensor && mAnyMotionDetector.hasSensor()) {
                     scheduleSensingTimeoutAlarmLocked(mConstants.SENSING_TIMEOUT);
                     mNotMoving = false;
                     mAnyMotionDetector.checkForAnyMotion();
                     break;
+                } else if (mNumBlockingConstraints != 0) {
+                    cancelAlarmLocked();
+                    break;
                 }
 
                 mNotMoving = true;
                 // Otherwise, fall through and check this off the list of requirements.
             case STATE_SENSING:
                 cancelSensingTimeoutAlarmLocked();
-                mState = STATE_LOCATING;
-                if (DEBUG) Slog.d(TAG, "Moved from STATE_SENSING to STATE_LOCATING.");
-                EventLogTags.writeDeviceIdle(mState, reason);
+                moveToStateLocked(STATE_LOCATING, reason);
                 scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT, false);
                 LocationManager locationManager = mInjector.getLocationManager();
                 if (locationManager != null
@@ -2665,12 +2838,11 @@
                 if (mNextIdleDelay < mConstants.IDLE_TIMEOUT) {
                     mNextIdleDelay = mConstants.IDLE_TIMEOUT;
                 }
-                mState = STATE_IDLE;
+                moveToStateLocked(STATE_IDLE, reason);
                 if (mLightState != LIGHT_STATE_OVERRIDE) {
                     mLightState = LIGHT_STATE_OVERRIDE;
                     cancelLightAlarmLocked();
                 }
-                EventLogTags.writeDeviceIdle(mState, reason);
                 addEvent(EVENT_DEEP_IDLE, null);
                 mGoingIdleWakeLock.acquire();
                 mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON);
@@ -2688,14 +2860,24 @@
                 if (mNextIdlePendingDelay < mConstants.IDLE_PENDING_TIMEOUT) {
                     mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;
                 }
-                mState = STATE_IDLE_MAINTENANCE;
-                EventLogTags.writeDeviceIdle(mState, reason);
+                moveToStateLocked(STATE_IDLE_MAINTENANCE, reason);
                 addEvent(EVENT_DEEP_MAINTENANCE, null);
                 mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
                 break;
         }
     }
 
+    private void moveToStateLocked(int state, String reason) {
+        final int oldState = mState;
+        mState = state;
+        if (DEBUG) {
+            Slog.d(TAG, String.format("Moved from STATE_%s to STATE_%s.",
+                    stateToString(oldState), stateToString(mState)));
+        }
+        EventLogTags.writeDeviceIdle(mState, reason);
+        updateActiveConstraintsLocked();
+    }
+
     void incActiveIdleOps() {
         synchronized (this) {
             mActiveIdleOpCount++;
@@ -2818,6 +3000,7 @@
             mMaintenanceStartTime = 0;
             EventLogTags.writeDeviceIdle(mState, type);
             becomeInactive = true;
+            updateActiveConstraintsLocked();
         }
         if (mLightState == LIGHT_STATE_OVERRIDE) {
             // We went out of light idle mode because we had started deep idle mode...  let's
@@ -3794,8 +3977,22 @@
             pw.print("  mScreenLocked="); pw.println(mScreenLocked);
             pw.print("  mNetworkConnected="); pw.println(mNetworkConnected);
             pw.print("  mCharging="); pw.println(mCharging);
-            pw.print("  mMotionActive="); pw.println(mMotionListener.active);
+            if (mConstraints.size() != 0) {
+                pw.println("  mConstraints={");
+                for (int i = 0; i < mConstraints.size(); i++) {
+                    final DeviceIdleConstraintTracker tracker = mConstraints.valueAt(i);
+                    pw.print("    \""); pw.print(tracker.name); pw.print("\"=");
+                    if (tracker.minState == mState) {
+                        pw.println(tracker.active);
+                    } else {
+                        pw.print("ignored <mMinState="); pw.print(stateToString(tracker.minState));
+                        pw.println(">");
+                    }
+                }
+                pw.println("  }");
+            }
             if (mUseMotionSensor) {
+                pw.print("  mMotionActive="); pw.println(mMotionListener.active);
                 pw.print("  mNotMoving="); pw.println(mNotMoving);
             }
             pw.print("  mLocating="); pw.print(mLocating); pw.print(" mHasGps=");
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 8b992eb..5cea56e 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -22,6 +22,7 @@
 
 import static com.android.internal.util.Preconditions.checkState;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -46,6 +47,7 @@
 import android.location.Criteria;
 import android.location.GeocoderParams;
 import android.location.Geofence;
+import android.location.GnssMeasurementCorrections;
 import android.location.IBatchedLocationCallback;
 import android.location.IGnssMeasurementsListener;
 import android.location.IGnssNavigationMessageListener;
@@ -62,7 +64,6 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
-import android.os.Message;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
@@ -85,6 +86,7 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
 import com.android.server.location.AbstractLocationProvider;
 import com.android.server.location.ActivityRecognitionProxy;
 import com.android.server.location.GeocoderProxy;
@@ -115,6 +117,11 @@
 import java.util.Map.Entry;
 import java.util.NoSuchElementException;
 import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
 
 /**
  * The service class that manages LocationProviders and issues location
@@ -169,10 +176,7 @@
     private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
 
     private final Context mContext;
-    private final AppOpsManager mAppOps;
-
-    // used internally for synchronization
-    private final Object mLock = new Object();
+    private AppOpsManager mAppOps;
 
     // --- fields below are final after systemRunning() ---
     private LocationFudger mLocationFudger;
@@ -184,15 +188,15 @@
     private GeocoderProxy mGeocodeProvider;
     private GnssStatusListenerHelper mGnssStatusProvider;
     private INetInitiatedListener mNetInitiatedListener;
-    private LocationWorkerHandler mLocationHandler;
+    private final Handler mHandler;
     private PassiveProvider mPassiveProvider;  // track passive provider for special cases
     private LocationBlacklist mBlacklist;
     private GnssMeasurementsProvider mGnssMeasurementsProvider;
     private GnssNavigationMessageProvider mGnssNavigationMessageProvider;
+    private String mLocationControllerExtraPackage;
+    private boolean mLocationControllerExtraPackageEnabled;
     private IGpsGeofenceHardware mGpsGeofenceProxy;
 
-    // --- fields below are protected by mLock ---
-
     // Mock (test) providers
     private final HashMap<String, MockProvider> mMockProviders =
             new HashMap<>();
@@ -201,8 +205,8 @@
     private final HashMap<Object, Receiver> mReceivers = new HashMap<>();
 
     // currently installed providers (with mocks replacing real providers)
-    private final ArrayList<LocationProvider> mProviders =
-            new ArrayList<>();
+    private final CopyOnWriteArrayList<LocationProvider> mProviders =
+            new CopyOnWriteArrayList<>();
 
     // real providers, saved here when mocked out
     private final HashMap<String, LocationProvider> mRealProviders =
@@ -228,8 +232,8 @@
 
     // all providers that operate over proxy, for authorizing incoming location and whitelisting
     // throttling
-    private final ArrayList<LocationProviderProxy> mProxyProviders =
-            new ArrayList<>();
+    private final CopyOnWriteArrayList<LocationProviderProxy> mProxyProviders =
+            new CopyOnWriteArrayList<>();
 
     private final ArraySet<String> mBackgroundThrottlePackageWhitelist = new ArraySet<>();
 
@@ -242,9 +246,6 @@
     private int mCurrentUserId = UserHandle.USER_SYSTEM;
     private int[] mCurrentUserProfiles = new int[]{UserHandle.USER_SYSTEM};
 
-    // Maximum age of last location returned to clients with foreground-only location permissions.
-    private long mLastLocationMaxAgeMs;
-
     private GnssLocationProvider.GnssSystemInfoProvider mGnssSystemInfoProvider;
 
     private GnssLocationProvider.GnssMetricsProvider mGnssMetricsProvider;
@@ -257,7 +258,7 @@
     public LocationManagerService(Context context) {
         super();
         mContext = context;
-        mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+        mHandler = BackgroundThread.getHandler();
 
         // Let the package manager query which are the default location
         // providers as they get certain permissions granted by default.
@@ -267,130 +268,92 @@
                 userId -> mContext.getResources().getStringArray(
                         com.android.internal.R.array.config_locationProviderPackageNames));
 
-        if (D) Log.d(TAG, "Constructed");
-
         // most startup is deferred until systemRunning()
     }
 
     public void systemRunning() {
-        synchronized (mLock) {
-            if (D) Log.d(TAG, "systemRunning()");
+        runInternal(this::initialize);
+    }
 
-            // fetch package manager
-            mPackageManager = mContext.getPackageManager();
+    private void initialize() {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
 
-            // fetch power manager
-            mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+        mPackageManager = mContext.getPackageManager();
+        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
 
-            // fetch activity manager
-            mActivityManager
-                    = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+        // prepare mHandler's dependents
+        mLocationFudger = new LocationFudger(mContext, mHandler);
+        mBlacklist = new LocationBlacklist(mContext, mHandler);
+        mBlacklist.init();
+        mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
 
-            // prepare worker thread
-            mLocationHandler = new LocationWorkerHandler(BackgroundThread.get().getLooper());
-
-            // prepare mLocationHandler's dependents
-            mLocationFudger = new LocationFudger(mContext, mLocationHandler);
-            mBlacklist = new LocationBlacklist(mContext, mLocationHandler);
-            mBlacklist.init();
-            mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
-
-            // Monitor for app ops mode changes.
-            AppOpsManager.OnOpChangedListener callback
-                    = new AppOpsManager.OnOpChangedInternalListener() {
-                public void onOpChanged(int op, String packageName) {
-                    synchronized (mLock) {
-                        for (Receiver receiver : mReceivers.values()) {
-                            receiver.updateMonitoring(true);
-                        }
-                        applyAllProviderRequirementsLocked();
+        mAppOps.startWatchingMode(
+                AppOpsManager.OP_COARSE_LOCATION,
+                null,
+                AppOpsManager.WATCH_FOREGROUND_CHANGES,
+                new AppOpsManager.OnOpChangedInternalListener() {
+                    public void onOpChanged(int op, String packageName) {
+                        mHandler.post(() -> onAppOpChanged());
                     }
-                }
-            };
-            mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null,
-                    AppOpsManager.WATCH_FOREGROUND_CHANGES, callback);
+                });
 
-            PackageManager.OnPermissionsChangedListener permissionListener = uid -> {
-                synchronized (mLock) {
-                    applyAllProviderRequirementsLocked();
-                }
-            };
-            mPackageManager.addOnPermissionsChangeListener(permissionListener);
+        mPackageManager.addOnPermissionsChangeListener(
+                uid -> runInternal(this::onPermissionsChanged));
 
-            // listen for background/foreground changes
-            ActivityManager.OnUidImportanceListener uidImportanceListener =
-                    (uid, importance) -> mLocationHandler.post(
-                            () -> onUidImportanceChanged(uid, importance));
-            mActivityManager.addOnUidImportanceListener(uidImportanceListener,
-                    FOREGROUND_IMPORTANCE_CUTOFF);
+        mActivityManager.addOnUidImportanceListener(
+                (uid, importance) -> mHandler.post(
+                        () -> onUidImportanceChanged(uid, importance)),
+                FOREGROUND_IMPORTANCE_CUTOFF);
 
-            mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-            updateUserProfiles(mCurrentUserId);
+        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        updateUserProfiles(mCurrentUserId);
 
-            updateBackgroundThrottlingWhitelistLocked();
-            updateLastLocationMaxAgeLocked();
+        updateBackgroundThrottlingWhitelist();
 
-            // prepare providers
-            loadProvidersLocked();
-            updateProvidersSettingsLocked();
-            for (LocationProvider provider : mProviders) {
-                applyRequirementsLocked(provider.getName());
-            }
+        // prepare providers
+        loadProvidersLocked();
+        updateProvidersSettings();
+        for (LocationProvider provider : mProviders) {
+            applyRequirements(provider.getName());
         }
 
         // listen for settings changes
         mContext.getContentResolver().registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.LOCATION_PROVIDERS_ALLOWED), true,
-                new ContentObserver(mLocationHandler) {
+                new ContentObserver(mHandler) {
                     @Override
                     public void onChange(boolean selfChange) {
-                        synchronized (mLock) {
-                            updateProvidersSettingsLocked();
-                        }
+                        onProviderAllowedChanged();
                     }
                 }, UserHandle.USER_ALL);
         mContext.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS),
                 true,
-                new ContentObserver(mLocationHandler) {
+                new ContentObserver(mHandler) {
                     @Override
                     public void onChange(boolean selfChange) {
-                        synchronized (mLock) {
-                            for (LocationProvider provider : mProviders) {
-                                applyRequirementsLocked(provider.getName());
-                            }
-                        }
+                        onBackgroundThrottleIntervalChanged();
                     }
                 }, UserHandle.USER_ALL);
         mContext.getContentResolver().registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS),
-                true,
-                new ContentObserver(mLocationHandler) {
-                    @Override
-                    public void onChange(boolean selfChange) {
-                        synchronized (mLock) {
-                            updateLastLocationMaxAgeLocked();
-                        }
-                    }
-                }
-        );
-        mContext.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(
                         Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST),
                 true,
-                new ContentObserver(mLocationHandler) {
+                new ContentObserver(mHandler) {
                     @Override
                     public void onChange(boolean selfChange) {
-                        synchronized (mLock) {
-                            updateBackgroundThrottlingWhitelistLocked();
-                            for (LocationProvider provider : mProviders) {
-                                applyRequirementsLocked(provider.getName());
-                            }
-                        }
+                        onBackgroundThrottleWhitelistChanged();
                     }
                 }, UserHandle.USER_ALL);
 
-        mPackageMonitor.register(mContext, mLocationHandler.getLooper(), true);
+        new PackageMonitor() {
+            @Override
+            public void onPackageDisappeared(String packageName, int reason) {
+                LocationManagerService.this.onPackageDisappeared(packageName);
+            }
+        }.register(mContext, mHandler.getLooper(), true);
 
         // listen for user change
         IntentFilter intentFilter = new IntentFilter();
@@ -409,73 +372,170 @@
                     updateUserProfiles(mCurrentUserId);
                 }
             }
-        }, UserHandle.ALL, intentFilter, null, mLocationHandler);
+        }, UserHandle.ALL, intentFilter, null, mHandler);
+    }
+
+    // will block until completion and propagate exceptions, and thus should be used from binder
+    // threads, particuarily binder threads from components that sit above LMS (ie, not location
+    // providers).
+    private void runFromBinderBlocking(Runnable runnable) throws RemoteException {
+        runFromBinderBlocking(Executors.callable(runnable));
+    }
+
+    // will block until completion and propagate exceptions, and thus should be used from binder
+    // threads, particuarily binder threads from components that sit above LMS (ie, not location
+    // providers).
+    private <T> T runFromBinderBlocking(Callable<T> callable) throws RemoteException {
+        FutureTask<T> task = new FutureTask<>(callable);
+        long identity = Binder.clearCallingIdentity();
+        try {
+            runInternal(task);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+        try {
+            return task.get();
+        } catch (ExecutionException e) {
+            // binder calls can handle 3 types of exceptions, runtimeexception and error (which can
+            // be thrown any time), and remote exception. we transfer all of these exceptions from
+            // the execution thread to the binder thread so that they may propagate normally. note
+            // that we are loosing some context in doing so (losing the stack trace from the binder
+            // thread).
+            if (e.getCause() instanceof RemoteException) {
+                throw (RemoteException) e.getCause();
+            } else if (e.getCause() instanceof RuntimeException) {
+                throw (RuntimeException) e.getCause();
+            } else if (e.getCause() instanceof Error) {
+                throw (Error) e.getCause();
+            }
+
+            // callers should not throw checked exceptions
+            Log.wtf(TAG, "caller threw checked exception", e);
+            throw new UnsupportedOperationException(e);
+        } catch (InterruptedException e) {
+            throw new RemoteException("Binder call interrupted", e, true, true);
+        }
+    }
+
+    // will return immediately and will not propagate exceptions. should be used for non-binder work
+    // that needs to be shifted onto the location thread, primarily listeners that do not support
+    // running on arbitrary threads.
+    private void runInternal(Runnable runnable) {
+        // it would be a better use of resources to use locks to manage cross thread access to
+        // various pieces on information. however, the history of the location package has mostly
+        // shown that this is difficult to maintain in a multi-dev environment, and tends to always
+        // lead towards the use of uber-locks and deadlocks. using a single thread to run everything
+        // is more understandable for most devs, and seems less likely to result in future bugs
+        if (Looper.myLooper() == mHandler.getLooper()) {
+            runnable.run();
+        } else {
+            mHandler.post(runnable);
+        }
+    }
+
+    private void onAppOpChanged() {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+        for (Receiver receiver : mReceivers.values()) {
+            receiver.updateMonitoring(true);
+        }
+        for (LocationProvider p : mProviders) {
+            applyRequirements(p.getName());
+        }
+    }
+
+    private void onPermissionsChanged() {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+        for (LocationProvider p : mProviders) {
+            applyRequirements(p.getName());
+        }
+    }
+
+    private void onProviderAllowedChanged() {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+        updateProvidersSettings();
+    }
+
+    private void onPackageDisappeared(String packageName) {
+        ArrayList<Receiver> deadReceivers = null;
+
+        for (Receiver receiver : mReceivers.values()) {
+            if (receiver.mIdentity.mPackageName.equals(packageName)) {
+                if (deadReceivers == null) {
+                    deadReceivers = new ArrayList<>();
+                }
+                deadReceivers.add(receiver);
+            }
+        }
+
+        // perform removal outside of mReceivers loop
+        if (deadReceivers != null) {
+            for (Receiver receiver : deadReceivers) {
+                removeUpdates(receiver);
+            }
+        }
     }
 
     private void onUidImportanceChanged(int uid, int importance) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         boolean foreground = isImportanceForeground(importance);
         HashSet<String> affectedProviders = new HashSet<>(mRecordsByProvider.size());
-        synchronized (mLock) {
-            for (Entry<String, ArrayList<UpdateRecord>> entry
-                    : mRecordsByProvider.entrySet()) {
-                String provider = entry.getKey();
-                for (UpdateRecord record : entry.getValue()) {
-                    if (record.mReceiver.mIdentity.mUid == uid
-                            && record.mIsForegroundUid != foreground) {
-                        if (D) {
-                            Log.d(TAG, "request from uid " + uid + " is now "
-                                    + (foreground ? "foreground" : "background)"));
-                        }
-                        record.updateForeground(foreground);
-
-                        if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
-                            affectedProviders.add(provider);
-                        }
-                    }
-                }
-            }
-            for (String provider : affectedProviders) {
-                applyRequirementsLocked(provider);
-            }
-
-            for (Entry<IBinder, Identity> entry : mGnssMeasurementsListeners.entrySet()) {
-                Identity callerIdentity = entry.getValue();
-                if (callerIdentity.mUid == uid) {
+        for (Entry<String, ArrayList<UpdateRecord>> entry
+                : mRecordsByProvider.entrySet()) {
+            String provider = entry.getKey();
+            for (UpdateRecord record : entry.getValue()) {
+                if (record.mReceiver.mIdentity.mUid == uid
+                        && record.mIsForegroundUid != foreground) {
                     if (D) {
-                        Log.d(TAG, "gnss measurements listener from uid " + uid
-                                + " is now " + (foreground ? "foreground" : "background)"));
-                    }
-                    if (foreground || isThrottlingExemptLocked(entry.getValue())) {
-                        mGnssMeasurementsProvider.addListener(
-                                IGnssMeasurementsListener.Stub.asInterface(entry.getKey()),
-                                callerIdentity.mUid, callerIdentity.mPackageName);
-                    } else {
-                        mGnssMeasurementsProvider.removeListener(
-                                IGnssMeasurementsListener.Stub.asInterface(entry.getKey()));
-                    }
-                }
-            }
-
-            for (Entry<IBinder, Identity> entry : mGnssNavigationMessageListeners.entrySet()) {
-                Identity callerIdentity = entry.getValue();
-                if (callerIdentity.mUid == uid) {
-                    if (D) {
-                        Log.d(TAG, "gnss navigation message listener from uid "
-                                + uid + " is now "
+                        Log.d(TAG, "request from uid " + uid + " is now "
                                 + (foreground ? "foreground" : "background)"));
                     }
-                    if (foreground || isThrottlingExemptLocked(entry.getValue())) {
-                        mGnssNavigationMessageProvider.addListener(
-                                IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()),
-                                callerIdentity.mUid, callerIdentity.mPackageName);
-                    } else {
-                        mGnssNavigationMessageProvider.removeListener(
-                                IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()));
+                    record.updateForeground(foreground);
+
+                    if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
+                        affectedProviders.add(provider);
                     }
                 }
             }
+        }
+        for (String provider : affectedProviders) {
+            applyRequirements(provider);
+        }
 
-            // TODO(b/120449926): The GNSS status listeners should be handled similar to the above.
+        for (Entry<IBinder, Identity> entry : mGnssMeasurementsListeners.entrySet()) {
+            Identity callerIdentity = entry.getValue();
+            if (callerIdentity.mUid == uid) {
+                if (D) {
+                    Log.d(TAG, "gnss measurements listener from uid " + uid
+                            + " is now " + (foreground ? "foreground" : "background)"));
+                }
+                if (foreground || isThrottlingExemptLocked(entry.getValue())) {
+                    mGnssMeasurementsProvider.addListener(
+                            IGnssMeasurementsListener.Stub.asInterface(entry.getKey()),
+                            callerIdentity.mUid, callerIdentity.mPackageName);
+                } else {
+                    mGnssMeasurementsProvider.removeListener(
+                            IGnssMeasurementsListener.Stub.asInterface(entry.getKey()));
+                }
+            }
+        }
+
+        for (Entry<IBinder, Identity> entry : mGnssNavigationMessageListeners.entrySet()) {
+            Identity callerIdentity = entry.getValue();
+            if (callerIdentity.mUid == uid) {
+                if (D) {
+                    Log.d(TAG, "gnss navigation message listener from uid "
+                            + uid + " is now "
+                            + (foreground ? "foreground" : "background)"));
+                }
+                if (foreground || isThrottlingExemptLocked(entry.getValue())) {
+                    mGnssNavigationMessageProvider.addListener(
+                            IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()),
+                            callerIdentity.mUid, callerIdentity.mPackageName);
+                } else {
+                    mGnssNavigationMessageProvider.removeListener(
+                            IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()));
+                }
+            }
         }
     }
 
@@ -483,31 +543,33 @@
         return importance <= FOREGROUND_IMPORTANCE_CUTOFF;
     }
 
-    /**
-     * Makes a list of userids that are related to the current user. This is
-     * relevant when using managed profiles. Otherwise the list only contains
-     * the current user.
-     *
-     * @param currentUserId the current user, who might have an alter-ego.
-     */
-    private void updateUserProfiles(int currentUserId) {
-        int[] profileIds = mUserManager.getProfileIdsWithDisabled(currentUserId);
-        synchronized (mLock) {
-            mCurrentUserProfiles = profileIds;
+    private void onBackgroundThrottleIntervalChanged() {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+        for (LocationProvider provider : mProviders) {
+            applyRequirements(provider.getName());
         }
     }
 
-    /**
-     * Checks if the specified userId matches any of the current foreground
-     * users stored in mCurrentUserProfiles.
-     */
-    private boolean isCurrentProfile(int userId) {
-        synchronized (mLock) {
-            return ArrayUtils.contains(mCurrentUserProfiles, userId);
+    private void onBackgroundThrottleWhitelistChanged() {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+        updateBackgroundThrottlingWhitelist();
+        for (LocationProvider provider : mProviders) {
+            applyRequirements(provider.getName());
         }
     }
 
+    private void updateUserProfiles(int currentUserId) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+        mCurrentUserProfiles = mUserManager.getProfileIdsWithDisabled(currentUserId);
+    }
+
+    private boolean isCurrentProfile(int userId) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+        return ArrayUtils.contains(mCurrentUserProfiles, userId);
+    }
+
     private void ensureFallbackFusedProviderPresentLocked(String[] pkgs) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         PackageManager pm = mContext.getPackageManager();
         String systemPackageName = mContext.getPackageName();
         ArrayList<HashSet<Signature>> sigSets = ServiceWatcher.getSignatureSets(mContext, pkgs);
@@ -578,12 +640,13 @@
     }
 
     private void loadProvidersLocked() {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         // create a passive location provider, which is always enabled
         LocationProvider passiveProviderManager = new LocationProvider(
                 LocationManager.PASSIVE_PROVIDER);
         PassiveProvider passiveProvider = new PassiveProvider(passiveProviderManager);
 
-        addProviderLocked(passiveProviderManager);
+        addProvider(passiveProviderManager);
         mPassiveProvider = passiveProvider;
 
         if (GnssLocationProvider.isSupported()) {
@@ -592,14 +655,14 @@
                     LocationManager.GPS_PROVIDER);
             GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext,
                     gnssProviderManager,
-                    mLocationHandler.getLooper());
+                    mHandler.getLooper());
 
             mGnssSystemInfoProvider = gnssProvider.getGnssSystemInfoProvider();
             mGnssBatchingProvider = gnssProvider.getGnssBatchingProvider();
             mGnssMetricsProvider = gnssProvider.getGnssMetricsProvider();
             mGnssStatusProvider = gnssProvider.getGnssStatusProvider();
             mNetInitiatedListener = gnssProvider.getNetInitiatedListener();
-            addProviderLocked(gnssProviderManager);
+            addProvider(gnssProviderManager);
             mRealProviders.put(LocationManager.GPS_PROVIDER, gnssProviderManager);
             mGnssMeasurementsProvider = gnssProvider.getGnssMeasurementsProvider();
             mGnssNavigationMessageProvider = gnssProvider.getGnssNavigationMessageProvider();
@@ -641,7 +704,7 @@
         if (networkProvider != null) {
             mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProviderManager);
             mProxyProviders.add(networkProvider);
-            addProviderLocked(networkProviderManager);
+            addProvider(networkProviderManager);
         } else {
             Slog.w(TAG, "no network location provider found");
         }
@@ -657,7 +720,7 @@
                 com.android.internal.R.string.config_fusedLocationProviderPackageName,
                 com.android.internal.R.array.config_locationProviderPackageNames);
         if (fusedProvider != null) {
-            addProviderLocked(fusedProviderManager);
+            addProvider(fusedProviderManager);
             mProxyProviders.add(fusedProvider);
             mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedProviderManager);
         } else {
@@ -722,28 +785,22 @@
                     Boolean.parseBoolean(fragments[7]) /* supportsBearing */,
                     Integer.parseInt(fragments[8]) /* powerRequirement */,
                     Integer.parseInt(fragments[9]) /* accuracy */);
-            addTestProviderLocked(name, properties);
+            addTestProvider(name, properties);
         }
     }
 
-    /**
-     * Called when the device's active user changes.
-     *
-     * @param userId the new active user's UserId
-     */
     private void switchUser(int userId) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         if (mCurrentUserId == userId) {
             return;
         }
         mBlacklist.switchUser(userId);
-        mLocationHandler.removeMessages(MSG_LOCATION_CHANGED);
-        synchronized (mLock) {
-            mLastLocation.clear();
-            mLastLocationCoarseInterval.clear();
-            updateUserProfiles(userId);
-            updateProvidersSettingsLocked();
-            mCurrentUserId = userId;
-        }
+        mHandler.removeMessages(MSG_LOCATION_CHANGED);
+        mLastLocation.clear();
+        mLastLocationCoarseInterval.clear();
+        updateUserProfiles(userId);
+        updateProvidersSettings();
+        mCurrentUserId = userId;
     }
 
     private static final class Identity {
@@ -802,10 +859,13 @@
         }
 
         public void setRequest(ProviderRequest request, WorkSource workSource) {
+            Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
             mProvider.setRequest(request, workSource);
         }
 
         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
             pw.println(mName + " provider:");
             pw.println(" setting=" + mSettingsEnabled);
             pw.println(" enabled=" + mEnabled);
@@ -814,34 +874,38 @@
         }
 
         public long getStatusUpdateTime() {
+            Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
             return mProvider.getStatusUpdateTime();
         }
 
         public int getStatus(Bundle extras) {
+            Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
             return mProvider.getStatus(extras);
         }
 
         public void sendExtraCommand(String command, Bundle extras) {
+            Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
             mProvider.sendExtraCommand(command, extras);
         }
 
         // called from any thread
         @Override
         public void onReportLocation(Location location) {
-            runOnHandler(() -> LocationManagerService.this.reportLocation(location,
+            // no security check necessary because this is coming from an internal-only interface
+            runInternal(() -> LocationManagerService.this.handleLocationChanged(location,
                     mProvider == mPassiveProvider));
         }
 
         // called from any thread
         @Override
         public void onReportLocation(List<Location> locations) {
-            runOnHandler(() -> LocationManagerService.this.reportLocationBatch(locations));
+            LocationManagerService.this.reportLocationBatch(locations);
         }
 
         // called from any thread
         @Override
         public void onSetEnabled(boolean enabled) {
-            runOnHandler(() -> {
+            runInternal(() -> {
                 if (enabled == mEnabled) {
                     return;
                 }
@@ -866,18 +930,16 @@
                         Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
                         "-" + LocationManager.FUSED_PROVIDER, mCurrentUserId);
 
-                synchronized (mLock) {
-                    if (!enabled) {
-                        // If any provider has been disabled, clear all last locations for all
-                        // providers. This is to be on the safe side in case a provider has location
-                        // derived from this disabled provider.
-                        mLastLocation.clear();
-                        mLastLocationCoarseInterval.clear();
-                    }
-
-                    updateProviderListenersLocked(mName);
+                if (!enabled) {
+                    // If any provider has been disabled, clear all last locations for all
+                    // providers. This is to be on the safe side in case a provider has location
+                    // derived from this disabled provider.
+                    mLastLocation.clear();
+                    mLastLocationCoarseInterval.clear();
                 }
 
+                updateProviderListeners(mName);
+
                 mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
                         UserHandle.ALL);
             });
@@ -885,34 +947,27 @@
 
         @Override
         public void onSetProperties(ProviderProperties properties) {
-            runOnHandler(() -> mProperties = properties);
+            runInternal(() -> {
+                mProperties = properties;
+            });
         }
 
         private void setSettingsEnabled(boolean enabled) {
-            synchronized (mLock) {
-                if (mSettingsEnabled == enabled) {
-                    return;
-                }
-
-                mSettingsEnabled = enabled;
-                if (!mSettingsEnabled) {
-                    // if any provider has been disabled, clear all last locations for all
-                    // providers. this is to be on the safe side in case a provider has location
-                    // derived from this disabled provider.
-                    mLastLocation.clear();
-                    mLastLocationCoarseInterval.clear();
-                    updateProviderListenersLocked(mName);
-                } else if (mEnabled) {
-                    updateProviderListenersLocked(mName);
-                }
+            Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+            if (mSettingsEnabled == enabled) {
+                return;
             }
-        }
 
-        private void runOnHandler(Runnable runnable) {
-            if (Looper.myLooper() == mLocationHandler.getLooper()) {
-                runnable.run();
-            } else {
-                mLocationHandler.post(runnable);
+            mSettingsEnabled = enabled;
+            if (!mSettingsEnabled) {
+                // if any provider has been disabled, clear all last locations for all
+                // providers. this is to be on the safe side in case a provider has location
+                // derived from this disabled provider.
+                mLastLocation.clear();
+                mLastLocationCoarseInterval.clear();
+                updateProviderListeners(mName);
+            } else if (mEnabled) {
+                updateProviderListeners(mName);
             }
         }
     }
@@ -1016,7 +1071,7 @@
                 // See if receiver has any enabled update records.  Also note if any update records
                 // are high power (has a high power provider with an interval under a threshold).
                 for (UpdateRecord updateRecord : mUpdateRecords.values()) {
-                    if (isAllowedByUserSettingsLockedForUser(updateRecord.mProvider,
+                    if (isAllowedByUserSettingsForUser(updateRecord.mProvider,
                             mCurrentUserId)) {
                         requestingLocation = true;
                         LocationManagerService.LocationProvider locationProvider
@@ -1116,7 +1171,7 @@
                     synchronized (this) {
                         // synchronize to ensure incrementPendingBroadcastsLocked()
                         // is called before decrementPendingBroadcasts()
-                        mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
+                        mPendingIntent.send(mContext, 0, statusChanged, this, mHandler,
                                 getResolutionPermission(mAllowedResolutionLevel),
                                 PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
                         // call this after broadcasting so we do not increment
@@ -1152,7 +1207,7 @@
                     synchronized (this) {
                         // synchronize to ensure incrementPendingBroadcastsLocked()
                         // is called before decrementPendingBroadcasts()
-                        mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
+                        mPendingIntent.send(mContext, 0, locationChanged, this, mHandler,
                                 getResolutionPermission(mAllowedResolutionLevel),
                                 PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
                         // call this after broadcasting so we do not increment
@@ -1195,7 +1250,7 @@
                     synchronized (this) {
                         // synchronize to ensure incrementPendingBroadcastsLocked()
                         // is called before decrementPendingBroadcasts()
-                        mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
+                        mPendingIntent.send(mContext, 0, providerIntent, this, mHandler,
                                 getResolutionPermission(mAllowedResolutionLevel),
                                 PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
                         // call this after broadcasting so we do not increment
@@ -1213,12 +1268,12 @@
         public void binderDied() {
             if (D) Log.d(TAG, "Location listener died");
 
-            synchronized (mLock) {
-                removeUpdatesLocked(this);
-            }
-            synchronized (this) {
-                clearPendingBroadcastsLocked();
-            }
+            runInternal(() -> {
+                removeUpdates(this);
+                synchronized (this) {
+                    clearPendingBroadcastsLocked();
+                }
+            });
         }
 
         @Override
@@ -1255,28 +1310,22 @@
     }
 
     @Override
-    public void locationCallbackFinished(ILocationListener listener) {
+    public void locationCallbackFinished(ILocationListener listener) throws RemoteException {
         //Do not use getReceiverLocked here as that will add the ILocationListener to
         //the receiver list if it is not found.  If it is not found then the
         //LocationListener was removed when it had a pending broadcast and should
         //not be added back.
-        synchronized (mLock) {
+        runFromBinderBlocking(() -> {
             IBinder binder = listener.asBinder();
             Receiver receiver = mReceivers.get(binder);
             if (receiver != null) {
                 synchronized (receiver) {
-                    // so wakelock calls will succeed
-                    long identity = Binder.clearCallingIdentity();
                     receiver.decrementPendingBroadcastsLocked();
-                    Binder.restoreCallingIdentity(identity);
                 }
             }
-        }
+        });
     }
 
-    /**
-     * Returns the year of the GNSS hardware.
-     */
     @Override
     public int getGnssYearOfHardware() {
         if (mGnssSystemInfoProvider != null) {
@@ -1286,10 +1335,6 @@
         }
     }
 
-
-    /**
-     * Returns the model name of the GNSS hardware.
-     */
     @Override
     @Nullable
     public String getGnssHardwareModelName() {
@@ -1300,10 +1345,6 @@
         }
     }
 
-    /**
-     * Runs some checks for GNSS (FINE) level permissions, used by several methods which directly
-     * (try to) access GNSS information at this layer.
-     */
     private boolean hasGnssPermissions(String packageName) {
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
         checkResolutionLevelIsSufficientForProviderUse(
@@ -1323,9 +1364,6 @@
         return hasLocationAccess;
     }
 
-    /**
-     * Returns the GNSS batching size, if available.
-     */
     @Override
     public int getGnssBatchSize(String packageName) {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1338,10 +1376,6 @@
         }
     }
 
-    /**
-     * Adds a callback for GNSS Batching events, if permissions allow, which are transported
-     * to potentially multiple listeners by the BatchedLocationCallbackTransport above this.
-     */
     @Override
     public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName) {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1385,9 +1419,6 @@
         }
     }
 
-    /**
-     * Removes callback for GNSS batching
-     */
     @Override
     public void removeGnssBatchingCallback() {
         try {
@@ -1402,10 +1433,6 @@
         mGnssBatchingDeathCallback = null;
     }
 
-
-    /**
-     * Starts GNSS batching, if available.
-     */
     @Override
     public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName) {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1426,9 +1453,6 @@
         return mGnssBatchingProvider.start(periodNanos, wakeOnFifoFull);
     }
 
-    /**
-     * Flushes a GNSS batch in progress
-     */
     @Override
     public void flushGnssBatch(String packageName) {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1448,9 +1472,6 @@
         }
     }
 
-    /**
-     * Stops GNSS batching
-     */
     @Override
     public boolean stopGnssBatch() {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1469,7 +1490,7 @@
         checkCallerIsProvider();
 
         // Currently used only for GNSS locations - update permissions check if changed
-        if (isAllowedByUserSettingsLockedForUser(LocationManager.GPS_PROVIDER, mCurrentUserId)) {
+        if (isAllowedByUserSettingsForUser(LocationManager.GPS_PROVIDER, mCurrentUserId)) {
             if (mGnssBatchingCallback == null) {
                 Slog.e(TAG, "reportLocationBatch() called without active Callback");
                 return;
@@ -1484,35 +1505,28 @@
         }
     }
 
-    private void addProviderLocked(LocationProvider provider) {
+    private void addProvider(LocationProvider provider) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         mProviders.add(provider);
         mProvidersByName.put(provider.getName(), provider);
     }
 
-    private void removeProviderLocked(LocationProvider provider) {
+    private void removeProvider(LocationProvider provider) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         mProviders.remove(provider);
         mProvidersByName.remove(provider.getName());
     }
 
-    /**
-     * Returns "true" if access to the specified location provider is allowed by the specified
-     * user's settings. Access to all location providers is forbidden to non-location-provider
-     * processes belonging to background users.
-     *
-     * @param provider the name of the location provider
-     * @param userId   the user id to query
-     */
-    private boolean isAllowedByUserSettingsLockedForUser(String provider, int userId) {
+    private boolean isAllowedByUserSettingsForUser(String provider, int userId) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
-            return isLocationEnabledForUser(userId);
+            return isLocationEnabledForUserInternal(userId);
         }
         if (LocationManager.FUSED_PROVIDER.equals(provider)) {
-            return isLocationEnabledForUser(userId);
+            return isLocationEnabledForUserInternal(userId);
         }
-        synchronized (mLock) {
-            if (mMockProviders.containsKey(provider)) {
-                return isLocationEnabledForUser(userId);
-            }
+        if (mMockProviders.containsKey(provider)) {
+            return isLocationEnabledForUserInternal(userId);
         }
 
         long identity = Binder.clearCallingIdentity();
@@ -1527,29 +1541,14 @@
         }
     }
 
-
-    /**
-     * Returns "true" if access to the specified location provider is allowed by the specified
-     * user's settings. Access to all location providers is forbidden to non-location-provider
-     * processes belonging to background users.
-     *
-     * @param provider the name of the location provider
-     * @param uid      the requestor's UID
-     * @param userId   the user id to query
-     */
-    private boolean isAllowedByUserSettingsLocked(String provider, int uid, int userId) {
+    private boolean isAllowedByUserSettings(String provider, int uid, int userId) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         if (!isCurrentProfile(UserHandle.getUserId(uid)) && !isUidALocationProvider(uid)) {
             return false;
         }
-        return isAllowedByUserSettingsLockedForUser(provider, userId);
+        return isAllowedByUserSettingsForUser(provider, userId);
     }
 
-    /**
-     * Returns the permission string associated with the specified resolution level.
-     *
-     * @param resolutionLevel the resolution level
-     * @return the permission string
-     */
     private String getResolutionPermission(int resolutionLevel) {
         switch (resolutionLevel) {
             case RESOLUTION_LEVEL_FINE:
@@ -1561,13 +1560,6 @@
         }
     }
 
-    /**
-     * Returns the resolution level allowed to the given PID/UID pair.
-     *
-     * @param pid the PID
-     * @param uid the UID
-     * @return resolution level allowed to the pid/uid pair
-     */
     private int getAllowedResolutionLevel(int pid, int uid) {
         if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
                 pid, uid) == PERMISSION_GRANTED) {
@@ -1580,32 +1572,16 @@
         }
     }
 
-    /**
-     * Returns the resolution level allowed to the caller
-     *
-     * @return resolution level allowed to caller
-     */
     private int getCallerAllowedResolutionLevel() {
         return getAllowedResolutionLevel(Binder.getCallingPid(), Binder.getCallingUid());
     }
 
-    /**
-     * Throw SecurityException if specified resolution level is insufficient to use geofences.
-     *
-     * @param allowedResolutionLevel resolution level allowed to caller
-     */
     private void checkResolutionLevelIsSufficientForGeofenceUse(int allowedResolutionLevel) {
         if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
             throw new SecurityException("Geofence usage requires ACCESS_FINE_LOCATION permission");
         }
     }
 
-    /**
-     * Return the minimum resolution level required to use the specified location provider.
-     *
-     * @param provider the name of the location provider
-     * @return minimum resolution level required for provider
-     */
     private int getMinimumResolutionLevelForProviderUse(String provider) {
         if (LocationManager.GPS_PROVIDER.equals(provider) ||
                 LocationManager.PASSIVE_PROVIDER.equals(provider)) {
@@ -1637,13 +1613,6 @@
         return RESOLUTION_LEVEL_FINE; // if in doubt, require FINE
     }
 
-    /**
-     * Throw SecurityException if specified resolution level is insufficient to use the named
-     * location provider.
-     *
-     * @param allowedResolutionLevel resolution level allowed to caller
-     * @param providerName           the name of the location provider
-     */
     private void checkResolutionLevelIsSufficientForProviderUse(int allowedResolutionLevel,
             String providerName) {
         int requiredResolutionLevel = getMinimumResolutionLevelForProviderUse(providerName);
@@ -1662,20 +1631,6 @@
         }
     }
 
-    /**
-     * Throw SecurityException if WorkSource use is not allowed (i.e. can't blame other packages
-     * for battery).
-     */
-    private void checkDeviceStatsAllowed() {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.UPDATE_DEVICE_STATS, null);
-    }
-
-    private void checkUpdateAppOpsAllowed() {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.UPDATE_APP_OPS_STATS, null);
-    }
-
     public static int resolutionLevelToOp(int allowedResolutionLevel) {
         if (allowedResolutionLevel != RESOLUTION_LEVEL_NONE) {
             if (allowedResolutionLevel == RESOLUTION_LEVEL_COARSE) {
@@ -1733,19 +1688,15 @@
      */
     @Override
     public List<String> getAllProviders() {
-        ArrayList<String> out;
-        synchronized (mLock) {
-            out = new ArrayList<>(mProviders.size());
-            for (LocationProvider provider : mProviders) {
-                String name = provider.getName();
-                if (LocationManager.FUSED_PROVIDER.equals(name)) {
-                    continue;
-                }
-                out.add(name);
+        ArrayList<String> providers = new ArrayList<>(mProviders.size());
+        for (LocationProvider provider : mProviders) {
+            String name = provider.getName();
+            if (LocationManager.FUSED_PROVIDER.equals(name)) {
+                continue;
             }
+            providers.add(name);
         }
-        if (D) Log.d(TAG, "getAllProviders()=" + out);
-        return out;
+        return providers;
     }
 
     /**
@@ -1754,39 +1705,33 @@
      * Can return passive provider, but never returns fused provider.
      */
     @Override
-    public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
+    public List<String> getProviders(Criteria criteria, boolean enabledOnly)
+            throws RemoteException {
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
-        ArrayList<String> out;
         int uid = Binder.getCallingUid();
-        long identity = Binder.clearCallingIdentity();
-        try {
-            synchronized (mLock) {
-                out = new ArrayList<>(mProviders.size());
-                for (LocationProvider provider : mProviders) {
-                    String name = provider.getName();
-                    if (LocationManager.FUSED_PROVIDER.equals(name)) {
-                        continue;
-                    }
-                    if (allowedResolutionLevel >= getMinimumResolutionLevelForProviderUse(name)) {
-                        if (enabledOnly
-                                && !isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) {
-                            continue;
-                        }
-                        if (criteria != null
-                                && !android.location.LocationProvider.propertiesMeetCriteria(
-                                name, provider.getProperties(), criteria)) {
-                            continue;
-                        }
-                        out.add(name);
-                    }
+        return runFromBinderBlocking(() -> {
+            ArrayList<String> providers = new ArrayList<>(mProviders.size());
+            for (LocationProvider provider : mProviders) {
+                String name = provider.getName();
+                if (LocationManager.FUSED_PROVIDER.equals(name)) {
+                    continue;
                 }
+                if (allowedResolutionLevel < getMinimumResolutionLevelForProviderUse(name)) {
+                    continue;
+                }
+                if (enabledOnly
+                        && !isAllowedByUserSettings(name, uid, mCurrentUserId)) {
+                    continue;
+                }
+                if (criteria != null
+                        && !android.location.LocationProvider.propertiesMeetCriteria(
+                        name, provider.getProperties(), criteria)) {
+                    continue;
+                }
+                providers.add(name);
             }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-
-        if (D) Log.d(TAG, "getProviders()=" + out);
-        return out;
+            return providers;
+        });
     }
 
     /**
@@ -1797,59 +1742,51 @@
      * some simplified logic.
      */
     @Override
-    public String getBestProvider(Criteria criteria, boolean enabledOnly) {
-        String result;
-
+    public String getBestProvider(Criteria criteria, boolean enabledOnly) throws RemoteException {
         List<String> providers = getProviders(criteria, enabledOnly);
-        if (!providers.isEmpty()) {
-            result = pickBest(providers);
-            if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
-            return result;
-        }
-        providers = getProviders(null, enabledOnly);
-        if (!providers.isEmpty()) {
-            result = pickBest(providers);
-            if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
-            return result;
+        if (providers.isEmpty()) {
+            providers = getProviders(null, enabledOnly);
         }
 
-        if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + null);
+        if (!providers.isEmpty()) {
+            if (providers.contains(LocationManager.GPS_PROVIDER)) {
+                return LocationManager.GPS_PROVIDER;
+            } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
+                return LocationManager.NETWORK_PROVIDER;
+            } else {
+                return providers.get(0);
+            }
+        }
+
         return null;
     }
 
-    private String pickBest(List<String> providers) {
-        if (providers.contains(LocationManager.GPS_PROVIDER)) {
-            return LocationManager.GPS_PROVIDER;
-        } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
-            return LocationManager.NETWORK_PROVIDER;
-        } else {
-            return providers.get(0);
-        }
-    }
-
     @Override
-    public boolean providerMeetsCriteria(String provider, Criteria criteria) {
-        LocationProvider p = mProvidersByName.get(provider);
-        if (p == null) {
-            throw new IllegalArgumentException("provider=" + provider);
-        }
+    public boolean providerMeetsCriteria(String provider, Criteria criteria)
+            throws RemoteException {
+        return runFromBinderBlocking(() -> {
+            LocationProvider p = mProvidersByName.get(provider);
+            if (p == null) {
+                throw new IllegalArgumentException("provider=" + provider);
+            }
 
-        boolean result = android.location.LocationProvider.propertiesMeetCriteria(
-                p.getName(), p.getProperties(), criteria);
-        if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result);
-        return result;
+            return android.location.LocationProvider.propertiesMeetCriteria(
+                    p.getName(), p.getProperties(), criteria);
+        });
     }
 
-    private void updateProvidersSettingsLocked() {
+    private void updateProvidersSettings() {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         for (LocationProvider p : mProviders) {
-            p.setSettingsEnabled(isAllowedByUserSettingsLockedForUser(p.getName(), mCurrentUserId));
+            p.setSettingsEnabled(isAllowedByUserSettingsForUser(p.getName(), mCurrentUserId));
         }
 
         mContext.sendBroadcastAsUser(new Intent(LocationManager.MODE_CHANGED_ACTION),
                 UserHandle.ALL);
     }
 
-    private void updateProviderListenersLocked(String provider) {
+    private void updateProviderListeners(String provider) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         LocationProvider p = mProvidersByName.get(provider);
         if (p == null) return;
 
@@ -1874,14 +1811,15 @@
 
         if (deadReceivers != null) {
             for (int i = deadReceivers.size() - 1; i >= 0; i--) {
-                removeUpdatesLocked(deadReceivers.get(i));
+                removeUpdates(deadReceivers.get(i));
             }
         }
 
-        applyRequirementsLocked(provider);
+        applyRequirements(provider);
     }
 
-    private void applyRequirementsLocked(String provider) {
+    private void applyRequirements(String provider) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         LocationProvider p = mProvidersByName.get(provider);
         if (p == null) return;
 
@@ -1987,14 +1925,13 @@
     }
 
     @Override
-    public String[] getBackgroundThrottlingWhitelist() {
-        synchronized (mLock) {
-            return mBackgroundThrottlePackageWhitelist.toArray(
-                    new String[0]);
-        }
+    public String[] getBackgroundThrottlingWhitelist() throws RemoteException {
+        return runFromBinderBlocking(
+                () -> mBackgroundThrottlePackageWhitelist.toArray(new String[0]));
     }
 
-    private void updateBackgroundThrottlingWhitelistLocked() {
+    private void updateBackgroundThrottlingWhitelist() {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         String setting = Settings.Global.getString(
                 mContext.getContentResolver(),
                 Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
@@ -2009,15 +1946,8 @@
                 Arrays.asList(setting.split(",")));
     }
 
-    private void updateLastLocationMaxAgeLocked() {
-        mLastLocationMaxAgeMs =
-                Settings.Global.getLong(
-                        mContext.getContentResolver(),
-                        Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
-                        DEFAULT_LAST_LOCATION_MAX_AGE_MS);
-    }
-
     private boolean isThrottlingExemptLocked(Identity identity) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         if (identity.mUid == Process.SYSTEM_UID) {
             return true;
         }
@@ -2099,7 +2029,7 @@
 
             // and also remove the Receiver if it has no more update records
             if (receiverRecords.size() == 0) {
-                removeUpdatesLocked(mReceiver);
+                removeUpdates(mReceiver);
             }
         }
 
@@ -2113,8 +2043,9 @@
         }
     }
 
-    private Receiver getReceiverLocked(ILocationListener listener, int pid, int uid,
+    private Receiver getReceiver(ILocationListener listener, int pid, int uid,
             String packageName, WorkSource workSource, boolean hideFromAppOps) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         IBinder binder = listener.asBinder();
         Receiver receiver = mReceivers.get(binder);
         if (receiver == null) {
@@ -2131,8 +2062,9 @@
         return receiver;
     }
 
-    private Receiver getReceiverLocked(PendingIntent intent, int pid, int uid, String packageName,
+    private Receiver getReceiver(PendingIntent intent, int pid, int uid, String packageName,
             WorkSource workSource, boolean hideFromAppOps) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         Receiver receiver = mReceivers.get(intent);
         if (receiver == null) {
             receiver = new Receiver(null, intent, pid, uid, packageName, workSource,
@@ -2196,29 +2128,9 @@
         throw new SecurityException("invalid package name: " + packageName);
     }
 
-    private void checkPendingIntent(PendingIntent intent) {
-        if (intent == null) {
-            throw new IllegalArgumentException("invalid pending intent: " + null);
-        }
-    }
-
-    private Receiver checkListenerOrIntentLocked(ILocationListener listener, PendingIntent intent,
-            int pid, int uid, String packageName, WorkSource workSource, boolean hideFromAppOps) {
-        if (intent == null && listener == null) {
-            throw new IllegalArgumentException("need either listener or intent");
-        } else if (intent != null && listener != null) {
-            throw new IllegalArgumentException("cannot register both listener and intent");
-        } else if (intent != null) {
-            checkPendingIntent(intent);
-            return getReceiverLocked(intent, pid, uid, packageName, workSource, hideFromAppOps);
-        } else {
-            return getReceiverLocked(listener, pid, uid, packageName, workSource, hideFromAppOps);
-        }
-    }
-
     @Override
     public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
-            PendingIntent intent, String packageName) {
+            PendingIntent intent, String packageName) throws RemoteException {
         if (request == null) request = DEFAULT_LOCATION_REQUEST;
         checkPackageName(packageName);
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
@@ -2226,11 +2138,13 @@
                 request.getProvider());
         WorkSource workSource = request.getWorkSource();
         if (workSource != null && !workSource.isEmpty()) {
-            checkDeviceStatsAllowed();
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.UPDATE_DEVICE_STATS, null);
         }
         boolean hideFromAppOps = request.getHideFromAppOps();
         if (hideFromAppOps) {
-            checkUpdateAppOpsAllowed();
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.UPDATE_APP_OPS_STATS, null);
         }
         boolean callerHasLocationHardwarePermission =
                 mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE)
@@ -2240,25 +2154,33 @@
 
         final int pid = Binder.getCallingPid();
         final int uid = Binder.getCallingUid();
-        // providers may use public location API's, need to clear identity
-        long identity = Binder.clearCallingIdentity();
-        try {
-            // We don't check for MODE_IGNORED here; we will do that when we go to deliver
-            // a location.
-            checkLocationAccess(pid, uid, packageName, allowedResolutionLevel);
 
-            synchronized (mLock) {
-                Receiver recevier = checkListenerOrIntentLocked(listener, intent, pid, uid,
-                        packageName, workSource, hideFromAppOps);
-                requestLocationUpdatesLocked(sanitizedRequest, recevier, uid, packageName);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
+        // We don't check for MODE_IGNORED here; we will do that when we go to deliver
+        // a location.
+        checkLocationAccess(pid, uid, packageName, allowedResolutionLevel);
+
+        if (intent == null && listener == null) {
+            throw new IllegalArgumentException("need either listener or intent");
+        } else if (intent != null && listener != null) {
+            throw new IllegalArgumentException("cannot register both listener and intent");
         }
+
+        runFromBinderBlocking(() -> {
+            Receiver receiver;
+            if (intent != null) {
+                receiver = getReceiver(intent, pid, uid, packageName, workSource,
+                        hideFromAppOps);
+            } else {
+                receiver = getReceiver(listener, pid, uid, packageName, workSource,
+                        hideFromAppOps);
+            }
+            requestLocationUpdates(sanitizedRequest, receiver, uid, packageName);
+        });
     }
 
-    private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
+    private void requestLocationUpdates(LocationRequest request, Receiver receiver,
             int uid, String packageName) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         // Figure out the provider. Either its explicitly request (legacy use cases), or
         // use the fused provider
         if (request == null) request = DEFAULT_LOCATION_REQUEST;
@@ -2287,7 +2209,7 @@
         }
 
         if (provider.isEnabled()) {
-            applyRequirementsLocked(name);
+            applyRequirements(name);
         } else {
             // Notify the listener that updates are currently disabled
             receiver.callProviderEnabledLocked(name, false);
@@ -2299,27 +2221,31 @@
 
     @Override
     public void removeUpdates(ILocationListener listener, PendingIntent intent,
-            String packageName) {
+            String packageName) throws RemoteException {
         checkPackageName(packageName);
 
-        final int pid = Binder.getCallingPid();
-        final int uid = Binder.getCallingUid();
+        int pid = Binder.getCallingPid();
+        int uid = Binder.getCallingUid();
 
-        synchronized (mLock) {
-            Receiver receiver = checkListenerOrIntentLocked(listener, intent, pid, uid,
-                    packageName, null, false);
-
-            // providers may use public location API's, need to clear identity
-            long identity = Binder.clearCallingIdentity();
-            try {
-                removeUpdatesLocked(receiver);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
+        if (intent == null && listener == null) {
+            throw new IllegalArgumentException("need either listener or intent");
+        } else if (intent != null && listener != null) {
+            throw new IllegalArgumentException("cannot register both listener and intent");
         }
+
+        runFromBinderBlocking(() -> {
+            Receiver receiver;
+            if (intent != null) {
+                receiver = getReceiver(intent, pid, uid, packageName, null, false);
+            } else {
+                receiver = getReceiver(listener, pid, uid, packageName, null, false);
+            }
+            removeUpdates(receiver);
+        });
     }
 
-    private void removeUpdatesLocked(Receiver receiver) {
+    private void removeUpdates(Receiver receiver) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         if (D) Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver)));
 
         if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
@@ -2346,20 +2272,14 @@
 
         // update provider
         for (String provider : providers) {
-            applyRequirementsLocked(provider);
-        }
-    }
-
-    private void applyAllProviderRequirementsLocked() {
-        for (LocationProvider p : mProviders) {
-            applyRequirementsLocked(p.getName());
+            applyRequirements(provider);
         }
     }
 
     @Override
-    public Location getLastLocation(LocationRequest request, String packageName) {
-        if (D) Log.d(TAG, "getLastLocation: " + request);
-        if (request == null) request = DEFAULT_LOCATION_REQUEST;
+    public Location getLastLocation(LocationRequest r, String packageName) throws RemoteException {
+        if (D) Log.d(TAG, "getLastLocation: " + r);
+        LocationRequest request = r != null ? r : DEFAULT_LOCATION_REQUEST;
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
         checkPackageName(packageName);
         checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
@@ -2385,68 +2305,60 @@
                 }
                 return null;
             }
-
-            synchronized (mLock) {
-                // Figure out the provider. Either its explicitly request (deprecated API's),
-                // or use the fused provider
-                String name = request.getProvider();
-                if (name == null) name = LocationManager.FUSED_PROVIDER;
-                LocationProvider provider = mProvidersByName.get(name);
-                if (provider == null) return null;
-
-                if (!isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) return null;
-
-                Location location;
-                if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
-                    // Make sure that an app with coarse permissions can't get frequent location
-                    // updates by calling LocationManager.getLastKnownLocation repeatedly.
-                    location = mLastLocationCoarseInterval.get(name);
-                } else {
-                    location = mLastLocation.get(name);
-                }
-                if (location == null) {
-                    return null;
-                }
-
-                // Don't return stale location to apps with foreground-only location permission.
-                String op = resolutionLevelToOpStr(allowedResolutionLevel);
-                long locationAgeMs = SystemClock.elapsedRealtime() -
-                        location.getElapsedRealtimeNanos() / NANOS_PER_MILLI;
-                if ((locationAgeMs > mLastLocationMaxAgeMs)
-                        && (mAppOps.unsafeCheckOp(op, uid, packageName)
-                        == AppOpsManager.MODE_FOREGROUND)) {
-                    return null;
-                }
-
-                if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
-                    Location noGPSLocation = location.getExtraLocation(
-                            Location.EXTRA_NO_GPS_LOCATION);
-                    if (noGPSLocation != null) {
-                        return new Location(mLocationFudger.getOrCreate(noGPSLocation));
-                    }
-                } else {
-                    return new Location(location);
-                }
-            }
-            return null;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
+
+        return runFromBinderBlocking(() -> {
+            // Figure out the provider. Either its explicitly request (deprecated API's),
+            // or use the fused provider
+            String name = request.getProvider();
+            if (name == null) name = LocationManager.FUSED_PROVIDER;
+            LocationProvider provider = mProvidersByName.get(name);
+            if (provider == null) return null;
+
+            if (!isAllowedByUserSettings(name, uid, mCurrentUserId)) return null;
+
+            Location location;
+            if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
+                // Make sure that an app with coarse permissions can't get frequent location
+                // updates by calling LocationManager.getLastKnownLocation repeatedly.
+                location = mLastLocationCoarseInterval.get(name);
+            } else {
+                location = mLastLocation.get(name);
+            }
+            if (location == null) {
+                return null;
+            }
+
+            // Don't return stale location to apps with foreground-only location permission.
+            String op = resolutionLevelToOpStr(allowedResolutionLevel);
+            long locationAgeMs = SystemClock.elapsedRealtime()
+                    - location.getElapsedRealtimeNanos() / NANOS_PER_MILLI;
+            if ((locationAgeMs > Settings.Global.getLong(
+                    mContext.getContentResolver(),
+                    Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
+                    DEFAULT_LAST_LOCATION_MAX_AGE_MS))
+                    && (mAppOps.unsafeCheckOp(op, uid, packageName)
+                    == AppOpsManager.MODE_FOREGROUND)) {
+                return null;
+            }
+
+            if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
+                Location noGPSLocation = location.getExtraLocation(
+                        Location.EXTRA_NO_GPS_LOCATION);
+                if (noGPSLocation != null) {
+                    return new Location(mLocationFudger.getOrCreate(noGPSLocation));
+                }
+            } else {
+                return new Location(location);
+            }
+            return null;
+        });
     }
 
-    /**
-     * Provides an interface to inject and set the last location if location is not available
-     * currently.
-     *
-     * This helps in cases where the product (Cars for example) has saved the last known location
-     * before powering off.  This interface lets the client inject the saved location while the GPS
-     * chipset is getting its first fix, there by improving user experience.
-     *
-     * @param location - Location object to inject
-     * @return true if update was successful, false if not
-     */
     @Override
-    public boolean injectLocation(Location location) {
+    public boolean injectLocation(Location location) throws RemoteException {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
                 "Location Hardware permission not granted to inject location");
         mContext.enforceCallingPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
@@ -2458,29 +2370,31 @@
             }
             return false;
         }
-        LocationProvider p = null;
-        String provider = location.getProvider();
-        if (provider != null) {
-            p = mProvidersByName.get(provider);
-        }
-        if (p == null) {
-            if (D) {
-                Log.d(TAG, "injectLocation(): unknown provider");
+
+        return runFromBinderBlocking(() -> {
+            LocationProvider p = null;
+            String provider = location.getProvider();
+            if (provider != null) {
+                p = mProvidersByName.get(provider);
             }
-            return false;
-        }
-        synchronized (mLock) {
-            if (!isAllowedByUserSettingsLockedForUser(provider, mCurrentUserId)) {
+            if (p == null) {
                 if (D) {
-                    Log.d(TAG, "Location disabled in Settings for current user:" + mCurrentUserId);
+                    Log.d(TAG, "injectLocation(): unknown provider");
+                }
+                return false;
+            }
+            if (!isAllowedByUserSettingsForUser(provider, mCurrentUserId)) {
+                if (D) {
+                    Log.d(TAG, "Location disabled in Settings for current user:"
+                            + mCurrentUserId);
                 }
                 return false;
             } else {
                 // NOTE: If last location is already available, location is not injected.  If
-                // provider's normal source (like a GPS chipset) have already provided an output,
+                // provider's normal source (like a GPS chipset) have already provided an output
                 // there is no need to inject this location.
                 if (mLastLocation.get(provider) == null) {
-                    updateLastLocationLocked(location, provider);
+                    updateLastLocation(location, provider);
                 } else {
                     if (D) {
                         Log.d(TAG, "injectLocation(): Location exists. Not updating");
@@ -2488,8 +2402,8 @@
                     return false;
                 }
             }
-        }
-        return true;
+            return true;
+        });
     }
 
     @Override
@@ -2498,7 +2412,9 @@
         if (request == null) request = DEFAULT_LOCATION_REQUEST;
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
         checkResolutionLevelIsSufficientForGeofenceUse(allowedResolutionLevel);
-        checkPendingIntent(intent);
+        if (intent == null) {
+            throw new IllegalArgumentException("invalid pending intent: " + null);
+        }
         checkPackageName(packageName);
         checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
                 request.getProvider());
@@ -2509,7 +2425,10 @@
         LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel,
                 callerHasLocationHardwarePermission);
 
-        if (D) Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " " + intent);
+        if (D) {
+            Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " "
+                    + intent);
+        }
 
         // geo-fence manager uses the public location API, need to clear identity
         int uid = Binder.getCallingUid();
@@ -2530,7 +2449,9 @@
 
     @Override
     public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) {
-        checkPendingIntent(intent);
+        if (intent == null) {
+            throw new IllegalArgumentException("invalid pending intent: " + null);
+        }
         checkPackageName(packageName);
 
         if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent);
@@ -2562,14 +2483,14 @@
 
     @Override
     public boolean addGnssMeasurementsListener(
-            IGnssMeasurementsListener listener, String packageName) {
+            IGnssMeasurementsListener listener, String packageName) throws RemoteException {
         if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) {
             return false;
         }
 
-        synchronized (mLock) {
-            Identity callerIdentity
-                    = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+        Identity callerIdentity =
+                new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+        return runFromBinderBlocking(() -> {
             // TODO(b/120481270): Register for client death notification and update map.
             mGnssMeasurementsListeners.put(listener.asBinder(), callerIdentity);
             long identity = Binder.clearCallingIdentity();
@@ -2585,32 +2506,58 @@
             }
 
             return true;
+        });
+    }
+
+    @Override
+    public void injectGnssMeasurementCorrections(
+            GnssMeasurementCorrections measurementCorrections, String packageName) {
+        mContext.enforceCallingPermission(
+                android.Manifest.permission.LOCATION_HARDWARE,
+                "Location Hardware permission not granted to inject GNSS measurement corrections.");
+        if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) {
+            mGnssMeasurementsProvider.injectGnssMeasurementCorrections(measurementCorrections);
+        } else {
+            Slog.e(TAG, "Can not inject GNSS corrections due to no permission.");
         }
     }
 
     @Override
-    public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
+    public int getGnssCapabilities(String packageName) {
+        mContext.enforceCallingPermission(
+                android.Manifest.permission.LOCATION_HARDWARE,
+                "Location Hardware permission not granted to obrain GNSS chipset capabilities.");
+        if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) {
+            return -1;
+        }
+        return mGnssMeasurementsProvider.getGnssCapabilities();
+    }
+
+    @Override
+    public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener)
+            throws RemoteException {
         if (mGnssMeasurementsProvider == null) {
             return;
         }
 
-        synchronized (mLock) {
+        runFromBinderBlocking(() -> {
             mGnssMeasurementsListeners.remove(listener.asBinder());
             mGnssMeasurementsProvider.removeListener(listener);
-        }
+        });
     }
 
     @Override
     public boolean addGnssNavigationMessageListener(
             IGnssNavigationMessageListener listener,
-            String packageName) {
+            String packageName) throws RemoteException {
         if (!hasGnssPermissions(packageName) || mGnssNavigationMessageProvider == null) {
             return false;
         }
 
-        synchronized (mLock) {
-            Identity callerIdentity
-                    = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+        Identity callerIdentity =
+                new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+
+        return runFromBinderBlocking(() -> {
             // TODO(b/120481270): Register for client death notification and update map.
             mGnssNavigationMessageListeners.put(listener.asBinder(), callerIdentity);
             long identity = Binder.clearCallingIdentity();
@@ -2626,21 +2573,23 @@
             }
 
             return true;
-        }
+        });
     }
 
     @Override
-    public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) {
+    public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener)
+            throws RemoteException {
         if (mGnssNavigationMessageProvider != null) {
-            synchronized (mLock) {
+            runFromBinderBlocking(() -> {
                 mGnssNavigationMessageListeners.remove(listener.asBinder());
                 mGnssNavigationMessageProvider.removeListener(listener);
-            }
+            });
         }
     }
 
     @Override
-    public boolean sendExtraCommand(String provider, String command, Bundle extras) {
+    public boolean sendExtraCommand(String provider, String command, Bundle extras)
+            throws RemoteException {
         if (provider == null) {
             // throw NullPointerException to remain compatible with previous implementation
             throw new NullPointerException();
@@ -2654,13 +2603,13 @@
             throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
         }
 
-        synchronized (mLock) {
+        return runFromBinderBlocking(() -> {
             LocationProvider p = mProvidersByName.get(provider);
             if (p == null) return false;
 
             p.sendExtraCommand(command, extras);
             return true;
-        }
+        });
     }
 
     @Override
@@ -2677,218 +2626,181 @@
         }
     }
 
-    /**
-     * @return null if the provider does not exist
-     * @throws SecurityException if the provider is not allowed to be
-     *                           accessed by the caller
-     */
     @Override
-    public ProviderProperties getProviderProperties(String provider) {
+    public ProviderProperties getProviderProperties(String provider) throws RemoteException {
         checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
                 provider);
 
-        LocationProvider p;
-        synchronized (mLock) {
-            p = mProvidersByName.get(provider);
-        }
-
-        if (p == null) return null;
-        return p.getProperties();
+        return runFromBinderBlocking(() -> {
+            LocationProvider p = mProvidersByName.get(provider);
+            if (p == null) return null;
+            return p.getProperties();
+        });
     }
 
-    /**
-     * @return null if the provider does not exist
-     * @throws SecurityException if the provider is not allowed to be
-     *                           accessed by the caller
-     */
     @Override
-    public String getNetworkProviderPackage() {
-        LocationProvider p;
-        synchronized (mLock) {
-            p = mProvidersByName.get(LocationManager.NETWORK_PROVIDER);
-        }
+    public String getNetworkProviderPackage() throws RemoteException {
+        return runFromBinderBlocking(() -> {
+            LocationProvider p = mProvidersByName.get(LocationManager.NETWORK_PROVIDER);
 
-        if (p == null) {
-            return null;
-        }
-        if (p.mProvider instanceof LocationProviderProxy) {
-            return ((LocationProviderProxy) p.mProvider).getConnectedPackageName();
-        }
-        return null;
-    }
-
-    /**
-     * Returns the current location enabled/disabled status for a user
-     *
-     * @param userId the id of the user
-     * @return true if location is enabled
-     */
-    @Override
-    public boolean isLocationEnabledForUser(int userId) {
-        // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
-        checkInteractAcrossUsersPermission(userId);
-
-        long identity = Binder.clearCallingIdentity();
-        try {
-            synchronized (mLock) {
-                final String allowedProviders = Settings.Secure.getStringForUser(
-                        mContext.getContentResolver(),
-                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                        userId);
-                if (allowedProviders == null) {
-                    return false;
-                }
-                final List<String> providerList = Arrays.asList(allowedProviders.split(","));
-                for (String provider : mRealProviders.keySet()) {
-                    if (provider.equals(LocationManager.PASSIVE_PROVIDER)
-                            || provider.equals(LocationManager.FUSED_PROVIDER)) {
-                        continue;
-                    }
-                    if (providerList.contains(provider)) {
-                        return true;
-                    }
-                }
-                return false;
+            if (p == null) {
+                return null;
             }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
+            if (p.mProvider instanceof LocationProviderProxy) {
+                return ((LocationProviderProxy) p.mProvider).getConnectedPackageName();
+            }
+            return null;
+        });
     }
 
-    /**
-     * Enable or disable location for a user
-     *
-     * @param enabled true to enable location, false to disable location
-     * @param userId  the id of the user
-     */
     @Override
-    public void setLocationEnabledForUser(boolean enabled, int userId) {
-        mContext.enforceCallingPermission(
+    public void setLocationControllerExtraPackage(String packageName) throws RemoteException {
+        mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+                Manifest.permission.LOCATION_HARDWARE + " permission required");
+
+        runFromBinderBlocking(() -> mLocationControllerExtraPackage = packageName);
+    }
+
+    @Override
+    public String getLocationControllerExtraPackage() throws RemoteException {
+        return runFromBinderBlocking(() -> mLocationControllerExtraPackage);
+    }
+
+    @Override
+    public void setLocationControllerExtraPackageEnabled(boolean enabled) throws RemoteException {
+        mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+                Manifest.permission.LOCATION_HARDWARE + " permission required");
+        runFromBinderBlocking(() -> mLocationControllerExtraPackageEnabled = enabled);
+    }
+
+    @Override
+    public boolean isLocationControllerExtraPackageEnabled() throws RemoteException {
+        return runFromBinderBlocking(() -> mLocationControllerExtraPackageEnabled
+                && (mLocationControllerExtraPackage != null));
+    }
+
+    @Override
+    public boolean isLocationEnabledForUser(int userId) throws RemoteException {
+        // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
+        if (UserHandle.getCallingUserId() != userId) {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS,
+                    "Requires INTERACT_ACROSS_USERS permission");
+        }
+
+        return runFromBinderBlocking(() -> isLocationEnabledForUserInternal(userId));
+    }
+
+    private boolean isLocationEnabledForUserInternal(int userId) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
+        final String allowedProviders = Settings.Secure.getStringForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                userId);
+        if (allowedProviders == null) {
+            return false;
+        }
+        final List<String> providerList = Arrays.asList(allowedProviders.split(","));
+
+        for (String provider : mRealProviders.keySet()) {
+            if (provider.equals(LocationManager.PASSIVE_PROVIDER)
+                    || provider.equals(LocationManager.FUSED_PROVIDER)) {
+                continue;
+            }
+            if (providerList.contains(provider)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void setLocationEnabledForUser(boolean enabled, int userId) throws RemoteException {
+        mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.WRITE_SECURE_SETTINGS,
                 "Requires WRITE_SECURE_SETTINGS permission");
 
         // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
-        checkInteractAcrossUsersPermission(userId);
-
-        long identity = Binder.clearCallingIdentity();
-        try {
-            synchronized (mLock) {
-                final Set<String> allRealProviders = mRealProviders.keySet();
-                // Update all providers on device plus gps and network provider when disabling
-                // location
-                Set<String> allProvidersSet = new ArraySet<>(allRealProviders.size() + 2);
-                allProvidersSet.addAll(allRealProviders);
-                // When disabling location, disable gps and network provider that could have been
-                // enabled by location mode api.
-                if (!enabled) {
-                    allProvidersSet.add(LocationManager.GPS_PROVIDER);
-                    allProvidersSet.add(LocationManager.NETWORK_PROVIDER);
-                }
-                if (allProvidersSet.isEmpty()) {
-                    return;
-                }
-                // to ensure thread safety, we write the provider name with a '+' or '-'
-                // and let the SettingsProvider handle it rather than reading and modifying
-                // the list of enabled providers.
-                final String prefix = enabled ? "+" : "-";
-                StringBuilder locationProvidersAllowed = new StringBuilder();
-                for (String provider : allProvidersSet) {
-                    if (provider.equals(LocationManager.PASSIVE_PROVIDER)
-                            || provider.equals(LocationManager.FUSED_PROVIDER)) {
-                        continue;
-                    }
-                    locationProvidersAllowed.append(prefix);
-                    locationProvidersAllowed.append(provider);
-                    locationProvidersAllowed.append(",");
-                }
-                // Remove the trailing comma
-                locationProvidersAllowed.setLength(locationProvidersAllowed.length() - 1);
-                Settings.Secure.putStringForUser(
-                        mContext.getContentResolver(),
-                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                        locationProvidersAllowed.toString(),
-                        userId);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
+        if (UserHandle.getCallingUserId() != userId) {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS,
+                    "Requires INTERACT_ACROSS_USERS permission");
         }
+
+        runFromBinderBlocking(() -> {
+            final Set<String> allRealProviders = mRealProviders.keySet();
+            // Update all providers on device plus gps and network provider when disabling
+            // location
+            Set<String> allProvidersSet = new ArraySet<>(allRealProviders.size() + 2);
+            allProvidersSet.addAll(allRealProviders);
+            // When disabling location, disable gps and network provider that could have been
+            // enabled by location mode api.
+            if (!enabled) {
+                allProvidersSet.add(LocationManager.GPS_PROVIDER);
+                allProvidersSet.add(LocationManager.NETWORK_PROVIDER);
+            }
+            if (allProvidersSet.isEmpty()) {
+                return;
+            }
+            // to ensure thread safety, we write the provider name with a '+' or '-'
+            // and let the SettingsProvider handle it rather than reading and modifying
+            // the list of enabled providers.
+            final String prefix = enabled ? "+" : "-";
+            StringBuilder locationProvidersAllowed = new StringBuilder();
+            for (String provider : allProvidersSet) {
+                if (provider.equals(LocationManager.PASSIVE_PROVIDER)
+                        || provider.equals(LocationManager.FUSED_PROVIDER)) {
+                    continue;
+                }
+                locationProvidersAllowed.append(prefix);
+                locationProvidersAllowed.append(provider);
+                locationProvidersAllowed.append(",");
+            }
+            // Remove the trailing comma
+            locationProvidersAllowed.setLength(locationProvidersAllowed.length() - 1);
+            Settings.Secure.putStringForUser(
+                    mContext.getContentResolver(),
+                    Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                    locationProvidersAllowed.toString(),
+                    userId);
+        });
     }
 
-    /**
-     * Returns the current enabled/disabled status of a location provider and user
-     *
-     * @param providerName name of the provider
-     * @param userId       the id of the user
-     * @return true if the provider exists and is enabled
-     */
     @Override
-    public boolean isProviderEnabledForUser(String providerName, int userId) {
+    public boolean isProviderEnabledForUser(String providerName, int userId)
+            throws RemoteException {
         // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
-        checkInteractAcrossUsersPermission(userId);
-
-        if (!isLocationEnabledForUser(userId)) {
-            return false;
+        if (UserHandle.getCallingUserId() != userId) {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS,
+                    "Requires INTERACT_ACROSS_USERS permission");
         }
 
         // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
         // so we discourage its use
         if (LocationManager.FUSED_PROVIDER.equals(providerName)) return false;
 
-        long identity = Binder.clearCallingIdentity();
-        try {
-            LocationProvider provider;
-            synchronized (mLock) {
-                provider = mProvidersByName.get(providerName);
-            }
-            return provider != null && provider.isEnabled();
-        } finally {
-            Binder.restoreCallingIdentity(identity);
+        if (!isLocationEnabledForUser(userId)) {
+            return false;
         }
+
+        return runFromBinderBlocking(() -> {
+            LocationProvider provider = mProvidersByName.get(providerName);
+            return provider != null && provider.isEnabled();
+        });
     }
 
-    /**
-     * Enable or disable a single location provider.
-     *
-     * @param provider name of the provider
-     * @param enabled  true to enable the provider. False to disable the provider
-     * @param userId   the id of the user to set
-     * @return true if the value was set, false on errors
-     */
     @Override
     public boolean setProviderEnabledForUser(String provider, boolean enabled, int userId) {
         return false;
     }
 
-    /**
-     * Method for checking INTERACT_ACROSS_USERS permission if specified user id is not the same as
-     * current user id
-     *
-     * @param userId the user id to get or set value
-     */
-    private void checkInteractAcrossUsersPermission(int userId) {
-        int uid = Binder.getCallingUid();
-        if (UserHandle.getUserId(uid) != userId) {
-            if (ActivityManager.checkComponentPermission(
-                    android.Manifest.permission.INTERACT_ACROSS_USERS, uid, -1, true)
-                    != PERMISSION_GRANTED) {
-                throw new SecurityException("Requires INTERACT_ACROSS_USERS permission");
-            }
-        }
-    }
-
-    /**
-     * Returns "true" if the UID belongs to a bound location provider.
-     *
-     * @param uid the uid
-     * @return true if uid belongs to a bound location provider
-     */
     private boolean isUidALocationProvider(int uid) {
         if (uid == Process.SYSTEM_UID) {
             return true;
         }
-        if (mGeocodeProvider != null) {
-            if (doesUidHavePackage(uid, mGeocodeProvider.getConnectedPackageName())) return true;
-        }
+
         for (LocationProviderProxy proxy : mProxyProviders) {
             if (doesUidHavePackage(uid, proxy.getConnectedPackageName())) return true;
         }
@@ -2907,7 +2819,6 @@
         // providers installed oustide the system image. So
         // also allow providers with a UID matching the
         // currently bound package name
-
         if (isUidALocationProvider(Binder.getCallingUid())) {
             return;
         }
@@ -2916,9 +2827,6 @@
                 "or UID of a currently bound location provider");
     }
 
-    /**
-     * Returns true if the given package belongs to the given uid.
-     */
     private boolean doesUidHavePackage(int uid, String packageName) {
         if (packageName == null) {
             return false;
@@ -2935,22 +2843,12 @@
         return false;
     }
 
+    // TODO: will be removed in future
     @Override
     public void reportLocation(Location location, boolean passive) {
-        checkCallerIsProvider();
-
-        if (!location.isComplete()) {
-            Log.w(TAG, "Dropping incomplete location: " + location);
-            return;
-        }
-
-        mLocationHandler.removeMessages(MSG_LOCATION_CHANGED, location);
-        Message m = Message.obtain(mLocationHandler, MSG_LOCATION_CHANGED, location);
-        m.arg1 = (passive ? 1 : 0);
-        mLocationHandler.sendMessageAtFrontOfQueue(m);
+        throw new IllegalStateException("operation unsupported");
     }
 
-
     private static boolean shouldBroadcastSafe(
             Location loc, Location lastLoc, UpdateRecord record, long now) {
         // Always broadcast the first update
@@ -2983,14 +2881,33 @@
         return record.mRealRequest.getExpireAt() >= now;
     }
 
-    private void handleLocationChangedLocked(Location location, boolean passive) {
-        if (D) Log.d(TAG, "incoming location: " + location);
+    private void handleLocationChanged(Location location, boolean passive) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
+        // create a working copy of the incoming Location so that the service can modify it without
+        // disturbing the caller's copy
+        Location myLocation = new Location(location);
+        String pr = myLocation.getProvider();
+
+        // set "isFromMockProvider" bit if location came from a mock provider. we do not clear this
+        // bit if location did not come from a mock provider because passive/fused providers can
+        // forward locations from mock providers, and should not grant them legitimacy in doing so.
+        if (!myLocation.isFromMockProvider() && isMockProvider(pr)) {
+            myLocation.setIsFromMockProvider(true);
+        }
+
+        if (!passive) {
+            // notify passive provider of the new location
+            mPassiveProvider.updateLocation(myLocation);
+        }
+
+        if (D) Log.d(TAG, "incoming location: " + myLocation);
         long now = SystemClock.elapsedRealtime();
-        String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
+        String provider = (passive ? LocationManager.PASSIVE_PROVIDER : myLocation.getProvider());
         // Skip if the provider is unknown.
         LocationProvider p = mProvidersByName.get(provider);
         if (p == null) return;
-        updateLastLocationLocked(location, provider);
+        updateLastLocation(myLocation, provider);
         // mLastLocation should have been updated from the updateLastLocationLocked call above.
         Location lastLocation = mLastLocation.get(provider);
         if (lastLocation == null) {
@@ -3001,13 +2918,13 @@
         // Update last known coarse interval location if enough time has passed.
         Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(provider);
         if (lastLocationCoarseInterval == null) {
-            lastLocationCoarseInterval = new Location(location);
+            lastLocationCoarseInterval = new Location(myLocation);
             mLastLocationCoarseInterval.put(provider, lastLocationCoarseInterval);
         }
-        long timeDiffNanos = location.getElapsedRealtimeNanos()
+        long timeDiffNanos = myLocation.getElapsedRealtimeNanos()
                 - lastLocationCoarseInterval.getElapsedRealtimeNanos();
         if (timeDiffNanos > LocationFudger.FASTEST_INTERVAL_MS * NANOS_PER_MILLI) {
-            lastLocationCoarseInterval.set(location);
+            lastLocationCoarseInterval.set(myLocation);
         }
         // Don't ever return a coarse location that is more recent than the allowed update
         // interval (i.e. don't allow an app to keep registering and unregistering for
@@ -3131,24 +3048,20 @@
         // remove dead records and receivers outside the loop
         if (deadReceivers != null) {
             for (Receiver receiver : deadReceivers) {
-                removeUpdatesLocked(receiver);
+                removeUpdates(receiver);
             }
         }
         if (deadUpdateRecords != null) {
             for (UpdateRecord r : deadUpdateRecords) {
                 r.disposeLocked(true);
             }
-            applyRequirementsLocked(provider);
+            applyRequirements(provider);
         }
     }
 
-    /**
-     * Updates last location with the given location
-     *
-     * @param location new location to update
-     * @param provider Location provider to update for
-     */
-    private void updateLastLocationLocked(Location location, String provider) {
+    private void updateLastLocation(Location location, String provider) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
         Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
         Location lastNoGPSLocation;
         Location lastLocation = mLastLocation.get(provider);
@@ -3166,75 +3079,11 @@
         lastLocation.set(location);
     }
 
-    private class LocationWorkerHandler extends Handler {
-        public LocationWorkerHandler(Looper looper) {
-            super(looper, null, true);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_LOCATION_CHANGED:
-                    handleLocationChanged((Location) msg.obj, msg.arg1 == 1);
-                    break;
-            }
-        }
-    }
-
     private boolean isMockProvider(String provider) {
-        synchronized (mLock) {
-            return mMockProviders.containsKey(provider);
-        }
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+        return mMockProviders.containsKey(provider);
     }
 
-    private void handleLocationChanged(Location location, boolean passive) {
-        // create a working copy of the incoming Location so that the service can modify it without
-        // disturbing the caller's copy
-        Location myLocation = new Location(location);
-        String provider = myLocation.getProvider();
-
-        // set "isFromMockProvider" bit if location came from a mock provider. we do not clear this
-        // bit if location did not come from a mock provider because passive/fused providers can
-        // forward locations from mock providers, and should not grant them legitimacy in doing so.
-        if (!myLocation.isFromMockProvider() && isMockProvider(provider)) {
-            myLocation.setIsFromMockProvider(true);
-        }
-
-        synchronized (mLock) {
-            if (!passive) {
-                // notify passive provider of the new location
-                mPassiveProvider.updateLocation(myLocation);
-            }
-            handleLocationChangedLocked(myLocation, passive);
-        }
-    }
-
-    private final PackageMonitor mPackageMonitor = new PackageMonitor() {
-        @Override
-        public void onPackageDisappeared(String packageName, int reason) {
-            // remove all receivers associated with this package name
-            synchronized (mLock) {
-                ArrayList<Receiver> deadReceivers = null;
-
-                for (Receiver receiver : mReceivers.values()) {
-                    if (receiver.mIdentity.mPackageName.equals(packageName)) {
-                        if (deadReceivers == null) {
-                            deadReceivers = new ArrayList<>();
-                        }
-                        deadReceivers.add(receiver);
-                    }
-                }
-
-                // perform removal outside of mReceivers loop
-                if (deadReceivers != null) {
-                    for (Receiver receiver : deadReceivers) {
-                        removeUpdatesLocked(receiver);
-                    }
-                }
-            }
-        }
-    };
-
     // Geocoder
 
     @Override
@@ -3275,7 +3124,8 @@
     }
 
     @Override
-    public void addTestProvider(String name, ProviderProperties properties, String opPackageName) {
+    public void addTestProvider(String name, ProviderProperties properties, String opPackageName)
+            throws RemoteException {
         if (!canCallerAccessMockLocation(opPackageName)) {
             return;
         }
@@ -3284,23 +3134,24 @@
             throw new IllegalArgumentException("Cannot mock the passive location provider");
         }
 
-        long identity = Binder.clearCallingIdentity();
-        synchronized (mLock) {
+        runFromBinderBlocking(() -> {
             // remove the real provider if we are replacing GPS or network provider
             if (LocationManager.GPS_PROVIDER.equals(name)
                     || LocationManager.NETWORK_PROVIDER.equals(name)
                     || LocationManager.FUSED_PROVIDER.equals(name)) {
                 LocationProvider p = mProvidersByName.get(name);
                 if (p != null) {
-                    removeProviderLocked(p);
+                    removeProvider(p);
                 }
             }
-            addTestProviderLocked(name, properties);
-        }
-        Binder.restoreCallingIdentity(identity);
+            addTestProvider(name, properties);
+            return null;
+        });
     }
 
-    private void addTestProviderLocked(String name, ProviderProperties properties) {
+    private void addTestProvider(String name, ProviderProperties properties) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
         if (mProvidersByName.get(name) != null) {
             throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
         }
@@ -3308,122 +3159,103 @@
         LocationProvider provider = new LocationProvider(name);
         MockProvider mockProvider = new MockProvider(provider, properties);
 
-        addProviderLocked(provider);
+        addProvider(provider);
         mMockProviders.put(name, mockProvider);
         mLastLocation.put(name, null);
         mLastLocationCoarseInterval.put(name, null);
     }
 
     @Override
-    public void removeTestProvider(String provider, String opPackageName) {
+    public void removeTestProvider(String provider, String opPackageName) throws RemoteException {
         if (!canCallerAccessMockLocation(opPackageName)) {
             return;
         }
 
-        synchronized (mLock) {
+        runFromBinderBlocking(() -> {
             MockProvider mockProvider = mMockProviders.remove(provider);
             if (mockProvider == null) {
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
 
-            long identity = Binder.clearCallingIdentity();
-            try {
-                removeProviderLocked(mProvidersByName.get(provider));
+            removeProvider(mProvidersByName.get(provider));
 
-                // reinstate real provider if available
-                LocationProvider realProvider = mRealProviders.get(provider);
-                if (realProvider != null) {
-                    addProviderLocked(realProvider);
-                }
-                mLastLocation.put(provider, null);
-                mLastLocationCoarseInterval.put(provider, null);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
+            // reinstate real provider if available
+            LocationProvider realProvider = mRealProviders.get(provider);
+            if (realProvider != null) {
+                addProvider(realProvider);
             }
-        }
+            mLastLocation.put(provider, null);
+            mLastLocationCoarseInterval.put(provider, null);
+        });
     }
 
     @Override
-    public void setTestProviderLocation(String provider, Location loc, String opPackageName) {
+    public void setTestProviderLocation(String provider, Location loc, String opPackageName)
+            throws RemoteException {
         if (!canCallerAccessMockLocation(opPackageName)) {
             return;
         }
 
-        synchronized (mLock) {
+        runFromBinderBlocking(() -> {
             MockProvider mockProvider = mMockProviders.get(provider);
             if (mockProvider == null) {
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
 
-            // Ensure that the location is marked as being mock. There's some logic to do this in
-            // handleLocationChanged(), but it fails if loc has the wrong provider (bug 33091107).
+            // Ensure that the location is marked as being mock. There's some logic to do this
+            // in handleLocationChanged(), but it fails if loc has the wrong provider
+            // (b/33091107).
             Location mock = new Location(loc);
             mock.setIsFromMockProvider(true);
 
             if (!TextUtils.isEmpty(loc.getProvider()) && !provider.equals(loc.getProvider())) {
-                // The location has an explicit provider that is different from the mock provider
-                // name. The caller may be trying to fool us via bug 33091107.
+                // The location has an explicit provider that is different from the mock
+                // provider name. The caller may be trying to fool us via bug 33091107.
                 EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(),
                         provider + "!=" + loc.getProvider());
             }
 
-            // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
-            long identity = Binder.clearCallingIdentity();
-            try {
-                mockProvider.setLocation(mock);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
+            mockProvider.setLocation(mock);
+        });
     }
 
     @Override
-    public void setTestProviderEnabled(String provider, boolean enabled, String opPackageName) {
+    public void setTestProviderEnabled(String provider, boolean enabled, String opPackageName)
+            throws RemoteException {
         if (!canCallerAccessMockLocation(opPackageName)) {
             return;
         }
 
-        synchronized (mLock) {
+        runFromBinderBlocking(() -> {
             MockProvider mockProvider = mMockProviders.get(provider);
             if (mockProvider == null) {
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
-            long identity = Binder.clearCallingIdentity();
-            try {
-                mockProvider.setEnabled(enabled);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
+            mockProvider.setEnabled(enabled);
+        });
     }
 
     @Override
     public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime,
-            String opPackageName) {
+            String opPackageName) throws RemoteException {
         if (!canCallerAccessMockLocation(opPackageName)) {
             return;
         }
 
-        synchronized (mLock) {
+        runFromBinderBlocking(() -> {
             MockProvider mockProvider = mMockProviders.get(provider);
             if (mockProvider == null) {
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
             mockProvider.setStatus(status, extras, updateTime);
-        }
-    }
-
-    private void log(String log) {
-        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-            Slog.d(TAG, log);
-        }
+        });
     }
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
-        synchronized (mLock) {
+        Runnable dump = () -> {
             if (args.length > 0 && args[0].equals("--gnssmetrics")) {
                 if (mGnssMetricsProvider != null) {
                     pw.append(mGnssMetricsProvider.getGnssMetricsAsProtoString());
@@ -3492,6 +3324,11 @@
                 }
             }
 
+            if (mLocationControllerExtraPackage != null) {
+                pw.println(" Location controller extra package: " + mLocationControllerExtraPackage
+                        + " enabled: " + mLocationControllerExtraPackageEnabled);
+            }
+
             if (!mBackgroundThrottlePackageWhitelist.isEmpty()) {
                 pw.println("  Throttling Whitelisted Packages:");
                 for (String packageName : mBackgroundThrottlePackageWhitelist) {
@@ -3511,6 +3348,14 @@
             if (mGnssBatchingInProgress) {
                 pw.println("  GNSS batching in progress");
             }
+        };
+
+        FutureTask<Void> task = new FutureTask<>(dump, null);
+        mHandler.postAtFrontOfQueue(task);
+        try {
+            task.get();
+        } catch (InterruptedException | ExecutionException e) {
+            pw.println("error dumping: " + e);
         }
     }
 }
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 8869af4..b0ca2df 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -2314,11 +2314,11 @@
     }
 
     @Override
-    public void createVirtualNetwork(int netId, boolean hasDNS, boolean secure) {
+    public void createVirtualNetwork(int netId, boolean secure) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
         try {
-            mNetdService.networkCreateVpn(netId, hasDNS, secure);
+            mNetdService.networkCreateVpn(netId, secure);
         } catch (RemoteException | ServiceSpecificException e) {
             throw new IllegalStateException(e);
         }
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
new file mode 100644
index 0000000..f27d373
--- /dev/null
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Monitors the health of packages on the system and notifies interested observers when packages
+ * fail. All registered observers will be notified until an observer takes a mitigation action.
+ */
+public class PackageWatchdog {
+    private static final String TAG = "PackageWatchdog";
+    // Duration to count package failures before it resets to 0
+    private static final int TRIGGER_DURATION_MS = 60000;
+    // Number of package failures within the duration above before we notify observers
+    private static final int TRIGGER_FAILURE_COUNT = 5;
+    private static final int DB_VERSION = 1;
+    private static final String TAG_PACKAGE_WATCHDOG = "package-watchdog";
+    private static final String TAG_PACKAGE = "package";
+    private static final String TAG_OBSERVER = "observer";
+    private static final String ATTR_VERSION = "version";
+    private static final String ATTR_NAME = "name";
+    private static final String ATTR_DURATION = "duration";
+
+    private static PackageWatchdog sPackageWatchdog;
+
+    private final Object mLock = new Object();
+    // System server context
+    private final Context mContext;
+    // Handler to run package cleanup runnables
+    private final Handler mTimerHandler;
+    private final Handler mIoHandler;
+    // Contains (observer-name -> external-observer-handle) that have been registered during the
+    // current boot.
+    // It is populated when observers call #registerHealthObserver and it does not survive reboots.
+    @GuardedBy("mLock")
+    final ArrayMap<String, PackageHealthObserver> mRegisteredObservers = new ArrayMap<>();
+    // Contains (observer-name -> internal-observer-handle) that have ever been registered from
+    // previous boots. Observers with all packages expired are periodically pruned.
+    // It is saved to disk on system shutdown and repouplated on startup so it survives reboots.
+    @GuardedBy("mLock")
+    final ArrayMap<String, ObserverInternal> mAllObservers = new ArrayMap<>();
+    // File containing the XML data of monitored packages /data/system/package-watchdog.xml
+    private final AtomicFile mPolicyFile =
+            new AtomicFile(new File(new File(Environment.getDataDirectory(), "system"),
+                           "package-watchdog.xml"));
+    // Runnable to prune monitored packages that have expired
+    private final Runnable mPackageCleanup;
+    // Last SystemClock#uptimeMillis a package clean up was executed.
+    // 0 if mPackageCleanup not running.
+    private long mUptimeAtLastRescheduleMs;
+    // Duration a package cleanup was last scheduled for.
+    // 0 if mPackageCleanup not running.
+    private long mDurationAtLastReschedule;
+
+    private PackageWatchdog(Context context) {
+        mContext = context;
+        mTimerHandler = new Handler(Looper.myLooper());
+        mIoHandler = BackgroundThread.getHandler();
+        mPackageCleanup = this::rescheduleCleanup;
+        loadFromFile();
+    }
+
+    /** Creates or gets singleton instance of PackageWatchdog. */
+    public static PackageWatchdog getInstance(Context context) {
+        synchronized (PackageWatchdog.class) {
+            if (sPackageWatchdog == null) {
+                sPackageWatchdog = new PackageWatchdog(context);
+            }
+            return sPackageWatchdog;
+        }
+    }
+
+    /**
+     * Registers {@code observer} to listen for package failures
+     *
+     * <p>Observers are expected to call this on boot. It does not specify any packages but
+     * it will resume observing any packages requested from a previous boot.
+     */
+    public void registerHealthObserver(PackageHealthObserver observer) {
+        synchronized (mLock) {
+            mRegisteredObservers.put(observer.getName(), observer);
+            if (mDurationAtLastReschedule == 0) {
+                // Nothing running, schedule
+                rescheduleCleanup();
+            }
+        }
+    }
+
+    /**
+     * Starts observing the health of the {@code packages} for {@code observer} and notifies
+     * {@code observer} of any package failures within the monitoring duration.
+     *
+     * <p>If {@code observer} is already monitoring a package in {@code packageNames},
+     * the monitoring window of that package will be reset to {@code durationMs}.
+     *
+     * @throws IllegalArgumentException if {@code packageNames} is empty
+     * or {@code durationMs} is less than 1
+     */
+    public void startObservingHealth(PackageHealthObserver observer, List<String> packageNames,
+            int durationMs) {
+        if (packageNames.isEmpty() || durationMs < 1) {
+            throw new IllegalArgumentException("Observation not started, no packages specified"
+                    + "or invalid duration");
+        }
+        List<MonitoredPackage> packages = new ArrayList<>();
+        for (int i = 0; i < packageNames.size(); i++) {
+            packages.add(new MonitoredPackage(packageNames.get(i), durationMs));
+        }
+        synchronized (mLock) {
+            ObserverInternal oldObserver = mAllObservers.get(observer.getName());
+            if (oldObserver == null) {
+                Slog.d(TAG, observer.getName() + " started monitoring health of packages "
+                        + packageNames);
+                mAllObservers.put(observer.getName(),
+                        new ObserverInternal(observer.getName(), packages));
+            } else {
+                Slog.d(TAG, observer.getName() + " added the following packages to monitor "
+                        + packageNames);
+                oldObserver.updatePackages(packages);
+            }
+        }
+        registerHealthObserver(observer);
+        // Always reschedule because we may need to expire packages
+        // earlier than we are already scheduled for
+        rescheduleCleanup();
+        saveToFileAsync();
+    }
+
+    /**
+     * Unregisters {@code observer} from listening to package failure.
+     * Additionally, this stops observing any packages that may have previously been observed
+     * even from a previous boot.
+     */
+    public void unregisterHealthObserver(PackageHealthObserver observer) {
+        synchronized (mLock) {
+            mAllObservers.remove(observer.getName());
+            mRegisteredObservers.remove(observer.getName());
+        }
+        saveToFileAsync();
+    }
+
+    // TODO(zezeozue:) Accept current versionCodes of failing packages?
+    /**
+     * Called when a process fails either due to a crash or ANR.
+     *
+     * <p>All registered observers for the packages contained in the process will be notified in
+     * order of priority until an observer signifies that it has taken action and other observers
+     * should not notified.
+     *
+     * <p>This method could be called frequently if there is a severe problem on the device.
+     */
+    public void onPackageFailure(String[] packages) {
+        ArrayMap<String, List<PackageHealthObserver>> packagesToReport = new ArrayMap<>();
+        synchronized (mLock) {
+            if (mRegisteredObservers.isEmpty()) {
+                return;
+            }
+
+            for (int pIndex = 0; pIndex < packages.length; pIndex++) {
+                for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
+                    // Observers interested in receiving packageName failures
+                    List<PackageHealthObserver> observersToNotify = new ArrayList<>();
+                    PackageHealthObserver activeObserver =
+                            mRegisteredObservers.get(mAllObservers.valueAt(oIndex).mName);
+                    if (activeObserver != null) {
+                        observersToNotify.add(activeObserver);
+                    }
+
+                    // Save interested observers and notify them outside the lock
+                    if (!observersToNotify.isEmpty()) {
+                        packagesToReport.put(packages[pIndex], observersToNotify);
+                    }
+                }
+            }
+        }
+
+        // Notify observers
+        for (int pIndex = 0; pIndex < packagesToReport.size(); pIndex++) {
+            List<PackageHealthObserver> observers = packagesToReport.valueAt(pIndex);
+            for (int oIndex = 0; oIndex < observers.size(); oIndex++) {
+                if (observers.get(oIndex).onHealthCheckFailed(packages[pIndex])) {
+                    // Observer has handled, do not notify others
+                    break;
+                }
+            }
+        }
+    }
+
+    // TODO(zezeozue): Optimize write? Maybe only write a separate smaller file?
+    // This currently adds about 7ms extra to shutdown thread
+    /** Writes the package information to file during shutdown. */
+    public void writeNow() {
+        if (!mAllObservers.isEmpty()) {
+            mIoHandler.removeCallbacks(this::saveToFile);
+            pruneObservers(SystemClock.uptimeMillis() - mUptimeAtLastRescheduleMs);
+            saveToFile();
+            Slog.i(TAG, "Last write to update package durations");
+        }
+    }
+
+    /** Register instances of this interface to receive notifications on package failure. */
+    public interface PackageHealthObserver {
+        /**
+         * Called when health check fails for the {@code packageName}.
+         * @return {@code true} if action was taken and other observers should not be notified of
+         * this failure, {@code false} otherwise.
+         */
+        boolean onHealthCheckFailed(String packageName);
+
+        // TODO(zezeozue): Ensure uniqueness?
+        /**
+         * Identifier for the observer, should not change across device updates otherwise the
+         * watchdog may drop observing packages with the old name.
+         */
+        String getName();
+    }
+
+    /** Reschedules handler to prune expired packages from observers. */
+    private void rescheduleCleanup() {
+        synchronized (mLock) {
+            long nextDurationToScheduleMs = getEarliestPackageExpiryLocked();
+            if (nextDurationToScheduleMs == Long.MAX_VALUE) {
+                Slog.i(TAG, "No monitored packages, ending package cleanup");
+                mDurationAtLastReschedule = 0;
+                mUptimeAtLastRescheduleMs = 0;
+                return;
+            }
+            long uptimeMs = SystemClock.uptimeMillis();
+            // O if mPackageCleanup not running
+            long elapsedDurationMs = mUptimeAtLastRescheduleMs == 0
+                    ? 0 : uptimeMs - mUptimeAtLastRescheduleMs;
+            // O if mPackageCleanup not running
+            long remainingDurationMs = mDurationAtLastReschedule - elapsedDurationMs;
+
+            if (mUptimeAtLastRescheduleMs == 0 || nextDurationToScheduleMs < remainingDurationMs) {
+                // First schedule or an earlier reschedule
+                pruneObservers(elapsedDurationMs);
+                mTimerHandler.removeCallbacks(mPackageCleanup);
+                mTimerHandler.postDelayed(mPackageCleanup, nextDurationToScheduleMs);
+                mDurationAtLastReschedule = nextDurationToScheduleMs;
+                mUptimeAtLastRescheduleMs = uptimeMs;
+            }
+        }
+    }
+
+    /**
+     * Returns the earliest time a package should expire.
+     * @returns Long#MAX_VALUE if there are no observed packages.
+     */
+    private long getEarliestPackageExpiryLocked() {
+        long shortestDurationMs = Long.MAX_VALUE;
+        for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
+            ArrayMap<String, MonitoredPackage> packages = mAllObservers.valueAt(oIndex).mPackages;
+            for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
+                long duration = packages.valueAt(pIndex).mDurationMs;
+                if (duration < shortestDurationMs) {
+                    shortestDurationMs = duration;
+                }
+            }
+        }
+        Slog.v(TAG, "Earliest package time is " + shortestDurationMs);
+        return shortestDurationMs;
+    }
+
+    /**
+     * Removes {@code elapsedMs} milliseconds from all durations on monitored packages.
+     * Discards expired packages and discards observers without any packages.
+     */
+    private void pruneObservers(long elapsedMs) {
+        if (elapsedMs == 0) {
+            return;
+        }
+        synchronized (mLock) {
+            Slog.d(TAG, "Removing expired packages after " + elapsedMs + "ms");
+            Iterator<ObserverInternal> it = mAllObservers.values().iterator();
+            while (it.hasNext()) {
+                ObserverInternal observer = it.next();
+                if (!observer.updateMonitoringDurations(elapsedMs)) {
+                    Slog.i(TAG, "Discarding observer " + observer.mName + ". All packages expired");
+                    it.remove();
+                }
+            }
+        }
+        saveToFileAsync();
+    }
+
+    /**
+     * Loads mAllObservers from file.
+     *
+     * <p>Note that this is <b>not</b> thread safe and should only called be called
+     * from the constructor.
+     */
+    private void loadFromFile() {
+        InputStream infile = null;
+        mAllObservers.clear();
+        try {
+            infile = mPolicyFile.openRead();
+            final XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(infile, StandardCharsets.UTF_8.name());
+            XmlUtils.beginDocument(parser, TAG_PACKAGE_WATCHDOG);
+            int outerDepth = parser.getDepth();
+            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+                ObserverInternal observer = ObserverInternal.read(parser);
+                if (observer != null) {
+                    mAllObservers.put(observer.mName, observer);
+                }
+            }
+        } catch (FileNotFoundException e) {
+            // Nothing to monitor
+        } catch (IOException | NumberFormatException | XmlPullParserException e) {
+            Log.wtf(TAG, "Unable to read monitored packages, deleting file", e);
+            mPolicyFile.delete();
+        } finally {
+            IoUtils.closeQuietly(infile);
+        }
+    }
+
+    /**
+     * Persists mAllObservers to file. Threshold information is ignored.
+     */
+    private boolean saveToFile() {
+        synchronized (mLock) {
+            FileOutputStream stream;
+            try {
+                stream = mPolicyFile.startWrite();
+            } catch (IOException e) {
+                Slog.w(TAG, "Cannot update monitored packages", e);
+                return false;
+            }
+
+            try {
+                XmlSerializer out = new FastXmlSerializer();
+                out.setOutput(stream, StandardCharsets.UTF_8.name());
+                out.startDocument(null, true);
+                out.startTag(null, TAG_PACKAGE_WATCHDOG);
+                out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
+                for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
+                    mAllObservers.valueAt(oIndex).write(out);
+                }
+                out.endTag(null, TAG_PACKAGE_WATCHDOG);
+                out.endDocument();
+                mPolicyFile.finishWrite(stream);
+                return true;
+            } catch (IOException e) {
+                Slog.w(TAG, "Failed to save monitored packages, restoring backup", e);
+                mPolicyFile.failWrite(stream);
+                return false;
+            } finally {
+                IoUtils.closeQuietly(stream);
+            }
+        }
+    }
+
+    private void saveToFileAsync() {
+        mIoHandler.removeCallbacks(this::saveToFile);
+        mIoHandler.post(this::saveToFile);
+    }
+
+    /**
+     * Represents an observer monitoring a set of packages along with the failure thresholds for
+     * each package.
+     */
+    static class ObserverInternal {
+        public final String mName;
+        public final ArrayMap<String, MonitoredPackage> mPackages;
+
+        ObserverInternal(String name, List<MonitoredPackage> packages) {
+            mName = name;
+            mPackages = new ArrayMap<>();
+            updatePackages(packages);
+        }
+
+        /**
+         * Writes important details to file. Doesn't persist any package failure thresholds.
+         *
+         * <p>Note that this method is <b>not</b> thread safe. It should only be called from
+         * #saveToFile which runs on a single threaded handler.
+         */
+        public boolean write(XmlSerializer out) {
+            try {
+                out.startTag(null, TAG_OBSERVER);
+                out.attribute(null, ATTR_NAME, mName);
+                for (int i = 0; i < mPackages.size(); i++) {
+                    MonitoredPackage p = mPackages.valueAt(i);
+                    out.startTag(null, TAG_PACKAGE);
+                    out.attribute(null, ATTR_NAME, p.mName);
+                    out.attribute(null, ATTR_DURATION, String.valueOf(p.mDurationMs));
+                    out.endTag(null, TAG_PACKAGE);
+                }
+                out.endTag(null, TAG_OBSERVER);
+                return true;
+            } catch (IOException e) {
+                Slog.w(TAG, "Cannot save observer", e);
+                return false;
+            }
+        }
+
+        public void updatePackages(List<MonitoredPackage> packages) {
+            synchronized (mName) {
+                for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
+                    MonitoredPackage p = packages.get(pIndex);
+                    mPackages.put(p.mName, p);
+                }
+            }
+        }
+
+        /**
+         * Reduces the monitoring durations of all packages observed by this observer by
+         *  {@code elapsedMs}. If any duration is less than 0, the package is removed from
+         * observation.
+         *
+         * @returns {@code true} if there are still packages to be observed, {@code false} otherwise
+         */
+        public boolean updateMonitoringDurations(long elapsedMs) {
+            List<MonitoredPackage> packages = new ArrayList<>();
+            synchronized (mName) {
+                Iterator<MonitoredPackage> it = mPackages.values().iterator();
+                while (it.hasNext()) {
+                    MonitoredPackage p = it.next();
+                    long newDuration = p.mDurationMs - elapsedMs;
+                    if (newDuration > 0) {
+                        p.mDurationMs = newDuration;
+                    } else {
+                        it.remove();
+                    }
+                }
+                return !mPackages.isEmpty();
+            }
+        }
+
+        /**
+         * Increments failure counts of {@code packageName}.
+         * @returns {@code true} if failure threshold is exceeded, {@code false} otherwise
+         */
+        public boolean onPackageFailure(String packageName) {
+            synchronized (mName) {
+                MonitoredPackage p = mPackages.get(packageName);
+                if (p != null) {
+                    return p.onFailure();
+                }
+                return false;
+            }
+        }
+
+        /**
+         * Returns one ObserverInternal from the {@code parser} and advances its state.
+         *
+         * <p>Note that this method is <b>not</b> thread safe. It should only be called from
+         * #loadFromFile which in turn is only called on construction of the
+         * singleton PackageWatchdog.
+         **/
+        public static ObserverInternal read(XmlPullParser parser) {
+            String observerName = null;
+            if (TAG_OBSERVER.equals(parser.getName())) {
+                observerName = parser.getAttributeValue(null, ATTR_NAME);
+                if (TextUtils.isEmpty(observerName)) {
+                    return null;
+                }
+            }
+            List<MonitoredPackage> packages = new ArrayList<>();
+            int innerDepth = parser.getDepth();
+            try {
+                while (XmlUtils.nextElementWithin(parser, innerDepth)) {
+                    if (TAG_PACKAGE.equals(parser.getName())) {
+                        String packageName = parser.getAttributeValue(null, ATTR_NAME);
+                        long duration = Long.parseLong(
+                                parser.getAttributeValue(null, ATTR_DURATION));
+                        if (!TextUtils.isEmpty(packageName)) {
+                            packages.add(new MonitoredPackage(packageName, duration));
+                        }
+                    }
+                }
+            } catch (IOException e) {
+                return null;
+            } catch (XmlPullParserException e) {
+                return null;
+            }
+            if (packages.isEmpty()) {
+                return null;
+            }
+            return new ObserverInternal(observerName, packages);
+        }
+    }
+
+    /** Represents a package along with the time it should be monitored for. */
+    static class MonitoredPackage {
+        public final String mName;
+        // System uptime duration to monitor package
+        public long mDurationMs;
+        // System uptime of first package failure
+        private long mUptimeStartMs;
+        // Number of failures since mUptimeStartMs
+        private int mFailures;
+
+        MonitoredPackage(String name, long durationMs) {
+            mName = name;
+            mDurationMs = durationMs;
+        }
+
+        /**
+         * Increment package failures or resets failure count depending on the last package failure.
+         *
+         * @return {@code true} if failure count exceeds a threshold, {@code false} otherwise
+         */
+        public synchronized boolean onFailure() {
+            final long now = SystemClock.uptimeMillis();
+            final long duration = now - mUptimeStartMs;
+            if (duration > TRIGGER_DURATION_MS) {
+                // TODO(zezeozue): Reseting to 1 is not correct
+                // because there may be more than 1 failure in the last trigger window from now
+                // This is the RescueParty impl, will leave for now
+                mFailures = 1;
+                mUptimeStartMs = now;
+            } else {
+                mFailures++;
+            }
+            return mFailures >= TRIGGER_FAILURE_COUNT;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
new file mode 100644
index 0000000..1cbcbe5
--- /dev/null
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.hardware.ISensorPrivacyListener;
+import android.hardware.ISensorPrivacyManager;
+import android.location.LocationManager;
+import android.net.ConnectivityManager;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.ArrayMap;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Xml;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+import com.android.internal.util.function.pooled.PooledLambda;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.NoSuchElementException;
+
+/** @hide */
+public final class SensorPrivacyService extends SystemService {
+
+    private static final String TAG = "SensorPrivacyService";
+
+    private static final String SENSOR_PRIVACY_XML_FILE = "sensor_privacy.xml";
+    private static final String XML_TAG_SENSOR_PRIVACY = "sensor-privacy";
+    private static final String XML_ATTRIBUTE_ENABLED = "enabled";
+
+    private final SensorPrivacyServiceImpl mSensorPrivacyServiceImpl;
+
+    public SensorPrivacyService(Context context) {
+        super(context);
+        mSensorPrivacyServiceImpl = new SensorPrivacyServiceImpl(context);
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(Context.SENSOR_PRIVACY_SERVICE, mSensorPrivacyServiceImpl);
+    }
+
+    class SensorPrivacyServiceImpl extends ISensorPrivacyManager.Stub {
+
+        private final SensorPrivacyHandler mHandler;
+        private final Context mContext;
+        private final Object mLock = new Object();
+        @GuardedBy("mLock")
+        private final AtomicFile mAtomicFile;
+        @GuardedBy("mLock")
+        private boolean mEnabled;
+
+        SensorPrivacyServiceImpl(Context context) {
+            mContext = context;
+            mHandler = new SensorPrivacyHandler(FgThread.get().getLooper(), mContext);
+            File sensorPrivacyFile = new File(Environment.getDataSystemDirectory(),
+                    SENSOR_PRIVACY_XML_FILE);
+            mAtomicFile = new AtomicFile(sensorPrivacyFile);
+            synchronized (mLock) {
+                mEnabled = readPersistedSensorPrivacyEnabledLocked();
+            }
+        }
+
+        /**
+         * Sets the sensor privacy to the provided state and notifies all listeners of the new
+         * state.
+         */
+        @Override
+        public void setSensorPrivacy(boolean enable) {
+            enforceSensorPrivacyPermission();
+            synchronized (mLock) {
+                mEnabled = enable;
+                FileOutputStream outputStream = null;
+                try {
+                    XmlSerializer serializer = new FastXmlSerializer();
+                    outputStream = mAtomicFile.startWrite();
+                    serializer.setOutput(outputStream, StandardCharsets.UTF_8.name());
+                    serializer.startDocument(null, true);
+                    serializer.startTag(null, XML_TAG_SENSOR_PRIVACY);
+                    serializer.attribute(null, XML_ATTRIBUTE_ENABLED, String.valueOf(enable));
+                    serializer.endTag(null, XML_TAG_SENSOR_PRIVACY);
+                    serializer.endDocument();
+                    mAtomicFile.finishWrite(outputStream);
+                } catch (IOException e) {
+                    Log.e(TAG, "Caught an exception persisting the sensor privacy state: ", e);
+                    mAtomicFile.failWrite(outputStream);
+                }
+            }
+            mHandler.onSensorPrivacyChanged(enable);
+        }
+
+        /**
+         * Enforces the caller contains the necessary permission to change the state of sensor
+         * privacy.
+         */
+        private void enforceSensorPrivacyPermission() {
+            if (mContext.checkCallingOrSelfPermission(
+                    android.Manifest.permission.MANAGE_SENSOR_PRIVACY) == PERMISSION_GRANTED) {
+                return;
+            }
+            throw new SecurityException(
+                    "Changing sensor privacy requires the following permission: "
+                            + android.Manifest.permission.MANAGE_SENSOR_PRIVACY);
+        }
+
+        /**
+         * Returns whether sensor privacy is enabled.
+         */
+        @Override
+        public boolean isSensorPrivacyEnabled() {
+            synchronized (mLock) {
+                return mEnabled;
+            }
+        }
+
+        /**
+         * Returns the state of sensor privacy from persistent storage.
+         */
+        private boolean readPersistedSensorPrivacyEnabledLocked() {
+            // if the file does not exist then sensor privacy has not yet been enabled on
+            // the device.
+            if (!mAtomicFile.exists()) {
+                return false;
+            }
+            boolean enabled;
+            try (FileInputStream inputStream = mAtomicFile.openRead()) {
+                XmlPullParser parser = Xml.newPullParser();
+                parser.setInput(inputStream, StandardCharsets.UTF_8.name());
+                XmlUtils.beginDocument(parser, XML_TAG_SENSOR_PRIVACY);
+                parser.next();
+                String tagName = parser.getName();
+                enabled = Boolean.valueOf(parser.getAttributeValue(null, XML_ATTRIBUTE_ENABLED));
+            } catch (IOException | XmlPullParserException e) {
+                Log.e(TAG, "Caught an exception reading the state from storage: ", e);
+                // Delete the file to prevent the same error on subsequent calls and assume sensor
+                // privacy is not enabled.
+                mAtomicFile.delete();
+                enabled = false;
+            }
+            return enabled;
+        }
+
+        /**
+         * Persists the state of sensor privacy.
+         */
+        private void persistSensorPrivacyState() {
+            synchronized (mLock) {
+                FileOutputStream outputStream = null;
+                try {
+                    XmlSerializer serializer = new FastXmlSerializer();
+                    outputStream = mAtomicFile.startWrite();
+                    serializer.setOutput(outputStream, StandardCharsets.UTF_8.name());
+                    serializer.startDocument(null, true);
+                    serializer.startTag(null, XML_TAG_SENSOR_PRIVACY);
+                    serializer.attribute(null, XML_ATTRIBUTE_ENABLED, String.valueOf(mEnabled));
+                    serializer.endTag(null, XML_TAG_SENSOR_PRIVACY);
+                    serializer.endDocument();
+                    mAtomicFile.finishWrite(outputStream);
+                } catch (IOException e) {
+                    Log.e(TAG, "Caught an exception persisting the sensor privacy state: ", e);
+                    mAtomicFile.failWrite(outputStream);
+                }
+            }
+        }
+
+        /**
+         * Registers a listener to be notified when the sensor privacy state changes.
+         */
+        @Override
+        public void addSensorPrivacyListener(ISensorPrivacyListener listener) {
+            if (listener == null) {
+                throw new NullPointerException("listener cannot be null");
+            }
+            mHandler.addListener(listener);
+        }
+
+        /**
+         * Unregisters a listener from sensor privacy state change notifications.
+         */
+        @Override
+        public void removeSensorPrivacyListener(ISensorPrivacyListener listener) {
+            if (listener == null) {
+                throw new NullPointerException("listener cannot be null");
+            }
+            mHandler.removeListener(listener);
+        }
+    }
+
+    /**
+     * Handles sensor privacy state changes and notifying listeners of the change.
+     */
+    private final class SensorPrivacyHandler extends Handler {
+        private static final int MESSAGE_SENSOR_PRIVACY_CHANGED = 1;
+
+        private final Object mListenerLock = new Object();
+
+        @GuardedBy("mListenerLock")
+        private final RemoteCallbackList<ISensorPrivacyListener> mListeners =
+                new RemoteCallbackList<>();
+        private final ArrayMap<ISensorPrivacyListener, DeathRecipient> mDeathRecipients;
+        private final Context mContext;
+
+        SensorPrivacyHandler(Looper looper, Context context) {
+            super(looper);
+            mDeathRecipients = new ArrayMap<>();
+            mContext = context;
+        }
+
+        public void onSensorPrivacyChanged(boolean enabled) {
+            sendMessage(PooledLambda.obtainMessage(SensorPrivacyHandler::handleSensorPrivacyChanged,
+                    this, enabled));
+            sendMessage(
+                    PooledLambda.obtainMessage(SensorPrivacyServiceImpl::persistSensorPrivacyState,
+                            mSensorPrivacyServiceImpl));
+        }
+
+        public void addListener(ISensorPrivacyListener listener) {
+            synchronized (mListenerLock) {
+                DeathRecipient deathRecipient = new DeathRecipient(listener);
+                mDeathRecipients.put(listener, deathRecipient);
+                mListeners.register(listener);
+            }
+        }
+
+        public void removeListener(ISensorPrivacyListener listener) {
+            synchronized (mListenerLock) {
+                DeathRecipient deathRecipient = mDeathRecipients.remove(listener);
+                if (deathRecipient != null) {
+                    deathRecipient.destroy();
+                }
+                mListeners.unregister(listener);
+            }
+        }
+
+        public void handleSensorPrivacyChanged(boolean enabled) {
+            final int count = mListeners.beginBroadcast();
+            for (int i = 0; i < count; i++) {
+                ISensorPrivacyListener listener = mListeners.getBroadcastItem(i);
+                try {
+                    listener.onSensorPrivacyChanged(enabled);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Caught an exception notifying listener " + listener + ": ", e);
+                }
+            }
+            mListeners.finishBroadcast();
+            // Handle the state of all sensors managed by this service.
+            SensorState.handleSensorPrivacyToggled(mContext, enabled);
+        }
+    }
+
+    private final class DeathRecipient implements IBinder.DeathRecipient {
+
+        private ISensorPrivacyListener mListener;
+
+        DeathRecipient(ISensorPrivacyListener listener) {
+            mListener = listener;
+            try {
+                mListener.asBinder().linkToDeath(this, 0);
+            } catch (RemoteException e) {
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            mSensorPrivacyServiceImpl.removeSensorPrivacyListener(mListener);
+        }
+
+        public void destroy() {
+            try {
+                mListener.asBinder().unlinkToDeath(this, 0);
+            } catch (NoSuchElementException e) {
+            }
+        }
+    }
+
+    /**
+     * Maintains the state of the sensors when sensor privacy is enabled to return them to their
+     * original state when sensor privacy is disabled.
+     */
+    private static final class SensorState {
+
+        private static Object sLock = new Object();
+        @GuardedBy("sLock")
+        private static SensorState sPreviousState;
+
+        private boolean mAirplaneEnabled;
+        private boolean mLocationEnabled;
+
+        SensorState(boolean airplaneEnabled, boolean locationEnabled) {
+            mAirplaneEnabled = airplaneEnabled;
+            mLocationEnabled = locationEnabled;
+        }
+
+        public static void handleSensorPrivacyToggled(Context context, boolean enabled) {
+            synchronized (sLock) {
+                SensorState state;
+                if (enabled) {
+                    // if sensor privacy is being enabled then obtain the current state of the
+                    // sensors to be persisted and restored when sensor privacy is disabled.
+                    state = getCurrentSensorState(context);
+                } else {
+                    // else obtain the previous sensor state to be restored, first from the saved
+                    // state if available, otherwise attempt to read it from Settings.
+                    if (sPreviousState != null) {
+                        state = sPreviousState;
+                    } else {
+                        state = getPersistedSensorState(context);
+                    }
+                    // if the previous state is not available then return without attempting to
+                    // modify the sensor state.
+                    if (state == null) {
+                        return;
+                    }
+                }
+                // The SensorState represents the state of the sensor before sensor privacy was
+                // enabled; if airplane mode was not enabled then the state of airplane mode should
+                // be the same as the state of sensor privacy.
+                if (!state.mAirplaneEnabled) {
+                    setAirplaneMode(context, enabled);
+                }
+                // Similar to airplane mode the state of location should be the opposite of sensor
+                // privacy mode, if it was enabled when sensor privacy was enabled then it should be
+                // disabled. If location is disabled when sensor privacy is enabled then it will be
+                // left disabled when sensor privacy is disabled.
+                if (state.mLocationEnabled) {
+                    setLocationEnabled(context, !enabled);
+                }
+
+                // if sensor privacy is being enabled then persist the current state.
+                if (enabled) {
+                    sPreviousState = state;
+                    persistState(context, sPreviousState);
+                }
+            }
+        }
+
+        public static SensorState getCurrentSensorState(Context context) {
+            LocationManager locationManager = (LocationManager) context.getSystemService(
+                    Context.LOCATION_SERVICE);
+            boolean airplaneEnabled = Settings.Global.getInt(context.getContentResolver(),
+                    Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
+            boolean locationEnabled = locationManager.isLocationEnabled();
+            return new SensorState(airplaneEnabled, locationEnabled);
+        }
+
+        public static void persistState(Context context, SensorState state) {
+            StringBuilder stateValue = new StringBuilder();
+            stateValue.append(state.mAirplaneEnabled
+                    ? Settings.Secure.MAINTAIN_AIRPLANE_MODE_AFTER_SP_DISABLED
+                    : Settings.Secure.DISABLE_AIRPLANE_MODE_AFTER_SP_DISABLED);
+            stateValue.append(",");
+            stateValue.append(
+                    state.mLocationEnabled ? Settings.Secure.REENABLE_LOCATION_AFTER_SP_DISABLED
+                            : Settings.Secure.MAINTAIN_LOCATION_AFTER_SP_DISABLED);
+            Settings.Secure.putString(context.getContentResolver(),
+                    Settings.Secure.SENSOR_PRIVACY_SENSOR_STATE, stateValue.toString());
+        }
+
+        public static SensorState getPersistedSensorState(Context context) {
+            String persistedState = Settings.Secure.getString(context.getContentResolver(),
+                    Settings.Secure.SENSOR_PRIVACY_SENSOR_STATE);
+            if (persistedState == null) {
+                Log.e(TAG, "The persisted sensor state could not be obtained from Settings");
+                return null;
+            }
+            String[] sensorStates = persistedState.split(",");
+            if (sensorStates.length < 2) {
+                Log.e(TAG, "The persisted sensor state does not contain the expected values: "
+                        + persistedState);
+                return null;
+            }
+            boolean airplaneEnabled = sensorStates[0].equals(
+                    Settings.Secure.MAINTAIN_AIRPLANE_MODE_AFTER_SP_DISABLED);
+            boolean locationEnabled = sensorStates[1].equals(
+                    Settings.Secure.REENABLE_LOCATION_AFTER_SP_DISABLED);
+            return new SensorState(airplaneEnabled, locationEnabled);
+        }
+
+        private static void setAirplaneMode(Context context, boolean enable) {
+            ConnectivityManager connectivityManager =
+                    (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+            connectivityManager.setAirplaneMode(enable);
+        }
+
+        private static void setLocationEnabled(Context context, boolean enable) {
+            LocationManager locationManager = (LocationManager) context.getSystemService(
+                    Context.LOCATION_SERVICE);
+            locationManager.setLocationEnabledForUser(enable,
+                    UserHandle.of(ActivityManager.getCurrentUser()));
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 581d435..2a80644 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -57,6 +57,7 @@
 import android.app.admin.SecurityLog;
 import android.app.usage.StorageStatsManager;
 import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -128,6 +129,7 @@
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.os.AppFuseMount;
 import com.android.internal.os.BackgroundThread;
@@ -777,6 +779,18 @@
                 }
             });
         refreshZramSettings();
+
+        // Toggle isolated-enable system property in response to settings
+        mContext.getContentResolver().registerContentObserver(
+            Settings.Global.getUriFor(Settings.Global.ISOLATED_STORAGE_REMOTE),
+            false /*notifyForDescendants*/,
+            new ContentObserver(null /* current thread */) {
+                @Override
+                public void onChange(boolean selfChange) {
+                    refreshIsolatedStorageSettings();
+                }
+            });
+        refreshIsolatedStorageSettings();
     }
 
     /**
@@ -802,6 +816,32 @@
         }
     }
 
+    private void refreshIsolatedStorageSettings() {
+        final int local = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.ISOLATED_STORAGE_LOCAL, 0);
+        final int remote = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.ISOLATED_STORAGE_REMOTE, 0);
+
+        // Walk down precedence chain; we prefer local settings first, then
+        // remote settings, before finally falling back to hard-coded default.
+        final boolean res;
+        if (local == -1) {
+            res = false;
+        } else if (local == 1) {
+            res = true;
+        } else if (remote == -1) {
+            res = false;
+        } else if (remote == 1) {
+            res = true;
+        } else {
+            res = false;
+        }
+
+        Slog.d(TAG, "Isolated storage local flag " + local + " and remote flag "
+                + remote + " resolved to " + res);
+        SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE, Boolean.toString(res));
+    }
+
     /**
      * MediaProvider has a ton of code that makes assumptions about storage
      * paths never changing, so we outright kill them to pick up new state.
@@ -1465,6 +1505,10 @@
     public StorageManagerService(Context context) {
         sSelf = this;
 
+        // Snapshot feature flag used for this boot
+        SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString(
+                SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)));
+
         mContext = context;
         mCallbacks = new Callbacks(FgThread.get().getLooper());
         mLockPatternUtils = new LockPatternUtils(mContext);
@@ -1666,6 +1710,10 @@
                 ServiceManager.getService("package"));
         mIAppOpsService = IAppOpsService.Stub.asInterface(
                 ServiceManager.getService(Context.APP_OPS_SERVICE));
+        try {
+            mIAppOpsService.startWatchingMode(OP_REQUEST_INSTALL_PACKAGES, null, mAppOpsCallback);
+        } catch (RemoteException e) {
+        }
         mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
     }
 
@@ -2208,18 +2256,22 @@
             }
         }
 
-        if ((mask & StorageManager.DEBUG_ISOLATED_STORAGE) != 0) {
-            final boolean enabled = (flags & StorageManager.DEBUG_ISOLATED_STORAGE) != 0;
+        if ((mask & (StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON
+                | StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF)) != 0) {
+            final int value;
+            if ((flags & StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON) != 0) {
+                value = 1;
+            } else if ((flags & StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF) != 0) {
+                value = -1;
+            } else {
+                value = 0;
+            }
 
             final long token = Binder.clearCallingIdentity();
             try {
-                SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE,
-                        Boolean.toString(enabled));
-
-                // Some of the storage related permissions get fiddled with during
-                // package scanning. So, delete the package cache to force PackageManagerService
-                // to do package scanning.
-                FileUtils.deleteContents(Environment.getPackageCacheDirectory());
+                Settings.Global.putInt(mContext.getContentResolver(),
+                        Settings.Global.ISOLATED_STORAGE_LOCAL, value);
+                refreshIsolatedStorageSettings();
 
                 // Perform hard reboot to kick policy into place
                 mContext.getSystemService(PowerManager.class).reboot(null);
@@ -3193,6 +3245,15 @@
         }
     }
 
+    private IAppOpsCallback.Stub mAppOpsCallback = new IAppOpsCallback.Stub() {
+        @Override
+        public void opChanged(int op, int uid, String packageName) throws RemoteException {
+            if (!ENABLE_ISOLATED_STORAGE) return;
+
+            remountUidExternalStorage(uid, getMountMode(uid, packageName));
+        }
+    };
+
     private static final Pattern PATTERN_TRANSLATE = Pattern.compile(
             "(?i)^(/storage/[^/]+/(?:[0-9]+/)?)(.*)");
 
@@ -3758,6 +3819,8 @@
 
             pw.println();
             pw.println("Primary storage UUID: " + mPrimaryStorageUuid);
+
+            pw.println();
             final Pair<String, Long> pair = StorageManager.getPrimaryStoragePathAndSize();
             if (pair == null) {
                 pw.println("Internal storage total size: N/A");
@@ -3770,8 +3833,18 @@
                 pw.print(DataUnit.MEBIBYTES.toBytes(pair.second));
                 pw.println(" MiB)");
             }
+
+            pw.println();
             pw.println("Local unlocked users: " + Arrays.toString(mLocalUnlockedUsers));
             pw.println("System unlocked users: " + Arrays.toString(mSystemUnlockedUsers));
+
+            final ContentResolver cr = mContext.getContentResolver();
+            pw.println();
+            pw.println("Isolated storage, local feature flag: "
+                    + Settings.Global.getInt(cr, Settings.Global.ISOLATED_STORAGE_LOCAL, 0));
+            pw.println("Isolated storage, remote feature flag: "
+                    + Settings.Global.getInt(cr, Settings.Global.ISOLATED_STORAGE_REMOTE, 0));
+            pw.println("Isolated storage, resolved: " + StorageManager.hasIsolatedStorage());
         }
 
         synchronized (mObbMounts) {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index b04ae17..e10e0d6 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -214,6 +214,10 @@
 
     private PreciseCallState mPreciseCallState = new PreciseCallState();
 
+    private int mCallDisconnectCause = DisconnectCause.NOT_VALID;
+
+    private int mCallPreciseDisconnectCause = PreciseDisconnectCause.NOT_VALID;
+
     private boolean mCarrierNetworkChangeState = false;
 
     private PhoneCapability mPhoneCapability = null;
@@ -714,6 +718,14 @@
                             remove(r.binder);
                         }
                     }
+                    if ((events & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) {
+                        try {
+                            r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause,
+                                    mCallPreciseDisconnectCause);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
                     if ((events & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) {
                         try {
                             r.callback.onPreciseDataConnectionStateChanged(
@@ -1301,16 +1313,17 @@
         }
     }
 
-    public void notifyDataConnection(int state, boolean isDataAllowed,
-            String reason, String apn, String apnType, LinkProperties linkProperties,
-            NetworkCapabilities networkCapabilities, int networkType, boolean roaming) {
+    public void notifyDataConnection(int state, boolean isDataAllowed, String apn, String apnType,
+                                     LinkProperties linkProperties,
+                                     NetworkCapabilities networkCapabilities, int networkType,
+                                     boolean roaming) {
         notifyDataConnectionForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, state,
-            isDataAllowed,reason, apn, apnType, linkProperties,
-            networkCapabilities, networkType, roaming);
+                isDataAllowed, apn, apnType, linkProperties,
+                networkCapabilities, networkType, roaming);
     }
 
-    public void notifyDataConnectionForSubscriber(int subId, int state,
-            boolean isDataAllowed, String reason, String apn, String apnType,
+    public void notifyDataConnectionForSubscriber(int subId, int state, boolean isDataAllowed,
+                                                  String apn, String apnType,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
             int networkType, boolean roaming) {
         if (!checkNotifyPermission("notifyDataConnection()" )) {
@@ -1319,7 +1332,6 @@
         if (VDBG) {
             log("notifyDataConnectionForSubscriber: subId=" + subId
                 + " state=" + state + " isDataAllowed=" + isDataAllowed
-                + " reason='" + reason
                 + "' apn='" + apn + "' apnType=" + apnType + " networkType=" + networkType
                 + " mRecords.size()=" + mRecords.size());
         }
@@ -1354,7 +1366,7 @@
                     mDataConnectionNetworkType[phoneId] = networkType;
                 }
                 mPreciseDataConnectionState = new PreciseDataConnectionState(state, networkType,
-                        apnType, apn, reason, linkProperties, "");
+                        apnType, apn, linkProperties, "");
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
                             PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)) {
@@ -1369,30 +1381,29 @@
             }
             handleRemoveListLocked();
         }
-        broadcastDataConnectionStateChanged(state, isDataAllowed, reason, apn,
-                apnType, linkProperties, networkCapabilities, roaming, subId);
-        broadcastPreciseDataConnectionStateChanged(state, networkType, apnType, apn, reason,
+        broadcastDataConnectionStateChanged(state, isDataAllowed, apn, apnType, linkProperties,
+                networkCapabilities, roaming, subId);
+        broadcastPreciseDataConnectionStateChanged(state, networkType, apnType, apn,
                 linkProperties, "");
     }
 
-    public void notifyDataConnectionFailed(String reason, String apnType) {
+    public void notifyDataConnectionFailed(String apnType) {
          notifyDataConnectionFailedForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
-                 reason, apnType);
+                 apnType);
     }
 
-    public void notifyDataConnectionFailedForSubscriber(int subId,
-            String reason, String apnType) {
+    public void notifyDataConnectionFailedForSubscriber(int subId, String apnType) {
         if (!checkNotifyPermission("notifyDataConnectionFailed()")) {
             return;
         }
         if (VDBG) {
             log("notifyDataConnectionFailedForSubscriber: subId=" + subId
-                + " reason=" + reason + " apnType=" + apnType);
+                    + " apnType=" + apnType);
         }
         synchronized (mRecords) {
             mPreciseDataConnectionState = new PreciseDataConnectionState(
                     TelephonyManager.DATA_UNKNOWN,TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                    apnType, "", reason, null, "");
+                    apnType, "", null, "");
             for (Record r : mRecords) {
                 if (r.matchPhoneStateListenerEvent(
                         PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)) {
@@ -1405,9 +1416,9 @@
             }
             handleRemoveListLocked();
         }
-        broadcastDataConnectionFailed(reason, apnType, subId);
+        broadcastDataConnectionFailed(apnType, subId);
         broadcastPreciseDataConnectionStateChanged(TelephonyManager.DATA_UNKNOWN,
-                TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, "", reason, null, "");
+                TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, "", null, "");
     }
 
     public void notifyCellLocation(Bundle cellLocation) {
@@ -1491,9 +1502,8 @@
             }
             handleRemoveListLocked();
         }
-        broadcastPreciseCallStateChanged(ringingCallState, foregroundCallState, backgroundCallState,
-                DisconnectCause.NOT_VALID,
-                PreciseDisconnectCause.NOT_VALID);
+        broadcastPreciseCallStateChanged(ringingCallState, foregroundCallState,
+                backgroundCallState);
     }
 
     public void notifyDisconnectCause(int disconnectCause, int preciseDisconnectCause) {
@@ -1501,12 +1511,14 @@
             return;
         }
         synchronized (mRecords) {
-            mPreciseCallState = new PreciseCallState(mRingingCallState, mForegroundCallState,
-                    mBackgroundCallState, disconnectCause, preciseDisconnectCause);
+            mCallDisconnectCause = disconnectCause;
+            mCallPreciseDisconnectCause = preciseDisconnectCause;
             for (Record r : mRecords) {
-                if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_PRECISE_CALL_STATE)) {
+                if (r.matchPhoneStateListenerEvent(PhoneStateListener
+                        .LISTEN_CALL_DISCONNECT_CAUSES)) {
                     try {
-                        r.callback.onPreciseCallStateChanged(mPreciseCallState);
+                        r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause,
+                                mCallPreciseDisconnectCause);
                     } catch (RemoteException ex) {
                         mRemoveList.add(r.binder);
                     }
@@ -1514,19 +1526,16 @@
             }
             handleRemoveListLocked();
         }
-        broadcastPreciseCallStateChanged(mRingingCallState, mForegroundCallState,
-                mBackgroundCallState, disconnectCause, preciseDisconnectCause);
     }
 
-    public void notifyPreciseDataConnectionFailed(String reason, String apnType,
-            String apn, String failCause) {
+    public void notifyPreciseDataConnectionFailed(String apnType, String apn, String failCause) {
         if (!checkNotifyPermission("notifyPreciseDataConnectionFailed()")) {
             return;
         }
         synchronized (mRecords) {
             mPreciseDataConnectionState = new PreciseDataConnectionState(
                     TelephonyManager.DATA_UNKNOWN, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                    apnType, apn, reason, null, failCause);
+                    apnType, apn, null, failCause);
             for (Record r : mRecords) {
                 if (r.matchPhoneStateListenerEvent(
                         PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)) {
@@ -1540,7 +1549,7 @@
             handleRemoveListLocked();
         }
         broadcastPreciseDataConnectionStateChanged(TelephonyManager.DATA_UNKNOWN,
-                TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, apn, reason, null, failCause);
+                TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, apn, null, failCause);
     }
 
     @Override
@@ -1684,7 +1693,9 @@
         }
 
         synchronized (mRecords) {
-            mEmergencyNumberList = TelephonyManager.getDefault().getCurrentEmergencyNumberList();
+            TelephonyManager tm = (TelephonyManager) mContext.getSystemService(
+                    Context.TELEPHONY_SERVICE);
+            mEmergencyNumberList = tm.getCurrentEmergencyNumberList();
 
             for (Record r : mRecords) {
                 if (r.matchPhoneStateListenerEvent(
@@ -1735,6 +1746,8 @@
             }
             pw.println("mPreciseDataConnectionState=" + mPreciseDataConnectionState);
             pw.println("mPreciseCallState=" + mPreciseCallState);
+            pw.println("mCallDisconnectCause=" + mCallDisconnectCause);
+            pw.println("mCallPreciseDisconnectCause=" + mCallPreciseDisconnectCause);
             pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState);
             pw.println("mRingingCallState=" + mRingingCallState);
             pw.println("mForegroundCallState=" + mForegroundCallState);
@@ -1866,10 +1879,10 @@
                         android.Manifest.permission.READ_CALL_LOG});
     }
 
-    private void broadcastDataConnectionStateChanged(int state,
-            boolean isDataAllowed,
-            String reason, String apn, String apnType, LinkProperties linkProperties,
-            NetworkCapabilities networkCapabilities, boolean roaming, int subId) {
+    private void broadcastDataConnectionStateChanged(int state, boolean isDataAllowed, String apn,
+                                                     String apnType, LinkProperties linkProperties,
+                                                     NetworkCapabilities networkCapabilities,
+                                                     boolean roaming, int subId) {
         // Note: not reporting to the battery stats service here, because the
         // status bar takes care of that after taking into account all of the
         // required info.
@@ -1879,9 +1892,6 @@
         if (!isDataAllowed) {
             intent.putExtra(PhoneConstants.NETWORK_UNAVAILABLE_KEY, true);
         }
-        if (reason != null) {
-            intent.putExtra(PhoneConstants.STATE_CHANGE_REASON_KEY, reason);
-        }
         if (linkProperties != null) {
             intent.putExtra(PhoneConstants.DATA_LINK_PROPERTIES_KEY, linkProperties);
             String iface = linkProperties.getInterfaceName();
@@ -1900,38 +1910,34 @@
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
-    private void broadcastDataConnectionFailed(String reason, String apnType,
-            int subId) {
+    private void broadcastDataConnectionFailed(String apnType, int subId) {
         Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
-        intent.putExtra(PhoneConstants.FAILURE_REASON_KEY, reason);
         intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
     private void broadcastPreciseCallStateChanged(int ringingCallState, int foregroundCallState,
-            int backgroundCallState, int disconnectCause, int preciseDisconnectCause) {
+                                                  int backgroundCallState) {
         Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_CALL_STATE_CHANGED);
         intent.putExtra(TelephonyManager.EXTRA_RINGING_CALL_STATE, ringingCallState);
         intent.putExtra(TelephonyManager.EXTRA_FOREGROUND_CALL_STATE, foregroundCallState);
         intent.putExtra(TelephonyManager.EXTRA_BACKGROUND_CALL_STATE, backgroundCallState);
-        intent.putExtra(TelephonyManager.EXTRA_DISCONNECT_CAUSE, disconnectCause);
-        intent.putExtra(TelephonyManager.EXTRA_PRECISE_DISCONNECT_CAUSE, preciseDisconnectCause);
         mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
                 android.Manifest.permission.READ_PRECISE_PHONE_STATE);
     }
 
     private void broadcastPreciseDataConnectionStateChanged(int state, int networkType,
-            String apnType, String apn, String reason, LinkProperties linkProperties,
-            String failCause) {
+                                                            String apnType, String apn,
+                                                            LinkProperties linkProperties,
+                                                            String failCause) {
         Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED);
         intent.putExtra(PhoneConstants.STATE_KEY, state);
         intent.putExtra(PhoneConstants.DATA_NETWORK_TYPE_KEY, networkType);
-        if (reason != null) intent.putExtra(PhoneConstants.STATE_CHANGE_REASON_KEY, reason);
         if (apnType != null) intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
         if (apn != null) intent.putExtra(PhoneConstants.DATA_APN_KEY, apn);
         if (linkProperties != null) {
-            intent.putExtra(PhoneConstants.DATA_LINK_PROPERTIES_KEY,linkProperties);
+            intent.putExtra(PhoneConstants.DATA_LINK_PROPERTIES_KEY, linkProperties);
         }
         if (failCause != null) intent.putExtra(PhoneConstants.DATA_FAILURE_CAUSE_KEY, failCause);
 
@@ -1996,6 +2002,18 @@
                     android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
         }
 
+        if ((events & PhoneStateListener.LISTEN_PREFERRED_DATA_SUBID_CHANGE) != 0) {
+            // It can have either READ_PHONE_STATE or READ_PRIVILEGED_PHONE_STATE.
+            TelephonyPermissions.checkReadPhoneState(mContext,
+                    SubscriptionManager.INVALID_SUBSCRIPTION_ID, Binder.getCallingPid(),
+                    Binder.getCallingUid(), callingPackage, "listen to "
+                            + "LISTEN_PREFERRED_DATA_SUBID_CHANGE");
+        }
+
+        if ((events & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
+        }
 
         return true;
     }
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index e80e9e1..1aeb689 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -91,6 +91,7 @@
         "/system/bin/mediaserver",
         "/system/bin/sdcard",
         "/system/bin/surfaceflinger",
+        "/system/bin/vold",
         "media.extractor", // system/bin/mediaextractor
         "media.metrics", // system/bin/mediametrics
         "media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 8751d24..ed39d83 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1665,7 +1665,7 @@
             AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
             ConnectionRecord c = new ConnectionRecord(b, activity,
                     connection, flags, clientLabel, clientIntent,
-                    callerApp.uid, callerApp.processName);
+                    callerApp.uid, callerApp.processName, callingPackage);
 
             IBinder binder = connection.asBinder();
             ArrayList<ConnectionRecord> clist = s.connections.get(binder);
@@ -1964,7 +1964,8 @@
                 + " type=" + resolvedType + " callingUid=" + callingUid);
 
         userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
-                ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE, "service", null);
+                ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE, "service",
+                callingPackage);
 
         ServiceMap smap = getServiceMapLocked(userId);
         final ComponentName comp;
@@ -2022,7 +2023,7 @@
                 if (!mAm.validateAssociationAllowedLocked(callingPackage, callingUid,
                         name.getPackageName(), sInfo.applicationInfo.uid)) {
                     String msg = "association not allowed between packages "
-                            + callingPackage + " and " + r.packageName;
+                            + callingPackage + " and " + name.getPackageName();
                     Slog.w(TAG, "Service lookup failed: " + msg);
                     return new ServiceLookupResult(null, msg);
                 }
@@ -2502,6 +2503,9 @@
                     && r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) {
                 hostingType = "webview_service";
             }
+            if ((r.serviceInfo.flags & ServiceInfo.FLAG_USE_APP_ZYGOTE) != 0) {
+                hostingType = "app_zygote";
+            }
         }
 
         // Not running -- get it started, and enqueue this service record
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 8571ae6..1c04a94 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -69,6 +69,7 @@
     static final String KEY_PROCESS_START_ASYNC = "process_start_async";
     static final String KEY_MEMORY_INFO_THROTTLE_TIME = "memory_info_throttle_time";
     static final String KEY_TOP_TO_FGS_GRACE_DURATION = "top_to_fgs_grace_duration";
+    static final String KEY_USE_COMPACTION = "use_compaction";
 
     private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
     private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000;
@@ -99,6 +100,7 @@
     private static final boolean DEFAULT_PROCESS_START_ASYNC = true;
     private static final long DEFAULT_MEMORY_INFO_THROTTLE_TIME = 5*60*1000;
     private static final long DEFAULT_TOP_TO_FGS_GRACE_DURATION = 15 * 1000;
+    private static final boolean DEFAULT_USE_COMPACTION = false;
 
     // Maximum number of cached processes we will allow.
     public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
@@ -218,6 +220,9 @@
     // this long.
     public long TOP_TO_FGS_GRACE_DURATION = DEFAULT_TOP_TO_FGS_GRACE_DURATION;
 
+    // Use compaction for background apps.
+    public boolean USE_COMPACTION = DEFAULT_USE_COMPACTION;
+
     // Indicates whether the activity starts logging is enabled.
     // Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED
     volatile boolean mFlagActivityStartsLoggingEnabled;
@@ -375,6 +380,7 @@
                     DEFAULT_MEMORY_INFO_THROTTLE_TIME);
             TOP_TO_FGS_GRACE_DURATION = mParser.getDurationMillis(KEY_TOP_TO_FGS_GRACE_DURATION,
                     DEFAULT_TOP_TO_FGS_GRACE_DURATION);
+            USE_COMPACTION = mParser.getBoolean(KEY_USE_COMPACTION, DEFAULT_USE_COMPACTION);
 
             updateMaxCachedProcesses();
         }
@@ -465,6 +471,8 @@
         pw.println(MEMORY_INFO_THROTTLE_TIME);
         pw.print("  "); pw.print(KEY_TOP_TO_FGS_GRACE_DURATION); pw.print("=");
         pw.println(TOP_TO_FGS_GRACE_DURATION);
+        pw.print("  "); pw.print(KEY_USE_COMPACTION); pw.print("=");
+        pw.println(USE_COMPACTION);
 
         pw.println();
         if (mOverrideMaxCachedProcesses >= 0) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6700a53..bc21610 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -25,6 +25,7 @@
 import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
 import static android.app.ActivityManager.INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL;
 import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY;
 import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
@@ -234,6 +235,7 @@
 import android.net.Proxy;
 import android.net.ProxyInfo;
 import android.net.Uri;
+import android.os.AppZygote;
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.BinderProxy;
@@ -335,6 +337,7 @@
 import com.android.server.LocalServices;
 import com.android.server.LockGuard;
 import com.android.server.NetworkManagementInternal;
+import com.android.server.PackageWatchdog;
 import com.android.server.RescueParty;
 import com.android.server.ServiceThread;
 import com.android.server.SystemConfig;
@@ -450,6 +453,9 @@
     // before we decide it must be hung.
     static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;
 
+    // How long we wait to kill an application zygote, after the last process using
+    // it has gone away.
+    static final int KILL_APP_ZYGOTE_DELAY_MS = 5 * 1000;
     /**
      * How long we wait for an provider to be published. Should be longer than
      * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT}.
@@ -586,6 +592,7 @@
     public final PendingIntentController mPendingIntentController;
 
     final AppErrors mAppErrors;
+    final PackageWatchdog mPackageWatchdog;
 
     /**
      * Indicates the maximum time spent waiting for the network rules to get updated.
@@ -646,6 +653,11 @@
     final ProcessStatsService mProcessStats;
 
     /**
+     * Service for compacting background apps.
+     */
+    final AppCompactor mAppCompact;
+
+    /**
      * Non-persistent appId whitelist for background restrictions
      */
     int[] mBackgroundAppIdWhitelist = new int[] {
@@ -1433,6 +1445,7 @@
     static final int PUSH_TEMP_WHITELIST_UI_MSG = 68;
     static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
     static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
+    static final int KILL_APP_ZYGOTE_MSG = 71;
 
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
 
@@ -1636,6 +1649,12 @@
                             false, userId, reason);
                 }
             } break;
+                case KILL_APP_ZYGOTE_MSG: {
+                    synchronized (ActivityManagerService.this) {
+                        final AppZygote appZygote = (AppZygote) msg.obj;
+                        mProcessList.killAppZygoteIfNeededLocked(appZygote);
+                    }
+                } break;
             case CHECK_EXCESSIVE_POWER_USE_MSG: {
                 synchronized (ActivityManagerService.this) {
                     checkExcessivePowerUsageLocked();
@@ -1924,7 +1943,8 @@
             synchronized (this) {
                 ProcessRecord app = mProcessList.newProcessRecordLocked(info, info.processName,
                         false,
-                        0);
+                        0,
+                        false);
                 app.setPersistent(true);
                 app.pid = MY_PID;
                 app.getWindowProcessController().setPid(MY_PID);
@@ -2203,6 +2223,7 @@
         mContext = mInjector.getContext();
         mUiContext = null;
         mAppErrors = null;
+        mPackageWatchdog = null;
         mActiveUids = new ActiveUids(this, false /* postChangesToAtm */);
         mAppOpsService = mInjector.getAppOpsService(null /* file */, null /* handler */);
         mBatteryStatsService = null;
@@ -2224,6 +2245,7 @@
                 ? new PendingIntentController(handlerThread.getLooper(), mUserController) : null;
         mProcStartHandlerThread = null;
         mProcStartHandler = null;
+        mAppCompact = null;
         mHiddenApiBlacklist = null;
         mFactoryTest = FACTORY_TEST_OFF;
     }
@@ -2268,7 +2290,8 @@
 
         mServices = new ActiveServices(this);
         mProviderMap = new ProviderMap(this);
-        mAppErrors = new AppErrors(mUiContext, this);
+        mPackageWatchdog = PackageWatchdog.getInstance(mUiContext);
+        mAppErrors = new AppErrors(mUiContext, this, mPackageWatchdog);
         mActiveUids = new ActiveUids(this, true /* postChangesToAtm */);
 
         final File systemDir = SystemServiceManager.ensureSystemDir();
@@ -2306,6 +2329,8 @@
                 DisplayThread.get().getLooper());
         mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
 
+        mAppCompact = new AppCompactor(this);
+
         mProcessCpuThread = new Thread("CpuTracker") {
             @Override
             public void run() {
@@ -2345,13 +2370,15 @@
         Watchdog.getInstance().addMonitor(this);
         Watchdog.getInstance().addThread(mHandler);
 
-        // bind background thread to little cores
+        // bind background threads to little cores
         // this is expected to fail inside of framework tests because apps can't touch cpusets directly
         // make sure we've already adjusted system_server's internal view of itself first
         updateOomAdjLocked();
         try {
             Process.setThreadGroupAndCpuset(BackgroundThread.get().getThreadId(),
-                    Process.THREAD_GROUP_BG_NONINTERACTIVE);
+                    Process.THREAD_GROUP_SYSTEM);
+            Process.setThreadGroupAndCpuset(mAppCompact.mCompactionThread.getThreadId(),
+                    Process.THREAD_GROUP_SYSTEM);
         } catch (Exception e) {
             Slog.w(TAG, "Setting background thread cpuset failed");
         }
@@ -3729,10 +3756,10 @@
                         intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
                         broadcastIntentInPackage("android", SYSTEM_UID, intent, null, null, 0,
                                 null, null, permission.ACCESS_INSTANT_APPS, null, false, false,
-                                resolvedUserId);
+                                resolvedUserId, false);
                     } else {
                         broadcastIntentInPackage("android", SYSTEM_UID, intent, null, null, 0,
-                                null, null, null, null, false, false, resolvedUserId);
+                                null, null, null, null, false, false, resolvedUserId, false);
                     }
 
                     if (observer != null) {
@@ -6187,7 +6214,7 @@
 
     ContentProviderConnection incProviderCountLocked(ProcessRecord r,
             final ContentProviderRecord cpr, IBinder externalProcessToken, int callingUid,
-            String callingTag, boolean stable) {
+            String callingPackage, String callingTag, boolean stable) {
         if (r != null) {
             for (int i=0; i<r.conProviders.size(); i++) {
                 ContentProviderConnection conn = r.conProviders.get(i);
@@ -6207,7 +6234,7 @@
                     return conn;
                 }
             }
-            ContentProviderConnection conn = new ContentProviderConnection(cpr, r);
+            ContentProviderConnection conn = new ContentProviderConnection(cpr, r, callingPackage);
             conn.startAssociationIfNeeded();
             if (stable) {
                 conn.stableCount = 1;
@@ -6314,8 +6341,8 @@
     }
 
     private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
-            String name, IBinder token, int callingUid, String callingTag, boolean stable,
-            int userId) {
+            String name, IBinder token, int callingUid, String callingPackage, String callingTag,
+            boolean stable, int userId) {
         ContentProviderRecord cpr;
         ContentProviderConnection conn = null;
         ProviderInfo cpi = null;
@@ -6438,7 +6465,8 @@
 
                 // In this case the provider instance already exists, so we can
                 // return it right away.
-                conn = incProviderCountLocked(r, cpr, token, callingUid, callingTag, stable);
+                conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
+                        stable);
                 if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
                     if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
                         // If this is a perceptible app accessing the provider,
@@ -6685,7 +6713,8 @@
                 }
 
                 mProviderMap.putProviderByName(name, cpr);
-                conn = incProviderCountLocked(r, cpr, token, callingUid, callingTag, stable);
+                conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
+                        stable);
                 if (conn != null) {
                     conn.waiting = true;
                 }
@@ -6827,7 +6856,8 @@
 
     @Override
     public final ContentProviderHolder getContentProvider(
-            IApplicationThread caller, String name, int userId, boolean stable) {
+            IApplicationThread caller, String callingPackage, String name, int userId,
+            boolean stable) {
         enforceNotIsolatedCaller("getContentProvider");
         if (caller == null) {
             String msg = "null IApplicationThread when getting content provider "
@@ -6837,8 +6867,14 @@
         }
         // The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
         // with cross-user grant.
-        return getContentProviderImpl(caller, name, null, Binder.getCallingUid(), null, stable,
-                userId);
+        final int callingUid = Binder.getCallingUid();
+        if (callingPackage != null && mAppOpsService.checkPackage(callingUid, callingPackage)
+                != AppOpsManager.MODE_ALLOWED) {
+            throw new SecurityException("Given calling package " + callingPackage
+                    + " does not match caller's uid " + callingUid);
+        }
+        return getContentProviderImpl(caller, name, null, callingUid, callingPackage,
+                null, stable, userId);
     }
 
     public ContentProviderHolder getContentProviderExternal(
@@ -6853,7 +6889,8 @@
 
     private ContentProviderHolder getContentProviderExternalUnchecked(String name,
             IBinder token, int callingUid, String callingTag, int userId) {
-        return getContentProviderImpl(null, name, token, callingUid, callingTag, true, userId);
+        return getContentProviderImpl(null, name, token, callingUid, null, callingTag,
+                true, userId);
     }
 
     /**
@@ -7371,7 +7408,7 @@
         }
 
         if (app == null) {
-            app = mProcessList.newProcessRecordLocked(info, customProcess, isolated, 0);
+            app = mProcessList.newProcessRecordLocked(info, customProcess, isolated, 0, false);
             mProcessList.updateLruProcessLocked(app, false, null);
             updateOomAdjLocked();
         }
@@ -13971,7 +14008,7 @@
                     BroadcastQueue queue = broadcastQueueForIntent(intent);
                     BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                             null, -1, -1, false, null, null, OP_NONE, null, receivers,
-                            null, 0, null, null, false, true, true, -1);
+                            null, 0, null, null, false, true, true, -1, false);
                     queue.enqueueParallelBroadcastLocked(r);
                     queue.scheduleBroadcastsLocked();
                 }
@@ -14210,6 +14247,18 @@
             IIntentReceiver resultTo, int resultCode, String resultData,
             Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
             boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
+        return broadcastIntentLocked(callerApp, callerPackage, intent, resolvedType, resultTo,
+            resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions, ordered,
+            sticky, callingPid, callingUid, userId, false /* allowBackgroundActivityStarts */);
+    }
+
+    @GuardedBy("this")
+    final int broadcastIntentLocked(ProcessRecord callerApp,
+            String callerPackage, Intent intent, String resolvedType,
+            IIntentReceiver resultTo, int resultCode, String resultData,
+            Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
+            boolean ordered, boolean sticky, int callingPid, int callingUid, int userId,
+            boolean allowBackgroundActivityStarts) {
         intent = new Intent(intent);
 
         final boolean callerInstantApp = isInstantApp(callerApp, callerPackage, callingUid);
@@ -14710,7 +14759,8 @@
             BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                     callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
                     requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
-                    resultCode, resultData, resultExtras, ordered, sticky, false, userId);
+                    resultCode, resultData, resultExtras, ordered, sticky, false, userId,
+                    allowBackgroundActivityStarts);
             if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
             final boolean replaced = replacePending
                     && (queue.replaceParallelBroadcastLocked(r) != null);
@@ -14806,7 +14856,8 @@
             BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                     callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
                     requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
-                    resultData, resultExtras, ordered, sticky, false, userId);
+                    resultData, resultExtras, ordered, sticky, false, userId,
+                    allowBackgroundActivityStarts);
 
             if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r
                     + ": prev had " + queue.mOrderedBroadcasts.size());
@@ -14953,7 +15004,7 @@
             Intent intent, String resolvedType, IIntentReceiver resultTo,
             int resultCode, String resultData, Bundle resultExtras,
             String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
-            int userId) {
+            int userId, boolean allowBackgroundActivityStarts) {
         synchronized(this) {
             intent = verifyBroadcastLocked(intent);
 
@@ -14963,7 +15014,7 @@
             int res = broadcastIntentLocked(null, packageName, intent, resolvedType,
                     resultTo, resultCode, resultData, resultExtras,
                     requiredPermissions, OP_NONE, bOptions, serialized,
-                    sticky, -1, uid, userId);
+                    sticky, -1, uid, userId, allowBackgroundActivityStarts);
             Binder.restoreCallingIdentity(origId);
             return res;
         }
@@ -15597,7 +15648,7 @@
     }
 
     private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,
-            boolean doingAll, long now) {
+            boolean doingAll, long now, boolean cycleReEval) {
         if (mAdjSeq == app.adjSeq) {
             if (app.adjSeq == app.completedAdjSeq) {
                 // This adjustment has already been computed successfully.
@@ -15663,20 +15714,21 @@
                 app.systemNoUi = false;
             }
             if (!app.systemNoUi) {
-              if (mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
-                  // screen on, promote UI
-                  app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
-                  app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
-              } else {
-                  // screen off, restrict UI scheduling
-                  app.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
-                  app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
-              }
+                if (mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
+                    // screen on, promote UI
+                    app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
+                    app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
+                } else {
+                    // screen off, restrict UI scheduling
+                    app.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+                    app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
+                }
             }
+            app.setCurRawProcState(app.getCurProcState());
             app.curAdj = app.maxAdj;
             app.completedAdjSeq = app.adjSeq;
             // if curAdj is less than prevAppAdj, then this process was promoted
-            return app.curAdj < prevAppAdj;
+            return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState;
         }
 
         app.systemNoUi = false;
@@ -15918,8 +15970,13 @@
         // By default, we use the computed adjustment.  It may be changed if
         // there are applications dependent on our services or providers, but
         // this gives us a baseline and makes sure we don't get into an
-        // infinite recursion.
-        app.setCurRawAdj(adj);
+        // infinite recursion. If we're re-evaluating due to cycles, use the previously computed
+        // values.
+        app.setCurRawAdj(!cycleReEval ? adj : Math.min(adj, app.getCurRawAdj()));
+        app.setCurRawProcState(!cycleReEval
+                ? procState
+                : Math.min(procState, app.getCurRawProcState()));
+
         app.hasStartedServices = false;
         app.adjSeq = mAdjSeq;
 
@@ -16021,21 +16078,15 @@
                     boolean trackedProcState = false;
                     if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
                         ProcessRecord client = cr.binding.client;
-                        computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
-                        if (client.containsCycle) {
-                            // We've detected a cycle. We should retry computeOomAdjLocked later in
-                            // case a later-checked connection from a client  would raise its
-                            // priority legitimately.
-                            app.containsCycle = true;
-                            // If the client has not been completely evaluated, skip using its
-                            // priority. Else use the conservative value for now and look for a
-                            // better state in the next iteration.
-                            if (client.completedAdjSeq < mAdjSeq) {
-                                continue;
-                            }
+                        computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now, cycleReEval);
+
+                        if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
+                            continue;
                         }
+
                         int clientAdj = client.getCurRawAdj();
-                        int clientProcState = client.getCurProcState();
+                        int clientProcState = client.getCurRawProcState();
+
                         if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
                             // If the other app is cached for any reason, for purposes here
                             // we are going to consider it empty.  The specific cached state
@@ -16120,6 +16171,7 @@
                                 }
                                 if (adj >  newAdj) {
                                     adj = newAdj;
+                                    app.setCurRawAdj(adj);
                                     adjType = "service";
                                 }
                             }
@@ -16191,6 +16243,7 @@
                         }
                         if (procState > clientProcState) {
                             procState = clientProcState;
+                            app.setCurRawProcState(procState);
                             if (adjType == null) {
                                 adjType = "service";
                             }
@@ -16222,6 +16275,7 @@
                         if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ
                                 && a.isActivityVisible()) {
                             adj = ProcessList.FOREGROUND_APP_ADJ;
+                            app.setCurRawAdj(adj);
                             if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
                                 if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
                                     schedGroup = ProcessList.SCHED_GROUP_TOP_APP_BOUND;
@@ -16263,21 +16317,15 @@
                     // Being our own client is not interesting.
                     continue;
                 }
-                computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
-                if (client.containsCycle) {
-                    // We've detected a cycle. We should retry computeOomAdjLocked later in
-                    // case a later-checked connection from a client  would raise its
-                    // priority legitimately.
-                    app.containsCycle = true;
-                    // If the client has not been completely evaluated, skip using its
-                    // priority. Else use the conservative value for now and look for a
-                    // better state in the next iteration.
-                    if (client.completedAdjSeq < mAdjSeq) {
-                        continue;
-                    }
+                computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now, cycleReEval);
+
+                if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
+                    continue;
                 }
+
                 int clientAdj = client.getCurRawAdj();
-                int clientProcState = client.getCurProcState();
+                int clientProcState = client.getCurRawProcState();
+
                 if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
                     // If the other app is cached for any reason, for purposes here
                     // we are going to consider it empty.
@@ -16291,6 +16339,7 @@
                     } else {
                         adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
                                 ? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
+                        app.setCurRawAdj(adj);
                         adjType = "provider";
                     }
                     app.cached &= client.cached;
@@ -16326,6 +16375,7 @@
                 conn.trackProcState(clientProcState, mAdjSeq, now);
                 if (procState > clientProcState) {
                     procState = clientProcState;
+                    app.setCurRawProcState(procState);
                 }
                 if (client.getCurrentSchedulingGroup() > schedGroup) {
                     schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
@@ -16351,6 +16401,7 @@
             if (cpr.hasExternalProcessHandles()) {
                 if (adj > ProcessList.FOREGROUND_APP_ADJ) {
                     adj = ProcessList.FOREGROUND_APP_ADJ;
+                    app.setCurRawAdj(adj);
                     schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                     app.cached = false;
                     app.adjType = "ext-provider";
@@ -16362,6 +16413,7 @@
                 }
                 if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
                     procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+                    app.setCurRawProcState(procState);
                     if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                         reportOomAdjMessageLocked(TAG_OOM_ADJ,
                                 "Raise procstate to external provider: " + app);
@@ -16506,6 +16558,7 @@
         app.curAdj = app.modifyRawOomAdj(adj);
         app.setCurrentSchedulingGroup(schedGroup);
         app.setCurProcState(procState);
+        app.setCurRawProcState(procState);
         app.setHasForegroundActivities(foregroundActivities);
         app.completedAdjSeq = mAdjSeq;
 
@@ -16513,6 +16566,44 @@
         return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState;
     }
 
+    /**
+     * Checks if for the given app and client, there's a cycle that should skip over the client
+     * for now or use partial values to evaluate the effect of the client binding.
+     * @param app
+     * @param client
+     * @param procState procstate evaluated so far for this app
+     * @param adj oom_adj evaluated so far for this app
+     * @param cycleReEval whether we're currently re-evaluating due to a cycle, and not the first
+     *                    evaluation.
+     * @return whether to skip using the client connection at this time
+     */
+    private boolean shouldSkipDueToCycle(ProcessRecord app, ProcessRecord client,
+            int procState, int adj, boolean cycleReEval) {
+        if (client.containsCycle) {
+            // We've detected a cycle. We should retry computeOomAdjLocked later in
+            // case a later-checked connection from a client  would raise its
+            // priority legitimately.
+            app.containsCycle = true;
+            // If the client has not been completely evaluated, check if it's worth
+            // using the partial values.
+            if (client.completedAdjSeq < mAdjSeq) {
+                if (cycleReEval) {
+                    // If the partial values are no better, skip until the next
+                    // attempt
+                    if (client.getCurRawProcState() >= procState
+                            && client.getCurRawAdj() >= adj) {
+                        return true;
+                    }
+                    // Else use the client's partial procstate and adj to adjust the
+                    // effect of the binding
+                } else {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     private static final class RecordPssRunnable implements Runnable {
         private final ActivityManagerService mService;
         private final ProcessRecord mProc;
@@ -16921,6 +17012,20 @@
         int changes = 0;
 
         if (app.curAdj != app.setAdj) {
+            // don't compact during bootup
+            if (mConstants.USE_COMPACTION && mBooted) {
+                // Perform a minor compaction when a perceptible app becomes the prev/home app
+                // Perform a major compaction when any app enters cached
+                // reminder: here, setAdj is previous state, curAdj is upcoming state
+                if (app.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ &&
+                    (app.curAdj == ProcessList.PREVIOUS_APP_ADJ ||
+                     app.curAdj == ProcessList.HOME_APP_ADJ)) {
+                    mAppCompact.compactAppSome(app);
+                } else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ &&
+                           app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
+                    mAppCompact.compactAppFull(app);
+                }
+            }
             ProcessList.setOomAdj(app.pid, app.uid, app.curAdj);
             if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mCurOomAdjUid == app.info.uid) {
                 String msg = "Set " + app.pid + " " + app.processName + " adj "
@@ -17357,7 +17462,7 @@
             return false;
         }
 
-        computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now);
+        computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now, false);
 
         return applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime());
     }
@@ -17732,12 +17837,14 @@
         for (int i=N-1; i>=0; i--) {
             ProcessRecord app = mProcessList.mLruProcesses.get(i);
             app.containsCycle = false;
+            app.setCurRawProcState(PROCESS_STATE_CACHED_EMPTY);
+            app.setCurRawAdj(ProcessList.UNKNOWN_ADJ);
         }
         for (int i=N-1; i>=0; i--) {
             ProcessRecord app = mProcessList.mLruProcesses.get(i);
             if (!app.killedByAm && app.thread != null) {
                 app.procStateChanged = false;
-                computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);
+                computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now, false);
 
                 // if any app encountered a cycle, we need to perform an additional loop later
                 retryCycles |= app.containsCycle;
@@ -17840,8 +17947,8 @@
             for (int i=0; i<N; i++) {
                 ProcessRecord app = mProcessList.mLruProcesses.get(i);
                 if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
-
-                    if (computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now)) {
+                    if (computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now,
+                            true)) {
                         retryCycles = true;
                     }
                 }
@@ -18963,6 +19070,19 @@
         }
 
         @Override
+        public void setPendingIntentAllowBgActivityStarts(IIntentSender target,
+                IBinder whitelistToken, int flags) {
+            if (!(target instanceof PendingIntentRecord)) {
+                Slog.w(TAG, "setPendingIntentAllowBgActivityStarts():"
+                        + " not a PendingIntentRecord: " + target);
+                return;
+            }
+            synchronized (ActivityManagerService.this) {
+                ((PendingIntentRecord) target).setAllowBgActivityStarts(whitelistToken, flags);
+            }
+        }
+
+        @Override
         public void setDeviceIdleWhitelist(int[] allAppids, int[] exceptIdleAppids) {
             synchronized (ActivityManagerService.this) {
                 mDeviceIdleWhitelist = allAppids;
@@ -19349,11 +19469,12 @@
         public int broadcastIntentInPackage(String packageName, int uid, Intent intent,
                 String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData,
                 Bundle resultExtras, String requiredPermission, Bundle bOptions, boolean serialized,
-                boolean sticky, int userId) {
+                boolean sticky, int userId, boolean allowBackgroundActivityStarts) {
             synchronized (ActivityManagerService.this) {
                 return ActivityManagerService.this.broadcastIntentInPackage(packageName, uid,
                         intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
-                        requiredPermission, bOptions, serialized, sticky, userId);
+                        requiredPermission, bOptions, serialized, sticky, userId,
+                        allowBackgroundActivityStarts);
             }
         }
 
@@ -19938,18 +20059,18 @@
         }
 
         @Override
-        public int checkOperation(int code, int uid, String packageName,
-                TriFunction<Integer, Integer, String, Integer> superImpl) {
+        public int checkOperation(int code, int uid, String packageName, boolean raw,
+                QuadFunction<Integer, Integer, String, Boolean, Integer> superImpl) {
             if (uid == mTargetUid && isTargetOp(code)) {
                 final long identity = Binder.clearCallingIdentity();
                 try {
                     return superImpl.apply(code, Process.SHELL_UID,
-                            "com.android.shell");
+                            "com.android.shell", raw);
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
             }
-            return superImpl.apply(code, uid, packageName);
+            return superImpl.apply(code, uid, packageName, raw);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/AppCompactor.java
new file mode 100644
index 0000000..aee16c3
--- /dev/null
+++ b/services/core/java/com/android/server/am/AppCompactor.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import com.android.internal.annotations.GuardedBy;
+
+import android.app.ActivityManager;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.Trace;
+
+import android.util.EventLog;
+import android.util.StatsLog;
+
+import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
+
+import com.android.server.ServiceThread;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+public final class AppCompactor {
+    /**
+     * Processes to compact.
+     */
+    final ArrayList<ProcessRecord> mPendingCompactionProcesses = new ArrayList<ProcessRecord>();
+
+    /*
+     * This thread must be moved to the system background cpuset.
+     * If that doesn't happen, it's probably going to draw a lot of power.
+     * However, this has to happen after the first updateOomAdjLocked, because
+     * that will wipe out the cpuset assignment for system_server threads.
+     * Accordingly, this is in the AMS constructor.
+     */
+    final ServiceThread mCompactionThread;
+
+    static final int COMPACT_PROCESS_SOME = 1;
+    static final int COMPACT_PROCESS_FULL = 2;
+    static final int COMPACT_PROCESS_MSG = 1;
+    final Handler mCompactionHandler;
+
+    final ActivityManagerService mAm;
+    final ActivityManagerConstants mConstants;
+
+    public AppCompactor(ActivityManagerService am) {
+        mAm = am;
+        mConstants = am.mConstants;
+
+        mCompactionThread = new ServiceThread("CompactionThread",
+                THREAD_PRIORITY_FOREGROUND, true);
+        mCompactionThread.start();
+        mCompactionHandler = new MemCompactionHandler(this);
+    }
+
+    // Must be called while holding AMS lock.
+    final void compactAppSome(ProcessRecord app) {
+        app.reqCompactAction = COMPACT_PROCESS_SOME;
+        mPendingCompactionProcesses.add(app);
+        mCompactionHandler.sendMessage(
+            mCompactionHandler.obtainMessage(
+                COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
+    }
+
+    // Must be called while holding AMS lock.
+    final void compactAppFull(ProcessRecord app) {
+        app.reqCompactAction = COMPACT_PROCESS_FULL;
+        mPendingCompactionProcesses.add(app);
+        mCompactionHandler.sendMessage(
+            mCompactionHandler.obtainMessage(
+                COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
+
+    }
+    final class MemCompactionHandler extends Handler {
+        AppCompactor mAc;
+
+        private MemCompactionHandler(AppCompactor ac) {
+            super(ac.mCompactionThread.getLooper());
+            mAc = ac;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+            case COMPACT_PROCESS_MSG: {
+                long start = SystemClock.uptimeMillis();
+                ProcessRecord proc;
+                int pid;
+                String action;
+                final String name;
+                int pendingAction, lastCompactAction;
+                long lastCompactTime;
+                synchronized(mAc.mAm) {
+                    proc = mAc.mPendingCompactionProcesses.remove(0);
+
+                    // don't compact if the process has returned to perceptible
+                    if (proc.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
+                        return;
+                    }
+
+                    pid = proc.pid;
+                    name = proc.processName;
+                    pendingAction = proc.reqCompactAction;
+                    lastCompactAction = proc.lastCompactAction;
+                    lastCompactTime = proc.lastCompactTime;
+                }
+                if (pid == 0) {
+                    // not a real process, either one being launched or one being killed
+                    return;
+                }
+
+                // basic throttling
+                if (pendingAction == COMPACT_PROCESS_SOME) {
+                    // if we're compacting some, then compact if >10s after last full
+                    // or >5s after last some
+                    if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < 5000)) ||
+                        (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < 10000))) {
+                        return;
+                    }
+                } else {
+                    // if we're compacting full, then compact if >10s after last full
+                    // or >.5s after last some
+                    if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < 500)) ||
+                        (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < 10000))) {
+                        return;
+                    }
+                }
+
+                try {
+                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Compact " +
+                            ((pendingAction == COMPACT_PROCESS_SOME) ? "some" : "full") +
+                            ": " + name);
+                    long[] rssBefore = Process.getRss(pid);
+                    FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim");
+                    if (pendingAction == COMPACT_PROCESS_SOME) {
+                        action = "file";
+                    } else {
+                        action = "all";
+                    }
+                    fos.write(action.getBytes());
+                    fos.close();
+                    long[] rssAfter = Process.getRss(pid);
+                    long end = SystemClock.uptimeMillis();
+                    long time = end - start;
+                    EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name, action,
+                            rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
+                            rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
+                            lastCompactAction, lastCompactTime, msg.arg1, msg.arg2);
+                    StatsLog.write(StatsLog.APP_COMPACTED, pid, name, pendingAction,
+                            rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
+                            rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
+                            lastCompactAction, lastCompactTime, msg.arg1,
+                            ActivityManager.processStateAmToProto(msg.arg2));
+                    synchronized(mAc.mAm) {
+                        proc.lastCompactTime = end;
+                        proc.lastCompactAction = pendingAction;
+                    }
+                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                } catch (Exception e) {
+                    // nothing to do, presumably the process died
+                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                }
+            }
+            }
+        }
+    }
+
+
+}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 1c1daff..a634b57 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -53,6 +53,7 @@
 import com.android.internal.app.ProcessMap;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.server.PackageWatchdog;
 import com.android.server.RescueParty;
 import com.android.server.wm.WindowProcessController;
 
@@ -69,6 +70,7 @@
 
     private final ActivityManagerService mService;
     private final Context mContext;
+    private final PackageWatchdog mPackageWatchdog;
 
     private ArraySet<String> mAppsNotReportingCrashes;
 
@@ -93,10 +95,11 @@
     private final ProcessMap<BadProcessInfo> mBadProcesses = new ProcessMap<>();
 
 
-    AppErrors(Context context, ActivityManagerService service) {
+    AppErrors(Context context, ActivityManagerService service, PackageWatchdog watchdog) {
         context.assertRuntimeOverlayThemable();
         mService = service;
         mContext = context;
+        mPackageWatchdog = watchdog;
     }
 
     void writeToProto(ProtoOutputStream proto, long fieldId, String dumpPackage) {
@@ -400,10 +403,16 @@
             longMsg = shortMsg;
         }
 
-        // If a persistent app is stuck in a crash loop, the device isn't very
-        // usable, so we want to consider sending out a rescue party.
-        if (r != null && r.isPersistent()) {
-            RescueParty.notePersistentAppCrash(mContext, r.uid);
+        if (r != null) {
+            if (r.isPersistent()) {
+                // If a persistent app is stuck in a crash loop, the device isn't very
+                // usable, so we want to consider sending out a rescue party.
+                RescueParty.notePersistentAppCrash(mContext, r.uid);
+            } else {
+                // If a non-persistent app is stuck in crash loop, we want to inform
+                // the package watchdog, maybe an update or experiment can be rolled back.
+                mPackageWatchdog.onPackageFailure(r.getPackageList());
+            }
         }
 
         final int relaunchReason = r != null
@@ -821,6 +830,7 @@
 
     void handleShowAnrUi(Message msg) {
         Dialog dialogToShow = null;
+        String[] packageList = null;
         synchronized (mService) {
             AppNotRespondingDialog.Data data = (AppNotRespondingDialog.Data) msg.obj;
             final ProcessRecord proc = data.proc;
@@ -828,6 +838,9 @@
                 Slog.e(TAG, "handleShowAnrUi: proc is null");
                 return;
             }
+            if (!proc.isPersistent()) {
+                packageList = proc.getPackageList();
+            }
             if (proc.anrDialog != null) {
                 Slog.e(TAG, "App already has anr dialog: " + proc);
                 MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
@@ -851,6 +864,10 @@
         if (dialogToShow != null) {
             dialogToShow.show();
         }
+        // Notify PackageWatchdog without the lock held
+        if (packageList != null) {
+            mPackageWatchdog.onPackageFailure(packageList);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index ab9ba08..a376e7a 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -724,6 +724,8 @@
         synchronized (mStats) {
             mStats.noteWifiOnLocked();
         }
+        StatsLog.write(StatsLog.WIFI_ENABLED_STATE_CHANGED,
+                StatsLog.WIFI_ENABLED_STATE_CHANGED__STATE__ON);
     }
 
     public void noteWifiOff() {
@@ -731,6 +733,8 @@
         synchronized (mStats) {
             mStats.noteWifiOffLocked();
         }
+        StatsLog.write(StatsLog.WIFI_ENABLED_STATE_CHANGED,
+                StatsLog.WIFI_ENABLED_STATE_CHANGED__STATE__OFF);
     }
 
     public void noteStartAudio(int uid) {
@@ -865,6 +869,9 @@
         synchronized (mStats) {
             mStats.noteWifiRunningLocked(ws);
         }
+        // TODO: Log WIFI_RUNNING_STATE_CHANGED in a better spot to include Hotspot too.
+        StatsLog.write(StatsLog.WIFI_RUNNING_STATE_CHANGED,
+                ws, StatsLog.WIFI_RUNNING_STATE_CHANGED__STATE__ON);
     }
 
     public void noteWifiRunningChanged(WorkSource oldWs, WorkSource newWs) {
@@ -872,6 +879,10 @@
         synchronized (mStats) {
             mStats.noteWifiRunningChangedLocked(oldWs, newWs);
         }
+        StatsLog.write(StatsLog.WIFI_RUNNING_STATE_CHANGED,
+                newWs, StatsLog.WIFI_RUNNING_STATE_CHANGED__STATE__ON);
+        StatsLog.write(StatsLog.WIFI_RUNNING_STATE_CHANGED,
+                oldWs, StatsLog.WIFI_RUNNING_STATE_CHANGED__STATE__OFF);
     }
 
     public void noteWifiStopped(WorkSource ws) {
@@ -879,6 +890,8 @@
         synchronized (mStats) {
             mStats.noteWifiStoppedLocked(ws);
         }
+        StatsLog.write(StatsLog.WIFI_RUNNING_STATE_CHANGED,
+                ws, StatsLog.WIFI_RUNNING_STATE_CHANGED__STATE__OFF);
     }
 
     public void noteWifiState(int wifiState, String accessPoint) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index c290fbe..65aacdc 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -287,6 +287,9 @@
         r.curApp = app;
         app.curReceivers.add(r);
         app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
+        if (r.allowBackgroundActivityStarts) {
+            app.addAllowBackgroundActivityStartsToken(r);
+        }
         mService.mProcessList.updateLruProcessLocked(app, false, null);
         if (!skipOomAdj) {
             mService.updateOomAdjLocked();
@@ -415,6 +418,9 @@
         if (state == BroadcastRecord.IDLE) {
             Slog.w(TAG, "finishReceiver [" + mQueueName + "] called but state is IDLE");
         }
+        if (r.allowBackgroundActivityStarts) {
+            r.curApp.removeAllowBackgroundActivityStartsToken(r);
+         }
         // If we're abandoning this broadcast before any receivers were actually spun up,
         // nextReceiver is zero; in which case time-to-process bookkeeping doesn't apply.
         if (r.nextReceiver > 0) {
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 9b7dc44..9e799f6 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -81,6 +81,10 @@
     int manifestSkipCount;  // number of manifest receivers skipped.
     BroadcastQueue queue;   // the outbound queue handling this broadcast
 
+    // if set to true, app's process will be temporarily whitelisted to start activities
+    // from background for the duration of the broadcast dispatch
+    final boolean allowBackgroundActivityStarts;
+
     static final int IDLE = 0;
     static final int APP_RECEIVE = 1;
     static final int CALL_IN_RECEIVE = 2;
@@ -223,7 +227,8 @@
             int _callingPid, int _callingUid, boolean _callerInstantApp, String _resolvedType,
             String[] _requiredPermissions, int _appOp, BroadcastOptions _options, List _receivers,
             IIntentReceiver _resultTo, int _resultCode, String _resultData, Bundle _resultExtras,
-            boolean _serialized, boolean _sticky, boolean _initialSticky, int _userId) {
+            boolean _serialized, boolean _sticky, boolean _initialSticky, int _userId,
+            boolean _allowBackgroundActivityStarts) {
         if (_intent == null) {
             throw new NullPointerException("Can't construct with a null intent");
         }
@@ -252,6 +257,7 @@
         userId = _userId;
         nextReceiver = 0;
         state = IDLE;
+        allowBackgroundActivityStarts = _allowBackgroundActivityStarts;
     }
 
     /**
@@ -295,6 +301,7 @@
         manifestCount = from.manifestCount;
         manifestSkipCount = from.manifestSkipCount;
         queue = from.queue;
+        allowBackgroundActivityStarts = from.allowBackgroundActivityStarts;
     }
 
     public BroadcastRecord maybeStripForHistory() {
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index aa76b3d..af1031e 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -43,6 +43,7 @@
     final PendingIntent clientIntent; // How to launch the client.
     final int clientUid;            // The identity of this connection's client
     final String clientProcessName; // The source process of this connection's client
+    final String clientPackageName; // The source package of this connection's client
     public AssociationState.SourceState association; // Association tracking
     String stringName;              // Caching of toString.
     boolean serviceDead;            // Well is it?
@@ -96,7 +97,7 @@
             ActivityServiceConnectionsHolder<ConnectionRecord> _activity,
             IServiceConnection _conn, int _flags,
             int _clientLabel, PendingIntent _clientIntent,
-            int _clientUid, String _clientProcessName) {
+            int _clientUid, String _clientProcessName, String _clientPackageName) {
         binding = _binding;
         activity = _activity;
         conn = _conn;
@@ -105,6 +106,7 @@
         clientIntent = _clientIntent;
         clientUid = _clientUid;
         clientProcessName = _clientProcessName;
+        clientPackageName = _clientPackageName;
     }
 
     public void startAssociationIfNeeded() {
@@ -125,7 +127,7 @@
             } else {
                 association = holder.pkg.getAssociationStateLocked(holder.state,
                         binding.service.instanceName.getClassName()).startSource(clientUid,
-                        clientProcessName);
+                        clientProcessName, clientPackageName);
 
             }
         }
diff --git a/services/core/java/com/android/server/am/ContentProviderConnection.java b/services/core/java/com/android/server/am/ContentProviderConnection.java
index f2d4f73..5f184c2 100644
--- a/services/core/java/com/android/server/am/ContentProviderConnection.java
+++ b/services/core/java/com/android/server/am/ContentProviderConnection.java
@@ -32,6 +32,7 @@
 public final class ContentProviderConnection extends Binder {
     public final ContentProviderRecord provider;
     public final ProcessRecord client;
+    public final String clientPackage;
     public AssociationState.SourceState association;
     public final long createTime;
     public int stableCount;
@@ -46,9 +47,11 @@
     public int numStableIncs;
     public int numUnstableIncs;
 
-    public ContentProviderConnection(ContentProviderRecord _provider, ProcessRecord _client) {
+    public ContentProviderConnection(ContentProviderRecord _provider, ProcessRecord _client,
+            String _clientPackage) {
         provider = _provider;
         client = _client;
+        clientPackage = _clientPackage;
         createTime = SystemClock.elapsedRealtime();
     }
 
@@ -69,7 +72,8 @@
                         + provider.name.toShortString() + ": proc=" + provider.proc);
             } else {
                 association = holder.pkg.getAssociationStateLocked(holder.state,
-                        provider.name.getClassName()).startSource(client.uid, client.processName);
+                        provider.name.getClassName()).startSource(client.uid, client.processName,
+                        clientPackage);
 
             }
         }
diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java
index 2fc4adc..46dfc7c 100644
--- a/services/core/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/core/java/com/android/server/am/ContentProviderRecord.java
@@ -305,7 +305,7 @@
                 } else {
                     mAssociation = holder.pkg.getAssociationStateLocked(holder.state,
                             provider.name.getClassName()).startSource(mOwningUid,
-                            mOwningProcessName);
+                            mOwningProcessName, null);
 
                 }
             }
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 65cd329..f9a77af 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -67,8 +67,8 @@
         sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS_GLES, String.class);
         sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYER_APP, String.class);
         sGlobalSettingToTypeMap.put(Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, int.class);
-        sGlobalSettingToTypeMap.put(Settings.Global.UPDATED_GFX_DRIVER_DEV_OPT_IN_APP,
-                                    String.class);
+        sGlobalSettingToTypeMap.put(Settings.Global.GUP_DEV_OPT_IN_APPS, String.class);
+        sGlobalSettingToTypeMap.put(Settings.Global.GUP_BLACK_LIST, String.class);
         // add other global settings here...
     }
 
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 09064f2..a71f6af 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -135,4 +135,7 @@
 30062 am_on_activity_result_called (User|1|5),(Component Name|3),(Reason|3)
 
 # The task is being removed from its parent stack
-30061 am_remove_task (Task ID|1|5), (Stack ID|1|5)
\ No newline at end of file
+30061 am_remove_task (Task ID|1|5), (Stack ID|1|5)
+
+# The task is being compacted
+30063 am_compact (Pid|1|5),(Process Name|3),(Action|3),(BeforeRssTotal|2|2),(BeforeRssFile|2|2),(BeforeRssAnon|2|2),(BeforeRssSwap|2|2),(AfterRssTotal|2|2),(AfterRssFile|2|2),(AfterRssAnon|2|2),(AfterRssSwap|2|2),(Time|2|3),(LastAction|1|2),(LastActionTimestamp|2|3),(setAdj|1|2),(procState|1|2)
diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java
index 90fe30c..a584914 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -123,9 +123,8 @@
      * if the file is not available.
      */
     public static String readCmdlineFromProcfs(int pid) {
-        String path = String.format(Locale.US, PROC_CMDLINE_FILE_FMT, pid);
-        String cmdline = readFileContents(path);
-        return cmdline != null ? cmdline : "";
+        final String path = String.format(Locale.US, PROC_CMDLINE_FILE_FMT, pid);
+        return parseCmdlineFromProcfs(readFileContents(path));
     }
 
     private static String readFileContents(String path) {
@@ -210,6 +209,24 @@
         return m.find() ? Long.parseLong(m.group(1)) * BYTES_IN_KILOBYTE : 0;
     }
 
+
+    /**
+     * Parses cmdline out of the contents of the /proc/pid/cmdline file in procfs.
+     *
+     * Parsing is required to strip anything after first null byte.
+     */
+    @VisibleForTesting
+    static String parseCmdlineFromProcfs(String cmdline) {
+        if (cmdline == null) {
+            return "";
+        }
+        int firstNullByte = cmdline.indexOf("\0");
+        if (firstNullByte == -1) {
+            return cmdline;
+        }
+        return cmdline.substring(0, firstNullByte);
+    }
+
     /**
      * Returns whether per-app memcg is available on device.
      */
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 5208ca5..e483b26 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -15,7 +15,7 @@
 jjaggi@google.com
 racarr@google.com
 chaviw@google.com
-brycelee@google.com
+vishnun@google.com
 akulian@google.com
 roosa@google.com
 
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 447243b..b675d9d 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -34,6 +34,7 @@
 import android.os.TransactionTooLargeException;
 import android.os.UserHandle;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Slog;
 import android.util.TimeUtils;
 
@@ -48,6 +49,9 @@
 public final class PendingIntentRecord extends IIntentSender.Stub {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "PendingIntentRecord" : TAG_AM;
 
+    public static final int FLAG_ACTIVITY_SENDER = 1 << 0;
+    public static final int FLAG_BROADCAST_SENDER = 1 << 1;
+
     final PendingIntentController controller;
     final Key key;
     final int uid;
@@ -56,6 +60,8 @@
     boolean canceled = false;
     private ArrayMap<IBinder, Long> whitelistDuration;
     private RemoteCallbackList<IResultReceiver> mCancelCallbacks;
+    private ArraySet<IBinder> mAllowBgActivityStartsForActivitySender = new ArraySet<>();
+    private ArraySet<IBinder> mAllowBgActivityStartsForBroadcastSender = new ArraySet<>();
 
     String stringName;
     String lastTagPrefix;
@@ -214,6 +220,16 @@
         this.stringName = null;
     }
 
+    void setAllowBgActivityStarts(IBinder token, int flags) {
+        if (token == null) return;
+        if ((flags & FLAG_ACTIVITY_SENDER) != 0) {
+            mAllowBgActivityStartsForActivitySender.add(token);
+        }
+        if ((flags & FLAG_BROADCAST_SENDER) != 0) {
+            mAllowBgActivityStartsForBroadcastSender.add(token);
+        }
+    }
+
     public void registerCancelListenerLocked(IResultReceiver receiver) {
         if (mCancelCallbacks == null) {
             mCancelCallbacks = new RemoteCallbackList<>();
@@ -370,14 +386,16 @@
                             res = controller.mAtmInternal.startActivitiesInPackage(
                                     uid, key.packageName, allIntents, allResolvedTypes, resultTo,
                                     mergedOptions, userId, false /* validateIncomingUser */,
-                                    this /* originatingPendingIntent */);
+                                    this /* originatingPendingIntent */,
+                                    mAllowBgActivityStartsForActivitySender.contains(whitelistToken));
                         } else {
                             res = controller.mAtmInternal.startActivityInPackage(
                                     uid, callingPid, callingUid, key.packageName, finalIntent,
                                     resolvedType, resultTo, resultWho, requestCode, 0,
                                     mergedOptions, userId, null, "PendingIntentRecord",
                                     false /* validateIncomingUser */,
-                                    this /* originatingPendingIntent */);
+                                    this /* originatingPendingIntent */,
+                                    mAllowBgActivityStartsForActivitySender.contains(whitelistToken));
                         }
                     } catch (RuntimeException e) {
                         Slog.w(TAG, "Unable to send startActivity intent", e);
@@ -394,7 +412,8 @@
                         int sent = controller.mAmInternal.broadcastIntentInPackage(key.packageName,
                                 uid, finalIntent, resolvedType, finishedReceiver, code, null, null,
                                 requiredPermission, options, (finishedReceiver != null),
-                                false, userId);
+                                false, userId,
+                                mAllowBgActivityStartsForBroadcastSender.contains(whitelistToken));
                         if (sent == ActivityManager.BROADCAST_SUCCESS) {
                             sendFinish = false;
                         }
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 117984e..9898d06 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -19,8 +19,6 @@
 import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
 import static android.app.ActivityThread.PROC_START_SEQ_IDENT;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO;
-import static android.os.Process.FIRST_ISOLATED_UID;
-import static android.os.Process.LAST_ISOLATED_UID;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
 import static android.os.Process.getFreeMemory;
@@ -34,6 +32,8 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityManagerService.KILL_APP_ZYGOTE_DELAY_MS;
+import static com.android.server.am.ActivityManagerService.KILL_APP_ZYGOTE_MSG;
 import static com.android.server.am.ActivityManagerService.PERSISTENT_MASK;
 import static com.android.server.am.ActivityManagerService.PROC_START_TIMEOUT;
 import static com.android.server.am.ActivityManagerService.PROC_START_TIMEOUT_MSG;
@@ -58,6 +58,7 @@
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
 import android.net.Uri;
+import android.os.AppZygote;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -75,10 +76,12 @@
 import android.os.storage.StorageManager;
 import android.os.storage.StorageManagerInternal;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.EventLog;
 import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.util.StatsLog;
 import android.view.Display;
 
@@ -108,6 +111,7 @@
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.BitSet;
 import java.util.List;
 
 /**
@@ -354,10 +358,136 @@
     final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<>();
 
     /**
-     * Counter for assigning isolated process uids, to avoid frequently reusing the
-     * same ones.
+     * The currently running application zygotes.
      */
-    int mNextIsolatedProcessUid = 0;
+    final ProcessMap<AppZygote> mAppZygotes = new ProcessMap<AppZygote>();
+
+    /**
+     * The processes that are forked off an application zygote.
+     */
+    final ArrayMap<AppZygote, ArrayList<ProcessRecord>> mAppZygoteProcesses =
+            new ArrayMap<AppZygote, ArrayList<ProcessRecord>>();
+
+    final class IsolatedUidRange {
+        @VisibleForTesting
+        public final int mFirstUid;
+        @VisibleForTesting
+        public final int mLastUid;
+
+        @GuardedBy("ProcessList.this.mService")
+        private final SparseBooleanArray mUidUsed = new SparseBooleanArray();
+
+        @GuardedBy("ProcessList.this.mService")
+        private int mNextUid;
+
+        IsolatedUidRange(int firstUid, int lastUid) {
+            mFirstUid = firstUid;
+            mLastUid = lastUid;
+            mNextUid = firstUid;
+        }
+
+        @GuardedBy("ProcessList.this.mService")
+        int allocateIsolatedUidLocked(int userId) {
+            int uid;
+            int stepsLeft = (mLastUid - mFirstUid + 1);
+            for (int i = 0; i < stepsLeft; ++i) {
+                if (mNextUid < mFirstUid || mNextUid > mLastUid) {
+                    mNextUid = mFirstUid;
+                }
+                uid = UserHandle.getUid(userId, mNextUid);
+                mNextUid++;
+                if (!mUidUsed.get(uid, false)) {
+                    mUidUsed.put(uid, true);
+                    return uid;
+                }
+            }
+            return -1;
+        }
+
+        @GuardedBy("ProcessList.this.mService")
+        void freeIsolatedUidLocked(int uid) {
+            // Strip out userId
+            final int appId = UserHandle.getAppId(uid);
+            mUidUsed.delete(appId);
+        }
+    };
+
+    /**
+     * A class that allocates ranges of isolated UIDs per application, and keeps track of them.
+     */
+    final class IsolatedUidRangeAllocator {
+        private final int mFirstUid;
+        private final int mNumUidRanges;
+        private final int mNumUidsPerRange;
+        /**
+         * We map the uid range [mFirstUid, mFirstUid + mNumUidRanges * mNumUidsPerRange)
+         * back to an underlying bitset of [0, mNumUidRanges) and allocate out of that.
+         */
+        @GuardedBy("ProcessList.this.mService")
+        private final BitSet mAvailableUidRanges;
+        @GuardedBy("ProcessList.this.mService")
+        private final ProcessMap<IsolatedUidRange> mAppRanges = new ProcessMap<IsolatedUidRange>();
+
+        IsolatedUidRangeAllocator(int firstUid, int lastUid, int numUidsPerRange) {
+            mFirstUid = firstUid;
+            mNumUidsPerRange = numUidsPerRange;
+            mNumUidRanges = (lastUid - firstUid + 1) / numUidsPerRange;
+            mAvailableUidRanges = new BitSet(mNumUidRanges);
+            // Mark all as available
+            mAvailableUidRanges.set(0, mNumUidRanges);
+        }
+
+        @GuardedBy("ProcessList.this.mService")
+        IsolatedUidRange getIsolatedUidRangeLocked(ApplicationInfo info) {
+            return mAppRanges.get(info.processName, info.uid);
+        }
+
+        @GuardedBy("ProcessList.this.mService")
+        IsolatedUidRange getOrCreateIsolatedUidRangeLocked(ApplicationInfo info) {
+            IsolatedUidRange range = getIsolatedUidRangeLocked(info);
+            if (range == null) {
+                int uidRangeIndex = mAvailableUidRanges.nextSetBit(0);
+                if (uidRangeIndex < 0) {
+                    // No free range
+                    return null;
+                }
+                mAvailableUidRanges.clear(uidRangeIndex);
+                int actualUid = mFirstUid + uidRangeIndex * mNumUidsPerRange;
+                range = new IsolatedUidRange(actualUid, actualUid + mNumUidsPerRange - 1);
+                mAppRanges.put(info.processName, info.uid, range);
+            }
+            return range;
+        }
+
+        @GuardedBy("ProcessList.this.mService")
+        void freeUidRangeLocked(ApplicationInfo info) {
+            // Find the UID range
+            IsolatedUidRange range = mAppRanges.get(info.processName, info.uid);
+            if (range != null) {
+                // Map back to starting uid
+                final int uidRangeIndex = (range.mFirstUid - mFirstUid) / mNumUidsPerRange;
+                // Mark it as available in the underlying bitset
+                mAvailableUidRanges.set(uidRangeIndex);
+                // And the map
+                mAppRanges.remove(info.processName, info.uid);
+            }
+        }
+    }
+
+    /**
+     * The available isolated UIDs for processes that are not spawned from an application zygote.
+     */
+    @VisibleForTesting
+    IsolatedUidRange mGlobalIsolatedUids = new IsolatedUidRange(Process.FIRST_ISOLATED_UID,
+            Process.LAST_ISOLATED_UID);
+
+    /**
+     * An allocator for isolated UID ranges for apps that use an application zygote.
+     */
+    @VisibleForTesting
+    IsolatedUidRangeAllocator mAppIsolatedUidRangeAllocator =
+            new IsolatedUidRangeAllocator(Process.FIRST_APP_ZYGOTE_ISOLATED_UID,
+                    Process.LAST_APP_ZYGOTE_ISOLATED_UID, Process.NUM_UIDS_PER_APP_ZYGOTE);
 
     /**
      * Processes that are being forcibly torn down.
@@ -1362,8 +1492,9 @@
                 mService.mNativeDebuggingApp = null;
             }
 
-            if (app.info.isPrivilegedApp() &&
-                    DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.keySet())) {
+            if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_PREFER_CODE_INTEGRITY) != 0
+                    || (app.info.isPrivilegedApp()
+                        && DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.keySet()))) {
                 runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
             }
 
@@ -1504,6 +1635,67 @@
         }
     }
 
+    @GuardedBy("mService")
+    public void killAppZygoteIfNeededLocked(AppZygote appZygote) {
+        final ApplicationInfo appInfo = appZygote.getAppInfo();
+        ArrayList<ProcessRecord> zygoteProcesses = mAppZygoteProcesses.get(appZygote);
+        if (zygoteProcesses.size() == 0) { // Only remove if no longer in use now
+            mAppZygotes.remove(appInfo.processName, appInfo.uid);
+            mAppZygoteProcesses.remove(appZygote);
+            mAppIsolatedUidRangeAllocator.freeUidRangeLocked(appInfo);
+            appZygote.stopZygote();
+        }
+    }
+
+    @GuardedBy("mService")
+    private void removeProcessFromAppZygoteLocked(final ProcessRecord app) {
+        // Free the isolated uid for this process
+        final IsolatedUidRange appUidRange =
+                mAppIsolatedUidRangeAllocator.getIsolatedUidRangeLocked(app.info);
+        if (appUidRange != null) {
+            appUidRange.freeIsolatedUidLocked(app.uid);
+        }
+
+        final AppZygote appZygote = mAppZygotes.get(app.info.processName, app.info.uid);
+        if (appZygote != null) {
+            ArrayList<ProcessRecord> zygoteProcesses = mAppZygoteProcesses.get(appZygote);
+            zygoteProcesses.remove(app);
+            if (zygoteProcesses.size() == 0) {
+                Message msg = mService.mHandler.obtainMessage(KILL_APP_ZYGOTE_MSG);
+                msg.obj = appZygote;
+                mService.mHandler.sendMessageDelayed(msg, KILL_APP_ZYGOTE_DELAY_MS);
+            }
+        }
+    }
+
+    private AppZygote createAppZygoteForProcessIfNeeded(final ProcessRecord app) {
+        synchronized (mService) {
+            AppZygote appZygote = mAppZygotes.get(app.info.processName, app.info.uid);
+            final ArrayList<ProcessRecord> zygoteProcessList;
+            if (appZygote == null) {
+                final int userId = UserHandle.getUserId(app.info.uid);
+                final IsolatedUidRange uidRange =
+                        mAppIsolatedUidRangeAllocator.getIsolatedUidRangeLocked(app.info);
+                // Allocate an isolated UID out of this range for the Zygote itself
+                final int zygoteIsolatedUid = uidRange.allocateIsolatedUidLocked(userId);
+                appZygote = new AppZygote(app.info, zygoteIsolatedUid);
+                mAppZygotes.put(app.info.processName, app.info.uid, appZygote);
+                zygoteProcessList = new ArrayList<ProcessRecord>();
+                mAppZygoteProcesses.put(appZygote, zygoteProcessList);
+            } else {
+                mService.mHandler.removeMessages(KILL_APP_ZYGOTE_MSG, appZygote);
+                zygoteProcessList = mAppZygoteProcesses.get(appZygote);
+            }
+            // Note that we already add the app to mAppZygoteProcesses here;
+            // this is so that another thread can't come in and kill the zygote
+            // before we've even tried to start the process. If the process launch
+            // goes wrong, we'll clean this up in removeProcessNameLocked()
+            zygoteProcessList.add(app);
+
+            return appZygote;
+        }
+    }
+
     private Process.ProcessStartResult startProcess(String hostingType, String entryPoint,
             ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
             String seInfo, String requiredAbi, String instructionSet, String invokeWith,
@@ -1524,6 +1716,15 @@
                         app.info.dataDir, null, app.info.packageName,
                         packageNames, visibleVolIds,
                         new String[] {PROC_START_SEQ_IDENT + app.startSeq});
+            } else if (hostingType.equals("app_zygote")) {
+                final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
+
+                startResult = appZygote.getProcess().start(entryPoint,
+                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
+                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
+                        app.info.dataDir, null, app.info.packageName,
+                        packageNames, visibleVolIds,
+                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
             } else {
                 startResult = Process.start(entryPoint,
                         app.processName, uid, uid, gids, runtimeFlags, mountExternal,
@@ -1628,8 +1829,9 @@
                 ? hostingName.flattenToShortString() : null;
 
         if (app == null) {
+            final boolean fromAppZygote = "app_zygote".equals(hostingType);
             checkSlow(startTime, "startProcess: creating new process record");
-            app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
+            app = newProcessRecordLocked(info, processName, isolated, isolatedUid, fromAppZygote);
             if (app == null) {
                 Slog.w(TAG, "Failed making new process record for "
                         + processName + "/" + info.uid + " isolated=" + isolated);
@@ -2002,29 +2204,31 @@
     }
 
     @GuardedBy("mService")
+    private IsolatedUidRange getOrCreateIsolatedUidRangeLocked(ApplicationInfo info,
+            boolean fromAppZygote) {
+        if (!fromAppZygote) {
+            // Allocate an isolated UID from the global range
+            return mGlobalIsolatedUids;
+        } else {
+            return mAppIsolatedUidRangeAllocator.getOrCreateIsolatedUidRangeLocked(info);
+        }
+    }
+
+    @GuardedBy("mService")
     final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
-            boolean isolated, int isolatedUid) {
+            boolean isolated, int isolatedUid, boolean fromAppZygote) {
         String proc = customProcess != null ? customProcess : info.processName;
         final int userId = UserHandle.getUserId(info.uid);
         int uid = info.uid;
         if (isolated) {
             if (isolatedUid == 0) {
-                int stepsLeft = LAST_ISOLATED_UID - FIRST_ISOLATED_UID + 1;
-                while (true) {
-                    if (mNextIsolatedProcessUid < FIRST_ISOLATED_UID
-                            || mNextIsolatedProcessUid > LAST_ISOLATED_UID) {
-                        mNextIsolatedProcessUid = FIRST_ISOLATED_UID;
-                    }
-                    uid = UserHandle.getUid(userId, mNextIsolatedProcessUid);
-                    mNextIsolatedProcessUid++;
-                    if (mIsolatedProcesses.indexOfKey(uid) < 0) {
-                        // No process for this uid, use it.
-                        break;
-                    }
-                    stepsLeft--;
-                    if (stepsLeft <= 0) {
-                        return null;
-                    }
+                IsolatedUidRange uidRange = getOrCreateIsolatedUidRangeLocked(info, fromAppZygote);
+                if (uidRange == null) {
+                    return null;
+                }
+                uid = uidRange.allocateIsolatedUidLocked(userId);
+                if (uid == -1) {
+                    return null;
                 }
             } else {
                 // Special case for startIsolatedProcess (internal only), where
@@ -2092,6 +2296,13 @@
             old.uidRecord = null;
         }
         mIsolatedProcesses.remove(uid);
+        mGlobalIsolatedUids.freeIsolatedUidLocked(uid);
+        // Remove the (expected) ProcessRecord from the app zygote
+        final ProcessRecord record = expecting != null ? expecting : old;
+        if (record != null && record.appZygote) {
+            removeProcessFromAppZygoteLocked(record);
+        }
+
         return old;
     }
 
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 4826f48..0d0824a 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -76,6 +76,7 @@
     private final ActivityManagerService mService; // where we came from
     final ApplicationInfo info; // all about the first app in the process
     final boolean isolated;     // true if this is a special isolated process
+    final boolean appZygote;    // true if this is forked from the app zygote
     final int uid;              // uid of process; may be different from 'info' if isolated
     final int userId;           // user of process.
     final String processName;   // name of the process
@@ -145,11 +146,15 @@
     int curAdj;                 // Current OOM adjustment for this process
     int setAdj;                 // Last set OOM adjustment for this process
     int verifiedAdj;            // The last adjustment that was verified as actually being set
+    long lastCompactTime;       // The last time that this process was compacted
+    int reqCompactAction;       // The most recent compaction action requested for this app.
+    int lastCompactAction;      // The most recent compaction action performed for this app.
     private int mCurSchedGroup; // Currently desired scheduling class
     int setSchedGroup;          // Last set to background scheduling class
     int trimMemoryLevel;        // Last selected memory trimming level
     private int mCurProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state
     private int mRepProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
+    private int mCurRawProcState = PROCESS_STATE_NONEXISTENT; // Temp state during computation
     int setProcState = PROCESS_STATE_NONEXISTENT; // Last set process state in process tracker
     int pssProcState = PROCESS_STATE_NONEXISTENT; // Currently requesting pss for
     int pssStatType;            // The type of stat collection that we are currently requesting
@@ -245,6 +250,9 @@
     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 whitelisted
+    // to start activities even if it's not in the foreground
+    final ArraySet<Binder> mAllowBackgroundActivityStartsTokens = new ArraySet<>();
 
     String isolatedEntryPoint;  // Class to run on start if this is a special isolated process.
     String[] isolatedEntryPointArgs; // Arguments to pass to isolatedEntryPoint's main().
@@ -382,6 +390,8 @@
                 pw.print(" setRaw="); pw.print(setRawAdj);
                 pw.print(" cur="); pw.print(curAdj);
                 pw.print(" set="); pw.println(setAdj);
+        pw.print(prefix); pw.print("lastCompactTime="); pw.print(lastCompactTime);
+                pw.print(" lastCompactAction="); pw.print(lastCompactAction);
         pw.print(prefix); pw.print("mCurSchedGroup="); pw.print(mCurSchedGroup);
                 pw.print(" setSchedGroup="); pw.print(setSchedGroup);
                 pw.print(" systemNoUi="); pw.print(systemNoUi);
@@ -550,6 +560,8 @@
         mService = _service;
         info = _info;
         isolated = _info.uid != _uid;
+        appZygote = (UserHandle.getAppId(_uid) >= Process.FIRST_APP_ZYGOTE_ISOLATED_UID
+                && UserHandle.getAppId(_uid) <= Process.LAST_APP_ZYGOTE_ISOLATED_UID);
         uid = _uid;
         userId = UserHandle.getUserId(_uid);
         processName = _processName;
@@ -897,6 +909,7 @@
         if (mRepProcState > newState) {
             mRepProcState = newState;
             setCurProcState(newState);
+            setCurRawProcState(newState);
             for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
                 StatsLog.write(StatsLog.PROCESS_STATE_CHANGED,
                         uid, processName, pkgList.keyAt(ipkg),
@@ -979,6 +992,14 @@
         return mCurProcState;
     }
 
+    void setCurRawProcState(int curRawProcState) {
+        mCurRawProcState = curRawProcState;
+    }
+
+    int getCurRawProcState() {
+        return mCurRawProcState;
+    }
+
     void setReportedProcState(int repProcState) {
         mRepProcState = repProcState;
         for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
@@ -1120,6 +1141,17 @@
         return mUsingWrapper;
     }
 
+    void addAllowBackgroundActivityStartsToken(Binder entity) {
+        mAllowBackgroundActivityStartsTokens.add(entity);
+        mWindowProcessController.setAllowBackgroundActivityStarts(true);
+    }
+
+    void removeAllowBackgroundActivityStartsToken(Binder entity) {
+        mAllowBackgroundActivityStartsTokens.remove(entity);
+        mWindowProcessController.setAllowBackgroundActivityStarts(
+                !mAllowBackgroundActivityStartsTokens.isEmpty());
+    }
+
     void setActiveInstrumentation(ActiveInstrumentation instr) {
         mInstr = instr;
         mWindowProcessController.setInstrumenting(instr != null);
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 4c4a090..d5ede5b 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -19,8 +19,10 @@
 import android.content.ContentResolver;
 import android.database.ContentObserver;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.Build;
 import android.os.SystemProperties;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Slog;
@@ -61,13 +63,18 @@
     // Add the global setting you want to push to native level as experiment flag into this list.
     //
     // NOTE: please grant write permission system property prefix
-    // with format persist.experiment.[experiment_category_name]. in system_server.te and grant read
-    // permission in the corresponding .te file your feature belongs to.
+    // with format persist.device_config.global_settings.[flag_name] in system_server.te and grant
+    // read permission in the corresponding .te file your feature belongs to.
     @VisibleForTesting
     static final String[] sGlobalSettings = new String[] {
             Settings.Global.NATIVE_FLAGS_HEALTH_CHECK_ENABLED,
     };
 
+    // All the flags under the listed DeviceConfig scopes will be synced to native level.
+    //
+    // NOTE: please grant write permission system property prefix
+    // with format persist.device_config.[device_config_scope]. in system_server.te and grant read
+    // permission in the corresponding .te file your feature belongs to.
     @VisibleForTesting
     static final String[] sDeviceConfigScopes = new String[] {
     };
@@ -104,19 +111,31 @@
             ContentObserver co = new ContentObserver(null) {
                 @Override
                 public void onChange(boolean selfChange) {
-                    updatePropertyFromSetting(globalSetting, propName, true);
+                    updatePropertyFromSetting(globalSetting, propName);
                 }
             };
 
             // only updating on starting up when no native flags reset is performed during current
             // booting.
             if (!isNativeFlagsResetPerformed()) {
-                updatePropertyFromSetting(globalSetting, propName, true);
+                updatePropertyFromSetting(globalSetting, propName);
             }
             mContentResolver.registerContentObserver(settingUri, false, co);
         }
 
-        // TODO: address sDeviceConfigScopes after DeviceConfig APIs are available.
+        for (String deviceConfigScope : mDeviceConfigScopes) {
+            DeviceConfig.addOnPropertyChangedListener(
+                    deviceConfigScope,
+                    AsyncTask.THREAD_POOL_EXECUTOR,
+                    (String scope, String name, String value) -> {
+                        String propertyName = makePropertyName(scope, name);
+                        if (propertyName == null) {
+                            log("unable to construct system property for " + scope + "/" + name);
+                            return;
+                        }
+                        setProperty(propertyName, value);
+                    });
+        }
     }
 
     public static SettingsToPropertiesMapper start(ContentResolver contentResolver) {
@@ -184,15 +203,6 @@
         return propertyName;
     }
 
-    private String getSetting(String name, boolean isGlobalSetting) {
-        if (isGlobalSetting) {
-            return Settings.Global.getString(mContentResolver, name);
-        } else {
-            // TODO: complete the code after DeviceConfig APIs implemented.
-            return null;
-        }
-    }
-
     private void setProperty(String key, String value) {
         // Check if need to clear the property
         if (value == null) {
@@ -259,8 +269,8 @@
     }
 
     @VisibleForTesting
-    void updatePropertyFromSetting(String settingName, String propName, boolean isGlobalSetting) {
-        String settingValue = getSetting(settingName, isGlobalSetting);
+    void updatePropertyFromSetting(String settingName, String propName) {
+        String settingValue = Settings.Global.getString(mContentResolver, settingName);
         setProperty(propName, settingValue);
     }
 }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 6cde4ad..d704a3e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -7822,6 +7822,36 @@
         return AudioManager.SUCCESS;
     }
 
+    /** see AudioPolicy.setUidDeviceAffinity() */
+    public int setUidDeviceAffinity(IAudioPolicyCallback pcb, int uid,
+            @NonNull int[] deviceTypes,
+            @NonNull String[] deviceAddresses) {
+        synchronized (mAudioPolicies) {
+            final AudioPolicyProxy app =
+                    checkUpdateForPolicy(pcb, "Cannot change device affinity in audio policy");
+            if (app == null) {
+                return AudioManager.ERROR;
+            }
+            if (!app.hasMixRoutedToDevices(deviceTypes, deviceAddresses)) {
+                return AudioManager.ERROR;
+            }
+        }
+        return AudioManager.SUCCESS;
+    }
+
+    /** see AudioPolicy.removeUidDeviceAffinity() */
+    public int removeUidDeviceAffinity(IAudioPolicyCallback pcb, int uid) {
+        synchronized (mAudioPolicies) {
+            final AudioPolicyProxy app =
+                    checkUpdateForPolicy(pcb, "Cannot remove device affinity in audio policy");
+            if (app == null) {
+                return AudioManager.ERROR;
+            }
+
+        }
+        return AudioManager.SUCCESS;
+    }
+
     public int setFocusPropertiesForPolicy(int duckingBehavior, IAudioPolicyCallback pcb) {
         if (DEBUG_AP) Log.d(TAG, "setFocusPropertiesForPolicy() duck behavior=" + duckingBehavior
                 + " policy " +  pcb.asBinder());
@@ -7994,6 +8024,15 @@
     //======================
     // Audio policy proxy
     //======================
+    private static final class AudioDeviceArray {
+        final @NonNull int[] mDeviceTypes;
+        final @NonNull String[] mDeviceAddresses;
+        AudioDeviceArray(@NonNull int[] types,  @NonNull String[] addresses) {
+            mDeviceTypes = types;
+            mDeviceAddresses = addresses;
+        }
+    }
+
     /**
      * This internal class inherits from AudioPolicyConfig, each instance contains all the
      * mixes of an AudioPolicy and their configurations.
@@ -8003,6 +8042,8 @@
         final IAudioPolicyCallback mPolicyCallback;
         final boolean mHasFocusListener;
         final boolean mIsVolumeController;
+        final HashMap<Integer, AudioDeviceArray> mUidDeviceAffinities =
+                new HashMap<Integer, AudioDeviceArray>();
         /**
          * Audio focus ducking behavior for an audio policy.
          * This variable reflects the value that was successfully set in
@@ -8075,6 +8116,26 @@
             return false;
         }
 
+        // Verify all the devices in the array are served by mixes defined in this policy
+        boolean hasMixRoutedToDevices(@NonNull int[] deviceTypes,
+                @NonNull String[] deviceAddresses) {
+            for (int i = 0; i < deviceTypes.length; i++) {
+                boolean hasDevice = false;
+                for (AudioMix mix : mMixes) {
+                    // this will check both that the mix has ROUTE_FLAG_RENDER and the device
+                    // is reached by this mix
+                    if (mix.isRoutedToDevice(deviceTypes[i], deviceAddresses[i])) {
+                        hasDevice = true;
+                        break;
+                    }
+                }
+                if (!hasDevice) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
         void addMixes(@NonNull ArrayList<AudioMix> mixes) {
             // TODO optimize to not have to unregister the mixes already in place
             synchronized (mMixes) {
@@ -8098,6 +8159,29 @@
             AudioSystem.registerPolicyMixes(mMixes, true);
             Binder.restoreCallingIdentity(identity);
         }
+
+        void setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) {
+            final Integer Uid = new Integer(uid);
+            if (mUidDeviceAffinities.remove(Uid) != null) {
+                final long identity = Binder.clearCallingIdentity();
+                AudioSystem.removeUidDeviceAffinities(uid);
+                Binder.restoreCallingIdentity(identity);
+            }
+            final long identity = Binder.clearCallingIdentity();
+            final int res = AudioSystem.setUidDeviceAffinities(uid, types, addresses);
+            Binder.restoreCallingIdentity(identity);
+            if (res == AudioSystem.SUCCESS) {
+                mUidDeviceAffinities.put(Uid, new AudioDeviceArray(types, addresses));
+            }
+        }
+
+        void removeUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) {
+            if (mUidDeviceAffinities.remove(new Integer(uid)) != null) {
+                final long identity = Binder.clearCallingIdentity();
+                AudioSystem.removeUidDeviceAffinities(uid);
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
     };
 
     //======================
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 2feea41..905f826 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -20,11 +20,11 @@
 import android.content.pm.PackageManager;
 import android.media.AudioFormat;
 import android.media.AudioManager;
-import android.media.AudioPlaybackConfiguration;
 import android.media.AudioRecordingConfiguration;
 import android.media.AudioSystem;
 import android.media.IRecordingConfigDispatcher;
 import android.media.MediaRecorder;
+import android.media.audiofx.AudioEffect;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
@@ -64,12 +64,19 @@
      * Implementation of android.media.AudioSystem.AudioRecordingCallback
      */
     public void onRecordingConfigurationChanged(int event, int uid, int session, int source,
-            int[] recordingInfo, String packName) {
+                                                int portId, boolean silenced, int[] recordingInfo,
+                                                AudioEffect.Descriptor[] clientEffects,
+                                                AudioEffect.Descriptor[] effects,
+                                                int activeSource, String packName) {
         if (MediaRecorder.isSystemOnlyAudioSource(source)) {
             return;
         }
+        String clientEffectName =  clientEffects.length == 0 ? "None" : clientEffects[0].name;
+        String effectName =  effects.length == 0 ? "None" : effects[0].name;
+
         final List<AudioRecordingConfiguration> configsSystem =
-                updateSnapshot(event, uid, session, source, recordingInfo);
+                updateSnapshot(event, uid, session, source, recordingInfo,
+                portId, silenced, activeSource, clientEffects, effects);
         if (configsSystem != null){
             synchronized (mClients) {
                 // list of recording configurations for "public consumption". It is only computed if
@@ -179,13 +186,20 @@
      * @param session
      * @param source
      * @param recordingFormat see
-     *     {@link AudioSystem.AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int[])}
+     *     {@link AudioSystem.AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int,\
+     int, int, boolean, int[], AudioEffect.Descriptor[], AudioEffect.Descriptor[], int, String)}
      *     for the definition of the contents of the array
+     * @param portId
+     * @param silenced
+     * @param activeSource
+     * @param clientEffects
+     * @param effects
      * @return null if the list of active recording sessions has not been modified, a list
      *     with the current active configurations otherwise.
      */
     private List<AudioRecordingConfiguration> updateSnapshot(int event, int uid, int session,
-            int source, int[] recordingInfo) {
+            int source, int[] recordingInfo, int portId, boolean silenced, int activeSource,
+            AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] effects) {
         final boolean configChanged;
         final ArrayList<AudioRecordingConfiguration> configs;
         synchronized(mRecordConfigs) {
@@ -211,7 +225,7 @@
                         .setSampleRate(recordingInfo[5])
                         .build();
                 final int patchHandle = recordingInfo[6];
-                final Integer sessionKey = new Integer(session);
+                final Integer portIdKey = new Integer(portId);
 
                 final String[] packages = mPackMan.getPackagesForUid(uid);
                 final String packageName;
@@ -222,19 +236,20 @@
                 }
                 final AudioRecordingConfiguration updatedConfig =
                         new AudioRecordingConfiguration(uid, session, source,
-                                clientFormat, deviceFormat, patchHandle, packageName);
+                                clientFormat, deviceFormat, patchHandle, packageName,
+                                portId, silenced, activeSource, clientEffects, effects);
 
-                if (mRecordConfigs.containsKey(sessionKey)) {
-                    if (updatedConfig.equals(mRecordConfigs.get(sessionKey))) {
+                if (mRecordConfigs.containsKey(portIdKey)) {
+                    if (updatedConfig.equals(mRecordConfigs.get(portIdKey))) {
                         configChanged = false;
                     } else {
                         // config exists but has been modified
-                        mRecordConfigs.remove(sessionKey);
-                        mRecordConfigs.put(sessionKey, updatedConfig);
+                        mRecordConfigs.remove(portIdKey);
+                        mRecordConfigs.put(portIdKey, updatedConfig);
                         configChanged = true;
                     }
                 } else {
-                    mRecordConfigs.put(sessionKey, updatedConfig);
+                    mRecordConfigs.put(portIdKey, updatedConfig);
                     configChanged = true;
                 }
                 if (configChanged) {
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 1882be2..36ca4dc 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -373,6 +373,13 @@
             public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token)
                     throws RemoteException {
                 try {
+                    // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
+                    // after user dismissed/canceled dialog).
+                    if (mCurrentAuthSession == null) {
+                        Slog.e(TAG, "onAuthenticationSucceeded(): Auth session is null");
+                        return;
+                    }
+
                     if (!requireConfirmation) {
                         mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
                         KeyStore.getInstance().addAuthToken(token);
@@ -398,6 +405,13 @@
             public void onAuthenticationFailed(int cookie, boolean requireConfirmation)
                     throws RemoteException {
                 try {
+                    // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
+                    // after user dismissed/canceled dialog).
+                    if (mCurrentAuthSession == null) {
+                        Slog.e(TAG, "onAuthenticationFailed(): Auth session is null");
+                        return;
+                    }
+
                     mStatusBarService.onBiometricHelp(getContext().getResources().getString(
                             com.android.internal.R.string.biometric_not_recognized));
                     if (requireConfirmation) {
@@ -486,6 +500,13 @@
 
             @Override
             public void onAcquired(int acquiredInfo, String message) throws RemoteException {
+                // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
+                // after user dismissed/canceled dialog).
+                if (mCurrentAuthSession == null) {
+                    Slog.e(TAG, "onAcquired(): Auth session is null");
+                    return;
+                }
+
                 if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
                     try {
                         mStatusBarService.onBiometricHelp(message);
@@ -653,7 +674,7 @@
             }
 
             mHandler.post(() -> {
-                final Pair<Integer, Integer> result = checkAndGetBiometricModality(callingUserId);
+                final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId);
                 final int modality = result.first;
                 final int error = result.second;
 
@@ -950,7 +971,7 @@
      * {@link BiometricAuthenticator#TYPE_FACE}
      * and the error containing one of the {@link BiometricConstants} errors.
      */
-    private Pair<Integer, Integer> checkAndGetBiometricModality(int callingUid) {
+    private Pair<Integer, Integer> checkAndGetBiometricModality(int userId) {
         int modality = TYPE_NONE;
 
         // No biometric features, send error
@@ -979,7 +1000,7 @@
                     // order.
                     firstHwAvailable = modality;
                 }
-                if (authenticator.hasEnrolledTemplates(callingUid)) {
+                if (authenticator.hasEnrolledTemplates(userId)) {
                     hasTemplatesEnrolled = true;
                     if (isEnabledForApp(modality)) {
                         // TODO(b/110907543): When face settings (and other settings) have both a
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 422f556..e40949b 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -217,6 +217,19 @@
     @Override
     // Called concurrently by multiple binder threads.
     // This method must not block or perform long-running operations.
+    public synchronized void onNat64PrefixEvent(int netId,
+            boolean added, String prefixString, int prefixLength)
+            throws RemoteException {
+        for (INetdEventCallback callback : mNetdEventCallbackList) {
+            if (callback != null) {
+                callback.onNat64PrefixEvent(netId, added, prefixString, prefixLength);
+            }
+        }
+    }
+
+    @Override
+    // Called concurrently by multiple binder threads.
+    // This method must not block or perform long-running operations.
     public synchronized void onPrivateDnsValidationEvent(int netId,
             String ipAddress, String hostname, boolean validated)
             throws RemoteException {
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index bf95210..2a00025 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -126,21 +126,21 @@
     private static final int DEFAULT_DATA_STALL_EVALUATION_TYPES =
             (1 << DATA_STALL_EVALUATION_TYPE_DNS);
 
-    static enum EvaluationResult {
+    enum EvaluationResult {
         VALIDATED(true),
         CAPTIVE_PORTAL(false);
-        final boolean isValidated;
+        final boolean mIsValidated;
         EvaluationResult(boolean isValidated) {
-            this.isValidated = isValidated;
+            this.mIsValidated = isValidated;
         }
     }
 
-    static enum ValidationStage {
+    enum ValidationStage {
         FIRST_VALIDATION(true),
         REVALIDATION(false);
-        final boolean isFirstValidation;
+        final boolean mIsFirstValidation;
         ValidationStage(boolean isFirstValidation) {
-            this.isFirstValidation = isFirstValidation;
+            this.mIsFirstValidation = isFirstValidation;
         }
     }
 
@@ -251,7 +251,7 @@
 
     // Start mReevaluateDelayMs at this value and double.
     private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
-    private static final int MAX_REEVALUATE_DELAY_MS = 10*60*1000;
+    private static final int MAX_REEVALUATE_DELAY_MS = 10 * 60 * 1000;
     // Before network has been evaluated this many times, ignore repeated reevaluate requests.
     private static final int IGNORE_REEVALUATE_ATTEMPTS = 5;
     private int mReevaluateToken = 0;
@@ -261,7 +261,7 @@
     // Stop blaming UID that requested re-evaluation after this many attempts.
     private static final int BLAME_FOR_EVALUATION_ATTEMPTS = 5;
     // Delay between reevaluations once a captive portal has been found.
-    private static final int CAPTIVE_PORTAL_REEVALUATE_DELAY_MS = 10*60*1000;
+    private static final int CAPTIVE_PORTAL_REEVALUATE_DELAY_MS = 10 * 60 * 1000;
 
     private static final int NUM_VALIDATION_LOG_LINES = 20;
 
@@ -393,10 +393,17 @@
         start();
     }
 
+    /**
+     * Request the NetworkMonitor to reevaluate the network.
+     */
     public void forceReevaluation(int responsibleUid) {
         sendMessage(CMD_FORCE_REEVALUATION, responsibleUid, 0);
     }
 
+    /**
+     * Send a notification to NetworkMonitor indicating that private DNS settings have changed.
+     * @param newCfg The new private DNS configuration.
+     */
     public void notifyPrivateDnsSettingsChanged(PrivateDnsConfig newCfg) {
         // Cancel any outstanding resolutions.
         removeMessages(CMD_PRIVATE_DNS_SETTINGS_CHANGED);
@@ -655,8 +662,9 @@
         public boolean processMessage(Message message) {
             switch (message.what) {
                 case CMD_REEVALUATE:
-                    if (message.arg1 != mReevaluateToken || mUserDoesNotWant)
+                    if (message.arg1 != mReevaluateToken || mUserDoesNotWant) {
                         return HANDLED;
+                    }
                     // Don't bother validating networks that don't satisfy the default request.
                     // This includes:
                     //  - VPNs which can be considered explicitly desired by the user and the
@@ -813,9 +821,9 @@
         }
 
         private boolean isStrictModeHostnameResolved() {
-            return (mPrivateDnsConfig != null) &&
-                   mPrivateDnsConfig.hostname.equals(mPrivateDnsProviderHostname) &&
-                   (mPrivateDnsConfig.ips.length > 0);
+            return (mPrivateDnsConfig != null)
+                    && mPrivateDnsConfig.hostname.equals(mPrivateDnsProviderHostname)
+                    && (mPrivateDnsConfig.ips.length > 0);
         }
 
         private void resolveStrictModeHostname() {
@@ -852,9 +860,9 @@
 
         private boolean sendPrivateDnsProbe() {
             // q.v. system/netd/server/dns/DnsTlsTransport.cpp
-            final String ONE_TIME_HOSTNAME_SUFFIX = "-dnsotls-ds.metric.gstatic.com";
-            final String host = UUID.randomUUID().toString().substring(0, 8) +
-                    ONE_TIME_HOSTNAME_SUFFIX;
+            final String oneTimeHostnameSuffix = "-dnsotls-ds.metric.gstatic.com";
+            final String host = UUID.randomUUID().toString().substring(0, 8)
+                    + oneTimeHostnameSuffix;
             final Stopwatch watch = new Stopwatch().start();
             try {
                 final InetAddress[] ips = mNetworkAgentInfo.network().getAllByName(host);
@@ -944,6 +952,10 @@
     private class WaitingForNextProbeState extends State {
         @Override
         public void enter() {
+            scheduleNextProbe();
+        }
+
+        private void scheduleNextProbe() {
             final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
             sendMessageDelayed(msg, mReevaluateDelayMs);
             mReevaluateDelayMs *= 2;
@@ -962,7 +974,7 @@
     // most one per address family. This ensures we only wait up to 20 seconds for TCP connections
     // to complete, regardless of how many IP addresses a host has.
     private static class OneAddressPerFamilyNetwork extends Network {
-        public OneAddressPerFamilyNetwork(Network network) {
+        OneAddressPerFamilyNetwork(Network network) {
             // Always bypass Private DNS.
             super(network.getPrivateDnsBypassingCopy());
         }
@@ -996,7 +1008,8 @@
     }
 
     public boolean getWifiScansAlwaysAvailableDisabled() {
-        return mDependencies.getSetting(mContext, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 0;
+        return mDependencies.getSetting(
+                mContext, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 0;
     }
 
     private String getCaptivePortalServerHttpsUrl() {
@@ -1242,10 +1255,10 @@
             // Time how long it takes to get a response to our request
             long responseTimestamp = SystemClock.elapsedRealtime();
 
-            validationLog(probeType, url, "time=" + (responseTimestamp - requestTimestamp) + "ms" +
-                    " ret=" + httpResponseCode +
-                    " request=" + requestHeader +
-                    " headers=" + urlConnection.getHeaderFields());
+            validationLog(probeType, url, "time=" + (responseTimestamp - requestTimestamp) + "ms"
+                    + " ret=" + httpResponseCode
+                    + " request=" + requestHeader
+                    + " headers=" + urlConnection.getHeaderFields());
             // NOTE: We may want to consider an "HTTP/1.0 204" response to be a captive
             // portal.  The only example of this seen so far was a captive portal.  For
             // the time being go with prior behavior of assuming it's not a captive
@@ -1263,7 +1276,7 @@
                     // sign-in to an empty page. Probably the result of a broken transparent proxy.
                     // See http://b/9972012.
                     validationLog(probeType, url,
-                        "200 response with Content-length=0 interpreted as 204 response.");
+                            "200 response with Content-length=0 interpreted as 204 response.");
                     httpResponseCode = CaptivePortalProbeResult.SUCCESS_CODE;
                 } else if (urlConnection.getContentLengthLong() == -1) {
                     // When no Content-length (default value == -1), attempt to read a byte from the
@@ -1305,7 +1318,7 @@
             private final boolean mIsHttps;
             private volatile CaptivePortalProbeResult mResult = CaptivePortalProbeResult.FAILED;
 
-            public ProbeThread(boolean isHttps) {
+            ProbeThread(boolean isHttps) {
                 mIsHttps = isHttps;
             }
 
@@ -1439,8 +1452,10 @@
                     if (cellInfo.isRegistered()) {
                         numRegisteredCellInfo++;
                         if (numRegisteredCellInfo > 1) {
-                            if (VDBG) logw("more than one registered CellInfo." +
-                                    " Can't tell which is active.  Bailing.");
+                            if (VDBG) {
+                                logw("more than one registered CellInfo."
+                                        + " Can't tell which is active.  Bailing.");
+                            }
                             return;
                         }
                         if (cellInfo instanceof CellInfoCdma) {
@@ -1488,14 +1503,14 @@
     }
 
     private int networkEventType(ValidationStage s, EvaluationResult r) {
-        if (s.isFirstValidation) {
-            if (r.isValidated) {
+        if (s.mIsFirstValidation) {
+            if (r.mIsValidated) {
                 return NetworkEvent.NETWORK_FIRST_VALIDATION_SUCCESS;
             } else {
                 return NetworkEvent.NETWORK_FIRST_VALIDATION_PORTAL_FOUND;
             }
         } else {
-            if (r.isValidated) {
+            if (r.mIsValidated) {
                 return NetworkEvent.NETWORK_REVALIDATION_SUCCESS;
             } else {
                 return NetworkEvent.NETWORK_REVALIDATION_PORTAL_FOUND;
@@ -1513,7 +1528,7 @@
 
     private void logValidationProbe(long durationMs, int probeType, int probeResult) {
         int[] transports = mNetworkAgentInfo.networkCapabilities.getTransportTypes();
-        boolean isFirstValidation = validationStage().isFirstValidation;
+        boolean isFirstValidation = validationStage().mIsFirstValidation;
         ValidationProbeEvent ev = new ValidationProbeEvent();
         ev.probeType = ValidationProbeEvent.makeProbeType(probeType, isFirstValidation);
         ev.returnCode = probeResult;
@@ -1531,10 +1546,20 @@
             return new Random();
         }
 
+        /**
+         * Get the value of a global integer setting.
+         * @param symbol Name of the setting
+         * @param defaultValue Value to return if the setting is not defined.
+         */
         public int getSetting(Context context, String symbol, int defaultValue) {
             return Settings.Global.getInt(context.getContentResolver(), symbol, defaultValue);
         }
 
+        /**
+         * Get the value of a global String setting.
+         * @param symbol Name of the setting
+         * @param defaultValue Value to return if the setting is not defined.
+         */
         public String getSetting(Context context, String symbol, String defaultValue) {
             final String value = Settings.Global.getString(context.getContentResolver(), symbol);
             return value != null ? value : defaultValue;
diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java
index c370959..3ea9810 100644
--- a/services/core/java/com/android/server/connectivity/PacManager.java
+++ b/services/core/java/com/android/server/connectivity/PacManager.java
@@ -43,8 +43,6 @@
 import com.android.net.IProxyPortListener;
 import com.android.net.IProxyService;
 
-import libcore.io.Streams;
-
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.net.URL;
@@ -71,6 +69,11 @@
     private static final int DELAY_LONG = 4;
     private static final long MAX_PAC_SIZE = 20 * 1000 * 1000;
 
+    // Return values for #setCurrentProxyScriptUrl
+    enum ToSendOrNotToSendBroadcast {
+        DONT_SEND_BROADCAST, DO_SEND_BROADCAST
+    }
+
     private String mCurrentPac;
     @GuardedBy("mProxyLock")
     private volatile Uri mPacUrl = Uri.EMPTY;
@@ -171,13 +174,13 @@
      * PacManager will trigger a new broadcast when it is ready.
      *
      * @param proxy Proxy information that is about to be broadcast.
-     * @return Returns true when the broadcast should not be sent
+     * @return Returns whether the broadcast should be sent : either DO_ or DONT_SEND_BROADCAST
      */
-    synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
+    synchronized ToSendOrNotToSendBroadcast setCurrentProxyScriptUrl(ProxyInfo proxy) {
         if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
             if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
                 // Allow to send broadcast, nothing to do.
-                return false;
+                return ToSendOrNotToSendBroadcast.DO_SEND_BROADCAST;
             }
             mPacUrl = proxy.getPacFileUrl();
             mCurrentDelay = DELAY_1;
@@ -185,7 +188,7 @@
             mHasDownloaded = false;
             getAlarmManager().cancel(mPacRefreshIntent);
             bind();
-            return true;
+            return ToSendOrNotToSendBroadcast.DONT_SEND_BROADCAST;
         } else {
             getAlarmManager().cancel(mPacRefreshIntent);
             synchronized (mProxyLock) {
@@ -201,7 +204,7 @@
                     }
                 }
             }
-            return false;
+            return ToSendOrNotToSendBroadcast.DO_SEND_BROADCAST;
         }
     }
 
@@ -296,7 +299,7 @@
         Intent intent = new Intent();
         intent.setClassName(PAC_PACKAGE, PAC_SERVICE);
         if ((mProxyConnection != null) && (mConnection != null)) {
-            // Already bound no need to bind again, just download the new file.
+            // Already bound: no need to bind again, just download the new file.
             mNetThreadHandler.post(mPacDownloader);
             return;
         }
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index 15468ff..fdddccd 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -208,7 +208,10 @@
     public void sendProxyBroadcast() {
         final ProxyInfo defaultProxy = getDefaultProxy();
         final ProxyInfo proxyInfo = null != defaultProxy ? defaultProxy : new ProxyInfo("", 0, "");
-        if (mPacManager.setCurrentProxyScriptUrl(proxyInfo)) return;
+        if (mPacManager.setCurrentProxyScriptUrl(proxyInfo)
+                == PacManager.ToSendOrNotToSendBroadcast.DONT_SEND_BROADCAST) {
+            return;
+        }
         if (DBG) Slog.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
         Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index d56b167..7daf71d 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -34,32 +34,53 @@
  * @hide
  */
 public class TetheringDependencies {
+    /**
+     * Get a reference to the offload hardware interface to be used by tethering.
+     */
     public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
         return new OffloadHardwareInterface(h, log);
     }
 
+    /**
+     * Get a reference to the UpstreamNetworkMonitor to be used by tethering.
+     */
     public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target,
             SharedLog log, int what) {
         return new UpstreamNetworkMonitor(ctx, target, log, what);
     }
 
+    /**
+     * Get a reference to the IPv6TetheringCoordinator to be used by tethering.
+     */
     public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
             ArrayList<IpServer> notifyList, SharedLog log) {
         return new IPv6TetheringCoordinator(notifyList, log);
     }
 
+    /**
+     * Get dependencies to be used by IpServer.
+     */
     public IpServer.Dependencies getIpServerDependencies() {
         return new IpServer.Dependencies();
     }
 
+    /**
+     * Indicates whether tethering is supported on the device.
+     */
     public boolean isTetheringSupported() {
         return true;
     }
 
+    /**
+     * Get the NetworkRequest that should be fulfilled by the default network.
+     */
     public NetworkRequest getDefaultNetworkRequest() {
         return null;
     }
 
+    /**
+     * Get a reference to the EntitlementManager to be used by tethering.
+     */
     public EntitlementManager getEntitlementManager(Context ctx, SharedLog log,
             MockableSystemProperties systemProperties) {
         return new EntitlementManager(ctx, log, systemProperties);
diff --git a/services/core/java/com/android/server/deviceidle/BluetoothConstraint.java b/services/core/java/com/android/server/deviceidle/BluetoothConstraint.java
new file mode 100644
index 0000000..cc319bf
--- /dev/null
+++ b/services/core/java/com/android/server/deviceidle/BluetoothConstraint.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.deviceidle;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Message;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.DeviceIdleController;
+
+/**
+ * Track whether there are any active Bluetooth devices connected.
+ */
+public class BluetoothConstraint implements IDeviceIdleConstraint {
+    private static final String TAG = BluetoothConstraint.class.getSimpleName();
+    private static final long INACTIVITY_TIMEOUT_MS = 20 * 60 * 1000L;
+
+    private final Context mContext;
+    private final Handler mHandler;
+    private final DeviceIdleController.LocalService mLocalService;
+    private final BluetoothManager mBluetoothManager;
+
+    private volatile boolean mConnected = true;
+    private volatile boolean mMonitoring = false;
+
+    public BluetoothConstraint(
+            Context context, Handler handler, DeviceIdleController.LocalService localService) {
+        mContext = context;
+        mHandler = handler;
+        mLocalService = localService;
+        mBluetoothManager = mContext.getSystemService(BluetoothManager.class);
+    }
+
+    @Override
+    public synchronized void startMonitoring() {
+        // Start by assuming we have a connected bluetooth device.
+        mConnected = true;
+        mMonitoring = true;
+
+        // Register a receiver to get updates on bluetooth devices disconnecting or the
+        // adapter state changing.
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+        filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
+        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+        mContext.registerReceiver(mReceiver, filter);
+
+        // Some devices will try to stay connected indefinitely. Set a timeout to ignore them.
+        mHandler.sendMessageDelayed(
+                Message.obtain(mHandler, mTimeoutCallback), INACTIVITY_TIMEOUT_MS);
+
+        // Now we have the receiver registered, make a direct check for connected devices.
+        updateAndReportActiveLocked();
+    }
+
+    @Override
+    public synchronized void stopMonitoring() {
+        mContext.unregisterReceiver(mReceiver);
+        mHandler.removeCallbacks(mTimeoutCallback);
+        mMonitoring = false;
+    }
+
+    private synchronized void cancelMonitoringDueToTimeout() {
+        if (mMonitoring) {
+            mMonitoring = false;
+            mLocalService.onConstraintStateChanged(this, /* active= */ false);
+        }
+    }
+
+    /**
+     * Check the latest data from BluetoothManager and let DeviceIdleController know whether we
+     * have connected devices (for example TV remotes / gamepads) and thus want to stay awake.
+     */
+    @GuardedBy("this")
+    private void updateAndReportActiveLocked() {
+        final boolean connected = isBluetoothConnected(mBluetoothManager);
+        if (connected != mConnected) {
+            mConnected = connected;
+            // If we lost all of our connections, we are on track to going into idle state.
+            mLocalService.onConstraintStateChanged(this, /* active= */ mConnected);
+        }
+    }
+
+    /**
+     * True if the bluetooth adapter exists, is enabled, and has at least one GATT device connected.
+     */
+    @VisibleForTesting
+    static boolean isBluetoothConnected(BluetoothManager bluetoothManager) {
+        BluetoothAdapter adapter = bluetoothManager.getAdapter();
+        if (adapter != null && adapter.isEnabled()) {
+            return bluetoothManager.getConnectedDevices(BluetoothProfile.GATT).size() > 0;
+        }
+        return false;
+    }
+
+    /**
+     * Registered in {@link #startMonitoring()}, unregistered in {@link #stopMonitoring()}.
+     */
+    @VisibleForTesting
+    final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(intent.getAction())) {
+                mLocalService.exitIdle("bluetooth");
+            } else {
+                updateAndReportActiveLocked();
+            }
+        }
+    };
+
+    private final Runnable mTimeoutCallback = () -> cancelMonitoringDueToTimeout();
+}
diff --git a/services/core/java/com/android/server/deviceidle/ConstraintController.java b/services/core/java/com/android/server/deviceidle/ConstraintController.java
new file mode 100644
index 0000000..6d52f71
--- /dev/null
+++ b/services/core/java/com/android/server/deviceidle/ConstraintController.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.deviceidle;
+
+/**
+ * Device idle constraints for a specific form factor or use-case.
+ */
+public interface ConstraintController {
+    /**
+     * Begin any general continuing work and register all constraints.
+     */
+    void start();
+
+    /**
+     * Unregister all constraints and stop any general work.
+     */
+    void stop();
+}
diff --git a/services/core/java/com/android/server/deviceidle/DeviceIdleConstraintTracker.java b/services/core/java/com/android/server/deviceidle/DeviceIdleConstraintTracker.java
new file mode 100644
index 0000000..4d5760e
--- /dev/null
+++ b/services/core/java/com/android/server/deviceidle/DeviceIdleConstraintTracker.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.deviceidle;
+
+/**
+ * Current state of an {@link IDeviceIdleConstraint}.
+ *
+ * If the current doze state is between leastActive and mostActive, then startMonitoring() will
+ * be the most recent call. Otherwise, stopMonitoring() is the most recent call.
+ */
+public class DeviceIdleConstraintTracker {
+
+    /**
+     * Appears in "dumpsys deviceidle".
+     */
+    public final String name;
+
+    /**
+     * Whenever a constraint is active, it will keep the device at or above
+     * minState (provided the rule is currently in effect).
+     *
+     */
+    public final int minState;
+
+    /**
+     * Whether this constraint currently prevents going below {@link #minState}.
+     *
+     * When the state is set to exactly minState, active is automatically
+     * overwritten with {@code true}.
+     */
+    public boolean active = false;
+
+    /**
+     * Internal tracking for whether the {@link IDeviceIdleConstraint} on the other
+     * side has been told it needs to send updates.
+     */
+    public boolean monitoring = false;
+
+    public DeviceIdleConstraintTracker(final String name, int minState) {
+        this.name = name;
+        this.minState = minState;
+    }
+}
diff --git a/services/core/java/com/android/server/deviceidle/IDeviceIdleConstraint.java b/services/core/java/com/android/server/deviceidle/IDeviceIdleConstraint.java
new file mode 100644
index 0000000..f1f95730
--- /dev/null
+++ b/services/core/java/com/android/server/deviceidle/IDeviceIdleConstraint.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.deviceidle;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Implemented by OEM and/or Form Factor. System ones are built into the
+ * image regardless of build flavour but may still be switched off at run time.
+ * Individual feature flags at build time control which are used. We may
+ * also explore a local override for quick testing.
+ */
+public interface IDeviceIdleConstraint {
+
+    /**
+     * A state for this constraint to block descent from.
+     *
+     * <p>These states are a subset of the states in DeviceIdleController that make sense for
+     * constraints to be able to block on. For example, {@link #SENSING_OR_ABOVE} clearly has
+     * defined "above" and "below" states. However, a hypothetical {@code QUICK_DOZE_OR_ABOVE}
+     * state would not have clear semantics as to what transitions should be blocked and which
+     * should be allowed.
+     */
+    @IntDef(flag = false, value = {
+            ACTIVE,
+            SENSING_OR_ABOVE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface MinimumState {}
+
+    int ACTIVE = 0;
+    int SENSING_OR_ABOVE = 1;
+
+    /**
+     * Begin tracking events for this constraint.
+     *
+     * <p>The device idle controller has reached a point where it is waiting for the all-clear
+     * from this tracker (possibly among others) in order to continue with progression into
+     * idle state. It will not proceed until one of the following happens:
+     * <ul>
+     *   <li>The constraint reports inactive with {@code .setActive(false)}.</li>
+     *   <li>The constraint is unregistered with {@code .unregisterDeviceIdleConstraint(this)}.</li>
+     *   <li>A transition timeout in DeviceIdleController fires.
+     * </ul>
+     */
+    void startMonitoring();
+
+    /** Stop checking for new events and do not call into LocalService with updates any more. */
+    void stopMonitoring();
+}
diff --git a/services/core/java/com/android/server/deviceidle/TvConstraintController.java b/services/core/java/com/android/server/deviceidle/TvConstraintController.java
new file mode 100644
index 0000000..2d472de6
--- /dev/null
+++ b/services/core/java/com/android/server/deviceidle/TvConstraintController.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.deviceidle;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+
+import com.android.server.DeviceIdleController;
+import com.android.server.LocalServices;
+
+/**
+ * Device idle constraints for television devices.
+ *
+ * <p>Televisions are devices with {@code FEATURE_LEANBACK_ONLY}. Other devices might support
+ * some kind of leanback mode but they should not follow the same rules for idle state.
+ */
+public class TvConstraintController implements ConstraintController {
+    private final Context mContext;
+    private final Handler mHandler;
+    private final DeviceIdleController.LocalService mDeviceIdleService;
+
+    @Nullable
+    private final BluetoothConstraint mBluetoothConstraint;
+
+    public TvConstraintController(Context context, Handler handler) {
+        mContext = context;
+        mHandler = handler;
+        mDeviceIdleService = LocalServices.getService(DeviceIdleController.LocalService.class);
+
+        final PackageManager pm = context.getPackageManager();
+        mBluetoothConstraint = pm.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
+                ? new BluetoothConstraint(mContext, mHandler, mDeviceIdleService)
+                : null;
+    }
+
+    @Override
+    public void start() {
+        if (mBluetoothConstraint != null) {
+            mDeviceIdleService.registerDeviceIdleConstraint(
+                    mBluetoothConstraint, "bluetooth", IDeviceIdleConstraint.SENSING_OR_ABOVE);
+        }
+    }
+
+    @Override
+    public void stop() {
+        if (mBluetoothConstraint != null) {
+            mDeviceIdleService.unregisterDeviceIdleConstraint(mBluetoothConstraint);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index b97e904..78b3c15 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -16,30 +16,26 @@
 
 package com.android.server.display;
 
-import com.android.server.EventLogTags;
-import com.android.server.LocalServices;
-
 import android.annotation.Nullable;
-import android.app.ActivityManager;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.hardware.display.BrightnessConfiguration;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
-import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.os.Trace;
-import android.text.format.DateUtils;
 import android.util.EventLog;
 import android.util.MathUtils;
 import android.util.Slog;
 import android.util.TimeUtils;
 
+import com.android.server.EventLogTags;
+
 import java.io.PrintWriter;
 
 class AutomaticBrightnessController {
@@ -127,7 +123,8 @@
     private final int mWeightingIntercept;
 
     // Configuration object for determining thresholds to change brightness dynamically
-    private final HysteresisLevels mHysteresisLevels;
+    private final HysteresisLevels mAmbientBrightnessThresholds;
+    private final HysteresisLevels mScreenBrightnessThresholds;
 
     // Amount of time to delay auto-brightness after screen on while waiting for
     // the light sensor to warm-up in milliseconds.
@@ -147,8 +144,12 @@
     private boolean mAmbientLuxValid;
 
     // The ambient light level threshold at which to brighten or darken the screen.
-    private float mBrighteningLuxThreshold;
-    private float mDarkeningLuxThreshold;
+    private float mAmbientBrighteningThreshold;
+    private float mAmbientDarkeningThreshold;
+
+    // The screen brightness threshold at which to brighten or darken the screen.
+    private float mScreenBrighteningThreshold;
+    private float mScreenDarkeningThreshold;
 
     // The most recent light sample.
     private float mLastObservedLux;
@@ -196,7 +197,8 @@
             int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor,
             int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig,
             long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig,
-            HysteresisLevels hysteresisLevels) {
+            HysteresisLevels ambientBrightnessThresholds,
+            HysteresisLevels screenBrightnessThresholds) {
         mCallbacks = callbacks;
         mSensorManager = sensorManager;
         mBrightnessMapper = mapper;
@@ -212,7 +214,8 @@
         mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig;
         mAmbientLightHorizon = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
         mWeightingIntercept = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
-        mHysteresisLevels = hysteresisLevels;
+        mAmbientBrightnessThresholds = ambientBrightnessThresholds;
+        mScreenBrightnessThresholds = screenBrightnessThresholds;
         mShortTermModelValid = true;
         mShortTermModelAnchor = -1;
 
@@ -364,8 +367,10 @@
         pw.println("  mCurrentLightSensorRate=" + mCurrentLightSensorRate);
         pw.println("  mAmbientLux=" + mAmbientLux);
         pw.println("  mAmbientLuxValid=" + mAmbientLuxValid);
-        pw.println("  mBrighteningLuxThreshold=" + mBrighteningLuxThreshold);
-        pw.println("  mDarkeningLuxThreshold=" + mDarkeningLuxThreshold);
+        pw.println("  mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold);
+        pw.println("  mAmbientDarkeningThreshold=" + mAmbientDarkeningThreshold);
+        pw.println("  mScreenBrighteningThreshold=" + mScreenBrighteningThreshold);
+        pw.println("  mScreenDarkeningThreshold=" + mScreenDarkeningThreshold);
         pw.println("  mLastObservedLux=" + mLastObservedLux);
         pw.println("  mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime));
         pw.println("  mRecentLightSamples=" + mRecentLightSamples);
@@ -384,7 +389,8 @@
         mBrightnessMapper.dump(pw);
 
         pw.println();
-        mHysteresisLevels.dump(pw);
+        mAmbientBrightnessThresholds.dump(pw);
+        mScreenBrightnessThresholds.dump(pw);
     }
 
     private boolean setLightSensorEnabled(boolean enable) {
@@ -460,8 +466,8 @@
             lux = 0;
         }
         mAmbientLux = lux;
-        mBrighteningLuxThreshold = mHysteresisLevels.getBrighteningThreshold(lux);
-        mDarkeningLuxThreshold = mHysteresisLevels.getDarkeningThreshold(lux);
+        mAmbientBrighteningThreshold = mAmbientBrightnessThresholds.getBrighteningThreshold(lux);
+        mAmbientDarkeningThreshold = mAmbientBrightnessThresholds.getDarkeningThreshold(lux);
 
         // If the short term model was invalidated and the change is drastic enough, reset it.
         if (!mShortTermModelValid && mShortTermModelAnchor != -1) {
@@ -552,7 +558,7 @@
         final int N = mAmbientLightRingBuffer.size();
         long earliestValidTime = time;
         for (int i = N - 1; i >= 0; i--) {
-            if (mAmbientLightRingBuffer.getLux(i) <= mBrighteningLuxThreshold) {
+            if (mAmbientLightRingBuffer.getLux(i) <= mAmbientBrighteningThreshold) {
                 break;
             }
             earliestValidTime = mAmbientLightRingBuffer.getTime(i);
@@ -564,7 +570,7 @@
         final int N = mAmbientLightRingBuffer.size();
         long earliestValidTime = time;
         for (int i = N - 1; i >= 0; i--) {
-            if (mAmbientLightRingBuffer.getLux(i) >= mDarkeningLuxThreshold) {
+            if (mAmbientLightRingBuffer.getLux(i) >= mAmbientDarkeningThreshold) {
                 break;
             }
             earliestValidTime = mAmbientLightRingBuffer.getTime(i);
@@ -617,20 +623,19 @@
         float slowAmbientLux = calculateAmbientLux(time, AMBIENT_LIGHT_LONG_HORIZON_MILLIS);
         float fastAmbientLux = calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS);
 
-        if ((slowAmbientLux >= mBrighteningLuxThreshold &&
-             fastAmbientLux >= mBrighteningLuxThreshold &&
-             nextBrightenTransition <= time)
-             ||
-            (slowAmbientLux <= mDarkeningLuxThreshold &&
-             fastAmbientLux <= mDarkeningLuxThreshold &&
-             nextDarkenTransition <= time)) {
+        if ((slowAmbientLux >= mAmbientBrighteningThreshold
+                && fastAmbientLux >= mAmbientBrighteningThreshold
+                && nextBrightenTransition <= time)
+                || (slowAmbientLux <= mAmbientDarkeningThreshold
+                        && fastAmbientLux <= mAmbientDarkeningThreshold
+                        && nextDarkenTransition <= time)) {
             setAmbientLux(fastAmbientLux);
             if (DEBUG) {
-                Slog.d(TAG, "updateAmbientLux: " +
-                        ((fastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": " +
-                        "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold + ", " +
-                        "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", " +
-                        "mAmbientLux=" + mAmbientLux);
+                Slog.d(TAG, "updateAmbientLux: "
+                        + ((fastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
+                        + "mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold + ", "
+                        + "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", "
+                        + "mAmbientLux=" + mAmbientLux);
             }
             updateAutoBrightness(true);
             nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
@@ -661,7 +666,22 @@
 
         int newScreenAutoBrightness =
                 clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
+
+        // If screenAutoBrightness is set, we should have screen{Brightening,Darkening}Threshold,
+        // in which case we ignore the new screen brightness if it doesn't differ enough from the
+        // previous one.
+        if (mScreenAutoBrightness != -1
+                && newScreenAutoBrightness > mScreenDarkeningThreshold
+                && newScreenAutoBrightness < mScreenBrighteningThreshold) {
+            if (DEBUG) {
+                Slog.d(TAG, "ignoring newScreenAutoBrightness: " + mScreenDarkeningThreshold
+                        + " < " + newScreenAutoBrightness + " < " + mScreenBrighteningThreshold);
+            }
+            return;
+        }
+
         if (mScreenAutoBrightness != newScreenAutoBrightness) {
+
             if (DEBUG) {
                 Slog.d(TAG, "updateAutoBrightness: " +
                         "mScreenAutoBrightness=" + mScreenAutoBrightness + ", " +
@@ -669,6 +689,11 @@
             }
 
             mScreenAutoBrightness = newScreenAutoBrightness;
+            mScreenBrighteningThreshold =
+                    mScreenBrightnessThresholds.getBrighteningThreshold(newScreenAutoBrightness);
+            mScreenDarkeningThreshold =
+                    mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness);
+
             if (sendUpdate) {
                 mCallbacks.updateBrightness();
             }
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index 521fa23..73d3d95 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.display;
 
+import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
 import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
 
 import android.animation.Animator;
@@ -31,6 +32,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.database.ContentObserver;
+import android.hardware.display.ColorDisplayManager;
 import android.hardware.display.IColorDisplayManager;
 import android.net.Uri;
 import android.opengl.Matrix;
@@ -39,26 +41,31 @@
 import android.os.Looper;
 import android.os.UserHandle;
 import android.provider.Settings.Secure;
+import android.provider.Settings.System;
 import android.util.MathUtils;
 import android.util.Slog;
 import android.view.animation.AnimationUtils;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ColorDisplayController;
+import com.android.server.DisplayThread;
 import com.android.server.SystemService;
 import com.android.server.twilight.TwilightListener;
 import com.android.server.twilight.TwilightManager;
 import com.android.server.twilight.TwilightState;
 
+import java.time.DateTimeException;
+import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
 import java.time.ZoneId;
+import java.time.format.DateTimeParseException;
 
 /**
  * Controls the display's color transforms.
  */
-public final class ColorDisplayService extends SystemService
-        implements ColorDisplayController.Callback {
+public final class ColorDisplayService extends SystemService {
 
     private static final String TAG = "ColorDisplayService";
 
@@ -71,6 +78,7 @@
      * The identity matrix, used if one of the given matrices is {@code null}.
      */
     private static final float[] MATRIX_IDENTITY = new float[16];
+
     static {
         Matrix.setIdentityM(MATRIX_IDENTITY, 0);
     }
@@ -80,20 +88,93 @@
      */
     private static final ColorMatrixEvaluator COLOR_MATRIX_EVALUATOR = new ColorMatrixEvaluator();
 
+    private final TintController mNightDisplayTintController = new TintController() {
+
+        private float[] mMatrixNightDisplay = new float[16];
+        private final float[] mColorTempCoefficients = new float[9];
+
+        /**
+         * Set coefficients based on whether the color matrix is linear or not.
+         */
+        @Override
+        public void setUp(Context context, boolean needsLinear) {
+            final String[] coefficients = context.getResources().getStringArray(needsLinear
+                    ? R.array.config_nightDisplayColorTemperatureCoefficients
+                    : R.array.config_nightDisplayColorTemperatureCoefficientsNative);
+            for (int i = 0; i < 9 && i < coefficients.length; i++) {
+                mColorTempCoefficients[i] = Float.parseFloat(coefficients[i]);
+            }
+        }
+
+        @Override
+        public void setMatrix(int cct) {
+            if (mMatrixNightDisplay.length != 16) {
+                Slog.d(TAG, "The display transformation matrix must be 4x4");
+                return;
+            }
+
+            Matrix.setIdentityM(mMatrixNightDisplay, 0);
+
+            final float squareTemperature = cct * cct;
+            final float red = squareTemperature * mColorTempCoefficients[0]
+                    + cct * mColorTempCoefficients[1] + mColorTempCoefficients[2];
+            final float green = squareTemperature * mColorTempCoefficients[3]
+                    + cct * mColorTempCoefficients[4] + mColorTempCoefficients[5];
+            final float blue = squareTemperature * mColorTempCoefficients[6]
+                    + cct * mColorTempCoefficients[7] + mColorTempCoefficients[8];
+            mMatrixNightDisplay[0] = red;
+            mMatrixNightDisplay[5] = green;
+            mMatrixNightDisplay[10] = blue;
+        }
+
+        @Override
+        public float[] getMatrix() {
+            return isActivated() ? mMatrixNightDisplay : MATRIX_IDENTITY;
+        }
+
+        @Override
+        public int getLevel() {
+            return LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
+        }
+    };
+
+    private final TintController mDisplayWhiteBalanceTintController = new TintController() {
+
+        private float[] mMatrixDisplayWhiteBalance = new float[16];
+
+        @Override
+        public void setUp(Context context, boolean needsLinear) {
+        }
+
+        @Override
+        public float[] getMatrix() {
+            return isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY;
+        }
+
+        @Override
+        public void setMatrix(int cct) {
+        }
+
+        @Override
+        public int getLevel() {
+            return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
+        }
+    };
+
     private final Handler mHandler;
 
-    private float[] mMatrixNight = new float[16];
-
-    private final float[] mColorTempCoefficients = new float[9];
-
     private int mCurrentUser = UserHandle.USER_NULL;
     private ContentObserver mUserSetupObserver;
     private boolean mBootCompleted;
 
-    private ColorDisplayController mController;
-    private ValueAnimator mColorMatrixAnimator;
-    private Boolean mIsActivated;
-    private AutoMode mAutoMode;
+    private ColorDisplayController mNightDisplayController;
+    private ContentObserver mContentObserver;
+
+    private DisplayWhiteBalanceListener mDisplayWhiteBalanceListener;
+
+    private NightDisplayAutoMode mNightDisplayAutoMode;
+
+    private Integer mDisplayWhiteBalanceColorTemperature;
 
     public ColorDisplayService(Context context) {
         super(context);
@@ -103,6 +184,7 @@
     @Override
     public void onStart() {
         publishBinderService(Context.COLOR_DISPLAY_SERVICE, new BinderService());
+        publishLocalService(ColorDisplayServiceInternal.class, new ColorDisplayServiceInternal());
     }
 
     @Override
@@ -186,176 +268,238 @@
     private void setUp() {
         Slog.d(TAG, "setUp: currentUser=" + mCurrentUser);
 
-        // Create a new controller for the current user and start listening for changes.
-        mController = new ColorDisplayController(getContext(), mCurrentUser);
-        mController.setListener(this);
+        mNightDisplayController = new ColorDisplayController(getContext(), mCurrentUser);
+
+        // Listen for external changes to any of the settings.
+        if (mContentObserver == null) {
+            mContentObserver = new ContentObserver(new Handler(DisplayThread.get().getLooper())) {
+                @Override
+                public void onChange(boolean selfChange, Uri uri) {
+                    super.onChange(selfChange, uri);
+
+                    final String setting = uri == null ? null : uri.getLastPathSegment();
+                    if (setting != null) {
+                        switch (setting) {
+                            case Secure.NIGHT_DISPLAY_ACTIVATED:
+                                onNightDisplayActivated(mNightDisplayController.isActivated());
+                                break;
+                            case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE:
+                                onNightDisplayColorTemperatureChanged(
+                                        mNightDisplayController.getColorTemperature());
+                                break;
+                            case Secure.NIGHT_DISPLAY_AUTO_MODE:
+                                onNightDisplayAutoModeChanged(
+                                        mNightDisplayController.getAutoMode());
+                                break;
+                            case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME:
+                                onNightDisplayCustomStartTimeChanged(
+                                        mNightDisplayController.getCustomStartTime());
+                                break;
+                            case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
+                                onNightDisplayCustomEndTimeChanged(
+                                        mNightDisplayController.getCustomEndTime());
+                                break;
+                            case System.DISPLAY_COLOR_MODE:
+                                onDisplayColorModeChanged(mNightDisplayController.getColorMode());
+                                break;
+                            case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
+                            case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED:
+                                onAccessibilityTransformChanged();
+                                break;
+                            case Secure.DISPLAY_WHITE_BALANCE_ENABLED:
+                                onDisplayWhiteBalanceEnabled(isDisplayWhiteBalanceSettingEnabled());
+                                break;
+                        }
+                    }
+                }
+            };
+        }
+        final ContentResolver cr = getContext().getContentResolver();
+        cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED),
+                false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+        cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE),
+                false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+        cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE),
+                false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+        cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME),
+                false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+        cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME),
+                false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+        cr.registerContentObserver(System.getUriFor(System.DISPLAY_COLOR_MODE),
+                false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+        cr.registerContentObserver(
+                Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED),
+                false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+        cr.registerContentObserver(
+                Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
+                false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+        cr.registerContentObserver(Secure.getUriFor(Secure.DISPLAY_WHITE_BALANCE_ENABLED),
+                false /* notifyForDescendants */, mContentObserver, mCurrentUser);
 
         // Set the color mode, if valid, and immediately apply the updated tint matrix based on the
         // existing activated state. This ensures consistency of tint across the color mode change.
-        onDisplayColorModeChanged(mController.getColorMode());
+        onDisplayColorModeChanged(mNightDisplayController.getColorMode());
 
-        // Reset the activated state.
-        mIsActivated = null;
+        if (ColorDisplayManager.isNightDisplayAvailable(getContext())) {
+            // Reset the activated state.
+            mNightDisplayTintController.setActivated(null);
 
-        setCoefficientMatrix(getContext(), DisplayTransformManager.needsLinearColorMatrix());
+            // Prepare the night display color transformation matrix.
+            mNightDisplayTintController
+                    .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
+            mNightDisplayTintController.setMatrix(mNightDisplayController.getColorTemperature());
 
-        // Prepare color transformation matrix.
-        setMatrix(mController.getColorTemperature(), mMatrixNight);
+            // Initialize the current auto mode.
+            onNightDisplayAutoModeChanged(mNightDisplayController.getAutoMode());
 
-        // Initialize the current auto mode.
-        onAutoModeChanged(mController.getAutoMode());
+            // Force the initialization current activated state.
+            if (mNightDisplayTintController.isActivatedStateNotSet()) {
+                onNightDisplayActivated(mNightDisplayController.isActivated());
+            }
+        }
 
-        // Force the initialization current activated state.
-        if (mIsActivated == null) {
-            onActivated(mController.isActivated());
+        if (ColorDisplayManager.isDisplayWhiteBalanceAvailable(getContext())) {
+            // Prepare the display white balance transform matrix.
+            mDisplayWhiteBalanceTintController
+                    .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
+            if (mDisplayWhiteBalanceColorTemperature != null) {
+                mDisplayWhiteBalanceTintController
+                        .setMatrix(mDisplayWhiteBalanceColorTemperature);
+            }
+
+            onDisplayWhiteBalanceEnabled(isDisplayWhiteBalanceSettingEnabled());
         }
     }
 
     private void tearDown() {
         Slog.d(TAG, "tearDown: currentUser=" + mCurrentUser);
 
-        if (mController != null) {
-            mController.setListener(null);
-            mController = null;
+        getContext().getContentResolver().unregisterContentObserver(mContentObserver);
+
+        if (mNightDisplayController != null) {
+            mNightDisplayController = null;
         }
 
-        if (mAutoMode != null) {
-            mAutoMode.onStop();
-            mAutoMode = null;
+        if (ColorDisplayManager.isNightDisplayAvailable(getContext())) {
+            if (mNightDisplayAutoMode != null) {
+                mNightDisplayAutoMode.onStop();
+                mNightDisplayAutoMode = null;
+            }
+            mNightDisplayTintController.endAnimator();
         }
 
-        if (mColorMatrixAnimator != null) {
-            mColorMatrixAnimator.end();
-            mColorMatrixAnimator = null;
+        if (ColorDisplayManager.isDisplayWhiteBalanceAvailable(getContext())) {
+            mDisplayWhiteBalanceTintController.endAnimator();
         }
     }
 
-    @Override
-    public void onActivated(boolean activated) {
-        if (mIsActivated == null || mIsActivated != activated) {
+    private void onNightDisplayActivated(boolean activated) {
+        if (mNightDisplayTintController.isActivatedStateNotSet()
+                || mNightDisplayTintController.isActivated() != activated) {
             Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
 
-            mIsActivated = activated;
+            mNightDisplayTintController.setActivated(activated);
 
-            if (mAutoMode != null) {
-                mAutoMode.onActivated(activated);
+            if (mNightDisplayAutoMode != null) {
+                mNightDisplayAutoMode.onActivated(activated);
             }
 
-            applyTint(false);
+            applyTint(mNightDisplayTintController, false);
         }
     }
 
-    @Override
-    public void onAutoModeChanged(int autoMode) {
-        Slog.d(TAG, "onAutoModeChanged: autoMode=" + autoMode);
+    private void onNightDisplayAutoModeChanged(int autoMode) {
+        Slog.d(TAG, "onNightDisplayAutoModeChanged: autoMode=" + autoMode);
 
-        if (mAutoMode != null) {
-            mAutoMode.onStop();
-            mAutoMode = null;
+        if (mNightDisplayAutoMode != null) {
+            mNightDisplayAutoMode.onStop();
+            mNightDisplayAutoMode = null;
         }
 
         if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM) {
-            mAutoMode = new CustomAutoMode();
+            mNightDisplayAutoMode = new CustomNightDisplayAutoMode();
         } else if (autoMode == ColorDisplayController.AUTO_MODE_TWILIGHT) {
-            mAutoMode = new TwilightAutoMode();
+            mNightDisplayAutoMode = new TwilightNightDisplayAutoMode();
         }
 
-        if (mAutoMode != null) {
-            mAutoMode.onStart();
+        if (mNightDisplayAutoMode != null) {
+            mNightDisplayAutoMode.onStart();
         }
     }
 
-    @Override
-    public void onCustomStartTimeChanged(LocalTime startTime) {
-        Slog.d(TAG, "onCustomStartTimeChanged: startTime=" + startTime);
+    private void onNightDisplayCustomStartTimeChanged(LocalTime startTime) {
+        Slog.d(TAG, "onNightDisplayCustomStartTimeChanged: startTime=" + startTime);
 
-        if (mAutoMode != null) {
-            mAutoMode.onCustomStartTimeChanged(startTime);
+        if (mNightDisplayAutoMode != null) {
+            mNightDisplayAutoMode.onCustomStartTimeChanged(startTime);
         }
     }
 
-    @Override
-    public void onCustomEndTimeChanged(LocalTime endTime) {
-        Slog.d(TAG, "onCustomEndTimeChanged: endTime=" + endTime);
+    private void onNightDisplayCustomEndTimeChanged(LocalTime endTime) {
+        Slog.d(TAG, "onNightDisplayCustomEndTimeChanged: endTime=" + endTime);
 
-        if (mAutoMode != null) {
-            mAutoMode.onCustomEndTimeChanged(endTime);
+        if (mNightDisplayAutoMode != null) {
+            mNightDisplayAutoMode.onCustomEndTimeChanged(endTime);
         }
     }
 
-    @Override
-    public void onColorTemperatureChanged(int colorTemperature) {
-        setMatrix(colorTemperature, mMatrixNight);
-        applyTint(true);
+    private void onNightDisplayColorTemperatureChanged(int colorTemperature) {
+        mNightDisplayTintController.setMatrix(colorTemperature);
+        applyTint(mNightDisplayTintController, true);
     }
 
-    @Override
-    public void onDisplayColorModeChanged(int mode) {
+    private void onDisplayColorModeChanged(int mode) {
         if (mode == -1) {
             return;
         }
 
-        // Cancel the night display tint animator if it's running.
-        if (mColorMatrixAnimator != null) {
-            mColorMatrixAnimator.cancel();
-        }
+        mNightDisplayTintController.cancelAnimator();
+        mDisplayWhiteBalanceTintController.cancelAnimator();
 
-        setCoefficientMatrix(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
-        setMatrix(mController.getColorTemperature(), mMatrixNight);
+        mNightDisplayTintController
+                .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
+        mNightDisplayTintController.setMatrix(mNightDisplayController.getColorTemperature());
+
+        mDisplayWhiteBalanceTintController
+                .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
+        if (mDisplayWhiteBalanceColorTemperature != null) {
+            mDisplayWhiteBalanceTintController.setMatrix(mDisplayWhiteBalanceColorTemperature);
+        }
 
         final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
-        dtm.setColorMode(mode, (mIsActivated != null && mIsActivated) ? mMatrixNight
-                : MATRIX_IDENTITY);
+        dtm.setColorMode(mode, mNightDisplayTintController.getMatrix());
     }
 
-    @Override
-    public void onAccessibilityTransformChanged(boolean state) {
-        onDisplayColorModeChanged(mController.getColorMode());
+    private void onAccessibilityTransformChanged() {
+        onDisplayColorModeChanged(mNightDisplayController.getColorMode());
     }
 
-    /**
-     * Set coefficients based on whether the color matrix is linear or not.
-     */
-    private void setCoefficientMatrix(Context context, boolean needsLinear) {
-        final String[] coefficients = context.getResources().getStringArray(needsLinear
-                ? R.array.config_nightDisplayColorTemperatureCoefficients
-                : R.array.config_nightDisplayColorTemperatureCoefficientsNative);
-        for (int i = 0; i < 9 && i < coefficients.length; i++) {
-            mColorTempCoefficients[i] = Float.parseFloat(coefficients[i]);
-        }
-    }
 
     /**
      * Applies current color temperature matrix, or removes it if deactivated.
      *
      * @param immediate {@code true} skips transition animation
      */
-    private void applyTint(boolean immediate) {
-        // Cancel the old animator if still running.
-        if (mColorMatrixAnimator != null) {
-            mColorMatrixAnimator.cancel();
-        }
+    private void applyTint(TintController tintController, boolean immediate) {
+        tintController.cancelAnimator();
 
         final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
-        final float[] from = dtm.getColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY);
-        final float[] to = mIsActivated ? mMatrixNight : MATRIX_IDENTITY;
+        final float[] from = dtm.getColorMatrix(tintController.getLevel());
+        final float[] to = tintController.getMatrix();
 
         if (immediate) {
-            dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, to);
+            dtm.setColorMatrix(tintController.getLevel(), to);
         } else {
-            mColorMatrixAnimator = ValueAnimator.ofObject(COLOR_MATRIX_EVALUATOR,
-                    from == null ? MATRIX_IDENTITY : from, to);
-            mColorMatrixAnimator.setDuration(TRANSITION_DURATION);
-            mColorMatrixAnimator.setInterpolator(AnimationUtils.loadInterpolator(
+            tintController.setAnimator(ValueAnimator.ofObject(COLOR_MATRIX_EVALUATOR,
+                    from == null ? MATRIX_IDENTITY : from, to));
+            tintController.getAnimator().setDuration(TRANSITION_DURATION);
+            tintController.getAnimator().setInterpolator(AnimationUtils.loadInterpolator(
                     getContext(), android.R.interpolator.fast_out_slow_in));
-            mColorMatrixAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animator) {
-                    final float[] value = (float[]) animator.getAnimatedValue();
-                    dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, value);
-                }
+            tintController.getAnimator().addUpdateListener((ValueAnimator animator) -> {
+                final float[] value = (float[]) animator.getAnimatedValue();
+                dtm.setColorMatrix(tintController.getLevel(), value);
             });
-            mColorMatrixAnimator.addListener(new AnimatorListenerAdapter() {
+            tintController.getAnimator().addListener(new AnimatorListenerAdapter() {
 
                 private boolean mIsCancelled;
 
@@ -370,49 +514,24 @@
                         // Ensure final color matrix is set at the end of the animation. If the
                         // animation is cancelled then don't set the final color matrix so the new
                         // animator can pick up from where this one left off.
-                        dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, to);
+                        dtm.setColorMatrix(tintController.getLevel(), to);
                     }
-                    mColorMatrixAnimator = null;
+                    tintController.setAnimator(null);
                 }
             });
-            mColorMatrixAnimator.start();
+            tintController.getAnimator().start();
         }
     }
 
     /**
-     * Set the color transformation {@code MATRIX_NIGHT} to the given color temperature.
-     *
-     * @param colorTemperature color temperature in Kelvin
-     * @param outTemp          the 4x4 display transformation matrix for that color temperature
-     */
-    private void setMatrix(int colorTemperature, float[] outTemp) {
-        if (outTemp.length != 16) {
-            Slog.d(TAG, "The display transformation matrix must be 4x4");
-            return;
-        }
-
-        Matrix.setIdentityM(mMatrixNight, 0);
-
-        final float squareTemperature = colorTemperature * colorTemperature;
-        final float red = squareTemperature * mColorTempCoefficients[0]
-                + colorTemperature * mColorTempCoefficients[1] + mColorTempCoefficients[2];
-        final float green = squareTemperature * mColorTempCoefficients[3]
-                + colorTemperature * mColorTempCoefficients[4] + mColorTempCoefficients[5];
-        final float blue = squareTemperature * mColorTempCoefficients[6]
-                + colorTemperature * mColorTempCoefficients[7] + mColorTempCoefficients[8];
-        outTemp[0] = red;
-        outTemp[5] = green;
-        outTemp[10] = blue;
-    }
-
-    /**
      * Returns the first date time corresponding to the local time that occurs before the provided
      * date time.
      *
      * @param compareTime the LocalDateTime to compare against
      * @return the prior LocalDateTime corresponding to this local time
      */
-    public static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
+    @VisibleForTesting
+    static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
         final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
                 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
 
@@ -427,7 +546,8 @@
      * @param compareTime the LocalDateTime to compare against
      * @return the next LocalDateTime corresponding to this local time
      */
-    public static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
+    @VisibleForTesting
+    static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
         final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
                 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
 
@@ -435,18 +555,64 @@
         return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
     }
 
+    private void onDisplayWhiteBalanceEnabled(boolean enabled) {
+        mDisplayWhiteBalanceTintController.setActivated(enabled);
+        if (mDisplayWhiteBalanceListener != null) {
+            mDisplayWhiteBalanceListener.onDisplayWhiteBalanceStatusChanged(enabled);
+        }
+    }
+
+    private boolean isDisplayWhiteBalanceSettingEnabled() {
+        return Secure.getIntForUser(getContext().getContentResolver(),
+                Secure.DISPLAY_WHITE_BALANCE_ENABLED, 0, mCurrentUser) == 1;
+    }
+
     private boolean isDeviceColorManagedInternal() {
         final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
         return dtm.isDeviceColorManaged();
     }
 
-    private abstract class AutoMode implements ColorDisplayController.Callback {
+    /**
+     * Returns the last time the night display transform activation state was changed, or {@link
+     * LocalDateTime#MIN} if night display has never been activated.
+     */
+    private LocalDateTime getNightDisplayLastActivatedTimeSetting() {
+        final ContentResolver cr = getContext().getContentResolver();
+        final String lastActivatedTime = Secure.getStringForUser(
+                cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, getContext().getUserId());
+        if (lastActivatedTime != null) {
+            try {
+                return LocalDateTime.parse(lastActivatedTime);
+            } catch (DateTimeParseException ignored) {
+            }
+            // Uses the old epoch time.
+            try {
+                return LocalDateTime.ofInstant(
+                        Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
+                        ZoneId.systemDefault());
+            } catch (DateTimeException | NumberFormatException ignored) {
+            }
+        }
+        return LocalDateTime.MIN;
+    }
+
+    private abstract class NightDisplayAutoMode {
+
+        public abstract void onActivated(boolean activated);
+
         public abstract void onStart();
 
         public abstract void onStop();
+
+        public void onCustomStartTimeChanged(LocalTime startTime) {
+        }
+
+        public void onCustomEndTimeChanged(LocalTime endTime) {
+        }
     }
 
-    private class CustomAutoMode extends AutoMode implements AlarmManager.OnAlarmListener {
+    private final class CustomNightDisplayAutoMode extends NightDisplayAutoMode implements
+            AlarmManager.OnAlarmListener {
 
         private final AlarmManager mAlarmManager;
         private final BroadcastReceiver mTimeChangedReceiver;
@@ -456,7 +622,7 @@
 
         private LocalDateTime mLastActivatedTime;
 
-        CustomAutoMode() {
+        CustomNightDisplayAutoMode() {
             mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
             mTimeChangedReceiver = new BroadcastReceiver() {
                 @Override
@@ -476,15 +642,16 @@
                 // Maintain the existing activated state if within the current period.
                 if (mLastActivatedTime.isBefore(now) && mLastActivatedTime.isAfter(start)
                         && (mLastActivatedTime.isAfter(end) || now.isBefore(end))) {
-                    activate = mController.isActivated();
+                    activate = mNightDisplayController.isActivated();
                 }
             }
 
-            if (mIsActivated == null || mIsActivated != activate) {
-                mController.setActivated(activate);
+            if (mNightDisplayTintController.isActivatedStateNotSet() || (
+                    mNightDisplayTintController.isActivated() != activate)) {
+                mNightDisplayController.setActivated(activate);
             }
 
-            updateNextAlarm(mIsActivated, now);
+            updateNextAlarm(mNightDisplayTintController.isActivated(), now);
         }
 
         private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) {
@@ -502,10 +669,10 @@
             intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
             getContext().registerReceiver(mTimeChangedReceiver, intentFilter);
 
-            mStartTime = mController.getCustomStartTime();
-            mEndTime = mController.getCustomEndTime();
+            mStartTime = mNightDisplayController.getCustomStartTime();
+            mEndTime = mNightDisplayController.getCustomEndTime();
 
-            mLastActivatedTime = mController.getLastActivatedTime();
+            mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
 
             // Force an update to initialize state.
             updateActivated();
@@ -521,7 +688,7 @@
 
         @Override
         public void onActivated(boolean activated) {
-            mLastActivatedTime = mController.getLastActivatedTime();
+            mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
             updateNextAlarm(activated, LocalDateTime.now());
         }
 
@@ -546,11 +713,13 @@
         }
     }
 
-    private class TwilightAutoMode extends AutoMode implements TwilightListener {
+    private final class TwilightNightDisplayAutoMode extends NightDisplayAutoMode implements
+            TwilightListener {
 
         private final TwilightManager mTwilightManager;
+        private LocalDateTime mLastActivatedTime;
 
-        TwilightAutoMode() {
+        TwilightNightDisplayAutoMode() {
             mTwilightManager = getLocalService(TwilightManager.class);
         }
 
@@ -562,26 +731,32 @@
             }
 
             boolean activate = state.isNight();
-            final LocalDateTime lastActivatedTime = mController.getLastActivatedTime();
-            if (lastActivatedTime != null) {
+            if (mLastActivatedTime != null) {
                 final LocalDateTime now = LocalDateTime.now();
                 final LocalDateTime sunrise = state.sunrise();
                 final LocalDateTime sunset = state.sunset();
                 // Maintain the existing activated state if within the current period.
-                if (lastActivatedTime.isBefore(now) && (lastActivatedTime.isBefore(sunrise)
-                        ^ lastActivatedTime.isBefore(sunset))) {
-                    activate = mController.isActivated();
+                if (mLastActivatedTime.isBefore(now) && (mLastActivatedTime.isBefore(sunrise)
+                        ^ mLastActivatedTime.isBefore(sunset))) {
+                    activate = mNightDisplayController.isActivated();
                 }
             }
 
-            if (mIsActivated == null || mIsActivated != activate) {
-                mController.setActivated(activate);
+            if (mNightDisplayTintController.isActivatedStateNotSet() || (
+                    mNightDisplayTintController.isActivated() != activate)) {
+                mNightDisplayController.setActivated(activate);
             }
         }
 
         @Override
+        public void onActivated(boolean activated) {
+            mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
+        }
+
+        @Override
         public void onStart() {
             mTwilightManager.registerListener(this, mHandler);
+            mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
 
             // Force an update to initialize state.
             updateActivated(mTwilightManager.getLastTwilightState());
@@ -590,10 +765,7 @@
         @Override
         public void onStop() {
             mTwilightManager.unregisterListener(this);
-        }
-
-        @Override
-        public void onActivated(boolean activated) {
+            mLastActivatedTime = null;
         }
 
         @Override
@@ -623,7 +795,117 @@
         }
     }
 
+    private abstract static class TintController {
+
+        private ValueAnimator mAnimator;
+        private Boolean mIsActivated;
+
+        public ValueAnimator getAnimator() {
+            return mAnimator;
+        }
+
+        public void setAnimator(ValueAnimator animator) {
+            mAnimator = animator;
+        }
+
+        /**
+         * Cancel the animator if it's still running.
+         */
+        public void cancelAnimator() {
+            if (mAnimator != null) {
+                mAnimator.cancel();
+            }
+        }
+
+        /**
+         * End the animator if it's still running, jumping to the end state.
+         */
+        public void endAnimator() {
+            if (mAnimator != null) {
+                mAnimator.end();
+                mAnimator = null;
+            }
+        }
+
+        public void setActivated(Boolean isActivated) {
+            mIsActivated = isActivated;
+        }
+
+        public boolean isActivated() {
+            return mIsActivated != null && mIsActivated;
+        }
+
+        public boolean isActivatedStateNotSet() {
+            return mIsActivated == null;
+        }
+
+        /**
+         * Set up any constants needed for computing the matrix.
+         */
+        public abstract void setUp(Context context, boolean needsLinear);
+
+        /**
+         * Sets the 4x4 matrix to apply.
+         */
+        public abstract void setMatrix(int value);
+
+        /**
+         * Get the 4x4 matrix to apply.
+         */
+        public abstract float[] getMatrix();
+
+        /**
+         * Get the color transform level to apply the matrix.
+         */
+        public abstract int getLevel();
+    }
+
+    /**
+     * Local service that allows color transforms to be enabled from other system services.
+     */
+    public final class ColorDisplayServiceInternal {
+
+        /**
+         * Set the current CCT value for the display white balance transform, and if the transform
+         * is enabled, apply it.
+         *
+         * @param cct the color temperature in Kelvin.
+         */
+        public boolean setDisplayWhiteBalanceColorTemperature(int cct) {
+            // Update the transform matrix even if it can't be applied.
+            mDisplayWhiteBalanceColorTemperature = cct;
+            mDisplayWhiteBalanceTintController.setMatrix(cct);
+
+            if (mDisplayWhiteBalanceTintController.isActivated()) {
+                applyTint(mDisplayWhiteBalanceTintController, true);
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * Sets the listener and returns whether display white balance is currently enabled.
+         */
+        public boolean setDisplayWhiteBalanceListener(DisplayWhiteBalanceListener listener) {
+            mDisplayWhiteBalanceListener = listener;
+            return mDisplayWhiteBalanceTintController.isActivated();
+        }
+    }
+
+    /**
+     * Listener for changes in display white balance status.
+     */
+    public interface DisplayWhiteBalanceListener {
+
+        /**
+         * Notify that the display white balance status has changed, either due to preemption by
+         * another transform or the feature being turned off.
+         */
+        void onDisplayWhiteBalanceStatusChanged(boolean enabled);
+    }
+
     private final class BinderService extends IColorDisplayManager.Stub {
+
         @Override
         public boolean isDeviceColorManaged() {
             final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index cf8d21b..0a1a9a2 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1296,58 +1296,21 @@
             return;
         }
         display.configureDisplayLocked(t, device, info.state == Display.STATE_OFF);
-
+        final int viewportType;
         // Update the corresponding viewport.
-        DisplayViewport internalViewport = getInternalViewportLocked();
         if ((info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) {
-            populateViewportLocked(internalViewport, display, device);
-        }
-        DisplayViewport externalViewport = getExternalViewportLocked();
-        if (info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) {
-            populateViewportLocked(externalViewport, display, device);
-        } else if (!externalViewport.valid) {
-            // TODO (b/116850516) move this logic into InputReader
-            externalViewport.copyFrom(internalViewport);
-            externalViewport.type = DisplayViewport.VIEWPORT_EXTERNAL;
+            viewportType = VIEWPORT_INTERNAL;
+        } else if (info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) {
+            viewportType = VIEWPORT_EXTERNAL;
+        } else if (info.touch == DisplayDeviceInfo.TOUCH_VIRTUAL
+                && !TextUtils.isEmpty(info.uniqueId)) {
+            viewportType = VIEWPORT_VIRTUAL;
+        } else {
+            Slog.wtf(TAG, "Unable to populate viewport for display device: " + info);
+            return;
         }
 
-        if (info.touch == DisplayDeviceInfo.TOUCH_VIRTUAL && !TextUtils.isEmpty(info.uniqueId)) {
-            final DisplayViewport viewport = getVirtualViewportLocked(info.uniqueId);
-            populateViewportLocked(viewport, display, device);
-        }
-    }
-
-    /** Get the virtual device viewport that has the specified uniqueId.
-     * If such viewport does not exist, create it. */
-    private DisplayViewport getVirtualViewportLocked(@NonNull String uniqueId) {
-        DisplayViewport viewport;
-        final int count = mViewports.size();
-        for (int i = 0; i < count; i++) {
-            viewport = mViewports.get(i);
-            if (uniqueId.equals(viewport.uniqueId)) {
-                if (viewport.type != VIEWPORT_VIRTUAL) {
-                    Slog.wtf(TAG, "Found a viewport with uniqueId '"  + uniqueId
-                            + "' but it has type " + DisplayViewport.typeToString(viewport.type)
-                            + " (expected VIRTUAL)");
-                    continue;
-                }
-                return viewport;
-            }
-        }
-
-        viewport = new DisplayViewport();
-        viewport.uniqueId = uniqueId;
-        viewport.type = VIEWPORT_VIRTUAL;
-        mViewports.add(viewport);
-        return viewport;
-    }
-
-    private DisplayViewport getInternalViewportLocked() {
-        return getViewportByTypeLocked(VIEWPORT_INTERNAL);
-    }
-
-    private DisplayViewport getExternalViewportLocked() {
-        return getViewportByTypeLocked(VIEWPORT_EXTERNAL);
+        populateViewportLocked(viewportType, display.getDisplayIdLocked(), device, info.uniqueId);
     }
 
     /**
@@ -1355,35 +1318,44 @@
      * @param viewportType - either INTERNAL or EXTERNAL
      * @return the viewport with the requested type
      */
-    private DisplayViewport getViewportByTypeLocked(int viewportType) {
-        // Only allow a single INTERNAL or EXTERNAL viewport, which makes this function possible.
-        // TODO (b/116824030) allow multiple EXTERNAL viewports and remove this function.
-        // Creates the viewport if none exists.
-        if (viewportType != VIEWPORT_INTERNAL && viewportType != VIEWPORT_EXTERNAL) {
+    private DisplayViewport getViewportLocked(int viewportType, String uniqueId) {
+        if (viewportType != VIEWPORT_INTERNAL && viewportType != VIEWPORT_EXTERNAL
+                && viewportType != VIEWPORT_VIRTUAL) {
             Slog.wtf(TAG, "Cannot call getViewportByTypeLocked for type "
                     + DisplayViewport.typeToString(viewportType));
             return null;
         }
+
+        // Only allow a single INTERNAL or EXTERNAL viewport by forcing their uniqueIds
+        // to be identical (in particular, empty).
+        // TODO (b/116824030) allow multiple EXTERNAL viewports and remove this function.
+        if (viewportType != VIEWPORT_VIRTUAL) {
+            uniqueId = "";
+        }
+
         DisplayViewport viewport;
         final int count = mViewports.size();
         for (int i = 0; i < count; i++) {
             viewport = mViewports.get(i);
-            if (viewport.type == viewportType) {
+            if (viewport.type == viewportType && uniqueId.equals(viewport.uniqueId)) {
                 return viewport;
             }
         }
 
+        // Creates the viewport if none exists.
         viewport = new DisplayViewport();
         viewport.type = viewportType;
+        viewport.uniqueId = uniqueId;
         mViewports.add(viewport);
         return viewport;
     }
 
-    private static void populateViewportLocked(DisplayViewport viewport,
-            LogicalDisplay display, DisplayDevice device) {
-        viewport.valid = true;
-        viewport.displayId = display.getDisplayIdLocked();
+    private void populateViewportLocked(int viewportType,
+            int displayId, DisplayDevice device, String uniqueId) {
+        final DisplayViewport viewport = getViewportLocked(viewportType, uniqueId);
         device.populateViewportLocked(viewport);
+        viewport.valid = true;
+        viewport.displayId = displayId;
     }
 
     private LogicalDisplay findLogicalDisplayForDeviceLocked(DisplayDevice device) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index e2c8ef9..249270b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -16,16 +16,11 @@
 
 package com.android.server.display;
 
-import android.app.ActivityManager;
-import com.android.internal.app.IBatteryStats;
-import com.android.server.LocalServices;
-import com.android.server.am.BatteryStatsService;
-import com.android.server.policy.WindowManagerPolicy;
-
 import android.animation.Animator;
 import android.animation.ObjectAnimator;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.ActivityManager;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.content.res.Resources;
@@ -54,6 +49,11 @@
 import android.util.TimeUtils;
 import android.view.Display;
 
+import com.android.internal.app.IBatteryStats;
+import com.android.server.LocalServices;
+import com.android.server.am.BatteryStatsService;
+import com.android.server.policy.WindowManagerPolicy;
+
 import java.io.PrintWriter;
 
 /**
@@ -422,14 +422,24 @@
                     com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor,
                     1, 1);
 
-            int[] brightLevels = resources.getIntArray(
-                    com.android.internal.R.array.config_dynamicHysteresisBrightLevels);
-            int[] darkLevels = resources.getIntArray(
-                    com.android.internal.R.array.config_dynamicHysteresisDarkLevels);
-            int[] luxHysteresisLevels = resources.getIntArray(
-                    com.android.internal.R.array.config_dynamicHysteresisLuxLevels);
-            HysteresisLevels hysteresisLevels = new HysteresisLevels(
-                    brightLevels, darkLevels, luxHysteresisLevels);
+            int[] ambientBrighteningThresholds = resources.getIntArray(
+                    com.android.internal.R.array.config_ambientBrighteningThresholds);
+            int[] ambientDarkeningThresholds = resources.getIntArray(
+                    com.android.internal.R.array.config_ambientDarkeningThresholds);
+            int[] ambientThresholdLevels = resources.getIntArray(
+                    com.android.internal.R.array.config_ambientThresholdLevels);
+            HysteresisLevels ambientBrightnessThresholds = new HysteresisLevels(
+                    ambientBrighteningThresholds, ambientDarkeningThresholds,
+                    ambientThresholdLevels);
+
+            int[] screenBrighteningThresholds = resources.getIntArray(
+                    com.android.internal.R.array.config_screenBrighteningThresholds);
+            int[] screenDarkeningThresholds = resources.getIntArray(
+                    com.android.internal.R.array.config_screenDarkeningThresholds);
+            int[] screenThresholdLevels = resources.getIntArray(
+                    com.android.internal.R.array.config_screenThresholdLevels);
+            HysteresisLevels screenBrightnessThresholds = new HysteresisLevels(
+                    screenBrighteningThresholds, screenDarkeningThresholds, screenThresholdLevels);
 
             long brighteningLightDebounce = resources.getInteger(
                     com.android.internal.R.integer.config_autoBrightnessBrighteningLightDebounce);
@@ -459,7 +469,8 @@
                         lightSensorWarmUpTimeConfig, mScreenBrightnessRangeMinimum,
                         mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,
                         initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
-                        autoBrightnessResetAmbientLuxAfterWarmUp, hysteresisLevels);
+                        autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,
+                        screenBrightnessThresholds);
             } else {
                 mUseSoftwareAutoBrightnessConfig = false;
             }
diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java
index 4ad26da..a5e9728 100644
--- a/services/core/java/com/android/server/display/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/DisplayTransformManager.java
@@ -45,6 +45,10 @@
      */
     public static final int LEVEL_COLOR_MATRIX_NIGHT_DISPLAY = 100;
     /**
+     * Color transform level used by display white balance to adjust the display's white point.
+     */
+    public static final int LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE = 125;
+    /**
      * Color transform level used to adjust the color saturation of the display.
      */
     public static final int LEVEL_COLOR_MATRIX_SATURATION = 150;
diff --git a/services/core/java/com/android/server/display/HysteresisLevels.java b/services/core/java/com/android/server/display/HysteresisLevels.java
index 1c02dd1..2db1d03 100644
--- a/services/core/java/com/android/server/display/HysteresisLevels.java
+++ b/services/core/java/com/android/server/display/HysteresisLevels.java
@@ -28,67 +28,67 @@
     private static final String TAG = "HysteresisLevels";
 
     // Default hysteresis constraints for brightening or darkening.
-    // The recent lux must have changed by at least this fraction relative to the
-    // current ambient lux before a change will be considered.
+    // The recent value must have changed by at least this fraction relative to the
+    // current value before a change will be considered.
     private static final float DEFAULT_BRIGHTENING_HYSTERESIS = 0.10f;
     private static final float DEFAULT_DARKENING_HYSTERESIS = 0.20f;
 
     private static final boolean DEBUG = false;
 
-    private final float[] mBrightLevels;
-    private final float[] mDarkLevels;
-    private final float[] mLuxLevels;
+    private final float[] mBrighteningThresholds;
+    private final float[] mDarkeningThresholds;
+    private final float[] mThresholdLevels;
 
-  /**
-   * Creates a {@code HysteresisLevels} object with the given equal-length
-   * integer arrays.
-   * @param brightLevels an array of brightening hysteresis constraint constants
-   * @param darkLevels an array of darkening hysteresis constraint constants
-   * @param luxLevels a monotonically increasing array of illuminance
-   *                  thresholds in units of lux
-   */
-    public HysteresisLevels(int[] brightLevels, int[] darkLevels, int[] luxLevels) {
-        if (brightLevels.length != darkLevels.length || darkLevels.length != luxLevels.length + 1) {
+    /**
+     * Creates a {@code HysteresisLevels} object with the given equal-length
+     * integer arrays.
+     * @param brighteningThresholds an array of brightening hysteresis constraint constants.
+     * @param darkeningThresholds an array of darkening hysteresis constraint constants.
+     * @param thresholdLevels a monotonically increasing array of threshold levels.
+    */
+    HysteresisLevels(int[] brighteningThresholds, int[] darkeningThresholds,
+            int[] thresholdLevels) {
+        if (brighteningThresholds.length != darkeningThresholds.length
+                || darkeningThresholds.length != thresholdLevels.length + 1) {
             throw new IllegalArgumentException("Mismatch between hysteresis array lengths.");
         }
-        mBrightLevels = setArrayFormat(brightLevels, 1000.0f);
-        mDarkLevels = setArrayFormat(darkLevels, 1000.0f);
-        mLuxLevels = setArrayFormat(luxLevels, 1.0f);
+        mBrighteningThresholds = setArrayFormat(brighteningThresholds, 1000.0f);
+        mDarkeningThresholds = setArrayFormat(darkeningThresholds, 1000.0f);
+        mThresholdLevels = setArrayFormat(thresholdLevels, 1.0f);
     }
 
     /**
-     * Return the brightening hysteresis threshold for the given lux level.
+     * Return the brightening hysteresis threshold for the given value level.
      */
-    public float getBrighteningThreshold(float lux) {
-        float brightConstant = getReferenceLevel(lux, mBrightLevels);
-        float brightThreshold = lux * (1.0f + brightConstant);
+    float getBrighteningThreshold(float value) {
+        float brightConstant = getReferenceLevel(value, mBrighteningThresholds);
+        float brightThreshold = value * (1.0f + brightConstant);
         if (DEBUG) {
-            Slog.d(TAG, "bright hysteresis constant=: " + brightConstant + ", threshold="
-                + brightThreshold + ", lux=" + lux);
+            Slog.d(TAG, "bright hysteresis constant=" + brightConstant + ", threshold="
+                    + brightThreshold + ", value=" + value);
         }
         return brightThreshold;
     }
 
     /**
-     * Return the darkening hysteresis threshold for the given lux level.
+     * Return the darkening hysteresis threshold for the given value level.
      */
-    public float getDarkeningThreshold(float lux) {
-        float darkConstant = getReferenceLevel(lux, mDarkLevels);
-        float darkThreshold = lux * (1.0f - darkConstant);
+    float getDarkeningThreshold(float value) {
+        float darkConstant = getReferenceLevel(value, mDarkeningThresholds);
+        float darkThreshold = value * (1.0f - darkConstant);
         if (DEBUG) {
             Slog.d(TAG, "dark hysteresis constant=: " + darkConstant + ", threshold="
-                + darkThreshold + ", lux=" + lux);
+                    + darkThreshold + ", value=" + value);
         }
         return darkThreshold;
     }
 
     /**
-     * Return the hysteresis constant for the closest lux threshold value to the
-     * current illuminance from the given array.
+     * Return the hysteresis constant for the closest threshold value from the given array.
      */
-    private float getReferenceLevel(float lux, float[] referenceLevels) {
+    private float getReferenceLevel(float value, float[] referenceLevels) {
         int index = 0;
-        while (mLuxLevels.length > index && lux >= mLuxLevels[index]) {
+        while (mThresholdLevels.length > index && value >= mThresholdLevels[index]) {
             ++index;
         }
         return referenceLevels[index];
@@ -105,10 +105,10 @@
         return levelArray;
     }
 
-    public void dump(PrintWriter pw) {
+    void dump(PrintWriter pw) {
         pw.println("HysteresisLevels");
-        pw.println("  mBrightLevels=" + Arrays.toString(mBrightLevels));
-        pw.println("  mDarkLevels=" + Arrays.toString(mDarkLevels));
-        pw.println("  mLuxLevels=" + Arrays.toString(mLuxLevels));
+        pw.println("  mBrighteningThresholds=" + Arrays.toString(mBrighteningThresholds));
+        pw.println("  mDarkeningThresholds=" + Arrays.toString(mDarkeningThresholds));
+        pw.println("  mThresholdLevels=" + Arrays.toString(mThresholdLevels));
     }
 }
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index d154830..1f0f94a 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.hardware.hdmi.HdmiDeviceInfo;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -316,7 +317,7 @@
      * <p>When ARC is initiated, this port will be used to turn on ARC.
      */
     static final String PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT =
-        "persist.sys.hdmi.property_sytem_audio_device_arc_port";
+            "persist.sys.hdmi.property_sytem_audio_device_arc_port";
 
     /**
      * Property to strip local audio of amplifier and use local speaker
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 3845954..2ac04d1 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -25,6 +25,7 @@
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.os.SystemProperties;
+import android.provider.Settings.Global;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -50,6 +51,11 @@
 
     private boolean mTvSystemAudioModeSupport;
 
+    // Whether the auido system will turn TV off when it's powering off
+    private boolean mAutoTvOff;
+    // Whether the auido system will broadcast standby to the system when it's powering off
+    private boolean mAutoDeviceOff;
+
     // Whether ARC is available or not. "true" means that ARC is established between TV and
     // AVR as audio receiver.
     @ServiceThreadOnly private boolean mArcEstablished = false;
@@ -60,6 +66,10 @@
         // TODO(amyjojo) make System Audio Control controllable by users
         /*mSystemAudioControlFeatureEnabled =
         mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);*/
+        mAutoDeviceOff = mService.readBooleanSetting(
+            Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, true);
+        mAutoTvOff = mService.readBooleanSetting(
+            Global.HDMI_CONTROL_AUTO_TV_OFF_ENABLED, true);
     }
 
     @Override
@@ -74,6 +84,21 @@
                     mSystemAudioActivated ? "true" : "false");
         }
         terminateSystemAudioMode();
+
+        HdmiLogger.debug(TAG + " onStandby, initiatedByCec:" + initiatedByCec
+                + ", mAutoDeviceOff: " + mAutoDeviceOff + ", mAutoTvOff: " + mAutoTvOff);
+        if (!mService.isControlEnabled() || initiatedByCec) {
+            return;
+        }
+        if (mAutoDeviceOff) {
+            mService.sendCecCommand(
+                    HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_BROADCAST));
+        } else if (mAutoTvOff) {
+            mService.sendCecCommand(
+                    HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV));
+        }
+        return;
+
     }
 
     @Override
@@ -95,6 +120,11 @@
         startQueuedActions();
     }
 
+    @Override
+    protected int findKeyReceiverAddress() {
+        return Constants.ADDR_TV;
+    }
+
     @VisibleForTesting
     protected void systemAudioControlOnPowerOn(
             int systemAudioOnPowerOnProp, boolean lastSystemAudioControlStatus) {
@@ -486,4 +516,17 @@
             return mArcEstablished;
         }
     }
+
+    @ServiceThreadOnly
+    protected void setAutoTvOff(boolean autoTvOff) {
+        assertRunOnServiceThread();
+        mAutoTvOff = autoTvOff;
+    }
+
+    @Override
+    @ServiceThreadOnly
+    void setAutoDeviceOff(boolean autoDeviceOff) {
+        assertRunOnServiceThread();
+        mAutoDeviceOff = autoDeviceOff;
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index c0d3fdf..e3a4084 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -18,6 +18,7 @@
 
 import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE;
 import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE;
+
 import static com.android.server.hdmi.Constants.DISABLED;
 import static com.android.server.hdmi.Constants.ENABLED;
 import static com.android.server.hdmi.Constants.OPTION_MHL_ENABLE;
@@ -67,6 +68,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
@@ -76,14 +78,18 @@
 import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
 import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
 import com.android.server.hdmi.HdmiCecLocalDevice.PendingActionClearedCallback;
+
+import libcore.util.EmptyArray;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
-import libcore.util.EmptyArray;
+import java.util.Map;
 
 /**
  * Provides a service for sending and processing HDMI control messages,
@@ -94,6 +100,31 @@
     private final Locale HONG_KONG = new Locale("zh", "HK");
     private final Locale MACAU = new Locale("zh", "MO");
 
+    private static final Map<String, String> mTerminologyToBibliographicMap;
+    static {
+        mTerminologyToBibliographicMap = new HashMap<>();
+        // NOTE: (TERMINOLOGY_CODE, BIBLIOGRAPHIC_CODE)
+        mTerminologyToBibliographicMap.put("sqi", "alb"); // Albanian
+        mTerminologyToBibliographicMap.put("hye", "arm"); // Armenian
+        mTerminologyToBibliographicMap.put("eus", "baq"); // Basque
+        mTerminologyToBibliographicMap.put("mya", "bur"); // Burmese
+        mTerminologyToBibliographicMap.put("ces", "cze"); // Czech
+        mTerminologyToBibliographicMap.put("nld", "dut"); // Dutch
+        mTerminologyToBibliographicMap.put("kat", "geo"); // Georgian
+        mTerminologyToBibliographicMap.put("deu", "ger"); // German
+        mTerminologyToBibliographicMap.put("ell", "gre"); // Greek
+        mTerminologyToBibliographicMap.put("fra", "fre"); // French
+        mTerminologyToBibliographicMap.put("isl", "ice"); // Icelandic
+        mTerminologyToBibliographicMap.put("mkd", "mac"); // Macedonian
+        mTerminologyToBibliographicMap.put("mri", "mao"); // Maori
+        mTerminologyToBibliographicMap.put("msa", "may"); // Malay
+        mTerminologyToBibliographicMap.put("fas", "per"); // Persian
+        mTerminologyToBibliographicMap.put("ron", "rum"); // Romanian
+        mTerminologyToBibliographicMap.put("slk", "slo"); // Slovak
+        mTerminologyToBibliographicMap.put("bod", "tib"); // Tibetan
+        mTerminologyToBibliographicMap.put("cym", "wel"); // Welsh
+    }
+
     static final String PERMISSION = "android.permission.HDMI_CEC";
 
     // The reason code to initiate initializeCec().
@@ -177,7 +208,18 @@
                 // Chinese used in Taiwan/Hong Kong/Macau.
                 return "chi";
             } else {
-                return locale.getISO3Language();
+                String language = locale.getISO3Language();
+
+                // locale.getISO3Language() returns terminology code and need to
+                // send it as bibliographic code instead since the Bibliographic
+                // codes of ISO/FDIS 639-2 shall be used.
+                // NOTE: Chinese also has terminology/bibliographic code "zho" and "chi"
+                // But, as it depends on the locale, is not handled here.
+                if (mTerminologyToBibliographicMap.containsKey(language)) {
+                    language = mTerminologyToBibliographicMap.get(language);
+                }
+
+                return language;
             }
         }
     }
@@ -550,6 +592,11 @@
                     }
                     // No need to propagate to HAL.
                     break;
+                case Global.HDMI_CONTROL_AUTO_TV_OFF_ENABLED:
+                    if (isAudioSystemDevice()) {
+                        audioSystem().setAutoTvOff(enabled);
+                    }
+                    break;
                 case Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED:
                     if (isTvDeviceEnabled()) {
                         tv().setSystemAudioControlFeatureEnabled(enabled);
@@ -1431,12 +1478,12 @@
 
         @Override
         public boolean getSystemAudioMode() {
+            // TODO(shubang): handle getSystemAudioMode() for all device types
             enforceAccessPermission();
             HdmiCecLocalDeviceTv tv = tv();
-            if (tv == null) {
-                return false;
-            }
-            return tv.isSystemAudioActivated();
+            HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
+            return (tv != null && tv.isSystemAudioActivated())
+                    || (audioSystem != null && audioSystem.isSystemAudioActivated());
         }
 
         @Override
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index c0c4a6e..a3ebe24 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -38,6 +38,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.PackageMonitor;
+import com.android.internal.infra.AbstractRemoteService;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
@@ -86,13 +87,22 @@
     protected final Object mLock = new Object();
 
     /**
+     * Object used to define the name of the service component used to create
+     * {@link com.android.internal.infra.AbstractRemoteService} instances.
+     */
+    @Nullable
+    protected final ServiceNameResolver mServiceNameResolver;
+
+    /**
      * Whether the service should log debug statements.
      */
+    //TODO(b/117779333): consider using constants for these guards
     public boolean verbose = false;
 
     /**
      * Whether the service should log verbose statements.
      */
+    //TODO(b/117779333): consider using constants for these guards
     public boolean debug = false;
 
     /**
@@ -119,13 +129,24 @@
      * Default constructor.
      *
      * @param context system context.
+     * @param serviceNameResolver resolver for
+     * {@link com.android.internal.infra.AbstractRemoteService} instances, or
+     * {@code null} when the service doesn't bind to remote services.
      * @param disallowProperty when not {@code null}, defines a {@link UserManager} restriction that
      *        disables the service.
      */
     protected AbstractMasterSystemService(@NonNull Context context,
+            @Nullable ServiceNameResolver serviceNameResolver,
             @Nullable String disallowProperty) {
         super(context);
 
+        mServiceNameResolver = serviceNameResolver;
+        if (mServiceNameResolver != null) {
+            mServiceNameResolver
+                    .setOnTemporaryServiceNameChangedCallback(
+                            (u, s) -> updateCachedServiceLocked(u));
+
+        }
         if (disallowProperty == null) {
             mDisabledUsers = null;
         } else {
@@ -199,6 +220,20 @@
     }
 
     /**
+     * Checks whether the service is allowed to bind to an instant-app.
+     *
+     * <p>Typically called by subclasses when creating {@link AbstractRemoteService} instances.
+     *
+     * <p><b>NOTE: </b>must not be called by {@code ShellCommand} as it does not check for
+     * permission.
+     */
+    public final boolean isBindInstantServiceAllowed() {
+        synchronized (mLock) {
+            return mAllowInstantService;
+        }
+    }
+
+    /**
      * Sets whether the service is allowed to bind to an instant-app.
      *
      * <p>Typically called by {@code ShellCommand} during CTS tests.
@@ -294,6 +329,7 @@
      *
      * @return a new instance.
      */
+    @Nullable
     protected abstract S newServiceLocked(@UserIdInt int resolvedUserId, boolean disabled);
 
     /**
@@ -421,11 +457,11 @@
     /**
      * Removes a cached service for a given user.
      *
-     * @return the removed service;
+     * @return the removed service.
      */
     @GuardedBy("mLock")
     @NonNull
-    private S removeCachedServiceLocked(@UserIdInt int userId) {
+    protected final S removeCachedServiceLocked(@UserIdInt int userId) {
         final S service = peekServiceForUserLocked(userId);
         if (service != null) {
             mServicesCache.delete(userId);
@@ -471,6 +507,10 @@
             final int size = mServicesCache.size();
             pw.print(prefix); pw.print("Debug: "); pw.print(realDebug);
             pw.print(" Verbose: "); pw.println(realVerbose);
+            if (mServiceNameResolver != null) {
+                pw.print(prefix); pw.print("Name resolver: ");
+                mServiceNameResolver.dumpShort(pw); pw.println();
+            }
             pw.print(prefix); pw.print("Disabled users: "); pw.println(mDisabledUsers);
             pw.print(prefix); pw.print("Allow instant service: "); pw.println(mAllowInstantService);
             final String settingsProperty = getServiceSettingsProperty();
diff --git a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
index 28a8094..556e489 100644
--- a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
@@ -55,8 +55,6 @@
 
     protected final M mMaster;
 
-    private final ServiceNameResolver mServiceNameResolver;
-
     /**
      * Whether service was disabled for user due to {@link UserManager} restrictions.
      */
@@ -72,13 +70,9 @@
     @GuardedBy("mLock")
     private ServiceInfo mServiceInfo;
 
-    protected AbstractPerUserSystemService(@NonNull M master,
-            @NonNull ServiceNameResolver serviceNamer, @NonNull Object lock,
+    protected AbstractPerUserSystemService(@NonNull M master, @NonNull Object lock,
             @UserIdInt int userId) {
         mMaster = master;
-        mServiceNameResolver = serviceNamer;
-        mServiceNameResolver
-                .setOnTemporaryServiceNameChangedCallback(() -> updateLocked(mDisabled));
         mLock = lock;
         mUserId = userId;
     }
@@ -86,11 +80,21 @@
     /**
      * Creates a new {@link ServiceInfo} for the given service name.
      *
+     * <p><b>MUST</b> be overridden by subclasses that bind to an
+     * {@link com.android.internal.infra.AbstractRemoteService}.
+     *
      * @throws NameNotFoundException if the service does not exist.
      * @throws SecurityException if the service does not have the proper permissions to be bound to.
+     * @throws UnsupportedOperationException if subclass binds to a remote service but does not
+     * overrides it.
+     *
+     * @return new {@link ServiceInfo},
      */
-    protected abstract ServiceInfo newServiceInfo(@NonNull ComponentName serviceComponent)
-            throws NameNotFoundException;
+    protected @NonNull ServiceInfo newServiceInfoLocked(
+            @SuppressWarnings("unused") @NonNull ComponentName serviceComponent)
+            throws NameNotFoundException {
+        throw new UnsupportedOperationException("not overridden");
+    }
 
     /**
      * Callback called when an app has been updated.
@@ -134,42 +138,54 @@
 
         mSetupComplete = isSetupCompletedLocked();
         mDisabled = disabled;
-        ComponentName serviceComponent = null;
-        ServiceInfo serviceInfo = null;
-        final String componentName = getComponentNameLocked();
-        if (!TextUtils.isEmpty(componentName)) {
-            try {
-                serviceComponent = ComponentName.unflattenFromString(componentName);
-                serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
-                        0, mUserId);
-                if (serviceInfo == null) {
-                    Slog.e(mTag, "Bad service name: " + componentName);
-                }
-            } catch (RuntimeException | RemoteException e) {
-                Slog.e(mTag, "Error getting service info for '" + componentName + "': " + e);
-                serviceInfo = null;
-            }
-        }
-        try {
-            if (serviceInfo != null) {
-                mServiceInfo = newServiceInfo(serviceComponent);
-                if (mMaster.debug) {
-                    Slog.d(mTag, "Set component for user " + mUserId + " as " + mServiceInfo);
-                }
-            } else {
-                mServiceInfo = null;
-                if (mMaster.debug) {
-                    Slog.d(mTag, "Reset component for user " + mUserId + ":" + componentName);
-                }
-            }
-        } catch (Exception e) {
-            Slog.e(mTag, "Bad ServiceInfo for '" + componentName + "': " + e);
-            mServiceInfo = null;
-        }
+
+        updateServiceInfoLocked();
         return wasEnabled != isEnabledLocked();
     }
 
     /**
+     * Updates the internal reference to the service info, and returns the service's component.
+     */
+    protected final ComponentName updateServiceInfoLocked() {
+        ComponentName serviceComponent = null;
+        if (mMaster.mServiceNameResolver != null) {
+            ServiceInfo serviceInfo = null;
+            final String componentName = getComponentNameLocked();
+            if (!TextUtils.isEmpty(componentName)) {
+                try {
+                    serviceComponent = ComponentName.unflattenFromString(componentName);
+                    serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+                            0, mUserId);
+                    if (serviceInfo == null) {
+                        Slog.e(mTag, "Bad service name: " + componentName);
+                    }
+                } catch (RuntimeException | RemoteException e) {
+                    Slog.e(mTag, "Error getting service info for '" + componentName + "': " + e);
+                    serviceInfo = null;
+                }
+            }
+            try {
+                if (serviceInfo != null) {
+                    mServiceInfo = newServiceInfoLocked(serviceComponent);
+                    if (mMaster.debug) {
+                        Slog.d(mTag, "Set component for user " + mUserId + " as "
+                                + serviceComponent + " and info as " + mServiceInfo);
+                    }
+                } else {
+                    mServiceInfo = null;
+                    if (mMaster.debug) {
+                        Slog.d(mTag, "Reset component for user " + mUserId + ":" + componentName);
+                    }
+                }
+            } catch (Exception e) {
+                Slog.e(mTag, "Bad ServiceInfo for '" + componentName + "': " + e);
+                mServiceInfo = null;
+            }
+        }
+        return serviceComponent;
+    }
+
+    /**
      * Gets the user associated with this service.
      */
     public final @UserIdInt int getUserId() {
@@ -200,15 +216,15 @@
      * Gets the current name of the service, which is either the default service or the
      *  {@link #setTemporaryServiceLocked(String, int) temporary one}.
      */
-    protected final String getComponentNameLocked() {
-        return mServiceNameResolver.getServiceNameLocked();
+    protected final @Nullable String getComponentNameLocked() {
+        return mMaster.mServiceNameResolver.getServiceName(mUserId);
     }
 
     /**
      * Checks whether the current service for the user was temporarily set.
      */
     public final boolean isTemporaryServiceSetLocked() {
-        return mServiceNameResolver.isTemporaryLocked();
+        return mMaster.mServiceNameResolver.isTemporary(mUserId);
     }
 
     /**
@@ -219,14 +235,14 @@
      * to the default component after this timeout expires).
      */
     protected final void setTemporaryServiceLocked(@NonNull String componentName, int durationMs) {
-        mServiceNameResolver.setTemporaryServiceLocked(componentName, durationMs);
+        mMaster.mServiceNameResolver.setTemporaryService(mUserId, componentName, durationMs);
     }
 
     /**
      * Resets the temporary service implementation to the default component.
      */
     protected final void resetTemporaryServiceLocked() {
-        mServiceNameResolver.resetTemporaryServiceLocked();
+        mMaster.mServiceNameResolver.resetTemporaryService(mUserId);
     }
 
     /**
@@ -272,8 +288,16 @@
     }
 
     /**
+     * Removes the service from the master's cache.
+     */
+    protected final void removeSelfFromCacheLocked() {
+        mMaster.removeCachedServiceLocked(mUserId);
+    }
+
+    /**
      * Whether the service should log debug statements.
      */
+    //TODO(b/117779333): consider using constants for these guards
     public final boolean isDebug() {
         return mMaster.debug;
     }
@@ -281,6 +305,7 @@
     /**
      * Whether the service should log verbose statements.
      */
+    //TODO(b/117779333): consider using constants for these guards
     public final boolean isVerbose() {
         return mMaster.verbose;
     }
@@ -313,13 +338,16 @@
     @GuardedBy("mLock")
     protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
         pw.print(prefix); pw.print("User: "); pw.println(mUserId);
+        if (mMaster.mServiceNameResolver != null) {
+            pw.print(prefix); pw.print("Name resolver: ");
+            mMaster.mServiceNameResolver.dumpShort(pw, mUserId); pw.println();
+        }
         pw.print(prefix); pw.print("Disabled by UserManager: "); pw.println(mDisabled);
         pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
         if (mServiceInfo != null) {
             pw.print(prefix); pw.print("Service UID: ");
             pw.println(mServiceInfo.applicationInfo.uid);
         }
-        pw.print(prefix); pw.print("Name resolver: "); mServiceNameResolver.dumpShortLocked(pw);
         pw.println();
     }
 }
diff --git a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
index 27822d5..7027246 100644
--- a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
@@ -26,6 +26,7 @@
 import android.os.SystemClock;
 import android.text.TextUtils;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.TimeUtils;
 
 import com.android.internal.annotations.GuardedBy;
@@ -42,23 +43,22 @@
 
     private static final String TAG = FrameworkResourcesServiceNameResolver.class.getSimpleName();
 
-    /** Handler message to {@link #resetTemporaryServiceLocked()} */
+    /** Handler message to {@link #resetTemporaryService(int)} */
     private static final int MSG_RESET_TEMPORARY_SERVICE = 0;
 
     private final @NonNull Context mContext;
-    private final @NonNull @UserIdInt int mUserId;
-    private final @NonNull Object mLock;
+    private final @NonNull Object mLock = new Object();
     private final @StringRes int mResourceId;
-    private @Nullable Runnable mOnSetCallback;
+    private @Nullable NameResolverListener mOnSetCallback;
 
     /**
-     * Temporary service name set by {@link #setTemporaryServiceLocked(String, int)}.
+     * Map of temporary service name set by {@link #setTemporaryService(int, String, int)},
+     * keyed by {@code userId}.
      *
      * <p>Typically used by Shell command and/or CTS tests.
      */
     @GuardedBy("mLock")
-    @Nullable
-    private String mTemporaryServiceName;
+    private final SparseArray<String> mTemporaryServiceNames = new SparseArray<>();
 
     /**
      * When the temporary service will expire (and reset back to the default).
@@ -72,99 +72,123 @@
     @GuardedBy("mLock")
     private Handler mTemporaryHandler;
 
-    public FrameworkResourcesServiceNameResolver(@NonNull Context context, @UserIdInt int userId,
-            @NonNull Object lock, @StringRes int resourceId) {
-        mLock = lock;
+    public FrameworkResourcesServiceNameResolver(@NonNull Context context,
+            @StringRes int resourceId) {
         mContext = context;
-        mUserId = userId;
         mResourceId = resourceId;
     }
 
     @Override
-    public void setOnTemporaryServiceNameChangedCallback(@NonNull Runnable callback) {
-        this.mOnSetCallback = callback;
-    }
-
-    @Override
-    public String getDefaultServiceName() {
-        final String name = mContext.getString(mResourceId);
-        return TextUtils.isEmpty(name) ? null : name;
-    }
-
-    @Override
-    public String getServiceNameLocked() {
-        if (mTemporaryServiceName != null) {
-            // Always log it, as it should only be used on CTS or during development
-            Slog.w(TAG, "getComponentName(): using temporary name " + mTemporaryServiceName);
-            return mTemporaryServiceName;
-        } else {
-            return getDefaultServiceName();
+    public void setOnTemporaryServiceNameChangedCallback(@NonNull NameResolverListener callback) {
+        synchronized (mLock) {
+            this.mOnSetCallback = callback;
         }
     }
 
     @Override
-    public boolean isTemporaryLocked() {
-        return mTemporaryServiceName != null;
+    public String getDefaultServiceName(@UserIdInt int userId) {
+        synchronized (mLock) {
+            final String name = mContext.getString(mResourceId);
+            return TextUtils.isEmpty(name) ? null : name;
+        }
     }
 
     @Override
-    public void setTemporaryServiceLocked(@NonNull String componentName, int durationMs) {
-        mTemporaryServiceName = componentName;
+    public String getServiceName(@UserIdInt int userId) {
+        synchronized (mLock) {
+            final String temporaryName = mTemporaryServiceNames.get(userId);
+            if (temporaryName != null) {
+                // Always log it, as it should only be used on CTS or during development
+                Slog.w(TAG, "getComponentName(): using temporary name " + temporaryName
+                        + " for user " + userId);
+                return temporaryName;
+            } else {
+                return getDefaultServiceName(userId);
+            }
+        }
+    }
 
-        if (mTemporaryHandler == null) {
-            mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
-                @Override
-                public void handleMessage(Message msg) {
-                    if (msg.what == MSG_RESET_TEMPORARY_SERVICE) {
-                        synchronized (mLock) {
-                            resetTemporaryServiceLocked();
+    @Override
+    public boolean isTemporary(@UserIdInt int userId) {
+        synchronized (mLock) {
+            return mTemporaryServiceNames.get(userId) != null;
+        }
+    }
+
+    @Override
+    public void setTemporaryService(@UserIdInt int userId, @NonNull String componentName,
+            int durationMs) {
+        synchronized (mLock) {
+            mTemporaryServiceNames.put(userId, componentName);
+
+            if (mTemporaryHandler == null) {
+                mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
+                    @Override
+                    public void handleMessage(Message msg) {
+                        if (msg.what == MSG_RESET_TEMPORARY_SERVICE) {
+                            synchronized (mLock) {
+                                resetTemporaryService(userId);
+                            }
+                        } else {
+                            Slog.wtf(TAG, "invalid handler msg: " + msg);
                         }
-                    } else {
-                        Slog.wtf(TAG, "invalid handler msg: " + msg);
                     }
-                }
-            };
-        } else {
-            removeResetTemporaryServiceMessageLocked();
+                };
+            } else {
+                mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
+            }
+            mTemporaryServiceExpiration = SystemClock.elapsedRealtime() + durationMs;
+            mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs);
+            notifyTemporaryServiceNameChangedLocked(userId, componentName);
         }
-        mTemporaryServiceExpiration = SystemClock.elapsedRealtime() + durationMs;
-        mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs);
-        onServiceNameChanged();
     }
 
     @Override
-    public void resetTemporaryServiceLocked() {
-        Slog.i(TAG, "resetting temporary service from " + mTemporaryServiceName);
-        mTemporaryServiceName = null;
-        if (mTemporaryHandler != null) {
-            removeResetTemporaryServiceMessageLocked();
-            mTemporaryHandler = null;
+    public void resetTemporaryService(@UserIdInt int userId) {
+        synchronized (mLock) {
+            Slog.i(TAG, "resetting temporary service for user " + userId + " from "
+                    + mTemporaryServiceNames.get(userId));
+            mTemporaryServiceNames.remove(userId);
+            if (mTemporaryHandler != null) {
+                mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
+                mTemporaryHandler = null;
+            }
+            notifyTemporaryServiceNameChangedLocked(userId, /* newTemporaryName= */ null);
         }
-        onServiceNameChanged();
+    }
+
+    @Override
+    public String toString() {
+        return "FrameworkResourcesServiceNamer: temps=" + mTemporaryServiceNames;
     }
 
     // TODO(b/117779333): support proto
     @Override
-    public void dumpShortLocked(@NonNull PrintWriter pw) {
-        pw.print("FrameworkResourcesServiceNamer: resId="); pw.print(mResourceId);
-        if (mTemporaryServiceName != null) {
-            pw.print(", tmpName="); pw.print(mTemporaryServiceName);
-            final long ttl = mTemporaryServiceExpiration - SystemClock.elapsedRealtime();
-            pw.print(" (expires in "); TimeUtils.formatDuration(ttl, pw); pw.print(")");
-            pw.print(", defaultName="); pw.println(getDefaultServiceName());
-        } else {
-            pw.print(", serviceName="); pw.println(getDefaultServiceName());
+    public void dumpShort(@NonNull PrintWriter pw) {
+        synchronized (mLock) {
+            pw.print("FrameworkResourcesServiceNamer: resId="); pw.print(mResourceId);
+            pw.print(", numberTemps="); pw.print(mTemporaryServiceNames.size());
         }
     }
 
-    private void onServiceNameChanged() {
+    // TODO(b/117779333): support proto
+    @Override
+    public void dumpShort(@NonNull PrintWriter pw, @UserIdInt int userId) {
+        synchronized (mLock) {
+            final String temporaryName = mTemporaryServiceNames.get(userId);
+            if (temporaryName != null) {
+                pw.print("tmpName="); pw.print(temporaryName);
+                final long ttl = mTemporaryServiceExpiration - SystemClock.elapsedRealtime();
+                pw.print(" (expires in "); TimeUtils.formatDuration(ttl, pw); pw.print("), ");
+            }
+            pw.print("defaultName="); pw.println(getDefaultServiceName(userId));
+        }
+    }
+
+    private void notifyTemporaryServiceNameChangedLocked(@UserIdInt int userId,
+            @Nullable String newTemporaryName) {
         if (mOnSetCallback != null) {
-            mOnSetCallback.run();
+            mOnSetCallback.onNameResolved(userId, newTemporaryName);
         }
     }
-
-    private void removeResetTemporaryServiceMessageLocked() {
-        // NOTE: caller should already have checked it
-        mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
-    }
 }
diff --git a/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java b/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java
index 7af8f60..bcaa918 100644
--- a/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java
@@ -31,27 +31,29 @@
 public final class SecureSettingsServiceNameResolver implements ServiceNameResolver {
 
     private final @NonNull Context mContext;
-    private final @NonNull @UserIdInt int mUserId;
 
     @NonNull
     private final String mProperty;
 
-    public SecureSettingsServiceNameResolver(@NonNull Context context, @UserIdInt int userId,
-            @NonNull String property) {
+    public SecureSettingsServiceNameResolver(@NonNull Context context, @NonNull String property) {
         mContext = context;
-        mUserId = userId;
         mProperty = property;
     }
 
     @Override
-    public String getDefaultServiceName() {
-        return Settings.Secure.getStringForUser(mContext.getContentResolver(), mProperty, mUserId);
+    public String getDefaultServiceName(@UserIdInt int userId) {
+        return Settings.Secure.getStringForUser(mContext.getContentResolver(), mProperty, userId);
     }
 
     // TODO(b/117779333): support proto
     @Override
-    public void dumpShortLocked(@NonNull PrintWriter pw) {
+    public void dumpShort(@NonNull PrintWriter pw) {
         pw.print("SecureSettingsServiceNamer: prop="); pw.print(mProperty);
-        pw.print(", value="); pw.println(getDefaultServiceName());
+    }
+
+    // TODO(b/117779333): support proto
+    @Override
+    public void dumpShort(@NonNull PrintWriter pw, @UserIdInt int userId) {
+        pw.print("defaultService="); pw.print(getDefaultServiceName(userId));
     }
 }
diff --git a/services/core/java/com/android/server/infra/ServiceNameResolver.java b/services/core/java/com/android/server/infra/ServiceNameResolver.java
index 205d40b..bc11ff3 100644
--- a/services/core/java/com/android/server/infra/ServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/ServiceNameResolver.java
@@ -17,6 +17,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+
+import com.android.internal.infra.AbstractRemoteService;
 
 import java.io.PrintWriter;
 
@@ -29,78 +32,90 @@
 public interface ServiceNameResolver {
 
     /**
+     * Listener for name changes.
+     */
+    public interface NameResolverListener {
+
+        /**
+         * The name change callback.
+         */
+        void onNameResolved(@UserIdInt int userId, @Nullable String serviceName);
+    }
+
+    /**
      * Sets a callback that is called after the service is
-     * {@link #setTemporaryServiceLocked(String, int) set} or
-     * {@link #resetTemporaryServiceLocked() reset}.
+     * {@link #setTemporaryService(int, String, int) set} or
+     * {@link #resetTemporaryService(int) reset}.
      *
      * <p>Typically called after the object is constructed.
      */
     default void setOnTemporaryServiceNameChangedCallback(
-            @SuppressWarnings("unused") @NonNull Runnable callback) {
+            @SuppressWarnings("unused") @NonNull NameResolverListener callback) {
         // ignored by default
     }
 
     /**
-     * Gets the default name of the service.
+     * Gets the default name of the service for the given user.
      *
      * <p>Typically implemented by reading a Settings property or framework resource.
      */
     @Nullable
-    String getDefaultServiceName();
+    String getDefaultServiceName(@UserIdInt int userId);
 
     /**
-     * Gets the current name of the service.
+     * Gets the current name of the service for the given user
      *
-     * <p>Must be called after acquiring this object's lock.
-     *
-     * @return either the temporary name (set by {@link #setTemporaryServiceLocked(String, int)}, or
-     * the {@link #getDefaultServiceName() default name}.
+     * @return either the temporary name (set by
+     * {@link #setTemporaryService(int, String, int)}, or the
+     * {@link #getDefaultServiceName(int) default name}.
      */
     @Nullable
-    default String getServiceNameLocked() {
-        return getDefaultServiceName();
+    default String getServiceName(@UserIdInt int userId) {
+        return getDefaultServiceName(userId);
     }
 
     /**
-     * Checks whether the current service is temporary.
-     *
-     * <p>Must be called after acquiring this object's lock.
+     * Checks whether the current service is temporary for the given user.
      */
-    default boolean isTemporaryLocked() {
+    default boolean isTemporary(@SuppressWarnings("unused") @UserIdInt int userId) {
         return false;
     }
 
     /**
-     * Temporarily sets the service implementation.
+     * Temporarily sets the service implementation for the given user.
      *
-     * <p>Must be called after acquiring this object's lock.
-     *
+     * @param userId user handle
      * @param componentName name of the new component
      * @param durationMs how long the change will be valid (the service will be automatically reset
      *            to the default component after this timeout expires).
      *
      * @throws UnsupportedOperationException if not implemented.
      */
-    default void setTemporaryServiceLocked(@NonNull String componentName, int durationMs) {
+    default void setTemporaryService(@UserIdInt int userId, @NonNull String componentName,
+            int durationMs) {
         throw new UnsupportedOperationException("temporary user not supported");
     }
 
     /**
-     * Resets the temporary service implementation to the default component.
+     * Resets the temporary service implementation to the default component for the given user.
      *
-     * <p>Must be called after acquiring this object's lock.
+     * @param userId user handle
      *
      * @throws UnsupportedOperationException if not implemented.
      */
-    default void resetTemporaryServiceLocked() {
+    default void resetTemporaryService(@UserIdInt int userId) {
         throw new UnsupportedOperationException("temporary user not supported");
     }
 
     /**
-     * Dump this object in just one line (without calling {@code println}.
-     *
-     * <p>Must be called after acquiring this object's lock.
+     * Dumps the generic info in just one line (without calling {@code println}.
      */
     // TODO(b/117779333): support proto
-    void dumpShortLocked(@NonNull PrintWriter pw);
+    void dumpShort(@NonNull PrintWriter pw);
+
+    /**
+     * Dumps the user-specific info in just one line (without calling {@code println}.
+     */
+    // TODO(b/117779333): support proto
+    void dumpShort(@NonNull PrintWriter pw, @UserIdInt int userId);
 }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index e7c3c7b..d96b6cb 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1951,11 +1951,6 @@
     }
 
     // Native callback.
-    private int getPointerDisplayId() {
-        return mWindowManagerCallbacks.getPointerDisplayId();
-    }
-
-    // Native callback.
     private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) {
         if (!mSystemReady) {
             return null;
@@ -2022,8 +2017,6 @@
                 KeyEvent event, int policyFlags);
 
         public int getPointerLayer();
-
-        public int getPointerDisplayId();
     }
 
     /**
diff --git a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
new file mode 100644
index 0000000..f79ac16
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.Xml;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.internal.util.FastXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility class to read/write subtype.xml.
+ */
+final class AdditionalSubtypeUtils {
+    private static final String TAG = "AdditionalSubtypeUtils";
+
+    private static final String SYSTEM_PATH = "system";
+    private static final String INPUT_METHOD_PATH = "inputmethod";
+    private static final String ADDITIONAL_SUBTYPES_FILE_NAME = "subtypes.xml";
+    private static final String NODE_SUBTYPES = "subtypes";
+    private static final String NODE_SUBTYPE = "subtype";
+    private static final String NODE_IMI = "imi";
+    private static final String ATTR_ID = "id";
+    private static final String ATTR_LABEL = "label";
+    private static final String ATTR_ICON = "icon";
+    private static final String ATTR_IME_SUBTYPE_ID = "subtypeId";
+    private static final String ATTR_IME_SUBTYPE_LOCALE = "imeSubtypeLocale";
+    private static final String ATTR_IME_SUBTYPE_LANGUAGE_TAG = "languageTag";
+    private static final String ATTR_IME_SUBTYPE_MODE = "imeSubtypeMode";
+    private static final String ATTR_IME_SUBTYPE_EXTRA_VALUE = "imeSubtypeExtraValue";
+    private static final String ATTR_IS_AUXILIARY = "isAuxiliary";
+    private static final String ATTR_IS_ASCII_CAPABLE = "isAsciiCapable";
+
+    private AdditionalSubtypeUtils() {
+    }
+
+    /**
+     * Returns a {@link File} that represents the directory at which subtype.xml will be placed.
+     *
+     * @param userId User ID with with subtype.xml path should be determined.
+     * @return {@link File} that represents the directory.
+     */
+    @NonNull
+    private static File getInputMethodDir(@UserIdInt int userId) {
+        final File systemDir = userId == UserHandle.USER_SYSTEM
+                ? new File(Environment.getDataDirectory(), SYSTEM_PATH)
+                : Environment.getUserSystemDirectory(userId);
+        return new File(systemDir, INPUT_METHOD_PATH);
+    }
+
+    /**
+     * Returns an {@link AtomicFile} to read/write additional subtype for the given user id.
+     *
+     * @param inputMethodDir Directory at which subtype.xml will be placed
+     * @return {@link AtomicFile} to be used to read/write additional subtype
+     */
+    @NonNull
+    private static AtomicFile getAdditionalSubtypeFile(File inputMethodDir) {
+        final File subtypeFile = new File(inputMethodDir, ADDITIONAL_SUBTYPES_FILE_NAME);
+        return new AtomicFile(subtypeFile, "input-subtypes");
+    }
+
+    /**
+     * Write additional subtypes into "subtype.xml".
+     *
+     * <p>This method does not confer any data/file locking semantics. Caller must make sure that
+     * multiple threads are not calling this method at the same time for the same {@code userId}.
+     * </p>
+     *
+     * @param allSubtypes {@link ArrayMap} from IME ID to additional subtype list. Passing an empty
+     *                    map deletes the file.
+     * @param methodMap {@link ArrayMap} from IME ID to {@link InputMethodInfo}.
+     * @param userId The user ID to be associated with.
+     */
+    static void save(ArrayMap<String, List<InputMethodSubtype>> allSubtypes,
+             ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId) {
+        final File inputMethodDir = getInputMethodDir(userId);
+
+        if (allSubtypes.isEmpty()) {
+            if (!inputMethodDir.exists()) {
+                // Even the parent directory doesn't exist.  There is nothing to clean up.
+                return;
+            }
+            final AtomicFile subtypesFile = getAdditionalSubtypeFile(inputMethodDir);
+            if (subtypesFile.exists()) {
+                subtypesFile.delete();
+            }
+            if (FileUtils.listFilesOrEmpty(inputMethodDir).length == 0) {
+                if (!inputMethodDir.delete()) {
+                    Slog.e(TAG, "Failed to delete the empty parent directory " + inputMethodDir);
+                }
+            }
+            return;
+        }
+
+        if (!inputMethodDir.exists() && !inputMethodDir.mkdirs()) {
+            Slog.e(TAG, "Failed to create a parent directory " + inputMethodDir);
+            return;
+        }
+
+        // Safety net for the case that this function is called before methodMap is set.
+        final boolean isSetMethodMap = methodMap != null && methodMap.size() > 0;
+        FileOutputStream fos = null;
+        final AtomicFile subtypesFile = getAdditionalSubtypeFile(inputMethodDir);
+        try {
+            fos = subtypesFile.startWrite();
+            final XmlSerializer out = new FastXmlSerializer();
+            out.setOutput(fos, StandardCharsets.UTF_8.name());
+            out.startDocument(null, true);
+            out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+            out.startTag(null, NODE_SUBTYPES);
+            for (String imiId : allSubtypes.keySet()) {
+                if (isSetMethodMap && !methodMap.containsKey(imiId)) {
+                    Slog.w(TAG, "IME uninstalled or not valid.: " + imiId);
+                    continue;
+                }
+                out.startTag(null, NODE_IMI);
+                out.attribute(null, ATTR_ID, imiId);
+                final List<InputMethodSubtype> subtypesList = allSubtypes.get(imiId);
+                final int numSubtypes = subtypesList.size();
+                for (int i = 0; i < numSubtypes; ++i) {
+                    final InputMethodSubtype subtype = subtypesList.get(i);
+                    out.startTag(null, NODE_SUBTYPE);
+                    if (subtype.hasSubtypeId()) {
+                        out.attribute(null, ATTR_IME_SUBTYPE_ID,
+                                String.valueOf(subtype.getSubtypeId()));
+                    }
+                    out.attribute(null, ATTR_ICON, String.valueOf(subtype.getIconResId()));
+                    out.attribute(null, ATTR_LABEL, String.valueOf(subtype.getNameResId()));
+                    out.attribute(null, ATTR_IME_SUBTYPE_LOCALE, subtype.getLocale());
+                    out.attribute(null, ATTR_IME_SUBTYPE_LANGUAGE_TAG,
+                            subtype.getLanguageTag());
+                    out.attribute(null, ATTR_IME_SUBTYPE_MODE, subtype.getMode());
+                    out.attribute(null, ATTR_IME_SUBTYPE_EXTRA_VALUE, subtype.getExtraValue());
+                    out.attribute(null, ATTR_IS_AUXILIARY,
+                            String.valueOf(subtype.isAuxiliary() ? 1 : 0));
+                    out.attribute(null, ATTR_IS_ASCII_CAPABLE,
+                            String.valueOf(subtype.isAsciiCapable() ? 1 : 0));
+                    out.endTag(null, NODE_SUBTYPE);
+                }
+                out.endTag(null, NODE_IMI);
+            }
+            out.endTag(null, NODE_SUBTYPES);
+            out.endDocument();
+            subtypesFile.finishWrite(fos);
+        } catch (java.io.IOException e) {
+            Slog.w(TAG, "Error writing subtypes", e);
+            if (fos != null) {
+                subtypesFile.failWrite(fos);
+            }
+        }
+    }
+
+    /**
+     * Read additional subtypes from "subtype.xml".
+     *
+     * <p>This method does not confer any data/file locking semantics. Caller must make sure that
+     * multiple threads are not calling this method at the same time for the same {@code userId}.
+     * </p>
+     *
+     * @param allSubtypes {@link ArrayMap} from IME ID to additional subtype list. This parameter
+     *                    will be used to return the result.
+     * @param userId The user ID to be associated with.
+     */
+    static void load(@NonNull ArrayMap<String, List<InputMethodSubtype>> allSubtypes,
+            @UserIdInt int userId) {
+        allSubtypes.clear();
+
+        final AtomicFile subtypesFile = getAdditionalSubtypeFile(getInputMethodDir(userId));
+        if (!subtypesFile.exists()) {
+            // Not having the file means there is no additional subtype.
+            return;
+        }
+        try (FileInputStream fis = subtypesFile.openRead()) {
+            final XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(fis, StandardCharsets.UTF_8.name());
+            int type = parser.getEventType();
+            // Skip parsing until START_TAG
+            while (true) {
+                type = parser.next();
+                if (type == XmlPullParser.START_TAG || type == XmlPullParser.END_DOCUMENT) {
+                    break;
+                }
+            }
+            String firstNodeName = parser.getName();
+            if (!NODE_SUBTYPES.equals(firstNodeName)) {
+                throw new XmlPullParserException("Xml doesn't start with subtypes");
+            }
+            final int depth = parser.getDepth();
+            String currentImiId = null;
+            ArrayList<InputMethodSubtype> tempSubtypesArray = null;
+            while (((type = parser.next()) != XmlPullParser.END_TAG
+                    || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+                if (type != XmlPullParser.START_TAG) {
+                    continue;
+                }
+                final String nodeName = parser.getName();
+                if (NODE_IMI.equals(nodeName)) {
+                    currentImiId = parser.getAttributeValue(null, ATTR_ID);
+                    if (TextUtils.isEmpty(currentImiId)) {
+                        Slog.w(TAG, "Invalid imi id found in subtypes.xml");
+                        continue;
+                    }
+                    tempSubtypesArray = new ArrayList<>();
+                    allSubtypes.put(currentImiId, tempSubtypesArray);
+                } else if (NODE_SUBTYPE.equals(nodeName)) {
+                    if (TextUtils.isEmpty(currentImiId) || tempSubtypesArray == null) {
+                        Slog.w(TAG, "IME uninstalled or not valid.: " + currentImiId);
+                        continue;
+                    }
+                    final int icon = Integer.parseInt(
+                            parser.getAttributeValue(null, ATTR_ICON));
+                    final int label = Integer.parseInt(
+                            parser.getAttributeValue(null, ATTR_LABEL));
+                    final String imeSubtypeLocale =
+                            parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LOCALE);
+                    final String languageTag =
+                            parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LANGUAGE_TAG);
+                    final String imeSubtypeMode =
+                            parser.getAttributeValue(null, ATTR_IME_SUBTYPE_MODE);
+                    final String imeSubtypeExtraValue =
+                            parser.getAttributeValue(null, ATTR_IME_SUBTYPE_EXTRA_VALUE);
+                    final boolean isAuxiliary = "1".equals(String.valueOf(
+                            parser.getAttributeValue(null, ATTR_IS_AUXILIARY)));
+                    final boolean isAsciiCapable = "1".equals(String.valueOf(
+                            parser.getAttributeValue(null, ATTR_IS_ASCII_CAPABLE)));
+                    final InputMethodSubtype.InputMethodSubtypeBuilder
+                            builder = new InputMethodSubtype.InputMethodSubtypeBuilder()
+                            .setSubtypeNameResId(label)
+                            .setSubtypeIconResId(icon)
+                            .setSubtypeLocale(imeSubtypeLocale)
+                            .setLanguageTag(languageTag)
+                            .setSubtypeMode(imeSubtypeMode)
+                            .setSubtypeExtraValue(imeSubtypeExtraValue)
+                            .setIsAuxiliary(isAuxiliary)
+                            .setIsAsciiCapable(isAsciiCapable);
+                    final String subtypeIdString =
+                            parser.getAttributeValue(null, ATTR_IME_SUBTYPE_ID);
+                    if (subtypeIdString != null) {
+                        builder.setSubtypeId(Integer.parseInt(subtypeIdString));
+                    }
+                    tempSubtypesArray.add(builder.build());
+                }
+            }
+        } catch (XmlPullParserException | IOException | NumberFormatException e) {
+            Slog.w(TAG, "Error reading subtypes", e);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 28a6ba4..5a7739c 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -72,7 +72,6 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Debug;
-import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IInterface;
@@ -96,14 +95,12 @@
 import android.text.style.SuggestionSpan;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.AtomicFile;
 import android.util.EventLog;
 import android.util.LruCache;
 import android.util.Pair;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
 import android.util.Slog;
-import android.util.Xml;
 import android.view.ContextThemeWrapper;
 import android.view.Display;
 import android.view.DisplayInfo;
@@ -124,7 +121,6 @@
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
-import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
 import android.widget.ArrayAdapter;
 import android.widget.CompoundButton;
 import android.widget.CompoundButton.OnCheckedChangeListener;
@@ -146,7 +142,6 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.util.DumpUtils;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethod;
@@ -163,21 +158,14 @@
 import com.android.server.statusbar.StatusBarManagerService;
 import com.android.server.wm.WindowManagerInternal;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
-import java.nio.charset.StandardCharsets;
 import java.security.InvalidParameterException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Date;
 import java.util.List;
@@ -313,7 +301,8 @@
     final WindowManagerInternal mWindowManagerInternal;
     final HandlerCaller mCaller;
     final boolean mHasFeature;
-    private InputMethodFileManager mFileManager;
+    private final ArrayMap<String, List<InputMethodSubtype>> mAdditionalSubtypeMap =
+            new ArrayMap<>();
     private final HardKeyboardListener mHardKeyboardListener;
     private final AppOpsManager mAppOpsManager;
     private final UserManager mUserManager;
@@ -401,12 +390,11 @@
     };
 
     private void restoreNonVrImeFromSettingsNoCheck() {
-        mIsVrImeStarted = false;
         // switch back to non-VR InputMethod from settings.
         synchronized (mMethodMap) {
-            final String lastInputId = mSettings.getSelectedInputMethod();
-            setInputMethodLocked(lastInputId,
-                    mSettings.getSelectedInputMethodSubtypeId(lastInputId));
+            if (!mIsVrImeStarted) return;
+            mIsVrImeStarted = false;
+            updateFromSettingsLocked(false);
         }
     }
 
@@ -552,10 +540,6 @@
      */
     private InputMethodSubtype mCurrentSubtype;
 
-    // This list contains the pairs of InputMethodInfo and InputMethodSubtype.
-    private final ArrayMap<InputMethodInfo, ArrayList<InputMethodSubtype>>
-            mShortcutInputMethodsAndSubtypes = new ArrayMap<>();
-
     // Was the keyguard locked when this client became current?
     private boolean mCurClientInKeyguard;
 
@@ -675,13 +659,6 @@
     private final int mHardKeyboardBehavior;
 
     /**
-     * Whether we temporarily allow IMEs implemented in instant apps to run for testing.
-     *
-     * <p>Note: This is quite dangerous.  Don't forget to reset after you finish testing.</p>
-     */
-    private boolean mBindInstantServiceAllowed = false;
-
-    /**
      * Internal state snapshot when {@link #MSG_START_INPUT} message is about to be posted to the
      * internal message queue. Any subsequent state change inside {@link InputMethodManagerService}
      * will not affect those tasks that are already posted.
@@ -1135,8 +1112,7 @@
                 final PackageManager pm = mContext.getPackageManager();
                 final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
                         new Intent(InputMethod.SERVICE_INTERFACE).setPackage(packageName),
-                        getComponentMatchingFlags(PackageManager.MATCH_DISABLED_COMPONENTS),
-                        getChangingUserId());
+                        PackageManager.MATCH_DISABLED_COMPONENTS, getChangingUserId());
                 // No need to lock this because we access it only on getRegisteredHandler().
                 if (!services.isEmpty()) {
                     mImePackageAppeared = true;
@@ -1231,7 +1207,9 @@
 
                         int change = isPackageDisappearing(imi.getPackageName());
                         if (isPackageModified(imi.getPackageName())) {
-                            mFileManager.deleteAllInputMethodSubtypes(imiId);
+                            mAdditionalSubtypeMap.remove(imi.getId());
+                            AdditionalSubtypeUtils.save(mAdditionalSubtypeMap, mMethodMap,
+                                    mSettings.getCurrentUserId());
                         }
                         if (change == PACKAGE_TEMPORARY_CHANGE
                                 || change == PACKAGE_PERMANENT_CHANGE) {
@@ -1463,7 +1441,7 @@
                 mRes, context.getContentResolver(), mMethodMap, mMethodList, userId, !mSystemReady);
 
         updateCurrentProfileIds();
-        mFileManager = new InputMethodFileManager(mMethodMap, userId);
+        AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, userId);
         mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
                 mSettings, context);
         // Register VR-state listener.
@@ -1509,8 +1487,8 @@
                 !mSystemReady || !mUserManager.isUserUnlockingOrUnlocked(newUserId);
         mSettings.switchCurrentUser(newUserId, useCopyOnWriteSettings);
         updateCurrentProfileIds();
-        // InputMethodFileManager should be reset when the user is changed
-        mFileManager = new InputMethodFileManager(mMethodMap, newUserId);
+        // Additional subtypes should be reset when the user is changed
+        AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, newUserId);
         final String defaultImiId = mSettings.getSelectedInputMethod();
 
         if (DEBUG) Slog.d(TAG, "Switching user stage 2/3. newUserId=" + newUserId
@@ -1684,9 +1662,6 @@
             Slog.e(TAG, "--- bind failed: service = " + service + ", conn = " + conn);
             return false;
         }
-        if (mBindInstantServiceAllowed) {
-            flags |= Context.BIND_ALLOW_INSTANT;
-        }
         return mContext.bindServiceAsUser(service, conn, flags,
                 new UserHandle(mSettings.getCurrentUserId()));
     }
@@ -1696,6 +1671,7 @@
         return getInputMethodList(false /* isVrOnly */);
     }
 
+    @Override
     public List<InputMethodInfo> getVrInputMethodList() {
         return getInputMethodList(true /* isVrOnly */);
     }
@@ -2544,7 +2520,6 @@
                 Slog.w(TAG, "Unknown input method from prefs: " + id, e);
                 resetCurrentMethodAndClient(UnbindReason.SWITCH_IME_FAILED);
             }
-            mShortcutInputMethodsAndSubtypes.clear();
         } else {
             // There is no longer an input method set, so stop any current one.
             resetCurrentMethodAndClient(UnbindReason.NO_IME);
@@ -3091,20 +3066,20 @@
         }
     }
 
-    @Override
-    public void setInputMethod(IBinder token, String id) {
+    @BinderThread
+    private void setInputMethod(IBinder token, String id) {
         synchronized (mMethodMap) {
-            if (!calledFromValidUserLocked()) {
+            if (!calledWithValidTokenLocked(token)) {
                 return;
             }
             setInputMethodWithSubtypeIdLocked(token, id, NOT_A_SUBTYPE_ID);
         }
     }
 
-    @Override
-    public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
+    @BinderThread
+    private void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
         synchronized (mMethodMap) {
-            if (!calledFromValidUserLocked()) {
+            if (!calledWithValidTokenLocked(token)) {
                 return;
             }
             if (subtype != null) {
@@ -3265,6 +3240,15 @@
         // By this IPC call, only a process which shares the same uid with the IME can add
         // additional input method subtypes to the IME.
         if (TextUtils.isEmpty(imiId) || subtypes == null) return;
+        final ArrayList<InputMethodSubtype> toBeAdded = new ArrayList<>();
+        for (InputMethodSubtype subtype : subtypes) {
+            if (!toBeAdded.contains(subtype)) {
+                toBeAdded.add(subtype);
+            } else {
+                Slog.w(TAG, "Duplicated subtype definition found: "
+                        + subtype.getLocale() + ", " + subtype.getMode());
+            }
+        }
         synchronized (mMethodMap) {
             if (!calledFromValidUserLocked()) {
                 return;
@@ -3285,7 +3269,13 @@
                 final int packageNum = packageInfos.length;
                 for (int i = 0; i < packageNum; ++i) {
                     if (packageInfos[i].equals(imi.getPackageName())) {
-                        mFileManager.addInputMethodSubtypes(imi, subtypes);
+                        if (subtypes.length > 0) {
+                            mAdditionalSubtypeMap.put(imi.getId(), toBeAdded);
+                        } else {
+                            mAdditionalSubtypeMap.remove(imi.getId());
+                        }
+                        AdditionalSubtypeUtils.save(mAdditionalSubtypeMap, mMethodMap,
+                                mSettings.getCurrentUserId());
                         final long ident = Binder.clearCallingIdentity();
                         try {
                             buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
@@ -3631,45 +3621,23 @@
         return false;
     }
 
-    @PackageManager.ResolveInfoFlags
-    private int getComponentMatchingFlags(@PackageManager.ResolveInfoFlags int baseFlags) {
-        synchronized (mMethodMap) {
-            if (mBindInstantServiceAllowed) {
-                baseFlags |= PackageManager.MATCH_INSTANT;
-            }
-            return baseFlags;
-        }
-    }
-
-    @GuardedBy("mMethodMap")
-    void buildInputMethodListLocked(boolean resetDefaultEnabledIme) {
-        if (DEBUG) {
-            Slog.d(TAG, "--- re-buildInputMethodList reset = " + resetDefaultEnabledIme
-                    + " \n ------ caller=" + Debug.getCallers(10));
-        }
-        if (!mSystemReady) {
-            Slog.e(TAG, "buildInputMethodListLocked is not allowed until system is ready");
-            return;
-        }
-        mMethodList.clear();
-        mMethodMap.clear();
-        mMethodMapUpdateCount++;
-        mMyPackageMonitor.clearKnownImePackageNamesLocked();
-
-        // Use for queryIntentServicesAsUser
-        final PackageManager pm = mContext.getPackageManager();
+    static void queryInputMethodServicesInternal(Context context,
+            @UserIdInt int userId, ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap,
+            ArrayMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList) {
+        methodList.clear();
+        methodMap.clear();
 
         // Note: We do not specify PackageManager.MATCH_ENCRYPTION_* flags here because the default
         // behavior of PackageManager is exactly what we want.  It by default picks up appropriate
         // services depending on the unlock state for the specified user.
-        final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
+        final List<ResolveInfo> services = context.getPackageManager().queryIntentServicesAsUser(
                 new Intent(InputMethod.SERVICE_INTERFACE),
-                getComponentMatchingFlags(PackageManager.GET_META_DATA
-                        | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS),
-                mSettings.getCurrentUserId());
+                PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS,
+                userId);
 
-        final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
-                mFileManager.getAllAdditionalInputMethodSubtypes();
+        methodList.ensureCapacity(services.size());
+        methodMap.ensureCapacity(services.size());
+
         for (int i = 0; i < services.size(); ++i) {
             ResolveInfo ri = services.get(i);
             ServiceInfo si = ri.serviceInfo;
@@ -3683,20 +3651,35 @@
 
             if (DEBUG) Slog.d(TAG, "Checking " + imeId);
 
-            final List<InputMethodSubtype> additionalSubtypes = additionalSubtypeMap.get(imeId);
             try {
-                InputMethodInfo p = new InputMethodInfo(mContext, ri, additionalSubtypes);
-                mMethodList.add(p);
-                final String id = p.getId();
-                mMethodMap.put(id, p);
-
+                final InputMethodInfo imi = new InputMethodInfo(context, ri,
+                        additionalSubtypeMap.get(imeId));
+                methodList.add(imi);
+                methodMap.put(imi.getId(), imi);
                 if (DEBUG) {
-                    Slog.d(TAG, "Found an input method " + p);
+                    Slog.d(TAG, "Found an input method " + imi);
                 }
             } catch (Exception e) {
                 Slog.wtf(TAG, "Unable to load input method " + imeId, e);
             }
         }
+    }
+
+    @GuardedBy("mMethodMap")
+    void buildInputMethodListLocked(boolean resetDefaultEnabledIme) {
+        if (DEBUG) {
+            Slog.d(TAG, "--- re-buildInputMethodList reset = " + resetDefaultEnabledIme
+                    + " \n ------ caller=" + Debug.getCallers(10));
+        }
+        if (!mSystemReady) {
+            Slog.e(TAG, "buildInputMethodListLocked is not allowed until system is ready");
+            return;
+        }
+        mMethodMapUpdateCount++;
+        mMyPackageMonitor.clearKnownImePackageNamesLocked();
+
+        queryInputMethodServicesInternal(mContext, mSettings.getCurrentUserId(),
+                mAdditionalSubtypeMap, mMethodMap, mMethodList);
 
         // Construct the set of possible IME packages for onPackageChanged() to avoid false
         // negatives when the package state remains to be the same but only the component state is
@@ -3705,10 +3688,10 @@
             // Here we intentionally use PackageManager.MATCH_DISABLED_COMPONENTS since the purpose
             // of this query is to avoid false negatives.  PackageManager.MATCH_ALL could be more
             // conservative, but it seems we cannot use it for now (Issue 35176630).
-            final List<ResolveInfo> allInputMethodServices = pm.queryIntentServicesAsUser(
-                    new Intent(InputMethod.SERVICE_INTERFACE),
-                    getComponentMatchingFlags(PackageManager.MATCH_DISABLED_COMPONENTS),
-                    mSettings.getCurrentUserId());
+            final List<ResolveInfo> allInputMethodServices =
+                    mContext.getPackageManager().queryIntentServicesAsUser(
+                            new Intent(InputMethod.SERVICE_INTERFACE),
+                            PackageManager.MATCH_DISABLED_COMPONENTS, mSettings.getCurrentUserId());
             final int N = allInputMethodServices.size();
             for (int i = 0; i < N; ++i) {
                 final ServiceInfo si = allInputMethodServices.get(i).serviceInfo;
@@ -4106,86 +4089,6 @@
         setSelectedInputMethodAndSubtypeLocked(imi, lastSubtypeId, false);
     }
 
-    // If there are no selected shortcuts, tries finding the most applicable ones.
-    private Pair<InputMethodInfo, InputMethodSubtype>
-            findLastResortApplicableShortcutInputMethodAndSubtypeLocked(String mode) {
-        List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
-        InputMethodInfo mostApplicableIMI = null;
-        InputMethodSubtype mostApplicableSubtype = null;
-        boolean foundInSystemIME = false;
-
-        // Search applicable subtype for each InputMethodInfo
-        for (InputMethodInfo imi: imis) {
-            final String imiId = imi.getId();
-            if (foundInSystemIME && !imiId.equals(mCurMethodId)) {
-                continue;
-            }
-            InputMethodSubtype subtype = null;
-            final List<InputMethodSubtype> enabledSubtypes =
-                    mSettings.getEnabledInputMethodSubtypeListLocked(mContext, imi, true);
-            // 1. Search by the current subtype's locale from enabledSubtypes.
-            if (mCurrentSubtype != null) {
-                subtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
-                        mRes, enabledSubtypes, mode, mCurrentSubtype.getLocale(), false);
-            }
-            // 2. Search by the system locale from enabledSubtypes.
-            // 3. Search the first enabled subtype matched with mode from enabledSubtypes.
-            if (subtype == null) {
-                subtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
-                        mRes, enabledSubtypes, mode, null, true);
-            }
-            final ArrayList<InputMethodSubtype> overridingImplicitlyEnabledSubtypes =
-                    InputMethodUtils.getOverridingImplicitlyEnabledSubtypes(imi, mode);
-            final ArrayList<InputMethodSubtype> subtypesForSearch =
-                    overridingImplicitlyEnabledSubtypes.isEmpty()
-                            ? InputMethodUtils.getSubtypes(imi)
-                            : overridingImplicitlyEnabledSubtypes;
-            // 4. Search by the current subtype's locale from all subtypes.
-            if (subtype == null && mCurrentSubtype != null) {
-                subtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
-                        mRes, subtypesForSearch, mode, mCurrentSubtype.getLocale(), false);
-            }
-            // 5. Search by the system locale from all subtypes.
-            // 6. Search the first enabled subtype matched with mode from all subtypes.
-            if (subtype == null) {
-                subtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
-                        mRes, subtypesForSearch, mode, null, true);
-            }
-            if (subtype != null) {
-                if (imiId.equals(mCurMethodId)) {
-                    // The current input method is the most applicable IME.
-                    mostApplicableIMI = imi;
-                    mostApplicableSubtype = subtype;
-                    break;
-                } else if (!foundInSystemIME) {
-                    // The system input method is 2nd applicable IME.
-                    mostApplicableIMI = imi;
-                    mostApplicableSubtype = subtype;
-                    if ((imi.getServiceInfo().applicationInfo.flags
-                            & ApplicationInfo.FLAG_SYSTEM) != 0) {
-                        foundInSystemIME = true;
-                    }
-                }
-            }
-        }
-        if (DEBUG) {
-            if (mostApplicableIMI != null) {
-                Slog.w(TAG, "Most applicable shortcut input method was:"
-                        + mostApplicableIMI.getId());
-                if (mostApplicableSubtype != null) {
-                    Slog.w(TAG, "Most applicable shortcut input method subtype was:"
-                            + "," + mostApplicableSubtype.getMode() + ","
-                            + mostApplicableSubtype.getLocale());
-                }
-            }
-        }
-        if (mostApplicableIMI != null) {
-            return new Pair<> (mostApplicableIMI, mostApplicableSubtype);
-        } else {
-            return null;
-        }
-    }
-
     /**
      * @return Return the current subtype of this input method.
      */
@@ -4239,35 +4142,6 @@
         return mCurrentSubtype;
     }
 
-    // TODO: We should change the return type from List to List<Parcelable>
-    @SuppressWarnings("rawtypes")
-    @Override
-    public List getShortcutInputMethodsAndSubtypes() {
-        synchronized (mMethodMap) {
-            ArrayList<Object> ret = new ArrayList<>();
-            if (mShortcutInputMethodsAndSubtypes.size() == 0) {
-                // If there are no selected shortcut subtypes, the framework will try to find
-                // the most applicable subtype from all subtypes whose mode is
-                // SUBTYPE_MODE_VOICE. This is an exceptional case, so we will hardcode the mode.
-                Pair<InputMethodInfo, InputMethodSubtype> info =
-                    findLastResortApplicableShortcutInputMethodAndSubtypeLocked(
-                            InputMethodUtils.SUBTYPE_MODE_VOICE);
-                if (info != null) {
-                    ret.add(info.first);
-                    ret.add(info.second);
-                }
-                return ret;
-            }
-            for (InputMethodInfo imi: mShortcutInputMethodsAndSubtypes.keySet()) {
-                ret.add(imi);
-                for (InputMethodSubtype subtype: mShortcutInputMethodsAndSubtypes.get(imi)) {
-                    ret.add(subtype);
-                }
-            }
-            return ret;
-        }
-    }
-
     @Override
     public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
         synchronized (mMethodMap) {
@@ -4287,217 +4161,6 @@
         }
     }
 
-    // TODO: Cache the state for each user and reset when the cached user is removed.
-    private static class InputMethodFileManager {
-        private static final String SYSTEM_PATH = "system";
-        private static final String INPUT_METHOD_PATH = "inputmethod";
-        private static final String ADDITIONAL_SUBTYPES_FILE_NAME = "subtypes.xml";
-        private static final String NODE_SUBTYPES = "subtypes";
-        private static final String NODE_SUBTYPE = "subtype";
-        private static final String NODE_IMI = "imi";
-        private static final String ATTR_ID = "id";
-        private static final String ATTR_LABEL = "label";
-        private static final String ATTR_ICON = "icon";
-        private static final String ATTR_IME_SUBTYPE_ID = "subtypeId";
-        private static final String ATTR_IME_SUBTYPE_LOCALE = "imeSubtypeLocale";
-        private static final String ATTR_IME_SUBTYPE_LANGUAGE_TAG = "languageTag";
-        private static final String ATTR_IME_SUBTYPE_MODE = "imeSubtypeMode";
-        private static final String ATTR_IME_SUBTYPE_EXTRA_VALUE = "imeSubtypeExtraValue";
-        private static final String ATTR_IS_AUXILIARY = "isAuxiliary";
-        private static final String ATTR_IS_ASCII_CAPABLE = "isAsciiCapable";
-        private final AtomicFile mAdditionalInputMethodSubtypeFile;
-        private final ArrayMap<String, InputMethodInfo> mMethodMap;
-        private final ArrayMap<String, List<InputMethodSubtype>> mAdditionalSubtypesMap =
-                new ArrayMap<>();
-        InputMethodFileManager(ArrayMap<String, InputMethodInfo> methodMap, int userId) {
-            if (methodMap == null) {
-                throw new NullPointerException("methodMap is null");
-            }
-            mMethodMap = methodMap;
-            final File systemDir = userId == UserHandle.USER_SYSTEM
-                    ? new File(Environment.getDataDirectory(), SYSTEM_PATH)
-                    : Environment.getUserSystemDirectory(userId);
-            final File inputMethodDir = new File(systemDir, INPUT_METHOD_PATH);
-            if (!inputMethodDir.exists() && !inputMethodDir.mkdirs()) {
-                Slog.w(TAG, "Couldn't create dir.: " + inputMethodDir.getAbsolutePath());
-            }
-            final File subtypeFile = new File(inputMethodDir, ADDITIONAL_SUBTYPES_FILE_NAME);
-            mAdditionalInputMethodSubtypeFile = new AtomicFile(subtypeFile, "input-subtypes");
-            if (!subtypeFile.exists()) {
-                // If "subtypes.xml" doesn't exist, create a blank file.
-                writeAdditionalInputMethodSubtypes(
-                        mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, methodMap);
-            } else {
-                readAdditionalInputMethodSubtypes(
-                        mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile);
-            }
-        }
-
-        private void deleteAllInputMethodSubtypes(String imiId) {
-            synchronized (mMethodMap) {
-                mAdditionalSubtypesMap.remove(imiId);
-                writeAdditionalInputMethodSubtypes(
-                        mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, mMethodMap);
-            }
-        }
-
-        public void addInputMethodSubtypes(
-                InputMethodInfo imi, InputMethodSubtype[] additionalSubtypes) {
-            synchronized (mMethodMap) {
-                final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
-                final int N = additionalSubtypes.length;
-                for (int i = 0; i < N; ++i) {
-                    final InputMethodSubtype subtype = additionalSubtypes[i];
-                    if (!subtypes.contains(subtype)) {
-                        subtypes.add(subtype);
-                    } else {
-                        Slog.w(TAG, "Duplicated subtype definition found: "
-                                + subtype.getLocale() + ", " + subtype.getMode());
-                    }
-                }
-                mAdditionalSubtypesMap.put(imi.getId(), subtypes);
-                writeAdditionalInputMethodSubtypes(
-                        mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, mMethodMap);
-            }
-        }
-
-        public ArrayMap<String, List<InputMethodSubtype>> getAllAdditionalInputMethodSubtypes() {
-            synchronized (mMethodMap) {
-                return mAdditionalSubtypesMap;
-            }
-        }
-
-        private static void writeAdditionalInputMethodSubtypes(
-                ArrayMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile,
-                ArrayMap<String, InputMethodInfo> methodMap) {
-            // Safety net for the case that this function is called before methodMap is set.
-            final boolean isSetMethodMap = methodMap != null && methodMap.size() > 0;
-            FileOutputStream fos = null;
-            try {
-                fos = subtypesFile.startWrite();
-                final XmlSerializer out = new FastXmlSerializer();
-                out.setOutput(fos, StandardCharsets.UTF_8.name());
-                out.startDocument(null, true);
-                out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
-                out.startTag(null, NODE_SUBTYPES);
-                for (String imiId : allSubtypes.keySet()) {
-                    if (isSetMethodMap && !methodMap.containsKey(imiId)) {
-                        Slog.w(TAG, "IME uninstalled or not valid.: " + imiId);
-                        continue;
-                    }
-                    out.startTag(null, NODE_IMI);
-                    out.attribute(null, ATTR_ID, imiId);
-                    final List<InputMethodSubtype> subtypesList = allSubtypes.get(imiId);
-                    final int N = subtypesList.size();
-                    for (int i = 0; i < N; ++i) {
-                        final InputMethodSubtype subtype = subtypesList.get(i);
-                        out.startTag(null, NODE_SUBTYPE);
-                        if (subtype.hasSubtypeId()) {
-                            out.attribute(null, ATTR_IME_SUBTYPE_ID,
-                                    String.valueOf(subtype.getSubtypeId()));
-                        }
-                        out.attribute(null, ATTR_ICON, String.valueOf(subtype.getIconResId()));
-                        out.attribute(null, ATTR_LABEL, String.valueOf(subtype.getNameResId()));
-                        out.attribute(null, ATTR_IME_SUBTYPE_LOCALE, subtype.getLocale());
-                        out.attribute(null, ATTR_IME_SUBTYPE_LANGUAGE_TAG,
-                                subtype.getLanguageTag());
-                        out.attribute(null, ATTR_IME_SUBTYPE_MODE, subtype.getMode());
-                        out.attribute(null, ATTR_IME_SUBTYPE_EXTRA_VALUE, subtype.getExtraValue());
-                        out.attribute(null, ATTR_IS_AUXILIARY,
-                                String.valueOf(subtype.isAuxiliary() ? 1 : 0));
-                        out.attribute(null, ATTR_IS_ASCII_CAPABLE,
-                                String.valueOf(subtype.isAsciiCapable() ? 1 : 0));
-                        out.endTag(null, NODE_SUBTYPE);
-                    }
-                    out.endTag(null, NODE_IMI);
-                }
-                out.endTag(null, NODE_SUBTYPES);
-                out.endDocument();
-                subtypesFile.finishWrite(fos);
-            } catch (java.io.IOException e) {
-                Slog.w(TAG, "Error writing subtypes", e);
-                if (fos != null) {
-                    subtypesFile.failWrite(fos);
-                }
-            }
-        }
-
-        private static void readAdditionalInputMethodSubtypes(
-                ArrayMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile) {
-            if (allSubtypes == null || subtypesFile == null) return;
-            allSubtypes.clear();
-            try (final FileInputStream fis = subtypesFile.openRead()) {
-                final XmlPullParser parser = Xml.newPullParser();
-                parser.setInput(fis, StandardCharsets.UTF_8.name());
-                int type = parser.getEventType();
-                // Skip parsing until START_TAG
-                while ((type = parser.next()) != XmlPullParser.START_TAG
-                        && type != XmlPullParser.END_DOCUMENT) {}
-                String firstNodeName = parser.getName();
-                if (!NODE_SUBTYPES.equals(firstNodeName)) {
-                    throw new XmlPullParserException("Xml doesn't start with subtypes");
-                }
-                final int depth =parser.getDepth();
-                String currentImiId = null;
-                ArrayList<InputMethodSubtype> tempSubtypesArray = null;
-                while (((type = parser.next()) != XmlPullParser.END_TAG
-                        || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
-                    if (type != XmlPullParser.START_TAG)
-                        continue;
-                    final String nodeName = parser.getName();
-                    if (NODE_IMI.equals(nodeName)) {
-                        currentImiId = parser.getAttributeValue(null, ATTR_ID);
-                        if (TextUtils.isEmpty(currentImiId)) {
-                            Slog.w(TAG, "Invalid imi id found in subtypes.xml");
-                            continue;
-                        }
-                        tempSubtypesArray = new ArrayList<>();
-                        allSubtypes.put(currentImiId, tempSubtypesArray);
-                    } else if (NODE_SUBTYPE.equals(nodeName)) {
-                        if (TextUtils.isEmpty(currentImiId) || tempSubtypesArray == null) {
-                            Slog.w(TAG, "IME uninstalled or not valid.: " + currentImiId);
-                            continue;
-                        }
-                        final int icon = Integer.parseInt(
-                                parser.getAttributeValue(null, ATTR_ICON));
-                        final int label = Integer.parseInt(
-                                parser.getAttributeValue(null, ATTR_LABEL));
-                        final String imeSubtypeLocale =
-                                parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LOCALE);
-                        final String languageTag =
-                                parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LANGUAGE_TAG);
-                        final String imeSubtypeMode =
-                                parser.getAttributeValue(null, ATTR_IME_SUBTYPE_MODE);
-                        final String imeSubtypeExtraValue =
-                                parser.getAttributeValue(null, ATTR_IME_SUBTYPE_EXTRA_VALUE);
-                        final boolean isAuxiliary = "1".equals(String.valueOf(
-                                parser.getAttributeValue(null, ATTR_IS_AUXILIARY)));
-                        final boolean isAsciiCapable = "1".equals(String.valueOf(
-                                parser.getAttributeValue(null, ATTR_IS_ASCII_CAPABLE)));
-                        final InputMethodSubtypeBuilder builder = new InputMethodSubtypeBuilder()
-                                .setSubtypeNameResId(label)
-                                .setSubtypeIconResId(icon)
-                                .setSubtypeLocale(imeSubtypeLocale)
-                                .setLanguageTag(languageTag)
-                                .setSubtypeMode(imeSubtypeMode)
-                                .setSubtypeExtraValue(imeSubtypeExtraValue)
-                                .setIsAuxiliary(isAuxiliary)
-                                .setIsAsciiCapable(isAsciiCapable);
-                        final String subtypeIdString =
-                                parser.getAttributeValue(null, ATTR_IME_SUBTYPE_ID);
-                        if (subtypeIdString != null) {
-                            builder.setSubtypeId(Integer.parseInt(subtypeIdString));
-                        }
-                        tempSubtypesArray.add(builder.build());
-                    }
-                }
-            } catch (XmlPullParserException | IOException | NumberFormatException e) {
-                Slog.w(TAG, "Error reading subtypes", e);
-                return;
-            }
-        }
-    }
-
     private static final class LocalServiceImpl extends InputMethodManagerInternal {
         @NonNull
         private final InputMethodManagerService mService;
@@ -4606,8 +4269,7 @@
         synchronized (mMethodMap) {
             p.println("Current Input Method Manager state:");
             int N = mMethodList.size();
-            p.println("  Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount
-                    + " mBindInstantServiceAllowed=" + mBindInstantServiceAllowed);
+            p.println("  Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount);
             for (int i=0; i<N; i++) {
                 InputMethodInfo info = mMethodList.get(i);
                 p.println("  InputMethod #" + i + ":");
@@ -4700,6 +4362,25 @@
             @Nullable FileDescriptor err,
             @NonNull String[] args, @Nullable ShellCallback callback,
             @NonNull ResultReceiver resultReceiver) throws RemoteException {
+        final int callingUid = Binder.getCallingUid();
+        // Reject any incoming calls from non-shell users, including ones from the system user.
+        if (callingUid != Process.ROOT_UID && callingUid != Process.SHELL_UID) {
+            // Note that Binder#onTransact() will automatically close "in", "out", and "err" when
+            // returned from this method, hence there is no need to close those FDs.
+            // "resultReceiver" is the only thing that needs to be taken care of here.
+            if (resultReceiver != null) {
+                resultReceiver.send(ShellCommandResult.FAILURE, null);
+            }
+            final String errorMsg = "InputMethodManagerService does not support shell commands from"
+                    + " non-shell users. callingUid=" + callingUid
+                    + " args=" + Arrays.toString(args);
+            if (Process.isCoreUid(callingUid)) {
+                // Let's not crash the calling process if the caller is one of core components.
+                Slog.e(TAG, errorMsg);
+                return;
+            }
+            throw new SecurityException(errorMsg);
+        }
         new ShellCommandImpl(this).exec(
                 this, in, out, err, args, callback, resultReceiver);
     }
@@ -4712,16 +4393,36 @@
             mService = service;
         }
 
+        @RequiresPermission(allOf = {
+                Manifest.permission.DUMP,
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                Manifest.permission.WRITE_SECURE_SETTINGS,
+        })
         @BinderThread
         @ShellCommandResult
         @Override
         public int onCommand(@Nullable String cmd) {
+            // For shell command, require all the permissions here in favor of code simplicity.
+            Arrays.asList(
+                    Manifest.permission.DUMP,
+                    Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                    Manifest.permission.WRITE_SECURE_SETTINGS
+            ).forEach(permission -> mService.mContext.enforceCallingPermission(permission, null));
+
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                return onCommandWithSystemIdentity(cmd);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @BinderThread
+        @ShellCommandResult
+        private int onCommandWithSystemIdentity(@Nullable String cmd) {
             if ("refresh_debug_properties".equals(cmd)) {
                 return refreshDebugProperties();
             }
-            if ("set-bind-instant-service-allowed".equals(cmd)) {
-                return setBindInstantServiceAllowed();
-            }
 
             // For existing "adb shell ime <command>".
             if ("ime".equals(cmd)) {
@@ -4752,12 +4453,6 @@
 
         @BinderThread
         @ShellCommandResult
-        private int setBindInstantServiceAllowed() {
-            return mService.handleSetBindInstantServiceAllowed(this);
-        }
-
-        @BinderThread
-        @ShellCommandResult
         private int refreshDebugProperties() {
             DebugFlags.FLAG_OPTIMIZE_START_INPUT.refresh();
             return ShellCommandResult.SUCCESS;
@@ -4774,9 +4469,6 @@
                 pw.println("    Synonym of dumpsys.");
                 pw.println("  ime <command> [options]");
                 pw.println("    Manipulate IMEs.  Run \"ime help\" for details.");
-                pw.println("  set-bind-instant-service-allowed true|false ");
-                pw.println("    Set whether binding to services provided by instant apps is "
-                        + "allowed.");
             }
         }
 
@@ -4825,53 +4517,6 @@
     // Shell command handlers:
 
     /**
-     * Handles {@code adb shell cmd input_method set-bind-instant-service-allowed}.
-     *
-     * @param shellCommand {@link ShellCommand} object that is handling this command.
-     * @return Exit code of the command.
-     */
-    @BinderThread
-    @RequiresPermission(android.Manifest.permission.MANAGE_BIND_INSTANT_SERVICE)
-    @ShellCommandResult
-    private int handleSetBindInstantServiceAllowed(@NonNull ShellCommand shellCommand) {
-        final String allowedString = shellCommand.getNextArgRequired();
-        if (allowedString == null) {
-            shellCommand.getErrPrintWriter().println("Error: no true/false specified");
-            return ShellCommandResult.FAILURE;
-        }
-        final boolean allowed = Boolean.parseBoolean(allowedString);
-        synchronized (mMethodMap) {
-            if (mContext.checkCallingOrSelfPermission(
-                    android.Manifest.permission.MANAGE_BIND_INSTANT_SERVICE)
-                    != PackageManager.PERMISSION_GRANTED) {
-                shellCommand.getErrPrintWriter().print(
-                        "Caller must have MANAGE_BIND_INSTANT_SERVICE permission");
-                return ShellCommandResult.FAILURE;
-            }
-
-            if (mBindInstantServiceAllowed == allowed) {
-                // Nothing to do.
-                return ShellCommandResult.SUCCESS;
-            }
-            mBindInstantServiceAllowed = allowed;
-
-            // Rebuild everything.
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                // Reset the current IME
-                resetSelectedInputMethodAndSubtypeLocked(null);
-                // Also reset the settings of the current IME
-                mSettings.putSelectedInputMethod(null);
-                buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
-                updateInputMethodsFromSettingsLocked(true /* enabledMayChange */);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-        return ShellCommandResult.SUCCESS;
-    }
-
-    /**
      * Handles {@code adb shell ime list}.
      * @param shellCommand {@link ShellCommand} object that is handling this command.
      * @return Exit code of the command.
@@ -4919,29 +4564,13 @@
      */
     @BinderThread
     @ShellCommandResult
-    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
     private int handleShellCommandEnableDisableInputMethod(
             @NonNull ShellCommand shellCommand, boolean enabled) {
         final String id = shellCommand.getNextArgRequired();
 
         final boolean previouslyEnabled;
         synchronized (mMethodMap) {
-            if (mContext.checkCallingOrSelfPermission(
-                    android.Manifest.permission.WRITE_SECURE_SETTINGS)
-                    != PackageManager.PERMISSION_GRANTED) {
-                shellCommand.getErrPrintWriter().print(
-                        "Caller must have WRITE_SECURE_SETTINGS permission");
-                throw new SecurityException(
-                        "Requires permission "
-                                + android.Manifest.permission.WRITE_SECURE_SETTINGS);
-            }
-
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                previouslyEnabled = setInputMethodEnabledLocked(id, enabled);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+            previouslyEnabled = setInputMethodEnabledLocked(id, enabled);
         }
         final PrintWriter pr = shellCommand.getOutPrintWriter();
         pr.print("Input method ");
@@ -4961,7 +4590,9 @@
     @ShellCommandResult
     private int handleShellCommandSetInputMethod(@NonNull ShellCommand shellCommand) {
         final String id = shellCommand.getNextArgRequired();
-        setInputMethod(null, id);
+        synchronized (mMethodMap) {
+            setInputMethodLocked(id, NOT_A_SUBTYPE_ID);
+        }
         final PrintWriter pr = shellCommand.getOutPrintWriter();
         pr.print("Input method ");
         pr.print(id);
@@ -4976,58 +4607,29 @@
      */
     @BinderThread
     @ShellCommandResult
-    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
     private int handleShellCommandResetInputMethod(@NonNull ShellCommand shellCommand) {
         synchronized (mMethodMap) {
-            if (mContext.checkCallingOrSelfPermission(
-                    android.Manifest.permission.WRITE_SECURE_SETTINGS)
-                    != PackageManager.PERMISSION_GRANTED) {
-                shellCommand.getErrPrintWriter().print(
-                        "Caller must have WRITE_SECURE_SETTINGS permission");
-                throw new SecurityException(
-                        "Requires permission "
-                                + android.Manifest.permission.WRITE_SECURE_SETTINGS);
-            }
             final String nextIme;
             final List<InputMethodInfo> nextEnabledImes;
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                synchronized (mMethodMap) {
-                    hideCurrentInputLocked(0, null);
-                    unbindCurrentMethodLocked();
-                    // Reset the current IME
-                    resetSelectedInputMethodAndSubtypeLocked(null);
-                    // Also reset the settings of the current IME
-                    mSettings.putSelectedInputMethod(null);
-                    // Disable all enabled IMEs.
-                    {
-                        final ArrayList<InputMethodInfo> enabledImes =
-                                mSettings.getEnabledInputMethodListLocked();
-                        final int N = enabledImes.size();
-                        for (int i = 0; i < N; ++i) {
-                            setInputMethodEnabledLocked(enabledImes.get(i).getId(), false);
-                        }
-                    }
-                    // Re-enable with default enabled IMEs.
-                    {
-                        final ArrayList<InputMethodInfo> defaultEnabledIme =
-                                InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList);
-                        final int N = defaultEnabledIme.size();
-                        for (int i = 0; i < N; ++i) {
-                            setInputMethodEnabledLocked(defaultEnabledIme.get(i).getId(), true);
-                        }
-                    }
-                    updateInputMethodsFromSettingsLocked(true /* enabledMayChange */);
-                    InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
-                            mSettings.getEnabledInputMethodListLocked(),
-                            mSettings.getCurrentUserId(),
-                            mContext.getBasePackageName());
-                    nextIme = mSettings.getSelectedInputMethod();
-                    nextEnabledImes = getEnabledInputMethodList();
-                }
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+            hideCurrentInputLocked(0, null);
+            unbindCurrentMethodLocked();
+            // Reset the current IME
+            resetSelectedInputMethodAndSubtypeLocked(null);
+            // Also reset the settings of the current IME
+            mSettings.putSelectedInputMethod(null);
+            // Disable all enabled IMEs.
+            mSettings.getEnabledInputMethodListLocked().forEach(
+                    imi -> setInputMethodEnabledLocked(imi.getId(), false));
+            // Re-enable with default enabled IMEs.
+            InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList).forEach(
+                    imi -> setInputMethodEnabledLocked(imi.getId(), true));
+            updateInputMethodsFromSettingsLocked(true /* enabledMayChange */);
+            InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
+                    mSettings.getEnabledInputMethodListLocked(),
+                    mSettings.getCurrentUserId(),
+                    mContext.getBasePackageName());
+            nextIme = mSettings.getSelectedInputMethod();
+            nextEnabledImes = getEnabledInputMethodList();
             final PrintWriter pr = shellCommand.getOutPrintWriter();
             pr.println("Reset current and enabled IMEs");
             pr.println("Newly selected IME:");
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 8e3f351..1137bf9 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -38,11 +38,11 @@
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype;
 import android.view.textservice.SpellCheckerInfo;
-import android.view.textservice.TextServicesManager;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.inputmethod.StartInputFlags;
+import com.android.server.textservices.TextServicesManagerInternal;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -63,7 +63,6 @@
     public static final int NOT_A_SUBTYPE_ID = -1;
     public static final String SUBTYPE_MODE_ANY = null;
     public static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
-    public static final String SUBTYPE_MODE_VOICE = "voice";
     private static final String TAG = "InputMethodUtils";
     private static final Locale ENGLISH_LOCALE = new Locale("en");
     private static final String NOT_A_SUBTYPE_ID_STR = String.valueOf(NOT_A_SUBTYPE_ID);
@@ -375,19 +374,6 @@
         return subtypes;
     }
 
-    public static ArrayList<InputMethodSubtype> getOverridingImplicitlyEnabledSubtypes(
-            InputMethodInfo imi, String mode) {
-        ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
-        final int subtypeCount = imi.getSubtypeCount();
-        for (int i = 0; i < subtypeCount; ++i) {
-            final InputMethodSubtype subtype = imi.getSubtypeAt(i);
-            if (subtype.overridesImplicitlyEnabledSubtype() && subtype.getMode().equals(mode)) {
-                subtypes.add(subtype);
-            }
-        }
-        return subtypes;
-    }
-
     public static InputMethodInfo getMostApplicableDefaultIME(List<InputMethodInfo> enabledImes) {
         if (enabledImes == null || enabledImes.isEmpty()) {
             return null;
@@ -641,7 +627,7 @@
         }
         // Only the current spell checker should be treated as an enabled one.
         final SpellCheckerInfo currentSpellChecker =
-                TextServicesManager.getInstance().getCurrentSpellChecker();
+                TextServicesManagerInternal.get().getCurrentSpellCheckerForUser(userId);
         for (final String packageName : systemImesDisabledUntilUsed) {
             if (DEBUG) {
                 Slog.d(TAG, "check " + packageName);
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 5edb5c8..98ed3ea 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -50,7 +50,6 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -65,6 +64,7 @@
 import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype;
+import android.view.inputmethod.InputMethodSystemProperty;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.inputmethod.IMultiClientInputMethod;
@@ -99,20 +99,6 @@
     static final String TAG = "MultiClientInputMethodManagerService";
     static final boolean DEBUG = false;
 
-    /**
-     * System property key for the production use. The value must be either empty or a valid
-     * (flattened) component name of the multi-client IME.
-     */
-    private static final String PROP_PROD_MULTI_CLIENT_IME = "ro.sys.multi_client_ime";
-
-    /**
-     * System property key for debugging purpose. The value must be either empty or a valid
-     * (flattened) component name of the multi-client IME.
-     *
-     * <p>This value will be ignored when {@link Build#IS_DEBUGGABLE} returns {@code false}</p>
-     */
-    private static final String PROP_DEBUG_MULTI_CLIENT_IME = "persist.debug.multi_client_ime";
-
     private static final long RECONNECT_DELAY_MSEC = 1000;
 
     /**
@@ -125,36 +111,8 @@
                     | Context.BIND_NOT_FOREGROUND
                     | Context.BIND_FOREGROUND_SERVICE;
 
-    /**
-     * Inner class to read system property on demand, not when
-     * {@link MultiClientInputMethodManagerService} class is accessed.
-     */
-    private static final class ImeComponentName {
-        private static ComponentName evaluate() {
-            if (Build.IS_DEBUGGABLE) {
-                // If debuggable, allow developers to override the multi-client IME component name
-                // with a different (writable) key.
-                final ComponentName debugIme = ComponentName.unflattenFromString(
-                        SystemProperties.get(PROP_DEBUG_MULTI_CLIENT_IME, ""));
-                if (debugIme != null) {
-                    return debugIme;
-                }
-            }
-            return ComponentName.unflattenFromString(
-                    SystemProperties.get(PROP_PROD_MULTI_CLIENT_IME, ""));
-        }
-
-        /**
-         * {@link ComponentName} of the multi-client IME.  {@code null} when the system is not
-         * configured to use multi-client IME.
-         */
-        @Nullable
-        static final ComponentName sValue = evaluate();
-    }
-
-    public static boolean isConfiguredToUse() {
-        return ImeComponentName.sValue != null;
-    }
+    private static final ComponentName sImeComponentName =
+            InputMethodSystemProperty.sMultiClientImeComponentName;
 
     private static void reportNotSupported() {
         if (DEBUG) {
@@ -270,10 +228,10 @@
                 return;
             }
 
-            final InputMethodInfo imi = queryInputMethod(mContext, userId, ImeComponentName.sValue);
+            final InputMethodInfo imi = queryInputMethod(mContext, userId, sImeComponentName);
             if (imi == null) {
                 Slog.w(TAG, "Multi-client InputMethod is not found. component="
-                        + ImeComponentName.sValue);
+                        + sImeComponentName);
                 synchronized (data.mLock) {
                     switch (data.mState) {
                         case PerUserState.USER_LOCKED:
@@ -518,9 +476,9 @@
                 return;
             }
             final String packageName = uri.getSchemeSpecificPart();
-            if (ImeComponentName.sValue == null
+            if (sImeComponentName == null
                     || packageName == null
-                    || !TextUtils.equals(ImeComponentName.sValue.getPackageName(), packageName)) {
+                    || !TextUtils.equals(sImeComponentName.getPackageName(), packageName)) {
                 return;
             }
             final int userId = UserHandle.getUserId(intent.getIntExtra(Intent.EXTRA_UID, 0));
@@ -1303,13 +1261,6 @@
 
         @BinderThread
         @Override
-        public List getShortcutInputMethodsAndSubtypes() {
-            reportNotSupported();
-            return null;
-        }
-
-        @BinderThread
-        @Override
         public boolean showSoftInput(
                 IInputMethodClient client, int flags, ResultReceiver resultReceiver) {
             final int callingUid = Binder.getCallingUid();
@@ -1562,18 +1513,6 @@
 
         @BinderThread
         @Override
-        public void setInputMethod(IBinder token, String id) {
-            reportNotSupported();
-        }
-
-        @BinderThread
-        @Override
-        public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
-            reportNotSupported();
-        }
-
-        @BinderThread
-        @Override
         public void registerSuggestionSpansForNotification(SuggestionSpan[] suggestionSpans) {
             reportNotSupported();
         }
diff --git a/services/core/java/com/android/server/job/JobConcurrencyManager.java b/services/core/java/com/android/server/job/JobConcurrencyManager.java
index ee1d847..827c0f1 100644
--- a/services/core/java/com/android/server/job/JobConcurrencyManager.java
+++ b/services/core/java/com/android/server/job/JobConcurrencyManager.java
@@ -44,17 +44,11 @@
      * We manipulate this array until we arrive at what jobs should be running on
      * what JobServiceContext.
      */
-    JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
+    JobStatus[] mRecycledAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
 
-    /**
-     * Indicates whether we need to act on this jobContext id
-     */
-    boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
+    boolean[] mRecycledSlotChanged = new boolean[MAX_JOB_CONTEXTS_COUNT];
 
-    /**
-     * The uid whose jobs we would like to assign to a context.
-     */
-    int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
+    int[] mRecycledPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
 
     JobConcurrencyManager(JobSchedulerService service) {
         mService = service;
@@ -99,28 +93,30 @@
                 break;
         }
 
-        JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
-        boolean[] act = mTmpAssignAct;
-        int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
-        int numActive = 0;
-        int numForeground = 0;
+        // To avoid GC churn, we recycle the arrays.
+        JobStatus[] contextIdToJobMap = mRecycledAssignContextIdToJobMap;
+        boolean[] slotChanged = mRecycledSlotChanged;
+        int[] preferredUidForContext = mRecycledPreferredUidForContext;
+
+        int numTotalRunningJobs = 0;
+        int numForegroundJobs = 0;
         for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
             final JobServiceContext js = mService.mActiveServices.get(i);
             final JobStatus status = js.getRunningJobLocked();
             if ((contextIdToJobMap[i] = status) != null) {
-                numActive++;
+                numTotalRunningJobs++;
                 if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
-                    numForeground++;
+                    numForegroundJobs++;
                 }
             }
-            act[i] = false;
+            slotChanged[i] = false;
             preferredUidForContext[i] = js.getPreferredUid();
         }
         if (DEBUG) {
             Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
         }
         for (int i=0; i<pendingJobs.size(); i++) {
-            JobStatus nextPending = pendingJobs.get(i);
+            final JobStatus nextPending = pendingJobs.get(i);
 
             // If job is already running, go to next job.
             int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
@@ -131,23 +127,32 @@
             final int priority = mService.evaluateJobPriorityLocked(nextPending);
             nextPending.lastEvaluatedPriority = priority;
 
-            // Find a context for nextPending. The context should be available OR
+            // Find an available slot for nextPending. The context should be available OR
             // it should have lowest priority among all running jobs
             // (sharing the same Uid as nextPending)
-            int minPriority = Integer.MAX_VALUE;
-            int minPriorityContextId = -1;
+            int minPriorityForPreemption = Integer.MAX_VALUE;
+            int selectedContextId = -1;
+            boolean startingJob = false;
             for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
                 JobStatus job = contextIdToJobMap[j];
                 int preferredUid = preferredUidForContext[j];
                 if (job == null) {
-                    if ((numActive < mService.mMaxActiveJobs ||
-                            (priority >= JobInfo.PRIORITY_TOP_APP &&
-                                    numForeground < mConstants.FG_JOB_COUNT)) &&
-                            (preferredUid == nextPending.getUid() ||
-                                    preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
+                    final boolean totalCountOk = numTotalRunningJobs < mService.mMaxActiveJobs;
+                    final boolean fgCountOk = (priority >= JobInfo.PRIORITY_TOP_APP)
+                            && (numForegroundJobs < mConstants.FG_JOB_COUNT);
+                    final boolean preferredUidOkay = (preferredUid == nextPending.getUid())
+                            || (preferredUid == JobServiceContext.NO_PREFERRED_UID);
+
+                    // TODO: The following check is slightly wrong.
+                    // Depending on how the pending jobs are sorted, we sometimes cap the total
+                    // job count at mMaxActiveJobs (when all jobs are FG jobs), or
+                    // at [mMaxActiveJobs + FG_JOB_COUNT] (when there are mMaxActiveJobs BG jobs
+                    // and then FG_JOB_COUNT FG jobs.)
+                    if ((totalCountOk || fgCountOk) && preferredUidOkay) {
                         // This slot is free, and we haven't yet hit the limit on
                         // concurrent jobs...  we can just throw the job in to here.
-                        minPriorityContextId = j;
+                        selectedContextId = j;
+                        startingJob = true;
                         break;
                     }
                     // No job on this context, but nextPending can't run here because
@@ -158,30 +163,39 @@
                 if (job.getUid() != nextPending.getUid()) {
                     continue;
                 }
-                if (mService.evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
+
+                final int jobPriority = mService.evaluateJobPriorityLocked(job);
+                if (jobPriority >= nextPending.lastEvaluatedPriority) {
                     continue;
                 }
-                if (minPriority > nextPending.lastEvaluatedPriority) {
-                    minPriority = nextPending.lastEvaluatedPriority;
-                    minPriorityContextId = j;
+
+                // TODO lastEvaluatedPriority should be evaluateJobPriorityLocked. (double check it)
+                if (minPriorityForPreemption > nextPending.lastEvaluatedPriority) {
+                    minPriorityForPreemption = nextPending.lastEvaluatedPriority;
+                    selectedContextId = j;
+                    // In this case, we're just going to preempt a low priority job, we're not
+                    // actually starting a job, so don't set startingJob.
                 }
             }
-            if (minPriorityContextId != -1) {
-                contextIdToJobMap[minPriorityContextId] = nextPending;
-                act[minPriorityContextId] = true;
-                numActive++;
+            if (selectedContextId != -1) {
+                contextIdToJobMap[selectedContextId] = nextPending;
+                slotChanged[selectedContextId] = true;
+            }
+            if (startingJob) {
+                // Increase the counters when we're going to start a job.
+                numTotalRunningJobs++;
                 if (priority >= JobInfo.PRIORITY_TOP_APP) {
-                    numForeground++;
+                    numForegroundJobs++;
                 }
             }
         }
         if (DEBUG) {
             Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
         }
-        tracker.noteConcurrency(numActive, numForeground);
+        tracker.noteConcurrency(numTotalRunningJobs, numForegroundJobs);
         for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
             boolean preservePreferredUid = false;
-            if (act[i]) {
+            if (slotChanged[i]) {
                 JobStatus js = activeServices.get(i).getRunningJobLocked();
                 if (js != null) {
                     if (DEBUG) {
@@ -195,7 +209,7 @@
                     final JobStatus pendingJob = contextIdToJobMap[i];
                     if (DEBUG) {
                         Slog.d(TAG, "About to run job on context "
-                                + String.valueOf(i) + ", job: " + pendingJob);
+                                + i + ", job: " + pendingJob);
                     }
                     for (int ic=0; ic<controllers.size(); ic++) {
                         controllers.get(ic).prepareForExecutionLocked(pendingJob);
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 1325f04..3f9d928 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -67,6 +67,7 @@
 import android.os.Temperature;
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
+import android.os.WorkSource;
 import android.provider.Settings;
 import android.text.format.DateUtils;
 import android.util.KeyValueListParser;
@@ -364,6 +365,8 @@
         private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
         private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
         private static final String KEY_USE_HEARTBEATS = "use_heartbeats";
+        private static final String KEY_TIME_CONTROLLER_SKIP_NOT_READY_JOBS =
+                "tc_skip_not_ready_jobs";
         private static final String KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
                 "qc_allowed_time_per_period_ms";
         private static final String KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
@@ -376,6 +379,8 @@
                 "qc_window_size_frequent_ms";
         private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
                 "qc_window_size_rare_ms";
+        private static final String KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS =
+                "qc_max_execution_time_ms";
 
         private static final int DEFAULT_MIN_IDLE_COUNT = 1;
         private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
@@ -402,6 +407,7 @@
         private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
         private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
         private static final boolean DEFAULT_USE_HEARTBEATS = true;
+        private static final boolean DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
         private static final long DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
                 10 * 60 * 1000L; // 10 minutes
         private static final long DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
@@ -414,6 +420,8 @@
                 8 * 60 * 60 * 1000L; // 8 hours
         private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
                 24 * 60 * 60 * 1000L; // 24 hours
+        private static final long DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS =
+                4 * 60 * 60 * 1000L; // 4 hours
 
         /**
          * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
@@ -538,6 +546,13 @@
          */
         public boolean USE_HEARTBEATS = DEFAULT_USE_HEARTBEATS;
 
+        /**
+         * Whether or not TimeController should skip setting wakeup alarms for jobs that aren't
+         * ready now.
+         */
+        public boolean TIME_CONTROLLER_SKIP_NOT_READY_JOBS =
+                DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS;
+
         /** How much time each app will have to run jobs within their standby bucket window. */
         public long QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
                 DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS;
@@ -581,6 +596,12 @@
         public long QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
                 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS;
 
+        /**
+         * The maximum amount of time an app can have its jobs running within a 24 hour window.
+         */
+        public long QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS =
+                DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS;
+
         private final KeyValueListParser mParser = new KeyValueListParser(',');
 
         void updateConstantsLocked(String value) {
@@ -653,6 +674,9 @@
             CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC,
                     DEFAULT_CONN_PREFETCH_RELAX_FRAC);
             USE_HEARTBEATS = mParser.getBoolean(KEY_USE_HEARTBEATS, DEFAULT_USE_HEARTBEATS);
+            TIME_CONTROLLER_SKIP_NOT_READY_JOBS = mParser.getBoolean(
+                    KEY_TIME_CONTROLLER_SKIP_NOT_READY_JOBS,
+                    DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS);
             QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = mParser.getDurationMillis(
                     KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS,
                     DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS);
@@ -671,6 +695,9 @@
             QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = mParser.getDurationMillis(
                     KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS,
                     DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS);
+            QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = mParser.getDurationMillis(
+                    KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS,
+                    DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS);
         }
 
         void dump(IndentingPrintWriter pw) {
@@ -705,6 +732,8 @@
             pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
             pw.printPair(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
             pw.printPair(KEY_USE_HEARTBEATS, USE_HEARTBEATS).println();
+            pw.printPair(KEY_TIME_CONTROLLER_SKIP_NOT_READY_JOBS,
+                    TIME_CONTROLLER_SKIP_NOT_READY_JOBS).println();
             pw.printPair(KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS,
                     QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS).println();
             pw.printPair(KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS,
@@ -717,6 +746,8 @@
                     QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS).println();
             pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS,
                     QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS).println();
+            pw.printPair(KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS,
+                    QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS).println();
             pw.decreaseIndent();
         }
 
@@ -748,6 +779,11 @@
             proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC);
             proto.write(ConstantsProto.USE_HEARTBEATS, USE_HEARTBEATS);
 
+            final long tcToken = proto.start(ConstantsProto.TIME_CONTROLLER);
+            proto.write(ConstantsProto.TimeController.SKIP_NOT_READY_JOBS,
+                    TIME_CONTROLLER_SKIP_NOT_READY_JOBS);
+            proto.end(tcToken);
+
             final long qcToken = proto.start(ConstantsProto.QUOTA_CONTROLLER);
             proto.write(ConstantsProto.QuotaController.ALLOWED_TIME_PER_PERIOD_MS,
                     QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS);
@@ -761,6 +797,8 @@
                     QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS);
             proto.write(ConstantsProto.QuotaController.RARE_WINDOW_SIZE_MS,
                     QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS);
+            proto.write(ConstantsProto.QuotaController.MAX_EXECUTION_TIME_MS,
+                    QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS);
             proto.end(qcToken);
 
             proto.end(token);
@@ -952,9 +990,15 @@
         return mConstants;
     }
 
+    public boolean isChainedAttributionEnabled() {
+        return WorkSource.isChainedBatteryAttributionEnabled(getContext());
+    }
+
     @Override
     public void onStartUser(int userHandle) {
-        mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
+        synchronized (mLock) {
+            mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
+        }
         // Let's kick any outstanding jobs for this user.
         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
     }
@@ -967,7 +1011,9 @@
 
     @Override
     public void onStopUser(int userHandle) {
-        mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
+        synchronized (mLock) {
+            mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
+        }
     }
 
     /**
@@ -1215,6 +1261,9 @@
     @Override
     public void onDeviceIdleStateChanged(boolean deviceIdle) {
         synchronized (mLock) {
+            if (DEBUG) {
+                Slog.d(TAG, "Doze state changed: " + deviceIdle);
+            }
             if (deviceIdle) {
                 // When becoming idle, make sure no jobs are actively running,
                 // except those using the idle exemption flag.
@@ -1783,6 +1832,9 @@
                         }
                     } break;
                     case MSG_CHECK_JOB:
+                        if (DEBUG) {
+                            Slog.d(TAG, "MSG_CHECK_JOB");
+                        }
                         if (mReportedActive) {
                             // if jobs are currently being run, queue all ready jobs for execution.
                             queueReadyJobsForExecutionLocked();
@@ -1792,6 +1844,9 @@
                         }
                         break;
                     case MSG_CHECK_JOB_GREEDY:
+                        if (DEBUG) {
+                            Slog.d(TAG, "MSG_CHECK_JOB_GREEDY");
+                        }
                         queueReadyJobsForExecutionLocked();
                         break;
                     case MSG_STOP_JOB:
@@ -2150,8 +2205,7 @@
 
         final boolean jobExists = mJobs.containsJob(job);
 
-        final int userId = job.getUserId();
-        final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
+        final boolean userStarted = areUsersStartedLocked(job);
 
         if (DEBUG) {
             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
@@ -2239,7 +2293,7 @@
         try {
             componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
                     job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
-                    userId) != null);
+                    job.getUserId()) != null);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -3052,6 +3106,13 @@
                     printed = true;
                     pw.println("user-stopped");
                 }
+                if (!ArrayUtils.contains(mStartedUsers, js.getSourceUserId())) {
+                    if (printed) {
+                        pw.print(" ");
+                    }
+                    printed = true;
+                    pw.println("source-user-stopped");
+                }
                 if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) {
                     if (printed) {
                         pw.print(" ");
@@ -3203,7 +3264,7 @@
                     pw.print(" (job=");
                     pw.print(job.isReady());
                     pw.print(" user=");
-                    pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
+                    pw.print(areUsersStartedLocked(job));
                     pw.print(" !pending=");
                     pw.print(!mPendingJobs.contains(job));
                     pw.print(" !active=");
@@ -3373,7 +3434,7 @@
                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY,
                             job.isReady());
                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_USER_STARTED,
-                            ArrayUtils.contains(mStartedUsers, job.getUserId()));
+                            areUsersStartedLocked(job));
                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_PENDING,
                             mPendingJobs.contains(job));
                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
diff --git a/services/core/java/com/android/server/job/controllers/QuotaController.java b/services/core/java/com/android/server/job/controllers/QuotaController.java
index 58ee217..ac2dbdf 100644
--- a/services/core/java/com/android/server/job/controllers/QuotaController.java
+++ b/services/core/java/com/android/server/job/controllers/QuotaController.java
@@ -54,14 +54,13 @@
 import com.android.server.job.StateControllerProto;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 /**
- * Controller that tracks whether a package has exceeded its standby bucket quota.
+ * Controller that tracks whether an app has exceeded its standby bucket quota.
  *
  * Each job in each bucket is given 10 minutes to run within its respective time window. Active
  * jobs can run indefinitely, working set jobs can run for 10 minutes within a 2 hour window,
@@ -203,6 +202,80 @@
         }
     }
 
+    private static int hashLong(long val) {
+        return (int) (val ^ (val >>> 32));
+    }
+
+    @VisibleForTesting
+    static class ExecutionStats {
+        /**
+         * The time at which this record should be considered invalid, in the elapsed realtime
+         * timebase.
+         */
+        public long invalidTimeElapsed;
+
+        public long windowSizeMs;
+
+        /** The total amount of time the app ran in its respective bucket window size. */
+        public long executionTimeInWindowMs;
+        public int bgJobCountInWindow;
+
+        /** The total amount of time the app ran in the last {@link MAX_PERIOD_MS}. */
+        public long executionTimeInMaxPeriodMs;
+        public int bgJobCountInMaxPeriod;
+
+        /**
+         * The time after which the sum of all the app's sessions plus {@link mQuotaBufferMs} equals
+         * the quota. This is only valid if
+         * executionTimeInWindowMs >= {@link mAllowedTimePerPeriodMs} or
+         * executionTimeInMaxPeriodMs >= {@link mMaxExecutionTimeMs}.
+         */
+        public long quotaCutoffTimeElapsed;
+
+        @Override
+        public String toString() {
+            return new StringBuilder()
+                    .append("invalidTime=").append(invalidTimeElapsed).append(", ")
+                    .append("windowSize=").append(windowSizeMs).append(", ")
+                    .append("executionTimeInWindow=").append(executionTimeInWindowMs).append(", ")
+                    .append("bgJobCountInWindow=").append(bgJobCountInWindow).append(", ")
+                    .append("executionTimeInMaxPeriod=").append(executionTimeInMaxPeriodMs)
+                    .append(", ")
+                    .append("bgJobCountInMaxPeriod=").append(bgJobCountInMaxPeriod).append(", ")
+                    .append("quotaCutoffTime=").append(quotaCutoffTimeElapsed)
+                    .toString();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof ExecutionStats) {
+                ExecutionStats other = (ExecutionStats) obj;
+                return this.invalidTimeElapsed == other.invalidTimeElapsed
+                        && this.windowSizeMs == other.windowSizeMs
+                        && this.executionTimeInWindowMs == other.executionTimeInWindowMs
+                        && this.bgJobCountInWindow == other.bgJobCountInWindow
+                        && this.executionTimeInMaxPeriodMs == other.executionTimeInMaxPeriodMs
+                        && this.bgJobCountInMaxPeriod == other.bgJobCountInMaxPeriod
+                        && this.quotaCutoffTimeElapsed == other.quotaCutoffTimeElapsed;
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            int result = 0;
+            result = 31 * result + hashLong(invalidTimeElapsed);
+            result = 31 * result + hashLong(windowSizeMs);
+            result = 31 * result + hashLong(executionTimeInWindowMs);
+            result = 31 * result + bgJobCountInWindow;
+            result = 31 * result + hashLong(executionTimeInMaxPeriodMs);
+            result = 31 * result + bgJobCountInMaxPeriod;
+            result = 31 * result + hashLong(quotaCutoffTimeElapsed);
+            return result;
+        }
+    }
+
     /** List of all tracked jobs keyed by source package-userId combo. */
     private final UserPackageMap<ArraySet<JobStatus>> mTrackedJobs = new UserPackageMap<>();
 
@@ -218,6 +291,9 @@
      */
     private final UserPackageMap<QcAlarmListener> mInQuotaAlarmListeners = new UserPackageMap<>();
 
+    /** Cached calculation results for each app, with the standby buckets as the array indices. */
+    private final UserPackageMap<ExecutionStats[]> mExecutionStatsCache = new UserPackageMap<>();
+
     private final AlarmManager mAlarmManager;
     private final ChargingTracker mChargeTracker;
     private final Handler mHandler;
@@ -235,11 +311,29 @@
     private long mAllowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS;
 
     /**
-     * How much time the package should have before transitioning from out-of-quota to in-quota.
-     * This should not affect processing if the package is already in-quota.
+     * The maximum amount of time an app can have its jobs running within a {@link MAX_PERIOD_MS}
+     * window.
+     */
+    private long mMaxExecutionTimeMs = 4 * 60 * MINUTE_IN_MILLIS;
+
+    /**
+     * How much time the app should have before transitioning from out-of-quota to in-quota.
+     * This should not affect processing if the app is already in-quota.
      */
     private long mQuotaBufferMs = 30 * 1000L; // 30 seconds
 
+    /**
+     * {@link mAllowedTimePerPeriodMs} - {@link mQuotaBufferMs}. This can be used to determine when
+     * an app will have enough quota to transition from out-of-quota to in-quota.
+     */
+    private long mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
+
+    /**
+     * {@link mMaxExecutionTimeMs} - {@link mQuotaBufferMs}. This can be used to determine when an
+     * app will have enough quota to transition from out-of-quota to in-quota.
+     */
+    private long mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
+
     private long mNextCleanupTimeElapsed = 0;
     private final AlarmManager.OnAlarmListener mSessionCleanupAlarmListener =
             new AlarmManager.OnAlarmListener() {
@@ -263,7 +357,7 @@
     /** The maximum period any bucket can have. */
     private static final long MAX_PERIOD_MS = 24 * 60 * MINUTE_IN_MILLIS;
 
-    /** A package has reached its quota. The message should contain a {@link Package} object. */
+    /** An app has reached its quota. The message should contain a {@link Package} object. */
     private static final int MSG_REACHED_QUOTA = 0;
     /** Drop any old timing sessions. */
     private static final int MSG_CLEAN_UP_SESSIONS = 1;
@@ -341,12 +435,15 @@
                 Math.max(MINUTE_IN_MILLIS, mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS));
         if (mAllowedTimePerPeriodMs != newAllowedTimeMs) {
             mAllowedTimePerPeriodMs = newAllowedTimeMs;
+            mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
             changed = true;
         }
         long newQuotaBufferMs = Math.max(0,
                 Math.min(5 * MINUTE_IN_MILLIS, mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS));
         if (mQuotaBufferMs != newQuotaBufferMs) {
             mQuotaBufferMs = newQuotaBufferMs;
+            mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
+            mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
             changed = true;
         }
         long newActivePeriodMs = Math.max(mAllowedTimePerPeriodMs,
@@ -373,6 +470,13 @@
             mBucketPeriodsMs[RARE_INDEX] = newRarePeriodMs;
             changed = true;
         }
+        long newMaxExecutionTimeMs = Math.max(60 * MINUTE_IN_MILLIS,
+                Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS));
+        if (mMaxExecutionTimeMs != newMaxExecutionTimeMs) {
+            mMaxExecutionTimeMs = newMaxExecutionTimeMs;
+            mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
+            changed = true;
+        }
 
         if (changed) {
             // Update job bookkeeping out of band.
@@ -406,6 +510,7 @@
             mAlarmManager.cancel(alarmListener);
             mInQuotaAlarmListeners.delete(userId, packageName);
         }
+        mExecutionStatsCache.delete(userId, packageName);
     }
 
     @Override
@@ -414,6 +519,7 @@
         mPkgTimers.delete(userId);
         mTimingSessions.delete(userId);
         mInQuotaAlarmListeners.delete(userId);
+        mExecutionStatsCache.delete(userId);
     }
 
     /**
@@ -439,7 +545,6 @@
     private boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName,
             final int standbyBucket) {
         if (standbyBucket == NEVER_INDEX) return false;
-        if (standbyBucket == ACTIVE_INDEX) return true;
         // This check is needed in case the flag is toggled after a job has been registered.
         if (!mShouldThrottle) return true;
 
@@ -472,46 +577,152 @@
         if (standbyBucket == NEVER_INDEX) {
             return 0;
         }
-        final long bucketWindowSizeMs = mBucketPeriodsMs[standbyBucket];
-        final long trailingRunDurationMs = getTrailingExecutionTimeLocked(
-                userId, packageName, bucketWindowSizeMs);
-        return mAllowedTimePerPeriodMs - trailingRunDurationMs;
+        final ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
+        return Math.min(mAllowedTimePerPeriodMs - stats.executionTimeInWindowMs,
+                mMaxExecutionTimeMs - stats.executionTimeInMaxPeriodMs);
     }
 
-    /** Returns how long the uid has had jobs running within the most recent window. */
+    /** Returns the execution stats of the app in the most recent window. */
     @VisibleForTesting
-    long getTrailingExecutionTimeLocked(final int userId, @NonNull final String packageName,
-            final long windowSizeMs) {
-        long totalTime = 0;
+    @NonNull
+    ExecutionStats getExecutionStatsLocked(final int userId, @NonNull final String packageName,
+            final int standbyBucket) {
+        if (standbyBucket == NEVER_INDEX) {
+            Slog.wtf(TAG, "getExecutionStatsLocked called for a NEVER app.");
+            return new ExecutionStats();
+        }
+        ExecutionStats[] appStats = mExecutionStatsCache.get(userId, packageName);
+        if (appStats == null) {
+            appStats = new ExecutionStats[mBucketPeriodsMs.length];
+            mExecutionStatsCache.add(userId, packageName, appStats);
+        }
+        ExecutionStats stats = appStats[standbyBucket];
+        if (stats == null) {
+            stats = new ExecutionStats();
+            appStats[standbyBucket] = stats;
+        }
+        final long bucketWindowSizeMs = mBucketPeriodsMs[standbyBucket];
+        Timer timer = mPkgTimers.get(userId, packageName);
+        if ((timer != null && timer.isActive())
+                || stats.invalidTimeElapsed <= sElapsedRealtimeClock.millis()
+                || stats.windowSizeMs != bucketWindowSizeMs) {
+            // The stats are no longer valid.
+            stats.windowSizeMs = bucketWindowSizeMs;
+            updateExecutionStatsLocked(userId, packageName, stats);
+        }
+
+        return stats;
+    }
+
+    @VisibleForTesting
+    void updateExecutionStatsLocked(final int userId, @NonNull final String packageName,
+            @NonNull ExecutionStats stats) {
+        stats.executionTimeInWindowMs = 0;
+        stats.bgJobCountInWindow = 0;
+        stats.executionTimeInMaxPeriodMs = 0;
+        stats.bgJobCountInMaxPeriod = 0;
+        stats.quotaCutoffTimeElapsed = 0;
 
         Timer timer = mPkgTimers.get(userId, packageName);
         final long nowElapsed = sElapsedRealtimeClock.millis();
+        stats.invalidTimeElapsed = nowElapsed + MAX_PERIOD_MS;
         if (timer != null && timer.isActive()) {
-            totalTime = timer.getCurrentDuration(nowElapsed);
+            stats.executionTimeInWindowMs =
+                    stats.executionTimeInMaxPeriodMs = timer.getCurrentDuration(nowElapsed);
+            stats.bgJobCountInWindow = stats.bgJobCountInMaxPeriod = timer.getBgJobCount();
+            // If the timer is active, the value will be stale at the next method call, so
+            // invalidate now.
+            stats.invalidTimeElapsed = nowElapsed;
+            if (stats.executionTimeInWindowMs >= mAllowedTimeIntoQuotaMs) {
+                stats.quotaCutoffTimeElapsed = Math.max(stats.quotaCutoffTimeElapsed,
+                        nowElapsed - mAllowedTimeIntoQuotaMs);
+            }
+            if (stats.executionTimeInMaxPeriodMs >= mMaxExecutionTimeIntoQuotaMs) {
+                stats.quotaCutoffTimeElapsed = Math.max(stats.quotaCutoffTimeElapsed,
+                        nowElapsed - mMaxExecutionTimeIntoQuotaMs);
+            }
         }
 
         List<TimingSession> sessions = mTimingSessions.get(userId, packageName);
         if (sessions == null || sessions.size() == 0) {
-            return totalTime;
+            return;
         }
 
-        final long startElapsed = nowElapsed - windowSizeMs;
+        final long startWindowElapsed = nowElapsed - stats.windowSizeMs;
+        final long startMaxElapsed = nowElapsed - MAX_PERIOD_MS;
+        // The minimum time between the start time and the beginning of the sessions that were
+        // looked at --> how much time the stats will be valid for.
+        long emptyTimeMs = Long.MAX_VALUE;
         // Sessions are non-overlapping and in order of occurrence, so iterating backwards will get
         // the most recent ones.
         for (int i = sessions.size() - 1; i >= 0; --i) {
             TimingSession session = sessions.get(i);
-            if (startElapsed < session.startTimeElapsed) {
-                totalTime += session.endTimeElapsed - session.startTimeElapsed;
-            } else if (startElapsed < session.endTimeElapsed) {
+
+            // Window management.
+            if (startWindowElapsed < session.startTimeElapsed) {
+                stats.executionTimeInWindowMs += session.endTimeElapsed - session.startTimeElapsed;
+                stats.bgJobCountInWindow += session.bgJobCount;
+                emptyTimeMs = Math.min(emptyTimeMs, session.startTimeElapsed - startWindowElapsed);
+                if (stats.executionTimeInWindowMs >= mAllowedTimeIntoQuotaMs) {
+                    stats.quotaCutoffTimeElapsed = Math.max(stats.quotaCutoffTimeElapsed,
+                            session.startTimeElapsed + stats.executionTimeInWindowMs
+                                    - mAllowedTimeIntoQuotaMs);
+                }
+            } else if (startWindowElapsed < session.endTimeElapsed) {
                 // The session started before the window but ended within the window. Only include
                 // the portion that was within the window.
-                totalTime += session.endTimeElapsed - startElapsed;
+                stats.executionTimeInWindowMs += session.endTimeElapsed - startWindowElapsed;
+                stats.bgJobCountInWindow += session.bgJobCount;
+                emptyTimeMs = 0;
+                if (stats.executionTimeInWindowMs >= mAllowedTimeIntoQuotaMs) {
+                    stats.quotaCutoffTimeElapsed = Math.max(stats.quotaCutoffTimeElapsed,
+                            startWindowElapsed + stats.executionTimeInWindowMs
+                                    - mAllowedTimeIntoQuotaMs);
+                }
+            }
+
+            // Max period check.
+            if (startMaxElapsed < session.startTimeElapsed) {
+                stats.executionTimeInMaxPeriodMs +=
+                        session.endTimeElapsed - session.startTimeElapsed;
+                stats.bgJobCountInMaxPeriod += session.bgJobCount;
+                emptyTimeMs = Math.min(emptyTimeMs, session.startTimeElapsed - startMaxElapsed);
+                if (stats.executionTimeInMaxPeriodMs >= mMaxExecutionTimeIntoQuotaMs) {
+                    stats.quotaCutoffTimeElapsed = Math.max(stats.quotaCutoffTimeElapsed,
+                            session.startTimeElapsed + stats.executionTimeInMaxPeriodMs
+                                    - mMaxExecutionTimeIntoQuotaMs);
+                }
+            } else if (startMaxElapsed < session.endTimeElapsed) {
+                // The session started before the window but ended within the window. Only include
+                // the portion that was within the window.
+                stats.executionTimeInMaxPeriodMs += session.endTimeElapsed - startMaxElapsed;
+                stats.bgJobCountInMaxPeriod += session.bgJobCount;
+                emptyTimeMs = 0;
+                if (stats.executionTimeInMaxPeriodMs >= mMaxExecutionTimeIntoQuotaMs) {
+                    stats.quotaCutoffTimeElapsed = Math.max(stats.quotaCutoffTimeElapsed,
+                            startMaxElapsed + stats.executionTimeInMaxPeriodMs
+                                    - mMaxExecutionTimeIntoQuotaMs);
+                }
             } else {
                 // This session ended before the window. No point in going any further.
-                return totalTime;
+                break;
             }
         }
-        return totalTime;
+        stats.invalidTimeElapsed = nowElapsed + emptyTimeMs;
+    }
+
+    private void invalidateAllExecutionStatsLocked(final int userId,
+            @NonNull final String packageName) {
+        ExecutionStats[] appStats = mExecutionStatsCache.get(userId, packageName);
+        if (appStats != null) {
+            final long nowElapsed = sElapsedRealtimeClock.millis();
+            for (int i = 0; i < appStats.length; ++i) {
+                ExecutionStats stats = appStats[i];
+                if (stats != null) {
+                    stats.invalidTimeElapsed = nowElapsed;
+                }
+            }
+        }
     }
 
     @VisibleForTesting
@@ -524,6 +735,8 @@
                 mTimingSessions.add(userId, packageName, sessions);
             }
             sessions.add(session);
+            // Adding a new session means that the current stats are now incorrect.
+            invalidateAllExecutionStatsLocked(userId, packageName);
 
             maybeScheduleCleanupAlarmLocked();
         }
@@ -657,87 +870,43 @@
     @VisibleForTesting
     void maybeScheduleStartAlarmLocked(final int userId, @NonNull final String packageName,
             final int standbyBucket) {
-        final String pkgString = string(userId, packageName);
         if (standbyBucket == NEVER_INDEX) {
             return;
-        } else if (standbyBucket == ACTIVE_INDEX) {
-            // ACTIVE apps are "always" in quota.
-            if (DEBUG) {
-                Slog.w(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString
-                        + " even though it is active");
-            }
-            mHandler.obtainMessage(MSG_CHECK_PACKAGE, userId, 0, packageName).sendToTarget();
+        }
 
-            QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
+        final String pkgString = string(userId, packageName);
+        ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
+        QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
+        if (stats.executionTimeInWindowMs < mAllowedTimePerPeriodMs
+                && stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs) {
+            // Already in quota. Why was this method called?
+            if (DEBUG) {
+                Slog.e(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString
+                        + " even though it already has "
+                        + getRemainingExecutionTimeLocked(userId, packageName, standbyBucket)
+                        + "ms in its quota.");
+            }
             if (alarmListener != null) {
                 // Cancel any pending alarm.
                 mAlarmManager.cancel(alarmListener);
                 // Set the trigger time to 0 so that the alarm doesn't think it's still waiting.
                 alarmListener.setTriggerTime(0);
             }
-            return;
-        }
-
-        List<TimingSession> sessions = mTimingSessions.get(userId, packageName);
-        if (sessions == null || sessions.size() == 0) {
-            // If there are no sessions, then the job is probably in quota.
-            if (DEBUG) {
-                Slog.wtf(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString
-                        + " even though it is likely within its quota.");
-            }
             mHandler.obtainMessage(MSG_CHECK_PACKAGE, userId, 0, packageName).sendToTarget();
             return;
         }
-
-        final long bucketWindowSizeMs = mBucketPeriodsMs[standbyBucket];
-        final long nowElapsed = sElapsedRealtimeClock.millis();
-        // How far back we need to look.
-        final long startElapsed = nowElapsed - bucketWindowSizeMs;
-
-        long totalTime = 0;
-        long cutoffTimeElapsed = nowElapsed;
-        for (int i = sessions.size() - 1; i >= 0; i--) {
-            TimingSession session = sessions.get(i);
-            if (startElapsed < session.startTimeElapsed) {
-                cutoffTimeElapsed = session.startTimeElapsed;
-                totalTime += session.endTimeElapsed - session.startTimeElapsed;
-            } else if (startElapsed < session.endTimeElapsed) {
-                // The session started before the window but ended within the window. Only
-                // include the portion that was within the window.
-                cutoffTimeElapsed = startElapsed;
-                totalTime += session.endTimeElapsed - startElapsed;
-            } else {
-                // This session ended before the window. No point in going any further.
-                break;
-            }
-            if (totalTime >= mAllowedTimePerPeriodMs) {
-                break;
-            }
-        }
-        if (totalTime < mAllowedTimePerPeriodMs) {
-            // Already in quota. Why was this method called?
-            if (DEBUG) {
-                Slog.w(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString
-                        + " even though it already has " + (mAllowedTimePerPeriodMs - totalTime)
-                        + "ms in its quota.");
-            }
-            mHandler.obtainMessage(MSG_CHECK_PACKAGE, userId, 0, packageName).sendToTarget();
-            return;
-        }
-
-        QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
         if (alarmListener == null) {
             alarmListener = new QcAlarmListener(userId, packageName);
             mInQuotaAlarmListeners.add(userId, packageName, alarmListener);
         }
 
-        // We add all the way back to the beginning of a session (or the window) even when we don't
-        // need to (in order to simplify the for loop above), so there might be some extra we
-        // need to add back.
-        final long extraTimeMs = totalTime - mAllowedTimePerPeriodMs;
         // The time this app will have quota again.
-        final long inQuotaTimeElapsed =
-                cutoffTimeElapsed + extraTimeMs + mQuotaBufferMs + bucketWindowSizeMs;
+        long inQuotaTimeElapsed =
+                stats.quotaCutoffTimeElapsed + stats.windowSizeMs;
+        if (stats.executionTimeInMaxPeriodMs >= mMaxExecutionTimeMs) {
+            inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed,
+                    stats.quotaCutoffTimeElapsed + MAX_PERIOD_MS);
+        }
         // Only schedule the alarm if:
         // 1. There isn't one currently scheduled
         // 2. The new alarm is significantly earlier than the previous alarm (which could be the
@@ -747,13 +916,15 @@
         // TODO: this might be overengineering. Simplify if proven safe.
         if (!alarmListener.isWaiting()
                 || inQuotaTimeElapsed < alarmListener.getTriggerTimeElapsed() - 3 * MINUTE_IN_MILLIS
-                || alarmListener.getTriggerTimeElapsed() < inQuotaTimeElapsed - mQuotaBufferMs) {
+                || alarmListener.getTriggerTimeElapsed() < inQuotaTimeElapsed) {
             if (DEBUG) Slog.d(TAG, "Scheduling start alarm for " + pkgString);
             // If the next time this app will have quota is at least 3 minutes before the
             // alarm is supposed to go off, reschedule the alarm.
             mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, inQuotaTimeElapsed,
                     ALARM_TAG_QUOTA_CHECK, alarmListener, mHandler);
             alarmListener.setTriggerTime(inQuotaTimeElapsed);
+        } else if (DEBUG) {
+            Slog.d(TAG, "No need to scheduling start alarm for " + pkgString);
         }
     }
 
@@ -816,10 +987,18 @@
         // How many background jobs ran during this session.
         public final int bgJobCount;
 
-        TimingSession(long startElapsed, long endElapsed, int jobCount) {
+        private final int mHashCode;
+
+        TimingSession(long startElapsed, long endElapsed, int bgJobCount) {
             this.startTimeElapsed = startElapsed;
             this.endTimeElapsed = endElapsed;
-            this.bgJobCount = jobCount;
+            this.bgJobCount = bgJobCount;
+
+            int hashCode = 0;
+            hashCode = 31 * hashCode + hashLong(startTimeElapsed);
+            hashCode = 31 * hashCode + hashLong(endTimeElapsed);
+            hashCode = 31 * hashCode + bgJobCount;
+            mHashCode = hashCode;
         }
 
         @Override
@@ -842,7 +1021,7 @@
 
         @Override
         public int hashCode() {
-            return Arrays.hashCode(new long[] {startTimeElapsed, endTimeElapsed, bgJobCount});
+            return mHashCode;
         }
 
         public void dump(IndentingPrintWriter pw) {
@@ -902,6 +1081,9 @@
                     if (mRunningBgJobs.size() == 1) {
                         // Started tracking the first job.
                         mStartTimeElapsed = sElapsedRealtimeClock.millis();
+                        // Starting the timer means that all cached execution stats are now
+                        // incorrect.
+                        invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName);
                         scheduleCutoff();
                     }
                 }
@@ -966,6 +1148,12 @@
             }
         }
 
+        int getBgJobCount() {
+            synchronized (mLock) {
+                return mBgJobCount;
+            }
+        }
+
         void onChargingChanged(long nowElapsed, boolean isCharging) {
             synchronized (mLock) {
                 if (isCharging) {
@@ -978,6 +1166,9 @@
                         // repeatedly plugged in and unplugged, the job count for a package may be
                         // artificially high.
                         mBgJobCount = mRunningBgJobs.size();
+                        // Starting the timer means that all cached execution stats are now
+                        // incorrect.
+                        invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName);
                         // Schedule cutoff since we're now actively tracking for quotas again.
                         scheduleCutoff();
                     }
@@ -1239,6 +1430,11 @@
     }
 
     @VisibleForTesting
+    long getMaxExecutionTimeMs() {
+        return mMaxExecutionTimeMs;
+    }
+
+    @VisibleForTesting
     @Nullable
     List<TimingSession> getTimingSessions(int userId, String packageName) {
         return mTimingSessions.get(userId, packageName);
diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java
index 04d5795..26f3caf 100644
--- a/services/core/java/com/android/server/job/controllers/TimeController.java
+++ b/services/core/java/com/android/server/job/controllers/TimeController.java
@@ -30,6 +30,7 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.StateControllerProto;
@@ -68,7 +69,7 @@
 
         mNextJobExpiredElapsedMillis = Long.MAX_VALUE;
         mNextDelayExpiredElapsedMillis = Long.MAX_VALUE;
-        mChainedAttributionEnabled = WorkSource.isChainedBatteryAttributionEnabled(mContext);
+        mChainedAttributionEnabled = mService.isChainedAttributionEnabled();
     }
 
     /**
@@ -110,11 +111,24 @@
                 it.next();
             }
             it.add(job);
+
             job.setTrackingController(JobStatus.TRACKING_TIME);
-            maybeUpdateAlarmsLocked(
-                    job.hasTimingDelayConstraint() ? job.getEarliestRunTime() : Long.MAX_VALUE,
-                    job.hasDeadlineConstraint() ? job.getLatestRunTimeElapsed() : Long.MAX_VALUE,
-                    deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
+            WorkSource ws = deriveWorkSource(job.getSourceUid(), job.getSourcePackageName());
+            final long deadlineExpiredElapsed =
+                    job.hasDeadlineConstraint() ? job.getLatestRunTimeElapsed() : Long.MAX_VALUE;
+            final long delayExpiredElapsed =
+                    job.hasTimingDelayConstraint() ? job.getEarliestRunTime() : Long.MAX_VALUE;
+            if (mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS) {
+                if (wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_TIMING_DELAY)) {
+                    maybeUpdateDelayAlarmLocked(delayExpiredElapsed, ws);
+                }
+                if (wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_DEADLINE)) {
+                    maybeUpdateDeadlineAlarmLocked(deadlineExpiredElapsed, ws);
+                }
+            } else {
+                maybeUpdateDelayAlarmLocked(delayExpiredElapsed, ws);
+                maybeUpdateDeadlineAlarmLocked(deadlineExpiredElapsed, ws);
+            }
         }
     }
 
@@ -133,6 +147,34 @@
         }
     }
 
+    @Override
+    public void onConstantsUpdatedLocked() {
+        checkExpiredDelaysAndResetAlarm();
+        checkExpiredDeadlinesAndResetAlarm();
+    }
+
+    @Override
+    public void evaluateStateLocked(JobStatus job) {
+        if (!mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS) {
+            return;
+        }
+
+        if (job.hasTimingDelayConstraint()
+                && job.getEarliestRunTime() <= mNextDelayExpiredElapsedMillis) {
+            checkExpiredDelaysAndResetAlarm();
+        }
+        if (job.hasDeadlineConstraint()
+                && job.getLatestRunTimeElapsed() <= mNextJobExpiredElapsedMillis) {
+            checkExpiredDeadlinesAndResetAlarm();
+        }
+    }
+
+    @Override
+    public void reevaluateStateLocked(int uid) {
+        checkExpiredDelaysAndResetAlarm();
+        checkExpiredDeadlinesAndResetAlarm();
+    }
+
     /**
      * Determines whether this controller can stop tracking the given job.
      * The controller is no longer interested in a job once its time constraint is satisfied, and
@@ -156,14 +198,15 @@
      * Checks list of jobs for ones that have an expired deadline, sending them to the JobScheduler
      * if so, removing them from this list, and updating the alarm for the next expiry time.
      */
-    private void checkExpiredDeadlinesAndResetAlarm() {
+    @VisibleForTesting
+    void checkExpiredDeadlinesAndResetAlarm() {
         synchronized (mLock) {
             long nextExpiryTime = Long.MAX_VALUE;
             int nextExpiryUid = 0;
             String nextExpiryPackageName = null;
             final long nowElapsedMillis = sElapsedRealtimeClock.millis();
 
-            Iterator<JobStatus> it = mTrackedJobs.iterator();
+            ListIterator<JobStatus> it = mTrackedJobs.listIterator();
             while (it.hasNext()) {
                 JobStatus job = it.next();
                 if (!job.hasDeadlineConstraint()) {
@@ -171,9 +214,22 @@
                 }
 
                 if (evaluateDeadlineConstraint(job, nowElapsedMillis)) {
-                    mStateChangedListener.onRunJobNow(job);
+                    if (job.isReady()) {
+                        // If the job still isn't ready, there's no point trying to rush the
+                        // Scheduler.
+                        mStateChangedListener.onRunJobNow(job);
+                    }
                     it.remove();
                 } else {  // Sorted by expiry time, so take the next one and stop.
+                    if (mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS
+                            && !wouldBeReadyWithConstraintLocked(
+                            job, JobStatus.CONSTRAINT_DEADLINE)) {
+                        if (DEBUG) {
+                            Slog.i(TAG,
+                                    "Skipping " + job + " because deadline won't make it ready.");
+                        }
+                        continue;
+                    }
                     nextExpiryTime = job.getLatestRunTimeElapsed();
                     nextExpiryUid = job.getSourceUid();
                     nextExpiryPackageName = job.getSourcePackageName();
@@ -202,7 +258,8 @@
      * Handles alarm that notifies us that a job's delay has expired. Iterates through the list of
      * tracked jobs and marks them as ready as appropriate.
      */
-    private void checkExpiredDelaysAndResetAlarm() {
+    @VisibleForTesting
+    void checkExpiredDelaysAndResetAlarm() {
         synchronized (mLock) {
             final long nowElapsedMillis = sElapsedRealtimeClock.millis();
             long nextDelayTime = Long.MAX_VALUE;
@@ -223,6 +280,15 @@
                         ready = true;
                     }
                 } else if (!job.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY)) {
+                    if (mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS
+                            && !wouldBeReadyWithConstraintLocked(
+                            job, JobStatus.CONSTRAINT_TIMING_DELAY)) {
+                        if (DEBUG) {
+                            Slog.i(TAG,
+                                    "Skipping " + job + " because delay won't make it ready.");
+                        }
+                        continue;
+                    }
                     // If this job still doesn't have its delay constraint satisfied,
                     // then see if it is the next upcoming delay time for the alarm.
                     final long jobDelayTime = job.getEarliestRunTime();
@@ -262,11 +328,13 @@
         return false;
     }
 
-    private void maybeUpdateAlarmsLocked(long delayExpiredElapsed, long deadlineExpiredElapsed,
-            WorkSource ws) {
+    private void maybeUpdateDelayAlarmLocked(long delayExpiredElapsed, WorkSource ws) {
         if (delayExpiredElapsed < mNextDelayExpiredElapsedMillis) {
             setDelayExpiredAlarmLocked(delayExpiredElapsed, ws);
         }
+    }
+
+    private void maybeUpdateDeadlineAlarmLocked(long deadlineExpiredElapsed, WorkSource ws) {
         if (deadlineExpiredElapsed < mNextJobExpiredElapsedMillis) {
             setDeadlineExpiredAlarmLocked(deadlineExpiredElapsed, ws);
         }
@@ -297,11 +365,7 @@
     }
 
     private long maybeAdjustAlarmTime(long proposedAlarmTimeElapsedMillis) {
-        final long earliestWakeupTimeElapsed = sElapsedRealtimeClock.millis();
-        if (proposedAlarmTimeElapsedMillis < earliestWakeupTimeElapsed) {
-            return earliestWakeupTimeElapsed;
-        }
-        return proposedAlarmTimeElapsedMillis;
+        return Math.max(proposedAlarmTimeElapsedMillis, sElapsedRealtimeClock.millis());
     }
 
     private void updateAlarmWithListenerLocked(String tag, OnAlarmListener listener,
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 29e1878..05d77ab 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -170,17 +170,13 @@
     private static final int GPS_CAPABILITY_SINGLE_SHOT = 0x0000008;
     private static final int GPS_CAPABILITY_ON_DEMAND_TIME = 0x0000010;
     private static final int GPS_CAPABILITY_GEOFENCING = 0x0000020;
-    private static final int GPS_CAPABILITY_MEASUREMENTS = 0x0000040;
+    public static final int GPS_CAPABILITY_MEASUREMENTS = 0x0000040;
     private static final int GPS_CAPABILITY_NAV_MESSAGES = 0x0000080;
 
     // The AGPS SUPL mode
     private static final int AGPS_SUPL_MODE_MSA = 0x02;
     private static final int AGPS_SUPL_MODE_MSB = 0x01;
 
-    // these need to match AGnssType enum in IAGnssCallback.hal
-    private static final int AGPS_TYPE_SUPL = 1;
-    private static final int AGPS_TYPE_C2K = 2;
-
     // Handler messages
     private static final int CHECK_LOCATION = 1;
     private static final int ENABLE = 2;
@@ -192,7 +188,7 @@
     private static final int ADD_LISTENER = 8;
     private static final int REMOVE_LISTENER = 9;
     private static final int DOWNLOAD_XTRA_DATA_FINISHED = 11;
-    private static final int SUBSCRIPTION_OR_SIM_CHANGED = 12;
+    private static final int SUBSCRIPTION_OR_CARRIER_CONFIG_CHANGED = 12;
     private static final int INITIALIZE_HANDLER = 13;
     private static final int REQUEST_SUPL_CONNECTION = 14;
     private static final int RELEASE_SUPL_CONNECTION = 15;
@@ -415,9 +411,6 @@
     private final static String ALARM_WAKEUP = "com.android.internal.location.ALARM_WAKEUP";
     private final static String ALARM_TIMEOUT = "com.android.internal.location.ALARM_TIMEOUT";
 
-    // SIM/Carrier info.
-    private final static String SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
-
     // Persist property for LPP_PROFILE
     private final static String LPP_PROFILE = "persist.sys.gps.lpp";
 
@@ -487,18 +480,20 @@
                 case Intent.ACTION_SCREEN_ON:
                     updateLowPowerMode();
                     break;
-                case SIM_STATE_CHANGED:
-                    subscriptionOrSimChanged(context);
+                case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
+                    subscriptionOrCarrierConfigChanged(context);
                     break;
             }
         }
     };
 
+    // TODO(b/119326010): replace OnSubscriptionsChangedListener with
+    // ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED broadcast reseiver.
     private final OnSubscriptionsChangedListener mOnSubscriptionsChangedListener =
             new OnSubscriptionsChangedListener() {
                 @Override
                 public void onSubscriptionsChanged() {
-                    sendMessage(SUBSCRIPTION_OR_SIM_CHANGED, 0, null);
+                    sendMessage(SUBSCRIPTION_OR_CARRIER_CONFIG_CHANGED, 0, null);
                 }
             };
 
@@ -510,7 +505,7 @@
         mHandler.post(() -> native_set_satellite_blacklist(constellations, svids));
     }
 
-    private void subscriptionOrSimChanged(Context context) {
+    private void subscriptionOrCarrierConfigChanged(Context context) {
         if (DEBUG) Log.d(TAG, "received SIM related action: ");
         TelephonyManager phone = (TelephonyManager)
                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
@@ -525,12 +520,12 @@
                     PersistableBundle b = configManager.getConfig();
                     if (b != null) {
                         isKeepLppProfile =
-                                b.getBoolean(CarrierConfigManager.KEY_PERSIST_LPP_MODE_BOOL);
+                                b.getBoolean(CarrierConfigManager.Gps.KEY_PERSIST_LPP_MODE_BOOL);
                     }
                 }
                 if (isKeepLppProfile) {
                     // load current properties for the carrier
-                    loadPropertiesFromResource(context, mProperties);
+                    loadPropertiesFromCarrierConfig(context, mProperties);
                     String lpp_profile = mProperties.getProperty("LPP_PROFILE");
                     // set the persist property LPP_PROFILE for the value
                     if (lpp_profile != null) {
@@ -575,7 +570,7 @@
 
     private void reloadGpsProperties(Context context, Properties properties) {
         if (DEBUG) Log.d(TAG, "Reset GPS properties, previous size = " + properties.size());
-        loadPropertiesFromResource(context, properties);
+        loadPropertiesFromCarrierConfig(context, properties);
 
         String lpp_prof = SystemProperties.get(LPP_PROFILE);
         if (!TextUtils.isEmpty(lpp_prof)) {
@@ -585,7 +580,7 @@
         /*
          * Overlay carrier properties from a debug configuration file.
          */
-        loadPropertiesFromFile(properties);
+        loadPropertiesFromGpsDebugConfig(properties);
         // TODO: we should get rid of C2K specific setting.
         setSuplHostPort(properties.getProperty("SUPL_HOST"),
                 properties.getProperty("SUPL_PORT"));
@@ -642,27 +637,47 @@
                 Log.e(TAG, "unable to parse SUPL_ES: " + suplESProperty);
             }
         }
+
+        String emergencyExtensionSecondsString
+                = properties.getProperty("ES_EXTENSION_SEC", "0");
+        try {
+            int emergencyExtensionSeconds =
+                    Integer.parseInt(emergencyExtensionSecondsString);
+            mNIHandler.setEmergencyExtensionSeconds(emergencyExtensionSeconds);
+        } catch (NumberFormatException e) {
+            Log.e(TAG, "unable to parse ES_EXTENSION_SEC: "
+                    + emergencyExtensionSecondsString);
+        }
     }
 
-    private void loadPropertiesFromResource(Context context,
-            Properties properties) {
-        String[] configValues = context.getResources().getStringArray(
-                com.android.internal.R.array.config_gpsParameters);
-        for (String item : configValues) {
-            if (DEBUG) Log.d(TAG, "GpsParamsResource: " + item);
-            // We need to support "KEY =", but not "=VALUE".
-            int index = item.indexOf("=");
-            if (index > 0 && index + 1 < item.length()) {
-                String key = item.substring(0, index);
-                String value = item.substring(index + 1);
-                properties.setProperty(key.trim().toUpperCase(), value);
-            } else {
-                Log.w(TAG, "malformed contents: " + item);
+    private void loadPropertiesFromCarrierConfig(Context context, Properties properties) {
+        CarrierConfigManager configManager = (CarrierConfigManager)
+                mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        if (configManager == null) {
+            return;
+        }
+        PersistableBundle configs = configManager.getConfigForSubId(
+                SubscriptionManager.getDefaultDataSubscriptionId());
+        if (configs == null) {
+            if (DEBUG) Log.d(TAG, "SIM not ready, use default carrier config.");
+            configs = CarrierConfigManager.getDefaultConfig();
+        }
+        for (String configKey : configs.keySet()) {
+            if (configKey.startsWith(CarrierConfigManager.Gps.KEY_PREFIX)) {
+                String key = configKey
+                        .substring(CarrierConfigManager.Gps.KEY_PREFIX.length())
+                        .toUpperCase();
+                Object value = configs.get(configKey);
+                if (value instanceof String) {
+                    // All GPS properties are of String type; convert so.
+                    if (DEBUG) Log.d(TAG, "Gps config: " + key + " = " + value);
+                    properties.setProperty(key, (String) value);
+                }
             }
         }
     }
 
-    private void loadPropertiesFromFile(Properties properties) {
+    private void loadPropertiesFromGpsDebugConfig(Properties properties) {
         try {
             File file = new File(DEBUG_PROPERTIES_FILE);
             FileInputStream stream = null;
@@ -962,7 +977,8 @@
         if (mSuplServerHost != null
                 && mSuplServerPort > TCP_MIN_PORT
                 && mSuplServerPort <= TCP_MAX_PORT) {
-            native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
+            native_set_agps_server(GnssNetworkConnectivityHandler.AGPS_TYPE_SUPL,
+                    mSuplServerHost, mSuplServerPort);
         }
     }
 
@@ -1014,10 +1030,12 @@
 
             // TODO: remove the following native calls if we can make sure they are redundant.
             if (mSuplServerHost != null) {
-                native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
+                native_set_agps_server(GnssNetworkConnectivityHandler.AGPS_TYPE_SUPL,
+                        mSuplServerHost, mSuplServerPort);
             }
             if (mC2KServerHost != null) {
-                native_set_agps_server(AGPS_TYPE_C2K, mC2KServerHost, mC2KServerPort);
+                native_set_agps_server(GnssNetworkConnectivityHandler.AGPS_TYPE_C2K,
+                        mC2KServerHost, mC2KServerPort);
             }
 
             mGnssMeasurementsProvider.onGpsEnabledChanged();
@@ -1564,8 +1582,8 @@
     }
 
     @NativeEntryPoint
-    private void reportAGpsStatus(int type, int status, byte[] ipaddr) {
-        mNetworkConnectivityHandler.onReportAGpsStatus(type, status, ipaddr);
+    private void reportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr) {
+        mNetworkConnectivityHandler.onReportAGpsStatus(agpsType, agpsStatus, suplIpAddr);
     }
 
     @NativeEntryPoint
@@ -1596,20 +1614,20 @@
     @NativeEntryPoint
     private void setEngineCapabilities(final int capabilities) {
         // send to handler thread for fast native return, and in-order handling
-        mHandler.post(() -> {
-            mEngineCapabilities = capabilities;
+        mHandler.post(
+                () -> {
+                    mEngineCapabilities = capabilities;
 
-            if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) {
-                mNtpTimeHelper.enablePeriodicTimeInjection();
-                requestUtcTime();
-            }
+                    if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) {
+                        mNtpTimeHelper.enablePeriodicTimeInjection();
+                        requestUtcTime();
+                    }
 
-            mGnssMeasurementsProvider.onCapabilitiesUpdated(hasCapability(
-                    GPS_CAPABILITY_MEASUREMENTS));
-            mGnssNavigationMessageProvider.onCapabilitiesUpdated(hasCapability(
-                    GPS_CAPABILITY_NAV_MESSAGES));
-            restartRequests();
-        });
+                    mGnssMeasurementsProvider.onCapabilitiesUpdated(capabilities);
+                    mGnssNavigationMessageProvider.onCapabilitiesUpdated(
+                            hasCapability(GPS_CAPABILITY_NAV_MESSAGES));
+                    restartRequests();
+                });
     }
 
     private void restartRequests() {
@@ -2007,8 +2025,8 @@
                 case UPDATE_LOCATION:
                     handleUpdateLocation((Location) msg.obj);
                     break;
-                case SUBSCRIPTION_OR_SIM_CHANGED:
-                    subscriptionOrSimChanged(mContext);
+                case SUBSCRIPTION_OR_CARRIER_CONFIG_CHANGED:
+                    subscriptionOrCarrierConfigChanged(mContext);
                     break;
                 case INITIALIZE_HANDLER:
                     handleInitialize();
@@ -2075,7 +2093,7 @@
             intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
             intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
             intentFilter.addAction(Intent.ACTION_SCREEN_ON);
-            intentFilter.addAction(SIM_STATE_CHANGED);
+            intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
             mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
 
             mNetworkConnectivityHandler.registerNetworkCallbacks();
@@ -2157,8 +2175,8 @@
                 return "DOWNLOAD_XTRA_DATA_FINISHED";
             case UPDATE_LOCATION:
                 return "UPDATE_LOCATION";
-            case SUBSCRIPTION_OR_SIM_CHANGED:
-                return "SUBSCRIPTION_OR_SIM_CHANGED";
+            case SUBSCRIPTION_OR_CARRIER_CONFIG_CHANGED:
+                return "SUBSCRIPTION_OR_CARRIER_CONFIG_CHANGED";
             case INITIALIZE_HANDLER:
                 return "INITIALIZE_HANDLER";
             case REPORT_LOCATION:
diff --git a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
index 3e2ba87..77dee82 100644
--- a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
@@ -17,6 +17,7 @@
 package com.android.server.location;
 
 import android.content.Context;
+import android.location.GnssMeasurementCorrections;
 import android.location.GnssMeasurementsEvent;
 import android.location.IGnssMeasurementsListener;
 import android.os.Handler;
@@ -27,14 +28,13 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 /**
- * An base implementation for GPS measurements provider.
- * It abstracts out the responsibility of handling listeners, while still allowing technology
- * specific implementations to be built.
+ * An base implementation for GPS measurements provider. It abstracts out the responsibility of
+ * handling listeners, while still allowing technology specific implementations to be built.
  *
  * @hide
  */
-public abstract class GnssMeasurementsProvider extends
-        RemoteListenerHelper<IGnssMeasurementsListener> {
+public abstract class GnssMeasurementsProvider
+        extends RemoteListenerHelper<IGnssMeasurementsListener> {
     private static final String TAG = "GnssMeasurementsProvider";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -42,19 +42,19 @@
 
     private boolean mIsCollectionStarted;
     private boolean mEnableFullTracking;
+    private int mGnssEngineCapabilities;
 
     protected GnssMeasurementsProvider(Context context, Handler handler) {
         this(context, handler, new GnssMeasurementProviderNative());
     }
 
     @VisibleForTesting
-    GnssMeasurementsProvider(Context context, Handler handler,
-            GnssMeasurementProviderNative aNative) {
+    GnssMeasurementsProvider(
+            Context context, Handler handler, GnssMeasurementProviderNative aNative) {
         super(context, handler, TAG);
         mNative = aNative;
     }
 
-    // TODO(b/37460011): Use this with death recovery logic.
     void resumeIfStarted() {
         if (DEBUG) {
             Log.d(TAG, "resumeIfStarted");
@@ -87,6 +87,25 @@
         }
     }
 
+    /**
+     * Injects GNSS measurement corrections into the GNSS chipset.
+     *
+     * @param measurementCorrections a {@link GnssMeasurementCorrections} object with the GNSS
+     *     measurement corrections to be injected into the GNSS chipset.
+     */
+    public void injectGnssMeasurementCorrections(
+            GnssMeasurementCorrections measurementCorrections) {
+        mHandler.post(
+                new Runnable() {
+                    @Override
+                    public void run() {
+                        if (!mNative.injectGnssMeasurementCorrections(measurementCorrections)) {
+                            Log.e(TAG, "Failure in injecting GNSS corrections.");
+                        }
+                    }
+                });
+    }
+
     @Override
     protected void unregisterFromService() {
         boolean stopped = mNative.stopMeasurementCollection();
@@ -96,20 +115,31 @@
     }
 
     public void onMeasurementsAvailable(final GnssMeasurementsEvent event) {
-        foreach((IGnssMeasurementsListener listener, int uid, String packageName) -> {
-            if (!hasPermission(uid, packageName)) {
-                logPermissionDisabledEventNotReported(TAG, packageName, "GNSS measurements");
-                return;
-            }
-            listener.onGnssMeasurementsReceived(event);
-        });
+        foreach(
+                (IGnssMeasurementsListener listener, int uid, String packageName) -> {
+                    if (!hasPermission(uid, packageName)) {
+                        logPermissionDisabledEventNotReported(
+                                TAG, packageName, "GNSS measurements");
+                        return;
+                    }
+                    listener.onGnssMeasurementsReceived(event);
+                });
     }
 
-    public void onCapabilitiesUpdated(boolean isGnssMeasurementsSupported) {
+    /** Updates the framework about the capabilities of the GNSS chipset */
+    public void onCapabilitiesUpdated(int capabilities) {
+        mGnssEngineCapabilities = capabilities;
+        boolean isGnssMeasurementsSupported =
+                (capabilities & GnssLocationProvider.GPS_CAPABILITY_MEASUREMENTS) != 0;
         setSupported(isGnssMeasurementsSupported);
         updateResult();
     }
 
+    /** Obtains the GNSS engine capabilities. */
+    public int getGnssCapabilities() {
+        return mGnssEngineCapabilities;
+    }
+
     public void onGpsEnabledChanged() {
         tryUpdateRegistrationWithService();
         updateResult();
@@ -170,6 +200,11 @@
         public boolean stopMeasurementCollection() {
             return native_stop_measurement_collection();
         }
+
+        public boolean injectGnssMeasurementCorrections(
+                GnssMeasurementCorrections measurementCorrections) {
+            return native_inject_gnss_measurement_corrections(measurementCorrections);
+        }
     }
 
     private static native boolean native_is_measurement_supported();
@@ -177,4 +212,7 @@
     private static native boolean native_start_measurement_collection(boolean enableFullTracking);
 
     private static native boolean native_stop_measurement_collection();
+
+    private static native boolean native_inject_gnss_measurement_corrections(
+            GnssMeasurementCorrections measurementCorrections);
 }
diff --git a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
index b211948..c38373f 100644
--- a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
@@ -23,7 +23,6 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkRequest;
-import android.net.Uri;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.PowerManager;
@@ -65,6 +64,16 @@
     private static final int APN_IPV6 = 2;
     private static final int APN_IPV4V6 = 3;
 
+    // these must match the NetworkCapability enum flags in IAGnssRil.hal
+    private static final int AGNSS_NET_CAPABILITY_NOT_METERED = 1 << 0;
+    private static final int AGNSS_NET_CAPABILITY_NOT_ROAMING = 1 << 1;
+
+    // these need to match AGnssType enum in IAGnssCallback.hal
+    public static final int AGPS_TYPE_SUPL = 1;
+    public static final int AGPS_TYPE_C2K = 2;
+    private static final int AGPS_TYPE_EIMS = 3;
+    private static final int AGPS_TYPE_IMS = 4;
+
     // Default time limit in milliseconds for the ConnectivityManager to find a suitable
     // network with SUPL connectivity or report an error.
     private static final int SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS = 10 * 1000;
@@ -83,6 +92,7 @@
 
     private int mAGpsDataConnectionState;
     private InetAddress mAGpsDataConnectionIpAddr;
+    private int mAGpsType;
 
     private final Context mContext;
 
@@ -95,23 +105,42 @@
      * Network attributes needed when updating HAL about network connectivity status changes.
      */
     private static class NetworkAttributes {
-        NetworkCapabilities mCapabilities;
-        String mApn;
-        int mType = ConnectivityManager.TYPE_NONE;
+        private NetworkCapabilities mCapabilities;
+        private String mApn;
+        private int mType = ConnectivityManager.TYPE_NONE;
 
         /**
          * Returns true if the capabilities that we pass on to HAL change between {@curCapabilities}
          * and {@code newCapabilities}.
          */
-        static boolean hasCapabilitiesChanged(NetworkCapabilities curCapabilities,
+        private static boolean hasCapabilitiesChanged(NetworkCapabilities curCapabilities,
                 NetworkCapabilities newCapabilities) {
             if (curCapabilities == null || newCapabilities == null) {
                 return true;
             }
 
-            return curCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)
-                    != newCapabilities.hasCapability(
-                    NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
+            // Monitor for roaming and metered capability changes.
+            return hasCapabilityChanged(curCapabilities, newCapabilities,
+                    NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)
+                    || hasCapabilityChanged(curCapabilities, newCapabilities,
+                    NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+        }
+
+        private static boolean hasCapabilityChanged(NetworkCapabilities curCapabilities,
+                NetworkCapabilities newCapabilities, int capability) {
+            return curCapabilities.hasCapability(capability)
+                    != newCapabilities.hasCapability(capability);
+        }
+
+        private static short getCapabilityFlags(NetworkCapabilities capabilities) {
+            short capabilityFlags = 0;
+            if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)) {
+                capabilityFlags |= AGNSS_NET_CAPABILITY_NOT_ROAMING;
+            }
+            if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)) {
+                capabilityFlags |= AGNSS_NET_CAPABILITY_NOT_METERED;
+            }
+            return capabilityFlags;
         }
     }
 
@@ -176,21 +205,11 @@
     /**
      * called from native code to update AGPS status
      */
-    public void onReportAGpsStatus(int type, int status, byte[] ipaddr) {
-        switch (status) {
+    public void onReportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr) {
+        switch (agpsStatus) {
             case GPS_REQUEST_AGPS_DATA_CONN:
                 if (DEBUG) Log.d(TAG, "GPS_REQUEST_AGPS_DATA_CONN");
-                Log.v(TAG, "Received SUPL IP addr[]: " + Arrays.toString(ipaddr));
-                InetAddress connectionIpAddress = null;
-                if (ipaddr != null) {
-                    try {
-                        connectionIpAddress = InetAddress.getByAddress(ipaddr);
-                        if (DEBUG) Log.d(TAG, "IP address converted to: " + connectionIpAddress);
-                    } catch (UnknownHostException e) {
-                        Log.e(TAG, "Bad IP Address: " + ipaddr, e);
-                    }
-                }
-                requestSuplConnection(connectionIpAddress);
+                requestSuplConnection(agpsType, suplIpAddr);
                 break;
             case GPS_RELEASE_AGPS_DATA_CONN:
                 if (DEBUG) Log.d(TAG, "GPS_RELEASE_AGPS_DATA_CONN");
@@ -206,7 +225,7 @@
                 if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONN_FAILED");
                 break;
             default:
-                if (DEBUG) Log.d(TAG, "Received Unknown AGPS status: " + status);
+                if (DEBUG) Log.d(TAG, "Received Unknown AGPS status: " + agpsStatus);
         }
     }
 
@@ -286,8 +305,8 @@
         };
     }
 
-    private void requestSuplConnection(InetAddress inetAddress) {
-        postEvent(() -> handleRequestSuplConnection(inetAddress));
+    private void requestSuplConnection(int agpsType, byte[] suplIpAddr) {
+        postEvent(() -> handleRequestSuplConnection(agpsType, suplIpAddr));
     }
 
     private void suplConnectionAvailable(Network network) {
@@ -328,36 +347,31 @@
         boolean networkAvailable = isConnected && TelephonyManager.getDefault().getDataEnabled();
         NetworkAttributes networkAttributes = updateTrackedNetworksState(isConnected, network,
                 capabilities);
-        String apnName = networkAttributes.mApn;
+        String apn = networkAttributes.mApn;
         int type = networkAttributes.mType;
         // When isConnected is false, capabilities argument is null. So, use last received
         // capabilities.
         capabilities = networkAttributes.mCapabilities;
-        boolean isRoaming = !capabilities.hasTransport(
-                NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
-
         Log.i(TAG, String.format(
                 "updateNetworkState, state=%s, connected=%s, network=%s, capabilities=%s"
-                        + ", availableNetworkCount: %d",
+                        + ", apn: %s, availableNetworkCount: %d",
                 agpsDataConnStateAsString(),
                 isConnected,
                 network,
                 capabilities,
+                apn,
                 mAvailableNetworkAttributes.size()));
 
         if (native_is_agps_ril_supported()) {
-            String defaultApn = getSelectedApn();
-            if (defaultApn == null) {
-                defaultApn = "dummy-apn";
-            }
-
             native_update_network_state(
                     isConnected,
                     type,
-                    isRoaming,
+                    !capabilities.hasTransport(
+                            NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING), /* isRoaming */
                     networkAvailable,
-                    apnName,
-                    defaultApn);
+                    apn != null ? apn : "",
+                    network.getNetworkHandle(),
+                    NetworkAttributes.getCapabilityFlags(capabilities));
         } else if (DEBUG) {
             Log.d(TAG, "Skipped network state update because GPS HAL AGPS-RIL is not  supported");
         }
@@ -398,9 +412,9 @@
         // TODO(b/119278134): The synchronous method ConnectivityManager.getNetworkInfo() must
         // not be called inside the asynchronous ConnectivityManager.NetworkCallback methods.
         NetworkInfo info = mConnMgr.getNetworkInfo(network);
-        String apnName = null;
+        String apn = null;
         if (info != null) {
-            apnName = info.getExtraInfo();
+            apn = info.getExtraInfo();
         }
 
         if (DEBUG) {
@@ -413,43 +427,61 @@
         }
 
         if (mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
-            if (apnName == null) {
+            if (apn == null) {
                 // assign a dummy value in the case of C2K as otherwise we will have a runtime
                 // exception in the following call to native_agps_data_conn_open
-                apnName = "dummy-apn";
+                apn = "dummy-apn";
             }
-            int apnIpType = getApnIpType(apnName);
-            setRouting();
+
+            // Setting route to host is needed for GNSS HAL implementations earlier than
+            // @2.0::IAgnssCallback. The HAL @2.0::IAgnssCallback.agnssStatusCb() method does
+            // not require setting route to SUPL host and hence does not provide an IP address.
+            if (mAGpsDataConnectionIpAddr != null) {
+                setRouting();
+            }
+
+            int apnIpType = getApnIpType(apn);
             if (DEBUG) {
                 String message = String.format(
                         "native_agps_data_conn_open: mAgpsApn=%s, mApnIpType=%s",
-                        apnName,
+                        apn,
                         apnIpType);
                 Log.d(TAG, message);
             }
-            native_agps_data_conn_open(apnName, apnIpType);
+            native_agps_data_conn_open(network.getNetworkHandle(), apn, apnIpType);
             mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
         }
     }
 
-    private void handleRequestSuplConnection(InetAddress address) {
+    private void handleRequestSuplConnection(int agpsType, byte[] suplIpAddr) {
+        mAGpsDataConnectionIpAddr = null;
+        mAGpsType = agpsType;
+        if (suplIpAddr != null) {
+            if (VERBOSE) Log.v(TAG, "Received SUPL IP addr[]: " + Arrays.toString(suplIpAddr));
+            try {
+                mAGpsDataConnectionIpAddr = InetAddress.getByAddress(suplIpAddr);
+                if (DEBUG) Log.d(TAG, "IP address converted to: " + mAGpsDataConnectionIpAddr);
+            } catch (UnknownHostException e) {
+                Log.e(TAG, "Bad IP Address: " + suplIpAddr, e);
+            }
+        }
+
         if (DEBUG) {
             String message = String.format(
-                    "requestSuplConnection, state=%s, address=%s",
+                    "requestSuplConnection, state=%s, agpsType=%s, address=%s",
                     agpsDataConnStateAsString(),
-                    address);
+                    agpsTypeAsString(agpsType),
+                    mAGpsDataConnectionIpAddr);
             Log.d(TAG, message);
         }
 
         if (mAGpsDataConnectionState != AGPS_DATA_CONNECTION_CLOSED) {
             return;
         }
-        mAGpsDataConnectionIpAddr = address;
         mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
 
         NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder();
-        requestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
-        requestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
+        requestBuilder.addCapability(getNetworkCapability(mAGpsType));
         NetworkRequest request = requestBuilder.build();
         mConnMgr.requestNetwork(
                 request,
@@ -457,6 +489,20 @@
                 SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS);
     }
 
+    private int getNetworkCapability(int agpsType) {
+        switch (agpsType) {
+            case AGPS_TYPE_C2K:
+            case AGPS_TYPE_SUPL:
+                return NetworkCapabilities.NET_CAPABILITY_SUPL;
+            case AGPS_TYPE_EIMS:
+                return NetworkCapabilities.NET_CAPABILITY_EIMS;
+            case AGPS_TYPE_IMS:
+                return NetworkCapabilities.NET_CAPABILITY_IMS;
+            default:
+                throw new IllegalArgumentException("agpsType: " + agpsType);
+        }
+    }
+
     private void handleReleaseSuplConnection(int agpsDataConnStatus) {
         if (DEBUG) {
             String message = String.format(
@@ -469,8 +515,8 @@
         if (mAGpsDataConnectionState == AGPS_DATA_CONNECTION_CLOSED) {
             return;
         }
-        mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
 
+        mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
         mConnMgr.unregisterNetworkCallback(mSuplConnectivityCallback);
         switch (agpsDataConnStatus) {
             case GPS_AGPS_DATA_CONN_FAILED:
@@ -484,12 +530,9 @@
         }
     }
 
+    // TODO(25876485): Delete this method when all devices upgrade to HAL @2.0::IAGnssCallback
+    //                 interface which does not require setting route to host.
     private void setRouting() {
-        if (mAGpsDataConnectionIpAddr == null) {
-            return;
-        }
-
-        // TODO(25876485): replace the use of this deprecated API
         boolean result = mConnMgr.requestRouteToHostAddress(
                 ConnectivityManager.TYPE_MOBILE_SUPL,
                 mAGpsDataConnectionIpAddr);
@@ -547,6 +590,21 @@
         }
     }
 
+    private String agpsTypeAsString(int agpsType) {
+        switch (agpsType) {
+            case AGPS_TYPE_SUPL:
+                return "SUPL";
+            case AGPS_TYPE_C2K:
+                return "C2K";
+            case AGPS_TYPE_EIMS:
+                return "EIMS";
+            case AGPS_TYPE_IMS:
+                return "IMS";
+            default:
+                return "<Unknown>";
+        }
+    }
+
     private int getApnIpType(String apn) {
         ensureInHandlerThread();
         if (apn == null) {
@@ -596,28 +654,8 @@
         return APN_INVALID;
     }
 
-    private String getSelectedApn() {
-        Uri uri = Uri.parse("content://telephony/carriers/preferapn");
-        try (Cursor cursor = mContext.getContentResolver().query(
-                uri,
-                new String[]{"apn"},
-                null /* selection */,
-                null /* selectionArgs */,
-                Carriers.DEFAULT_SORT_ORDER)) {
-            if (cursor != null && cursor.moveToFirst()) {
-                return cursor.getString(0);
-            } else {
-                Log.e(TAG, "No APN found to select.");
-            }
-        } catch (Exception e) {
-            Log.e(TAG, "Error encountered on selecting the APN.", e);
-        }
-
-        return null;
-    }
-
     // AGPS support
-    private native void native_agps_data_conn_open(String apn, int apnIpType);
+    private native void native_agps_data_conn_open(long networkHandle, String apn, int apnIpType);
 
     private native void native_agps_data_conn_closed();
 
@@ -626,6 +664,6 @@
     // AGPS ril support
     private static native boolean native_is_agps_ril_supported();
 
-    private native void native_update_network_state(boolean connected, int type,
-            boolean roaming, boolean available, String extraInfo, String defaultAPN);
-}
+    private native void native_update_network_state(boolean connected, int type, boolean roaming,
+            boolean available, String apn, long networkHandle, short capabilities);
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index 37d43fc..e69b2ec 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -43,7 +43,7 @@
     protected static final int RESULT_UNKNOWN = 5;
     protected static final int RESULT_NOT_ALLOWED = 6;
 
-    private final Handler mHandler;
+    protected final Handler mHandler;
     private final String mTag;
 
     private final Map<IBinder, LinkedListener> mListenerMap = new HashMap<>();
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index adfa8d5..ea8c792 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -112,6 +112,7 @@
 import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
+import com.android.server.wm.WindowManagerInternal;
 
 import libcore.util.HexEncoding;
 
@@ -1870,6 +1871,7 @@
             DevicePolicyManager dpm = (DevicePolicyManager)
                     mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
             dpm.reportPasswordChanged(userId);
+            LocalServices.getService(WindowManagerInternal.class).reportPasswordChanged(userId);
         });
     }
 
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index c938f5e..139d8ac 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -20,10 +20,10 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
+import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.AudioManagerInternal;
 import android.media.AudioSystem;
-import android.media.MediaDescription;
 import android.media.MediaMetadata;
 import android.media.Rating;
 import android.media.VolumeProvider;
@@ -36,7 +36,6 @@
 import android.media.session.MediaSession;
 import android.media.session.ParcelableVolumeInfo;
 import android.media.session.PlaybackState;
-import android.media.AudioAttributes;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -94,8 +93,9 @@
     private PendingIntent mLaunchIntent;
 
     // TransportPerformer fields
-
     private Bundle mExtras;
+    // Note: Avoid unparceling the bundle inside MediaMetadata since unparceling in system process
+    // may result in throwing an exception.
     private MediaMetadata mMetadata;
     private PlaybackState mPlaybackState;
     private ParceledListSlice mQueue;
@@ -117,6 +117,9 @@
     private boolean mIsActive = false;
     private boolean mDestroyed = false;
 
+    private long mDuration;
+    private String mMetadataDescription;
+
     public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName,
             ISessionCallback cb, String tag, MediaSessionService service, Looper handlerLooper) {
         mOwnerPid = ownerPid;
@@ -233,6 +236,7 @@
      * {@link AudioManager#ADJUST_SAME}.
      *
      * @param packageName The package that made the original volume request.
+     * @param opPackageName The op package that made the original volume request.
      * @param pid The pid that made the original volume request.
      * @param uid The uid that made the original volume request.
      * @param caller caller binder. can be {@code null} if it's from the volume key.
@@ -245,7 +249,7 @@
      * @param flags Any of the flags from {@link AudioManager}.
      * @param useSuggested True to use adjustSuggestedStreamVolume instead of
      */
-    public void adjustVolume(String packageName, int pid, int uid,
+    public void adjustVolume(String packageName, String opPackageName, int pid, int uid,
             ISessionControllerCallback caller, boolean asSystemService, int direction, int flags,
             boolean useSuggested) {
         int previousFlagPlaySound = flags & AudioManager.FLAG_PLAY_SOUND;
@@ -255,8 +259,8 @@
         if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
             // Adjust the volume with a handler not to be blocked by other system service.
             int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
-            postAdjustLocalVolume(stream, direction, flags, packageName, uid, asSystemService,
-                    useSuggested, previousFlagPlaySound);
+            postAdjustLocalVolume(stream, direction, flags, opPackageName, pid, uid,
+                    asSystemService, useSuggested, previousFlagPlaySound);
         } else {
             if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) {
                 // Nothing to do, the volume cannot be changed
@@ -287,11 +291,23 @@
         }
     }
 
-    private void setVolumeTo(String packageName, int pid, int uid,
+    private void setVolumeTo(String packageName, String opPackageName, int pid, int uid,
             ISessionControllerCallback caller, int value, int flags) {
         if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
             int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
-            mAudioManagerInternal.setStreamVolumeForUid(stream, value, flags, packageName, uid);
+            final int volumeValue = value;
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        mAudioManagerInternal.setStreamVolumeForUid(stream, volumeValue, flags,
+                                opPackageName, uid);
+                    } catch (IllegalArgumentException | SecurityException e) {
+                        Log.e(TAG, "Cannot set volume: stream=" + stream + ", value=" + volumeValue
+                                + ", flags=" + flags, e);
+                    }
+                }
+            });
         } else {
             if (mVolumeControlType != VolumeProvider.VOLUME_CONTROL_ABSOLUTE) {
                 // Nothing to do. The volume can't be set directly.
@@ -451,7 +467,7 @@
         pw.println(indent + "audioAttrs=" + mAudioAttrs);
         pw.println(indent + "volumeType=" + mVolumeType + ", controlType=" + mVolumeControlType
                 + ", max=" + mMaxVolume + ", current=" + mCurrentVolume);
-        pw.println(indent + "metadata:" + getShortMetadataString());
+        pw.println(indent + "metadata: " + mMetadataDescription);
         pw.println(indent + "queueTitle=" + mQueueTitle + ", size="
                 + (mQueue == null ? 0 : mQueue.getList().size()));
     }
@@ -462,11 +478,19 @@
     }
 
     private void postAdjustLocalVolume(final int stream, final int direction, final int flags,
-            final String callingPackageName, final int callingUid, final boolean asSystemService,
-            final boolean useSuggested, final int previousFlagPlaySound) {
-        final String packageName = asSystemService
-                ? mContext.getOpPackageName() : callingPackageName;
-        final int uid = asSystemService ? Process.SYSTEM_UID : callingUid;
+            final String callingOpPackageName, final int callingPid, final int callingUid,
+            final boolean asSystemService, final boolean useSuggested,
+            final int previousFlagPlaySound) {
+        // Must use opPackageName for adjusting volumes with UID.
+        final String opPackageName;
+        final int uid;
+        if (asSystemService) {
+            opPackageName = mContext.getOpPackageName();
+            uid = Process.SYSTEM_UID;
+        } else {
+            opPackageName = callingOpPackageName;
+            uid = callingUid;
+        }
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -474,19 +498,19 @@
                     if (useSuggested) {
                         if (AudioSystem.isStreamActive(stream, 0)) {
                             mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream,
-                                    direction, flags, packageName, uid);
+                                    direction, flags, opPackageName, uid);
                         } else {
                             mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(
                                     AudioManager.USE_DEFAULT_STREAM_TYPE, direction,
-                                    flags | previousFlagPlaySound, packageName, uid);
+                                    flags | previousFlagPlaySound, opPackageName, uid);
                         }
                     } else {
                         mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags,
-                                packageName, uid);
+                                opPackageName, uid);
                     }
                 } catch (IllegalArgumentException | SecurityException e) {
                     Log.e(TAG, "Cannot adjust volume: direction=" + direction + ", stream="
-                            + stream + ", flags=" + flags + ", packageName=" + packageName
+                            + stream + ", flags=" + flags + ", opPackageName=" + opPackageName
                             + ", uid=" + uid + ", useSuggested=" + useSuggested
                             + ", previousFlagPlaySound=" + previousFlagPlaySound, e);
                 }
@@ -494,13 +518,6 @@
         });
     }
 
-    private String getShortMetadataString() {
-        int fields = mMetadata == null ? 0 : mMetadata.size();
-        MediaDescription description = mMetadata == null ? null : mMetadata
-                .getDescription();
-        return "size=" + fields + ", description=" + description;
-    }
-
     private void logCallbackException(
             String msg, ISessionControllerCallbackHolder holder, Exception e) {
         Log.v(TAG, msg + ", this=" + this + ", callback package=" + holder.mPackageName
@@ -670,12 +687,10 @@
 
     private PlaybackState getStateWithUpdatedPosition() {
         PlaybackState state;
-        long duration = -1;
+        long duration;
         synchronized (mLock) {
             state = mPlaybackState;
-            if (mMetadata != null && mMetadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) {
-                duration = mMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
-            }
+            duration = mDuration;
         }
         PlaybackState result = null;
         if (state != null) {
@@ -793,7 +808,7 @@
         }
 
         @Override
-        public void setMetadata(MediaMetadata metadata) {
+        public void setMetadata(MediaMetadata metadata, long duration, String metadataDescription) {
             synchronized (mLock) {
                 MediaMetadata temp = metadata == null ? null : new MediaMetadata.Builder(metadata)
                         .build();
@@ -804,6 +819,8 @@
                     temp.size();
                 }
                 mMetadata = temp;
+                mDuration = duration;
+                mMetadataDescription = metadataDescription;
             }
             mHandler.post(MessageHandler.MSG_UPDATE_METADATA);
         }
@@ -1236,27 +1253,29 @@
         }
 
         @Override
-        public void adjustVolume(String packageName, ISessionControllerCallback caller,
-                boolean asSystemService, int direction, int flags) {
+        public void adjustVolume(String packageName, String opPackageName,
+                ISessionControllerCallback caller, boolean asSystemService, int direction,
+                int flags) {
             int pid = Binder.getCallingPid();
             int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
             try {
-                MediaSessionRecord.this.adjustVolume(packageName, pid, uid, caller, asSystemService,
-                        direction, flags, false /* useSuggested */);
+                MediaSessionRecord.this.adjustVolume(packageName, opPackageName, pid, uid, caller,
+                        asSystemService, direction, flags, false /* useSuggested */);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
         }
 
         @Override
-        public void setVolumeTo(String packageName, ISessionControllerCallback caller,
-                int value, int flags) {
+        public void setVolumeTo(String packageName, String opPackageName,
+                ISessionControllerCallback caller, int value, int flags) {
             int pid = Binder.getCallingPid();
             int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
             try {
-                MediaSessionRecord.this.setVolumeTo(packageName, pid, uid, caller, value, flags);
+                MediaSessionRecord.this.setVolumeTo(packageName, opPackageName, pid, uid, caller,
+                        value, flags);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 93b6620..052d579 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.media;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.INotificationManager;
 import android.app.KeyguardManager;
@@ -27,15 +28,19 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.media.AudioManager;
+import android.media.AudioManagerInternal;
 import android.media.AudioPlaybackConfiguration;
 import android.media.AudioSystem;
 import android.media.IAudioService;
 import android.media.IRemoteVolumeController;
+import android.media.Session2Token;
 import android.media.session.IActiveSessionsListener;
 import android.media.session.ICallback;
 import android.media.session.IOnMediaKeyListener;
@@ -69,6 +74,7 @@
 import android.view.ViewConfiguration;
 
 import com.android.internal.util.DumpUtils;
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.Watchdog;
 import com.android.server.Watchdog.Monitor;
@@ -105,6 +111,8 @@
 
     private KeyguardManager mKeyguardManager;
     private IAudioService mAudioService;
+    private AudioManagerInternal mAudioManagerInternal;
+    private ActivityManager mActivityManager;
     private ContentResolver mContentResolver;
     private SettingsObserver mSettingsObserver;
     private boolean mHasFeatureLeanback;
@@ -136,6 +144,9 @@
         mKeyguardManager =
                 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
         mAudioService = getAudioService();
+        mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
+        mActivityManager =
+                (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE);
         mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
         mAudioPlayerStateMonitor.registerListener(
                 (config, isRemoved) -> {
@@ -634,11 +645,17 @@
      * <p>The contents of this object is guarded by {@link #mLock}.
      */
     final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener {
+        public static final int COMPONENT_TYPE_INVALID = 0;
+        public static final int COMPONENT_TYPE_BROADCAST = 1;
+        public static final int COMPONENT_TYPE_ACTIVITY = 2;
+        public static final int COMPONENT_TYPE_SERVICE = 3;
         private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
+
         private final int mFullUserId;
         private final MediaSessionStack mPriorityStack;
         private PendingIntent mLastMediaButtonReceiver;
         private ComponentName mRestoredMediaButtonReceiver;
+        private int mRestoredMediaButtonReceiverComponentType;
         private int mRestoredMediaButtonReceiverUserId;
 
         private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
@@ -655,17 +672,23 @@
             mFullUserId = fullUserId;
             mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this);
             // Restore the remembered media button receiver before the boot.
-            String mediaButtonReceiver = Settings.Secure.getStringForUser(mContentResolver,
+            String mediaButtonReceiverInfo = Settings.Secure.getStringForUser(mContentResolver,
                     Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId);
-            if (mediaButtonReceiver == null) {
+            if (mediaButtonReceiverInfo == null) {
                 return;
             }
-            String[] tokens = mediaButtonReceiver.split(COMPONENT_NAME_USER_ID_DELIM);
-            if (tokens == null || tokens.length != 2) {
+            String[] tokens = mediaButtonReceiverInfo.split(COMPONENT_NAME_USER_ID_DELIM);
+            if (tokens == null || (tokens.length != 2 && tokens.length != 3)) {
                 return;
             }
             mRestoredMediaButtonReceiver = ComponentName.unflattenFromString(tokens[0]);
             mRestoredMediaButtonReceiverUserId = Integer.parseInt(tokens[1]);
+            if (tokens.length == 3) {
+                mRestoredMediaButtonReceiverComponentType = Integer.parseInt(tokens[2]);
+            } else {
+                mRestoredMediaButtonReceiverComponentType =
+                        getComponentType(mRestoredMediaButtonReceiver);
+            }
         }
 
         public void destroySessionsForUserLocked(int userId) {
@@ -696,6 +719,8 @@
             pw.println(indent + "Callback: " + mCallback);
             pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver);
             pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver);
+            pw.println(indent + "Restored MediaButtonReceiverComponentType: "
+                    + mRestoredMediaButtonReceiverComponentType);
             mPriorityStack.dump(pw, indent);
         }
 
@@ -722,17 +747,22 @@
             PendingIntent receiver = record.getMediaButtonReceiver();
             mLastMediaButtonReceiver = receiver;
             mRestoredMediaButtonReceiver = null;
-            String componentName = "";
+            mRestoredMediaButtonReceiverComponentType = COMPONENT_TYPE_INVALID;
+
+            String mediaButtonReceiverInfo = "";
             if (receiver != null) {
                 ComponentName component = receiver.getIntent().getComponent();
                 if (component != null
                         && record.getPackageName().equals(component.getPackageName())) {
-                    componentName = component.flattenToString();
+                    String componentName = component.flattenToString();
+                    int componentType = getComponentType(component);
+                    mediaButtonReceiverInfo = String.join(COMPONENT_NAME_USER_ID_DELIM,
+                            componentName, String.valueOf(record.getUserId()),
+                            String.valueOf(componentType));
                 }
             }
             Settings.Secure.putStringForUser(mContentResolver,
-                    Settings.System.MEDIA_BUTTON_RECEIVER,
-                    componentName + COMPONENT_NAME_USER_ID_DELIM + record.getUserId(),
+                    Settings.System.MEDIA_BUTTON_RECEIVER, mediaButtonReceiverInfo,
                     mFullUserId);
         }
 
@@ -762,6 +792,35 @@
             return isGlobalPriorityActiveLocked()
                     ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
         }
+
+        private int getComponentType(@Nullable ComponentName componentName) {
+            if (componentName == null) {
+                return COMPONENT_TYPE_INVALID;
+            }
+            PackageManager pm = getContext().getPackageManager();
+            try {
+                ActivityInfo activityInfo = pm.getActivityInfo(componentName,
+                        PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                                | PackageManager.GET_ACTIVITIES);
+                if (activityInfo != null) {
+                    return COMPONENT_TYPE_ACTIVITY;
+                }
+            } catch (NameNotFoundException e) {
+            }
+            try {
+                ServiceInfo serviceInfo = pm.getServiceInfo(componentName,
+                        PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                                | PackageManager.GET_SERVICES);
+                if (serviceInfo != null) {
+                    return COMPONENT_TYPE_SERVICE;
+                }
+            } catch (NameNotFoundException e) {
+            }
+            // Pick legacy behavior for BroadcastReceiver or unknown.
+            return COMPONENT_TYPE_BROADCAST;
+        }
     }
 
     final class SessionsListenerRecord implements IBinder.DeathRecipient {
@@ -837,6 +896,21 @@
         }
 
         @Override
+        public void notifySession2Created(Session2Token sessionToken) throws RemoteException {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (DEBUG) {
+                    Log.d(TAG, "Session2 is created " + sessionToken);
+                }
+                // TODO: Keep the session.
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
         public List<IBinder> getSessions(ComponentName componentName, int userId) {
             final int pid = Binder.getCallingPid();
             final int uid = Binder.getCallingUid();
@@ -1151,7 +1225,8 @@
          * there's no active global priority session, long-pressess will be sent to the
          * long-press listener instead of adjusting volume.
          *
-         * @param packageName The caller package.
+         * @param packageName The caller's package name, obtained by Context#getPackageName()
+         * @param opPackageName The caller's op package name, obtained by Context#getOpPackageName()
          * @param asSystemService {@code true} if the event sent to the session as if it was come
          *          from the system service instead of the app process. This helps sessions to
          *          distinguish between the key injection by the app and key events from the
@@ -1166,8 +1241,8 @@
          * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
          */
         @Override
-        public void dispatchVolumeKeyEvent(String packageName, boolean asSystemService,
-                KeyEvent keyEvent, int stream, boolean musicOnly) {
+        public void dispatchVolumeKeyEvent(String packageName, String opPackageName,
+                boolean asSystemService, KeyEvent keyEvent, int stream, boolean musicOnly) {
             if (keyEvent == null ||
                     (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP
                              && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
@@ -1189,8 +1264,8 @@
                 synchronized (mLock) {
                     if (isGlobalPriorityActiveLocked()
                             || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
-                        dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService,
-                                keyEvent, stream, musicOnly);
+                        dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
+                                asSystemService, keyEvent, stream, musicOnly);
                     } else {
                         // TODO: Consider the case when both volume up and down keys are pressed
                         //       at the same time.
@@ -1223,12 +1298,13 @@
                                     && mCurrentFullUserRecord.mInitialDownVolumeKeyEvent
                                             .getDownTime() == keyEvent.getDownTime()) {
                                 // Short-press. Should change volume.
-                                dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService,
+                                dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
+                                        asSystemService,
                                         mCurrentFullUserRecord.mInitialDownVolumeKeyEvent,
                                         mCurrentFullUserRecord.mInitialDownVolumeStream,
                                         mCurrentFullUserRecord.mInitialDownMusicOnly);
-                                dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService,
-                                        keyEvent, stream, musicOnly);
+                                dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
+                                        asSystemService, keyEvent, stream, musicOnly);
                             } else {
                                 dispatchVolumeKeyLongPressLocked(keyEvent);
                             }
@@ -1240,8 +1316,9 @@
             }
         }
 
-        private void dispatchVolumeKeyEventLocked(String packageName, int pid, int uid,
-                boolean asSystemService, KeyEvent keyEvent, int stream, boolean musicOnly) {
+        private void dispatchVolumeKeyEventLocked(String packageName, String opPackageName, int pid,
+                int uid, boolean asSystemService, KeyEvent keyEvent, int stream,
+                boolean musicOnly) {
             boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
             boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
             int direction = 0;
@@ -1275,26 +1352,26 @@
                     if (up) {
                         direction = 0;
                     }
-                    dispatchAdjustVolumeLocked(packageName, pid, uid, asSystemService, stream,
-                            direction, flags);
+                    dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid,
+                            asSystemService, stream, direction, flags);
                 } else if (isMute) {
                     if (down && keyEvent.getRepeatCount() == 0) {
-                        dispatchAdjustVolumeLocked(packageName, pid, uid, asSystemService, stream,
-                                AudioManager.ADJUST_TOGGLE_MUTE, flags);
+                        dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid,
+                                asSystemService, stream, AudioManager.ADJUST_TOGGLE_MUTE, flags);
                     }
                 }
             }
         }
 
         @Override
-        public void dispatchAdjustVolume(String packageName, int suggestedStream, int delta,
-                int flags) {
+        public void dispatchAdjustVolume(String packageName, String opPackageName,
+                int suggestedStream, int delta, int flags) {
             final int pid = Binder.getCallingPid();
             final int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    dispatchAdjustVolumeLocked(packageName, pid, uid, false,
+                    dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid, false,
                             suggestedStream, delta, flags);
                 }
             } finally {
@@ -1358,24 +1435,14 @@
             final int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
             try {
-                int controllerUserId = UserHandle.getUserId(controllerUid);
-                int controllerUidFromPackageName;
-                try {
-                    controllerUidFromPackageName = getContext().getPackageManager()
-                            .getPackageUidAsUser(controllerPackageName, controllerUserId);
-                } catch (NameNotFoundException e) {
-                    if (DEBUG) {
-                        Log.d(TAG, "Package " + controllerPackageName + " doesn't exist");
-                    }
-                    return false;
-                }
-                if (controllerUidFromPackageName != controllerUid) {
-                    if (DEBUG) {
-                        Log.d(TAG, "Package name " + controllerPackageName
-                                + " doesn't match with the uid " + controllerUid);
-                    }
-                    return false;
-                }
+                // Don't perform sanity check between controllerPackageName and controllerUid.
+                // When an (activity|service) runs on the another apps process by specifying
+                // android:process in the AndroidManifest.xml, then PID and UID would have the
+                // running process' information instead of the (activity|service) that has created
+                // MediaController.
+                // Note that we can use Context#getOpPackageName() instead of
+                // 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(UserHandle.getUserId(uid), controllerPackageName,
                         controllerPid, controllerUid);
             } finally {
@@ -1446,8 +1513,8 @@
             return false;
         }
 
-        private void dispatchAdjustVolumeLocked(String packageName, int pid, int uid,
-                boolean asSystemService, int suggestedStream, int direction, int flags) {
+        private void dispatchAdjustVolumeLocked(String packageName, String opPackageName, int pid,
+                int uid, boolean asSystemService, int suggestedStream, int direction, int flags) {
             MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
                     : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
 
@@ -1478,21 +1545,28 @@
                 mHandler.post(new Runnable() {
                     @Override
                     public void run() {
+                        final String callingOpPackageName;
+                        final int callingUid;
+                        if (asSystemService) {
+                            callingOpPackageName = getContext().getOpPackageName();
+                            callingUid = Process.myUid();
+                        } else {
+                            callingOpPackageName = opPackageName;
+                            callingUid = uid;
+                        }
                         try {
-                            String packageName = getContext().getOpPackageName();
-                            mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
-                                    flags, packageName, TAG);
-                        } catch (RemoteException|SecurityException e) {
-                            Log.e(TAG, "Error adjusting default volume.", e);
-                        } catch (IllegalArgumentException e) {
+                            mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(suggestedStream,
+                                    direction, flags, callingOpPackageName, callingUid);
+                        } catch (SecurityException | IllegalArgumentException e) {
                             Log.e(TAG, "Cannot adjust volume: direction=" + direction
-                                    + ", suggestedStream=" + suggestedStream + ", flags=" + flags,
-                                    e);
+                                    + ", suggestedStream=" + suggestedStream + ", flags=" + flags
+                                    + ", packageName=" + packageName + ", uid=" + uid
+                                    + ", asSystemService=" + asSystemService, e);
                         }
                     }
                 });
             } else {
-                session.adjustVolume(packageName, pid, uid, null, asSystemService,
+                session.adjustVolume(packageName, opPackageName, pid, uid, null, asSystemService,
                         direction, flags, true);
             }
         }
@@ -1580,14 +1654,32 @@
                     } else {
                         ComponentName receiver =
                                 mCurrentFullUserRecord.mRestoredMediaButtonReceiver;
+                        int componentType = mCurrentFullUserRecord
+                                .mRestoredMediaButtonReceiverComponentType;
+                        UserHandle userHandle = UserHandle.of(mCurrentFullUserRecord
+                                .mRestoredMediaButtonReceiverUserId);
                         if (DEBUG_KEY_EVENT) {
                             Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
-                                    + receiver);
+                                    + receiver + ", type=" + componentType);
                         }
                         mediaButtonIntent.setComponent(receiver);
-                        getContext().sendBroadcastAsUser(mediaButtonIntent,
-                                UserHandle.of(mCurrentFullUserRecord
-                                      .mRestoredMediaButtonReceiverUserId));
+                        try {
+                            switch (componentType) {
+                                case FullUserRecord.COMPONENT_TYPE_ACTIVITY:
+                                    getContext().startActivityAsUser(mediaButtonIntent, userHandle);
+                                    break;
+                                case FullUserRecord.COMPONENT_TYPE_SERVICE:
+                                    getContext().startForegroundServiceAsUser(mediaButtonIntent,
+                                            userHandle);
+                                    break;
+                                default:
+                                    // Legacy behavior for other cases.
+                                    getContext().sendBroadcastAsUser(mediaButtonIntent, userHandle);
+                            }
+                        } catch (Exception e) {
+                            Log.w(TAG, "Error sending media button to the restored intent "
+                                    + receiver + ", type=" + componentType, e);
+                        }
                         if (mCurrentFullUserRecord.mCallback != null) {
                             mCurrentFullUserRecord.mCallback
                                     .onMediaKeyEventDispatchedToMediaButtonReceiver(
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 0d6dadf..af55605 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -270,14 +270,12 @@
  * enforcement.
  *
  * <p>
- * This class uses 2-3 locks to synchronize state:
+ * This class uses 2 locks to synchronize state:
  * <ul>
  * <li>{@code mUidRulesFirstLock}: used to guard state related to individual UIDs (such as firewall
  * rules).
  * <li>{@code mNetworkPoliciesSecondLock}: used to guard state related to network interfaces (such
  * as network policies).
- * <li>{@code allLocks}: not a "real" lock, but an indication (through @GuardedBy) that all locks
- * must be held.
  * </ul>
  *
  * <p>
@@ -419,7 +417,8 @@
     final Object mUidRulesFirstLock = new Object();
     final Object mNetworkPoliciesSecondLock = new Object();
 
-    @GuardedBy("allLocks") volatile boolean mSystemReady;
+    @GuardedBy({"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
+    volatile boolean mSystemReady;
 
     @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictBackground;
     @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictPower;
@@ -558,7 +557,7 @@
 
     private final ServiceThread mUidEventThread;
 
-    @GuardedBy("allLocks")
+    @GuardedBy({"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
     private final AtomicFile mPolicyFile;
 
     private final AppOpsManager mAppOps;
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 6187583..c222e69 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -558,7 +558,7 @@
         if (pkgList != null && (pkgList.length > 0)) {
             boolean anyServicesInvolved = false;
             // Remove notification settings for uninstalled package
-            if (removingPackage) {
+            if (removingPackage && uidList != null) {
                 int size = Math.min(pkgList.length, uidList.length);
                 for (int i = 0; i < size; i++) {
                     final String pkg = pkgList[i];
@@ -570,9 +570,11 @@
                 if (mEnabledServicesPackageNames.contains(pkgName)) {
                     anyServicesInvolved = true;
                 }
-                for (int uid : uidList) {
-                    if (isPackageAllowed(pkgName, UserHandle.getUserId(uid))) {
-                        anyServicesInvolved = true;
+                if (uidList != null && uidList.length > 0) {
+                    for (int uid : uidList) {
+                        if (isPackageAllowed(pkgName, UserHandle.getUserId(uid))) {
+                            anyServicesInvolved = true;
+                        }
                     }
                 }
             }
@@ -765,6 +767,23 @@
         return installed;
     }
 
+    protected Set<String> getAllowedPackages() {
+        final Set<String> allowedPackages = new ArraySet<>();
+        for (int k = 0; k < mApproved.size(); k++) {
+            ArrayMap<Boolean, ArraySet<String>> allowedByType = mApproved.valueAt(k);
+            for (int i = 0; i < allowedByType.size(); i++) {
+                final ArraySet<String> allowed = allowedByType.valueAt(i);
+                for (int j = 0; j < allowed.size(); j++) {
+                    String pkgName = getPackageName(allowed.valueAt(j));
+                    if (!TextUtils.isEmpty(pkgName)) {
+                        allowedPackages.add(pkgName);
+                    }
+                }
+            }
+        }
+        return allowedPackages;
+    }
+
     private void trimApprovedListsAccordingToInstalledServices() {
         int N = mApproved.size();
         for (int i = 0 ; i < N; i++) {
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 84bb13e..ee60daa 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -45,7 +45,12 @@
     void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded);
     void onNotificationDirectReplied(String key);
     void onNotificationSettingsViewed(String key);
-    void onNotificationSmartRepliesAdded(String key, int replyCount);
+
+    /**
+     * Notifies that smart replies and actions have been added to the UI.
+     */
+    void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount, int smartActionCount,
+            boolean generatedByAssistant);
 
     /**
      * Notifies a smart reply is sent.
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 36a027a..c68e0f9 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -47,17 +47,12 @@
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
 import static android.os.UserHandle.USER_NULL;
 import static android.os.UserHandle.USER_SYSTEM;
-import static android.service.notification.NotificationListenerService
-        .HINT_HOST_DISABLE_CALL_EFFECTS;
+import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
-import static android.service.notification.NotificationListenerService
-        .HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
-import static android.service.notification.NotificationListenerService
-        .NOTIFICATION_CHANNEL_OR_GROUP_ADDED;
-import static android.service.notification.NotificationListenerService
-        .NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
-import static android.service.notification.NotificationListenerService
-        .NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
+import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
@@ -65,8 +60,7 @@
 import static android.service.notification.NotificationListenerService.REASON_CHANNEL_BANNED;
 import static android.service.notification.NotificationListenerService.REASON_CLICK;
 import static android.service.notification.NotificationListenerService.REASON_ERROR;
-import static android.service.notification.NotificationListenerService
-        .REASON_GROUP_SUMMARY_CANCELED;
+import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
 import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL_ALL;
 import static android.service.notification.NotificationListenerService.REASON_PACKAGE_BANNED;
@@ -81,6 +75,8 @@
 import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
+import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
+import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
 import static com.android.server.utils.PriorityDump.PRIORITY_ARG;
 import static com.android.server.utils.PriorityDump.PRIORITY_ARG_CRITICAL;
 import static com.android.server.utils.PriorityDump.PRIORITY_ARG_NORMAL;
@@ -150,7 +146,6 @@
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.ShellCallback;
-import android.os.ShellCommand;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -768,7 +763,12 @@
                         .setType(MetricsEvent.TYPE_ACTION)
                         .setSubtype(actionIndex)
                         .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank)
-                        .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count));
+                        .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count)
+                        .addTaggedData(MetricsEvent.NOTIFICATION_ACTION_IS_SMART,
+                                action.isContextual() ? 1 : 0)
+                        .addTaggedData(
+                                MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED,
+                                generatedByAssistant ? 1 : 0));
                 EventLogTags.writeNotificationActionClicked(key, actionIndex,
                         r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now),
                         nv.rank, nv.count);
@@ -842,20 +842,13 @@
                         // Report to usage stats that notification was made visible
                         if (DBG) Slog.d(TAG, "Marking notification as visible " + nv.key);
                         reportSeen(r);
-
-                        // If the newly visible notification has smart replies
-                        // then log that the user has seen them.
-                        if (r.getNumSmartRepliesAdded() > 0
-                                && !r.hasSeenSmartReplies()) {
-                            r.setSeenSmartReplies(true);
-                            LogMaker logMaker = r.getLogMaker()
-                                    .setCategory(MetricsEvent.SMART_REPLY_VISIBLE)
-                                    .addTaggedData(MetricsEvent.NOTIFICATION_SMART_REPLY_COUNT,
-                                            r.getNumSmartRepliesAdded());
-                            mMetricsLogger.write(logMaker);
-                        }
                     }
                     r.setVisibility(true, nv.rank, nv.count);
+                    // hasBeenVisiblyExpanded must be called after updating the expansion state of
+                    // the NotificationRecord to ensure the expansion state is up-to-date.
+                    if (r.hasBeenVisiblyExpanded()) {
+                        logSmartSuggestionsVisible(r);
+                    }
                     maybeRecordInterruptionLocked(r);
                     nv.recycle();
                 }
@@ -879,6 +872,11 @@
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r != null) {
                     r.stats.onExpansionChanged(userAction, expanded);
+                    // hasBeenVisiblyExpanded must be called after updating the expansion state of
+                    // the NotificationRecord to ensure the expansion state is up-to-date.
+                    if (r.hasBeenVisiblyExpanded()) {
+                        logSmartSuggestionsVisible(r);
+                    }
                     final long now = System.currentTimeMillis();
                     if (userAction) {
                         MetricsLogger.action(r.getItemLogMaker()
@@ -914,11 +912,14 @@
         }
 
         @Override
-        public void onNotificationSmartRepliesAdded(String key, int replyCount) {
+        public void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount,
+                int smartActionCount, boolean generatedByAssistant) {
             synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r != null) {
-                    r.setNumSmartRepliesAdded(replyCount);
+                    r.setNumSmartRepliesAdded(smartReplyCount);
+                    r.setNumSmartActionsAdded(smartActionCount);
+                    r.setSuggestionsGeneratedByAssistant(generatedByAssistant);
                 }
             }
         }
@@ -926,6 +927,7 @@
         @Override
         public void onNotificationSmartReplySent(String key, int replyIndex, CharSequence reply,
                 boolean generatedByAssistant) {
+
             synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r != null) {
@@ -952,6 +954,26 @@
         }
     };
 
+    @VisibleForTesting
+    void logSmartSuggestionsVisible(NotificationRecord r) {
+        // If the newly visible notification has smart suggestions
+        // then log that the user has seen them.
+        if ((r.getNumSmartRepliesAdded() > 0 || r.getNumSmartActionsAdded() > 0)
+                && !r.hasSeenSmartReplies()) {
+            r.setSeenSmartReplies(true);
+            LogMaker logMaker = r.getLogMaker()
+                    .setCategory(MetricsEvent.SMART_REPLY_VISIBLE)
+                    .addTaggedData(MetricsEvent.NOTIFICATION_SMART_REPLY_COUNT,
+                            r.getNumSmartRepliesAdded())
+                    .addTaggedData(MetricsEvent.NOTIFICATION_SMART_ACTION_COUNT,
+                            r.getNumSmartActionsAdded())
+                    .addTaggedData(
+                            MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED,
+                            r.getSuggestionsGeneratedByAssistant() ? 1 : 0);
+            mMetricsLogger.write(logMaker);
+        }
+    }
+
     @GuardedBy("mNotificationLock")
     private void clearSoundLocked() {
         mSoundNotificationKey = null;
@@ -1293,6 +1315,11 @@
     }
 
     @VisibleForTesting
+    void setHints(int hints) {
+        mListenerHints = hints;
+    }
+
+    @VisibleForTesting
     void setVibrator(Vibrator vibrator) {
         mVibrator = vibrator;
     }
@@ -1674,8 +1701,16 @@
     }
 
     private void sendRegisteredOnlyBroadcast(String action) {
-        getContext().sendBroadcastAsUser(new Intent(action)
-                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
+        Intent intent = new Intent(action);
+        getContext().sendBroadcastAsUser(intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
+                UserHandle.ALL, null);
+        // explicitly send the broadcast to all DND packages, even if they aren't currently running
+        intent.setFlags(0);
+        final Set<String> dndApprovedPackages = mConditionProviders.getAllowedPackages();
+        for (String pkg : dndApprovedPackages) {
+            intent.setPackage(pkg);
+            getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
+        }
     }
 
     @Override
@@ -1930,9 +1965,11 @@
      */
     @GuardedBy("mNotificationLock")
     protected void reportSeen(NotificationRecord r) {
-        mAppUsageStats.reportEvent(r.sbn.getPackageName(),
-                getRealUserId(r.sbn.getUserId()),
-                UsageEvents.Event.NOTIFICATION_SEEN);
+        if (!r.isProxied()) {
+            mAppUsageStats.reportEvent(r.sbn.getPackageName(),
+                    getRealUserId(r.sbn.getUserId()),
+                    UsageEvents.Event.NOTIFICATION_SEEN);
+        }
     }
 
     protected int calculateSuppressedVisualEffects(Policy incomingPolicy, Policy currPolicy,
@@ -2270,6 +2307,26 @@
         }
 
         @Override
+        public boolean areAppOverlaysAllowed(String pkg) {
+            return areAppOverlaysAllowedForPackage(pkg, Binder.getCallingUid());
+        }
+
+        @Override
+        public boolean areAppOverlaysAllowedForPackage(String pkg, int uid) {
+            checkCallerIsSystemOrSameApp(pkg);
+
+            return mPreferencesHelper.areAppOverlaysAllowed(pkg, uid);
+        }
+
+        @Override
+        public void setAppOverlaysAllowed(String pkg, int uid, boolean allowed) {
+            checkCallerIsSystem();
+
+            mPreferencesHelper.setAppOverlaysAllowed(pkg, uid, allowed);
+            handleSavePolicyFile();
+        }
+
+        @Override
         public int getPackageImportance(String pkg) {
             checkCallerIsSystemOrSameApp(pkg);
             return mPreferencesHelper.getImportance(pkg, Binder.getCallingUid());
@@ -2592,6 +2649,9 @@
             // Zen
             mConditionProviders.onPackagesChanged(true, packages, uids);
 
+            // Snoozing
+            mSnoozeHelper.clearData(UserHandle.getUserId(uid), packageName);
+
             // Reset notification preferences
             if (!fromApp) {
                 mPreferencesHelper.onPackagesChanged(
@@ -2638,6 +2698,10 @@
          * Note that since notification posting is done asynchronously, this will not return
          * notifications that are in the process of being posted.
          *
+         * From {@link Build.VERSION_CODES#Q}, will also return notifications you've posted as
+         * an app's notification delegate via
+         * {@link NotificationManager#notifyAsPackage(String, String, int, Notification)}.
+         *
          * @returns A list of all the package's notifications, in natural order.
          */
         @Override
@@ -2680,16 +2744,18 @@
 
         private StatusBarNotification sanitizeSbn(String pkg, int userId,
                 StatusBarNotification sbn) {
-            if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId) {
-                // We could pass back a cloneLight() but clients might get confused and
-                // try to send this thing back to notify() again, which would not work
-                // very well.
-                return new StatusBarNotification(
-                        sbn.getPackageName(),
-                        sbn.getOpPkg(),
-                        sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
-                        sbn.getNotification().clone(),
-                        sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
+            if (sbn.getUserId() == userId) {
+                if (sbn.getPackageName().equals(pkg) || sbn.getOpPkg().equals(pkg)) {
+                    // We could pass back a cloneLight() but clients might get confused and
+                    // try to send this thing back to notify() again, which would not work
+                    // very well.
+                    return new StatusBarNotification(
+                            sbn.getPackageName(),
+                            sbn.getOpPkg(),
+                            sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
+                            sbn.getNotification().clone(),
+                            sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
+                }
             }
             return null;
         }
@@ -3137,7 +3203,11 @@
                 throws RemoteException {
             Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
             Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
-            Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
+            if (automaticZenRule.getOwner() == null
+                    && automaticZenRule.getConfigurationActivity() == null) {
+                throw new NullPointerException(
+                        "Rule must have a conditionproviderservice and/or configuration activity");
+            }
             Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
             enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
 
@@ -3150,7 +3220,11 @@
                 throws RemoteException {
             Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
             Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
-            Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
+            if (automaticZenRule.getOwner() == null
+                    && automaticZenRule.getConfigurationActivity() == null) {
+                throw new NullPointerException(
+                        "Rule must have a conditionproviderservice and/or configuration activity");
+            }
             Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
             enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
 
@@ -3184,6 +3258,16 @@
         }
 
         @Override
+        public void setAutomaticZenRuleState(String id, Condition condition) {
+            Preconditions.checkNotNull(id, "id is null");
+            Preconditions.checkNotNull(condition, "Condition is null");
+
+            enforcePolicyAccess(Binder.getCallingUid(), "setAutomaticZenRuleState");
+
+            mZenModeHelper.setAutomaticZenRuleState(id, condition);
+        }
+
+        @Override
         public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
             enforcePolicyAccess(pkg, "setInterruptionFilter");
             final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
@@ -3784,7 +3868,8 @@
         public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
                 String[] args, ShellCallback callback, ResultReceiver resultReceiver)
                 throws RemoteException {
-            new ShellCmd().exec(this, in, out, err, args, callback, resultReceiver);
+            new NotificationShellCmd(NotificationManagerService.this)
+                    .exec(this, in, out, err, args, callback, resultReceiver);
         }
     };
 
@@ -3922,6 +4007,20 @@
         if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
             return "listenerHints";
         }
+        if (record != null && record.getAudioAttributes() != null) {
+            if ((mListenerHints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
+                if (record.getAudioAttributes().getUsage()
+                        != AudioAttributes.USAGE_VOICE_COMMUNICATION) {
+                    return "listenerNoti";
+                }
+            }
+            if ((mListenerHints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
+                if (record.getAudioAttributes().getUsage()
+                        == AudioAttributes.USAGE_VOICE_COMMUNICATION) {
+                    return "listenerCall";
+                }
+            }
+        }
         if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
             return "callState";
         }
@@ -3990,7 +4089,7 @@
                 final ArraySet<ComponentName> listeners =
                     mListenersDisablingEffects.valueAt(i);
                 for (int j = 0; j < listeners.size(); j++) {
-                    final ComponentName componentName = listeners.valueAt(i);
+                    final ComponentName componentName = listeners.valueAt(j);
                     componentName.writeToProto(proto,
                             ListenersDisablingEffectsProto.LISTENER_COMPONENTS);
                 }
@@ -4135,8 +4234,8 @@
                     final int listenerSize = listeners.size();
 
                     for (int j = 0; j < listenerSize; j++) {
-                        if (i > 0) pw.print(',');
-                        final ComponentName listener = listeners.valueAt(i);
+                        if (j > 0) pw.print(',');
+                        final ComponentName listener = listeners.valueAt(j);
                         if (listener != null) {
                             pw.print(listener);
                         }
@@ -4352,6 +4451,8 @@
                     if (pendingIntent != null) {
                         am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
                                 WHITELIST_TOKEN, duration);
+                        am.setPendingIntentAllowBgActivityStarts(pendingIntent.getTarget(),
+                                WHITELIST_TOKEN, (FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER));
                     }
                 }
             }
@@ -4376,7 +4477,7 @@
             notification.flags &= ~Notification.FLAG_CAN_COLORIZE;
         }
 
-        if (ai.targetSdkVersion >= Build.VERSION_CODES.Q) {
+        if (notification.fullScreenIntent != null && ai.targetSdkVersion >= Build.VERSION_CODES.Q) {
             int fullscreenIntentPermission = mPackageManagerClient.checkPermission(
                     android.Manifest.permission.USE_FULL_SCREEN_INTENT, pkg);
             if (fullscreenIntentPermission != PERMISSION_GRANTED) {
@@ -4871,7 +4972,7 @@
                         Slog.e(TAG, "Not posting notification without small icon: " + notification);
                         if (old != null && !old.isCanceled) {
                             mListeners.notifyRemovedLocked(r,
-                                    NotificationListenerService.REASON_ERROR, null);
+                                    NotificationListenerService.REASON_ERROR, r.getStats());
                             mHandler.post(new Runnable() {
                                 @Override
                                 public void run() {
@@ -5190,22 +5291,25 @@
                     }
                     if (DBG) Slog.v(TAG, "Interrupting!");
                     if (hasValidSound) {
-                        mSoundNotificationKey = key;
                         if (mInCall) {
                             playInCallNotification();
                             beep = true;
                         } else {
                             beep = playSound(record, soundUri);
                         }
+                        if(beep) {
+                            mSoundNotificationKey = key;
+                        }
                     }
 
                     final boolean ringerModeSilent =
                             mAudioManager.getRingerModeInternal()
                                     == AudioManager.RINGER_MODE_SILENT;
                     if (!mInCall && hasValidVibrate && !ringerModeSilent) {
-                        mVibrateNotificationKey = key;
-
                         buzz = playVibration(record, vibration, hasValidSound);
+                        if(buzz) {
+                            mVibrateNotificationKey = key;
+                        }
                     }
                 } else if ((record.getFlags() & Notification.FLAG_INSISTENT) != 0) {
                     hasValidSound = false;
@@ -5386,8 +5490,17 @@
                     try {
                         Thread.sleep(waitMs);
                     } catch (InterruptedException e) { }
-                    mVibrator.vibrate(record.sbn.getUid(), record.sbn.getPackageName(),
-                            effect, "Notification (delayed)", record.getAudioAttributes());
+
+                    // Notifications might be canceled before it actually vibrates due to waitMs,
+                    // so need to check the notification still valide for vibrate.
+                    synchronized (mNotificationLock) {
+                        if (mNotificationsByKey.get(record.getKey()) != null) {
+                            mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
+                                    effect, "Notification (delayed)", record.getAudioAttributes());
+                        } else {
+                            Slog.e(TAG, "No vibration for canceled notification : " + record.getKey());
+                        }
+                    }
                 }).start();
             } else {
                 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getPackageName(),
@@ -5595,19 +5708,17 @@
             }
             int indexBefore = findNotificationRecordIndexLocked(record);
             boolean interceptBefore = record.isIntercepted();
-            float contactAffinityBefore = record.getContactAffinity();
             int visibilityBefore = record.getPackageVisibilityOverride();
             recon.applyChangesLocked(record);
             applyZenModeLocked(record);
             mRankingHelper.sort(mNotificationList);
             int indexAfter = findNotificationRecordIndexLocked(record);
             boolean interceptAfter = record.isIntercepted();
-            float contactAffinityAfter = record.getContactAffinity();
             int visibilityAfter = record.getPackageVisibilityOverride();
             changed = indexBefore != indexAfter || interceptBefore != interceptAfter
                     || visibilityBefore != visibilityAfter;
             if (interceptBefore && !interceptAfter
-                    && Float.compare(contactAffinityBefore, contactAffinityAfter) != 0) {
+                    && record.isNewEnoughForAlerting(System.currentTimeMillis())) {
                 buzzBeepBlinkLocked(record);
             }
         }
@@ -6695,7 +6806,7 @@
         Bundle hidden = new Bundle();
         Bundle systemGeneratedSmartActions = new Bundle();
         Bundle smartReplies = new Bundle();
-        Bundle audiblyAlerted = new Bundle();
+        Bundle lastAudiblyAlerted = new Bundle();
         Bundle noisy = new Bundle();
         for (int i = 0; i < N; i++) {
             NotificationRecord record = mNotificationList.get(i);
@@ -6727,7 +6838,7 @@
             systemGeneratedSmartActions.putParcelableArrayList(key,
                     record.getSystemGeneratedSmartActions());
             smartReplies.putCharSequenceArrayList(key, record.getSmartReplies());
-            audiblyAlerted.putBoolean(key, record.getAudiblyAlerted());
+            lastAudiblyAlerted.putLong(key, record.getLastAudiblyAlertedMs());
             noisy.putBoolean(key, record.getSound() != null || record.getVibration() != null);
         }
         final int M = keys.size();
@@ -6740,7 +6851,7 @@
         return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
                 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
                 channels, overridePeople, snoozeCriteria, showBadge, userSentiment, hidden,
-                systemGeneratedSmartActions, smartReplies, audiblyAlerted, noisy);
+                systemGeneratedSmartActions, smartReplies, lastAudiblyAlerted, noisy);
     }
 
     boolean hasCompanionDevice(ManagedServiceInfo info) {
@@ -7647,110 +7758,6 @@
         }
     }
 
-    private class ShellCmd extends ShellCommand {
-        public static final String USAGE = "help\n"
-                + "allow_listener COMPONENT [user_id]\n"
-                + "disallow_listener COMPONENT [user_id]\n"
-                + "allow_assistant COMPONENT\n"
-                + "remove_assistant COMPONENT\n"
-                + "allow_dnd PACKAGE\n"
-                + "disallow_dnd PACKAGE\n"
-                + "suspend_package PACKAGE\n"
-                + "unsuspend_package PACKAGE";
-
-        @Override
-        public int onCommand(String cmd) {
-            if (cmd == null) {
-                return handleDefaultCommands(cmd);
-            }
-            final PrintWriter pw = getOutPrintWriter();
-            try {
-                switch (cmd) {
-                    case "allow_dnd": {
-                        getBinderService().setNotificationPolicyAccessGranted(
-                                getNextArgRequired(), true);
-                    }
-                    break;
-
-                    case "disallow_dnd": {
-                        getBinderService().setNotificationPolicyAccessGranted(
-                                getNextArgRequired(), false);
-                    }
-                    break;
-                    case "allow_listener": {
-                        ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
-                        if (cn == null) {
-                            pw.println("Invalid listener - must be a ComponentName");
-                            return -1;
-                        }
-                        String userId = getNextArg();
-                        if (userId == null) {
-                            getBinderService().setNotificationListenerAccessGranted(cn, true);
-                        } else {
-                            getBinderService().setNotificationListenerAccessGrantedForUser(
-                                    cn, Integer.parseInt(userId), true);
-                        }
-                    }
-                    break;
-                    case "disallow_listener": {
-                        ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
-                        if (cn == null) {
-                            pw.println("Invalid listener - must be a ComponentName");
-                            return -1;
-                        }
-                        String userId = getNextArg();
-                        if (userId == null) {
-                            getBinderService().setNotificationListenerAccessGranted(cn, false);
-                        } else {
-                            getBinderService().setNotificationListenerAccessGrantedForUser(
-                                    cn, Integer.parseInt(userId), false);
-                        }
-                    }
-                    break;
-                    case "allow_assistant": {
-                        ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
-                        if (cn == null) {
-                            pw.println("Invalid assistant - must be a ComponentName");
-                            return -1;
-                        }
-                        getBinderService().setNotificationAssistantAccessGranted(cn, true);
-                    }
-                    break;
-                    case "disallow_assistant": {
-                        ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
-                        if (cn == null) {
-                            pw.println("Invalid assistant - must be a ComponentName");
-                            return -1;
-                        }
-                        getBinderService().setNotificationAssistantAccessGranted(cn, false);
-                    }
-                    break;
-                    case "suspend_package": {
-                        // only use for testing
-                        simulatePackageSuspendBroadcast(true, getNextArgRequired());
-                    }
-                    break;
-                    case "unsuspend_package": {
-                        // only use for testing
-                        simulatePackageSuspendBroadcast(false, getNextArgRequired());
-                    }
-                    break;
-                    default:
-                        return handleDefaultCommands(cmd);
-                }
-            } catch (Exception e) {
-                pw.println("Error occurred. Check logcat for details. " + e.getMessage());
-                Slog.e(TAG, "Error running shell command", e);
-            }
-            return 0;
-        }
-
-        @Override
-        public void onHelp() {
-            getOutPrintWriter().println(USAGE);
-        }
-    }
-
     private void writeSecureNotificationsPolicy(XmlSerializer out) throws IOException {
         out.startTag(null, LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_TAG);
         out.attribute(null, LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE,
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 50810cc..9942f59 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -92,6 +92,8 @@
     static final String TAG = "NotificationRecord";
     static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
     private static final int MAX_LOGTAG_LENGTH = 35;
+    // the period after which a notification is updated where it can make sound
+    private static final int MAX_SOUND_DELAY_MS = 2000;
     final StatusBarNotification sbn;
     IActivityManager mAm;
     UriGrantsManagerInternal mUgmInternal;
@@ -125,13 +127,17 @@
     private long mVisibleSinceMs;
 
     // The most recent update time, or the creation time if no updates.
-    private long mUpdateTimeMs;
+    @VisibleForTesting
+    final long mUpdateTimeMs;
 
     // The most recent interruption time, or the creation time if no updates. Differs from the
     // above value because updates are filtered based on whether they actually interrupted the
     // user
     private long mInterruptionTimeMs;
 
+    // The most recent time the notification made noise or buzzed the device, or -1 if it did not.
+    private long mLastAudiblyAlertedMs;
+
     // Is this record an update of an old record?
     public boolean isUpdate;
     private int mPackagePriority;
@@ -148,7 +154,6 @@
 
     private int mSuppressedVisualEffects = 0;
     private String mUserExplanation;
-    private String mPeopleExplanation;
     private boolean mPreChannelsNotification = true;
     private Uri mSound;
     private long[] mVibration;
@@ -163,7 +168,7 @@
     private String mChannelIdLogTag;
     /**
      * This list contains system generated smart actions from NAS, app-generated smart actions are
-     * stored in Notification.actions marked as SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION.
+     * stored in Notification.actions with isContextual() set to true.
      */
     private ArrayList<Notification.Action> mSystemGeneratedSmartActions;
     private ArrayList<CharSequence> mSmartReplies;
@@ -172,10 +177,11 @@
     private final NotificationStats mStats;
     private int mUserSentiment;
     private boolean mIsInterruptive;
-    private boolean mAudiblyAlerted;
     private boolean mTextChanged;
     private boolean mRecordedInterruption;
     private int mNumberOfSmartRepliesAdded;
+    private int mNumberOfSmartActionsAdded;
+    private boolean mSuggestionsGeneratedByAssistant;
     private boolean mHasSeenSmartReplies;
     /**
      * Whether this notification (and its channels) should be considered user locked. Used in
@@ -823,6 +829,10 @@
         return mIntercept;
     }
 
+    public boolean isNewEnoughForAlerting(long now) {
+        return getFreshnessMs(now) <= MAX_SOUND_DELAY_MS;
+    }
+
     public void setHidden(boolean hidden) {
         mHidden = hidden;
     }
@@ -1051,7 +1061,7 @@
     }
 
     public void setAudiblyAlerted(boolean audiblyAlerted) {
-        mAudiblyAlerted = audiblyAlerted;
+        mLastAudiblyAlertedMs = audiblyAlerted ? System.currentTimeMillis() : -1;
     }
 
     public void setTextChanged(boolean textChanged) {
@@ -1070,9 +1080,9 @@
         return mIsInterruptive;
     }
 
-    /** Returns true if the notification audibly alerted the user. */
-    public boolean getAudiblyAlerted() {
-        return mAudiblyAlerted;
+    /** Returns the time the notification audibly alerted the user. */
+    public long getLastAudiblyAlertedMs() {
+        return mLastAudiblyAlertedMs;
     }
 
     protected void setPeopleOverride(ArrayList<String> people) {
@@ -1138,6 +1148,22 @@
         return mNumberOfSmartRepliesAdded;
     }
 
+    public void setNumSmartActionsAdded(int noActions) {
+        mNumberOfSmartActionsAdded = noActions;
+    }
+
+    public int getNumSmartActionsAdded() {
+        return mNumberOfSmartActionsAdded;
+    }
+
+    public void setSuggestionsGeneratedByAssistant(boolean generatedByAssistant) {
+        mSuggestionsGeneratedByAssistant = generatedByAssistant;
+    }
+
+    public boolean getSuggestionsGeneratedByAssistant() {
+        return mSuggestionsGeneratedByAssistant;
+    }
+
     public boolean hasSeenSmartReplies() {
         return mHasSeenSmartReplies;
     }
@@ -1146,6 +1172,13 @@
         mHasSeenSmartReplies = hasSeenSmartReplies;
     }
 
+    /**
+     * Returns whether this notification has been visible and expanded at the same time.
+     */
+    public boolean hasBeenVisiblyExpanded() {
+        return stats.hasBeenVisiblyExpanded();
+    }
+
     public void setSystemGeneratedSmartActions(
             ArrayList<Notification.Action> systemGeneratedSmartActions) {
         mSystemGeneratedSmartActions = systemGeneratedSmartActions;
@@ -1164,6 +1197,13 @@
     }
 
     /**
+     * Returns whether this notification was posted by a secondary app
+     */
+    public boolean isProxied() {
+        return !Objects.equals(sbn.getPackageName(), sbn.getOpPkg());
+    }
+
+    /**
      * @return all {@link Uri} that should have permission granted to whoever
      *         will be rendering it. This list has already been vetted to only
      *         include {@link Uri} that the enqueuing app can grant.
diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java
new file mode 100644
index 0000000..3d88f20
--- /dev/null
+++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import android.app.INotificationManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Person;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ParceledListSlice;
+import android.content.res.Resources;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ShellCommand;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.net.URISyntaxException;
+import java.util.Collections;
+
+/**
+ * Implementation of `cmd notification` in NotificationManagerService.
+ */
+public class NotificationShellCmd extends ShellCommand {
+    private static final String USAGE =
+              "usage: cmd notification SUBCMD [args]\n\n"
+            + "SUBCMDs:\n"
+            + "  allow_listener COMPONENT [user_id]\n"
+            + "  disallow_listener COMPONENT [user_id]\n"
+            + "  allow_assistant COMPONENT\n"
+            + "  remove_assistant COMPONENT\n"
+            + "  allow_dnd PACKAGE\n"
+            + "  disallow_dnd PACKAGE\n"
+            + "  suspend_package PACKAGE\n"
+            + "  unsuspend_package PACKAGE\n"
+            + "  post [--help | flags] TAG TEXT";
+
+    private static final String NOTIFY_USAGE =
+              "usage: cmd notification post [flags] <tag> <text>\n\n"
+            + "flags:\n"
+            + "  -h|--help\n"
+            + "  -v|--verbose\n"
+            + "  -t|--title <text>\n"
+            + "  -i|--icon <iconspec>\n"
+            + "  -I|--large-icon <iconspec>\n"
+            + "  -S|--style <style> [styleargs]\n"
+            + "  -c|--content-intent <intentspec>\n"
+            + "\n"
+            + "styles: (default none)\n"
+            + "  bigtext\n"
+            + "  bigpicture --picture <iconspec>\n"
+            + "  inbox --line <text> --line <text> ...\n"
+            + "  messaging --conversation <title> --message <who>:<text> ...\n"
+            + "  media\n"
+            + "\n"
+            + "an <iconspec> is one of\n"
+            + "  file:///data/local/tmp/<img.png>\n"
+            + "  content://<provider>/<path>\n"
+            + "  @[<package>:]drawable/<img>\n"
+            + "  data:base64,<B64DATA==>\n"
+            + "\n"
+            + "an <intentspec> is (broadcast|service|activity) <args>\n"
+            + "  <args> are as described in `am start`";
+
+    public static final int NOTIFICATION_ID = 1138;
+    public static final String NOTIFICATION_PACKAGE = "com.android.shell";
+    public static final String CHANNEL_ID = "shellcmd";
+    public static final String CHANNEL_NAME = "Shell command";
+    public static final int CHANNEL_IMP = NotificationManager.IMPORTANCE_DEFAULT;
+
+    private final NotificationManagerService mDirectService;
+    private final INotificationManager mBinderService;
+
+    public NotificationShellCmd(NotificationManagerService service) {
+        mDirectService = service;
+        mBinderService = service.getBinderService();
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(cmd);
+        }
+        final PrintWriter pw = getOutPrintWriter();
+        try {
+            switch (cmd.replace('-', '_')) {
+                case "allow_dnd": {
+                    mBinderService.setNotificationPolicyAccessGranted(
+                            getNextArgRequired(), true);
+                }
+                break;
+
+                case "disallow_dnd": {
+                    mBinderService.setNotificationPolicyAccessGranted(
+                            getNextArgRequired(), false);
+                }
+                break;
+                case "allow_listener": {
+                    ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
+                    if (cn == null) {
+                        pw.println("Invalid listener - must be a ComponentName");
+                        return -1;
+                    }
+                    String userId = getNextArg();
+                    if (userId == null) {
+                        mBinderService.setNotificationListenerAccessGranted(cn, true);
+                    } else {
+                        mBinderService.setNotificationListenerAccessGrantedForUser(
+                                cn, Integer.parseInt(userId), true);
+                    }
+                }
+                break;
+                case "disallow_listener": {
+                    ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
+                    if (cn == null) {
+                        pw.println("Invalid listener - must be a ComponentName");
+                        return -1;
+                    }
+                    String userId = getNextArg();
+                    if (userId == null) {
+                        mBinderService.setNotificationListenerAccessGranted(cn, false);
+                    } else {
+                        mBinderService.setNotificationListenerAccessGrantedForUser(
+                                cn, Integer.parseInt(userId), false);
+                    }
+                }
+                break;
+                case "allow_assistant": {
+                    ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
+                    if (cn == null) {
+                        pw.println("Invalid assistant - must be a ComponentName");
+                        return -1;
+                    }
+                    mBinderService.setNotificationAssistantAccessGranted(cn, true);
+                }
+                break;
+                case "disallow_assistant": {
+                    ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
+                    if (cn == null) {
+                        pw.println("Invalid assistant - must be a ComponentName");
+                        return -1;
+                    }
+                    mBinderService.setNotificationAssistantAccessGranted(cn, false);
+                }
+                break;
+                case "suspend_package": {
+                    // only use for testing
+                    mDirectService.simulatePackageSuspendBroadcast(true, getNextArgRequired());
+                }
+                break;
+                case "unsuspend_package": {
+                    // only use for testing
+                    mDirectService.simulatePackageSuspendBroadcast(false, getNextArgRequired());
+                }
+                break;
+                case "post":
+                case "notify":
+                    doNotify(pw);
+                    break;
+                default:
+                    return handleDefaultCommands(cmd);
+            }
+        } catch (Exception e) {
+            pw.println("Error occurred. Check logcat for details. " + e.getMessage());
+            Slog.e(NotificationManagerService.TAG, "Error running shell command", e);
+        }
+        return 0;
+    }
+
+    void ensureChannel() throws RemoteException {
+        final int uid = Binder.getCallingUid();
+        final int userid = UserHandle.getCallingUserId();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            if (mBinderService.getNotificationChannelForPackage(NOTIFICATION_PACKAGE,
+                    uid, CHANNEL_ID, false) == null) {
+                final NotificationChannel chan = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME,
+                        CHANNEL_IMP);
+                Slog.v(NotificationManagerService.TAG,
+                        "creating shell channel for user " + userid + " uid " + uid + ": " + chan);
+                mBinderService.createNotificationChannelsForPackage(NOTIFICATION_PACKAGE, uid,
+                        new ParceledListSlice<NotificationChannel>(
+                                Collections.singletonList(chan)));
+                Slog.v(NotificationManagerService.TAG, "created channel: "
+                        + mBinderService.getNotificationChannelForPackage(NOTIFICATION_PACKAGE,
+                                uid, CHANNEL_ID, false));
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    Icon parseIcon(Resources res, String encoded) throws IllegalArgumentException {
+        if (TextUtils.isEmpty(encoded)) return null;
+        if (encoded.startsWith("/")) {
+            encoded = "file://" + encoded;
+        }
+        if (encoded.startsWith("http:")
+                || encoded.startsWith("https:")
+                || encoded.startsWith("content:")
+                || encoded.startsWith("file:")
+                || encoded.startsWith("android.resource:")) {
+            Uri asUri = Uri.parse(encoded);
+            return Icon.createWithContentUri(asUri);
+        } else if (encoded.startsWith("@")) {
+            final int resid = res.getIdentifier(encoded.substring(1),
+                    "drawable", "android");
+            if (resid != 0) {
+                return Icon.createWithResource(res, resid);
+            }
+        } else if (encoded.startsWith("data:")) {
+            encoded = encoded.substring(encoded.indexOf(',') + 1);
+            byte[] bits = android.util.Base64.decode(encoded, android.util.Base64.DEFAULT);
+            return Icon.createWithData(bits, 0, bits.length);
+        }
+        return null;
+    }
+
+    private int doNotify(PrintWriter pw) throws RemoteException, URISyntaxException {
+        final Context context = mDirectService.getContext();
+        final Resources res = context.getResources();
+        final Notification.Builder builder = new Notification.Builder(context, CHANNEL_ID);
+        String opt;
+
+        boolean verbose = false;
+        Notification.BigPictureStyle bigPictureStyle = null;
+        Notification.BigTextStyle bigTextStyle = null;
+        Notification.InboxStyle inboxStyle = null;
+        Notification.MediaStyle mediaStyle = null;
+        Notification.MessagingStyle messagingStyle = null;
+
+        Icon smallIcon = null;
+        while ((opt = getNextOption()) != null) {
+            boolean large = false;
+            switch (opt) {
+                case "-v":
+                case "--verbose":
+                    verbose = true;
+                    break;
+                case "-t":
+                case "--title":
+                case "title":
+                    builder.setContentTitle(getNextArgRequired());
+                    break;
+                case "-I":
+                case "--large-icon":
+                case "--largeicon":
+                case "largeicon":
+                case "large-icon":
+                    large = true;
+                    // fall through
+                case "-i":
+                case "--icon":
+                case "icon":
+                    final String iconSpec = getNextArgRequired();
+                    final Icon icon = parseIcon(res, iconSpec);
+                    if (icon == null) {
+                        pw.println("error: invalid icon: " + iconSpec);
+                        return -1;
+                    }
+                    if (large) {
+                        builder.setLargeIcon(icon);
+                        large = false;
+                    } else {
+                        smallIcon = icon;
+                    }
+                    break;
+                case "-c":
+                case "--content-intent":
+                case "content-intent":
+                case "--intent":
+                case "intent":
+                    String intentKind = null;
+                    switch (peekNextArg()) {
+                        case "broadcast":
+                        case "service":
+                        case "activity":
+                            intentKind = getNextArg();
+                    }
+                    final Intent intent = Intent.parseCommandArgs(this, null);
+                    if (intent.getData() == null) {
+                        // force unique intents unless you know what you're doing
+                        intent.setData(Uri.parse("xyz:" + System.currentTimeMillis()));
+                    }
+                    final PendingIntent pi;
+                    if ("broadcast".equals(intentKind)) {
+                        pi = PendingIntent.getBroadcastAsUser(
+                                context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT,
+                                UserHandle.CURRENT);
+                    } else if ("service".equals(intentKind)) {
+                        pi = PendingIntent.getService(
+                                context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+                    } else {
+                        pi = PendingIntent.getActivityAsUser(
+                                context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT, null,
+                                UserHandle.CURRENT);
+                    }
+                    builder.setContentIntent(pi);
+                    break;
+                case "-S":
+                case "--style":
+                    final String styleSpec = getNextArgRequired().toLowerCase();
+                    switch (styleSpec) {
+                        case "bigtext":
+                            bigTextStyle = new Notification.BigTextStyle();
+                            builder.setStyle(bigTextStyle);
+                            break;
+                        case "bigpicture":
+                            bigPictureStyle = new Notification.BigPictureStyle();
+                            builder.setStyle(bigPictureStyle);
+                            break;
+                        case "inbox":
+                            inboxStyle = new Notification.InboxStyle();
+                            builder.setStyle(inboxStyle);
+                            break;
+                        case "messaging":
+                            String name = "You";
+                            if ("--user".equals(peekNextArg())) {
+                                getNextArg();
+                                name = getNextArgRequired();
+                            }
+                            messagingStyle = new Notification.MessagingStyle(
+                                    new Person.Builder().setName(name).build());
+                            builder.setStyle(messagingStyle);
+                            break;
+                        case "media":
+                            mediaStyle = new Notification.MediaStyle();
+                            builder.setStyle(mediaStyle);
+                            break;
+                        default:
+                            throw new IllegalArgumentException(
+                                    "unrecognized notification style: " + styleSpec);
+                    }
+                    break;
+                case "--bigText": case "--bigtext": case "--big-text":
+                    if (bigTextStyle == null) {
+                        throw new IllegalArgumentException("--bigtext requires --style bigtext");
+                    }
+                    bigTextStyle.bigText(getNextArgRequired());
+                    break;
+                case "--picture":
+                    if (bigPictureStyle == null) {
+                        throw new IllegalArgumentException("--picture requires --style bigpicture");
+                    }
+                    final String pictureSpec = getNextArgRequired();
+                    final Icon pictureAsIcon = parseIcon(res, pictureSpec);
+                    if (pictureAsIcon == null) {
+                        throw new IllegalArgumentException("bad picture spec: " + pictureSpec);
+                    }
+                    final Drawable d = pictureAsIcon.loadDrawable(context);
+                    if (d instanceof BitmapDrawable) {
+                        bigPictureStyle.bigPicture(((BitmapDrawable) d).getBitmap());
+                    } else {
+                        throw new IllegalArgumentException("not a bitmap: " + pictureSpec);
+                    }
+                    break;
+                case "--line":
+                    if (inboxStyle == null) {
+                        throw new IllegalArgumentException("--line requires --style inbox");
+                    }
+                    inboxStyle.addLine(getNextArgRequired());
+                    break;
+                case "--message":
+                    if (messagingStyle == null) {
+                        throw new IllegalArgumentException(
+                                "--message requires --style messaging");
+                    }
+                    String arg = getNextArgRequired();
+                    String[] parts = arg.split(":", 2);
+                    if (parts.length > 1) {
+                        messagingStyle.addMessage(parts[1], System.currentTimeMillis(),
+                                parts[0]);
+                    } else {
+                        messagingStyle.addMessage(parts[0], System.currentTimeMillis(),
+                                new String[]{
+                                        messagingStyle.getUserDisplayName().toString(),
+                                        "Them"
+                                }[messagingStyle.getMessages().size() % 2]);
+                    }
+                    break;
+                case "--conversation":
+                    if (messagingStyle == null) {
+                        throw new IllegalArgumentException(
+                                "--conversation requires --style messaging");
+                    }
+                    messagingStyle.setConversationTitle(getNextArgRequired());
+                    break;
+                case "-h":
+                case "--help":
+                case "--wtf":
+                default:
+                    pw.println(NOTIFY_USAGE);
+                    return 0;
+            }
+        }
+
+        final String tag = getNextArg();
+        final String text = getNextArg();
+        if (tag == null || text == null) {
+            pw.println(NOTIFY_USAGE);
+            return -1;
+        }
+
+        builder.setContentText(text);
+
+        if (smallIcon == null) {
+            // uh oh, let's substitute something
+            builder.setSmallIcon(com.android.internal.R.drawable.stat_notify_chat);
+        } else {
+            builder.setSmallIcon(smallIcon);
+        }
+
+        ensureChannel();
+
+        final Notification n = builder.build();
+        pw.println("posting:\n  " + n);
+        Slog.v("NotificationManager", "posting: " + n);
+
+        final int userId = UserHandle.getCallingUserId();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mBinderService.enqueueNotificationWithTag(
+                    NOTIFICATION_PACKAGE, "android",
+                    tag, NOTIFICATION_ID,
+                    n, userId);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+
+        if (verbose) {
+            NotificationRecord nr = mDirectService.findNotificationLocked(
+                    NOTIFICATION_PACKAGE, tag, NOTIFICATION_ID, userId);
+            for (int tries = 3; tries-- > 0; ) {
+                if (nr != null) break;
+                try {
+                    pw.println("waiting for notification to post...");
+                    Thread.sleep(500);
+                } catch (InterruptedException e) {
+                }
+                nr = mDirectService.findNotificationLocked(
+                        NOTIFICATION_PACKAGE, tag, NOTIFICATION_ID, userId);
+            }
+            if (nr == null) {
+                pw.println("warning: couldn't find notification after enqueueing");
+            } else {
+                pw.println("posted: ");
+                nr.dump(pw, "  ", context, false);
+            }
+        }
+
+        return 0;
+    }
+
+    @Override
+    public void onHelp() {
+        getOutPrintWriter().println(USAGE);
+    }
+}
+
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index e40dad6..d630b9a 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -916,6 +916,13 @@
             updateVisiblyExpandedStats();
         }
 
+        /**
+         * Returns whether this notification has been visible and expanded at the same.
+         */
+        public boolean hasBeenVisiblyExpanded() {
+            return posttimeToFirstVisibleExpansionMs >= 0;
+        }
+
         private void updateVisiblyExpandedStats() {
             long elapsedNowMs = SystemClock.elapsedRealtime();
             if (isExpanded && isVisible) {
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index eb46d53..7c0e0b0 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -80,6 +80,7 @@
     private static final String ATT_NAME = "name";
     private static final String ATT_UID = "uid";
     private static final String ATT_ID = "id";
+    private static final String ATT_APP_OVERLAY = "overlay";
     private static final String ATT_PRIORITY = "priority";
     private static final String ATT_VISIBILITY = "visibility";
     private static final String ATT_IMPORTANCE = "importance";
@@ -92,6 +93,7 @@
     private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
     private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED;
     private static final boolean DEFAULT_SHOW_BADGE = true;
+    private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true;
     /**
      * Default value for what fields are user locked. See {@link LockableAppFields} for all lockable
      * fields.
@@ -104,6 +106,7 @@
     @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE})
     public @interface LockableAppFields {
         int USER_LOCKED_IMPORTANCE = 0x00000001;
+        int USER_LOCKED_APP_OVERLAY = 0x00000002;
     }
 
     // pkg|uid => PackagePreferences
@@ -169,7 +172,9 @@
                                     XmlUtils.readIntAttribute(
                                             parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
                                     XmlUtils.readBooleanAttribute(
-                                            parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE));
+                                            parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE),
+                                    XmlUtils.readBooleanAttribute(
+                                            parser, ATT_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY));
                             r.importance = XmlUtils.readIntAttribute(
                                     parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
                             r.priority = XmlUtils.readIntAttribute(
@@ -264,11 +269,12 @@
 
     private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid) {
         return getOrCreatePackagePreferences(pkg, uid,
-                DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE);
+                DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE,
+                DEFAULT_ALLOW_APP_OVERLAY);
     }
 
     private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid, int importance,
-            int priority, int visibility, boolean showBadge) {
+            int priority, int visibility, boolean showBadge, boolean allowAppOverlay) {
         final String key = packagePreferencesKey(pkg, uid);
         synchronized (mPackagePreferences) {
             PackagePreferences
@@ -282,6 +288,7 @@
                 r.priority = priority;
                 r.visibility = visibility;
                 r.showBadge = showBadge;
+                r.appOverlay = allowAppOverlay;
 
                 try {
                     createDefaultChannelIfNeeded(r);
@@ -382,7 +389,8 @@
                                 || r.lockedAppFields != DEFAULT_LOCKED_APP_FIELDS
                                 || r.channels.size() > 0
                                 || r.groups.size() > 0
-                                || r.delegate != null;
+                                || r.delegate != null
+                                || r.appOverlay != DEFAULT_ALLOW_APP_OVERLAY;
                 if (hasNonDefaultSettings) {
                     out.startTag(null, TAG_PACKAGE);
                     out.attribute(null, ATT_NAME, r.pkg);
@@ -395,6 +403,9 @@
                     if (r.visibility != DEFAULT_VISIBILITY) {
                         out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
                     }
+                    if (r.appOverlay != DEFAULT_ALLOW_APP_OVERLAY) {
+                        out.attribute(null, ATT_APP_OVERLAY, Boolean.toString(r.appOverlay));
+                    }
                     out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
                     out.attribute(null, ATT_APP_USER_LOCKED_FIELDS,
                             Integer.toString(r.lockedAppFields));
@@ -439,6 +450,20 @@
         out.endTag(null, TAG_RANKING);
     }
 
+    public void setAppOverlaysAllowed(String pkg, int uid, boolean allowed) {
+        PackagePreferences p = getOrCreatePackagePreferences(pkg, uid);
+        p.appOverlay = allowed;
+        p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_APP_OVERLAY;
+    }
+
+    public boolean areAppOverlaysAllowed(String pkg, int uid) {
+        return getOrCreatePackagePreferences(pkg, uid).appOverlay;
+    }
+
+    public int getAppLockedFields(String pkg, int uid) {
+        return getOrCreatePackagePreferences(pkg, uid).lockedAppFields;
+    }
+
     /**
      * Gets importance.
      */
@@ -512,7 +537,6 @@
             // apps can't update the blocked status or app overlay permission
             if (fromTargetApp) {
                 group.setBlocked(oldGroup.isBlocked());
-                group.setAllowAppOverlay(oldGroup.canOverlayApps());
                 group.unlockFields(group.getUserLockedFields());
                 group.lockFields(oldGroup.getUserLockedFields());
             } else {
@@ -521,9 +545,6 @@
                     group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE);
                     updateChannelsBypassingDnd(mContext.getUserId());
                 }
-                if (group.canOverlayApps() != oldGroup.canOverlayApps()) {
-                    group.lockFields(NotificationChannelGroup.USER_LOCKED_ALLOW_APP_OVERLAY);
-                }
             }
         }
         r.groups.put(group.getId(), group);
@@ -1581,6 +1602,7 @@
         int priority = DEFAULT_PRIORITY;
         int visibility = DEFAULT_VISIBILITY;
         boolean showBadge = DEFAULT_SHOW_BADGE;
+        boolean appOverlay = DEFAULT_ALLOW_APP_OVERLAY;
         int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
 
         Delegate delegate = null;
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 2b581d6..abc9841 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.app.AlarmManager;
+import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -163,7 +164,6 @@
                     mSnoozedNotifications.get(userId).get(pkg);
             if (recordsForPkg != null) {
                 final Set<Map.Entry<String, NotificationRecord>> records = recordsForPkg.entrySet();
-                String key = null;
                 for (Map.Entry<String, NotificationRecord> record : records) {
                     final StatusBarNotification sbn = record.getValue().sbn;
                     if (Objects.equals(sbn.getTag(), tag) && sbn.getId() == id) {
@@ -305,6 +305,30 @@
         }
     }
 
+    protected void clearData(int userId, String pkg) {
+        ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
+                mSnoozedNotifications.get(userId);
+        if (records == null) {
+            return;
+        }
+        ArrayMap<String, NotificationRecord> pkgRecords = records.get(pkg);
+        if (pkgRecords == null) {
+            return;
+        }
+        for (int i = pkgRecords.size() - 1; i >= 0; i--) {
+            final NotificationRecord r = pkgRecords.removeAt(i);
+            if (r != null) {
+                mPackages.remove(r.getKey());
+                mUsers.remove(r.getKey());
+                final PendingIntent pi = createPendingIntent(pkg, r.getKey(), userId);
+                mAm.cancel(pi);
+                MetricsLogger.action(r.getLogMaker()
+                        .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
+                        .setType(MetricsProto.MetricsEvent.TYPE_DISMISS));
+            }
+        }
+    }
+
     private PendingIntent createPendingIntent(String pkg, String key, int userId) {
         return PendingIntent.getBroadcast(mContext,
                 REQUEST_CODE_REPOST,
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index b080a73..571f799 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -29,8 +29,11 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.PrintWriter;
-import java.util.Objects;
 
+/**
+ * Helper class for managing active rules from
+ * {@link android.service.notification.ConditionProviderService CPSes}.
+ */
 public class ZenModeConditions implements ConditionProviders.Callback {
     private static final String TAG = ZenModeHelper.TAG;
     private static final boolean DEBUG = ZenModeHelper.DEBUG;
@@ -41,8 +44,6 @@
     @VisibleForTesting
     protected final ArrayMap<Uri, ComponentName> mSubscriptions = new ArrayMap<>();
 
-    private boolean mFirstEvaluation = true;
-
     public ZenModeConditions(ZenModeHelper helper, ConditionProviders conditionProviders) {
         mHelper = helper;
         mConditionProviders = conditionProviders;
@@ -73,8 +74,10 @@
         final ArraySet<Uri> current = new ArraySet<>();
         evaluateRule(config.manualRule, current, null, processSubscriptions);
         for (ZenRule automaticRule : config.automaticRules.values()) {
-            evaluateRule(automaticRule, current, trigger, processSubscriptions);
-            updateSnoozing(automaticRule);
+            if (automaticRule.component != null) {
+                evaluateRule(automaticRule, current, trigger, processSubscriptions);
+                updateSnoozing(automaticRule);
+            }
         }
 
         synchronized (mSubscriptions) {
@@ -90,7 +93,6 @@
                 }
             }
         }
-        mFirstEvaluation = false;
     }
 
     @Override
@@ -114,23 +116,14 @@
         if (DEBUG) Log.d(TAG, "onConditionChanged " + id + " " + condition);
         ZenModeConfig config = mHelper.getConfig();
         if (config == null) return;
-        ComponentName trigger = null;
-        boolean updated = updateCondition(id, condition, config.manualRule);
-        for (ZenRule automaticRule : config.automaticRules.values()) {
-            updated |= updateCondition(id, condition, automaticRule);
-            updated |= updateSnoozing(automaticRule);
-            if (updated) {
-                trigger = automaticRule.component;
-            }
-        }
-        if (updated) {
-            mHelper.setConfig(config, trigger, "conditionChanged");
-        }
+        mHelper.setAutomaticZenRuleState(id, condition);
     }
 
+    // Only valid for CPS backed rules
     private void evaluateRule(ZenRule rule, ArraySet<Uri> current, ComponentName trigger,
             boolean processSubscriptions) {
         if (rule == null || rule.conditionId == null) return;
+        if (rule.configurationActivity != null) return;
         final Uri id = rule.conditionId;
         boolean isSystemCondition = false;
         for (SystemConditionProviderService sp : mConditionProviders.getSystemProviders()) {
@@ -140,6 +133,7 @@
                 isSystemCondition = true;
             }
         }
+        // ensure that we have a record of the rule if it's backed by an currently alive CPS
         if (!isSystemCondition) {
             final IConditionProvider cp = mConditionProviders.findConditionProvider(rule.component);
             if (DEBUG) Log.d(TAG, "Ensure external rule exists: " + (cp != null) + " for " + id);
@@ -147,7 +141,8 @@
                 mConditionProviders.ensureRecordExists(rule.component, id, cp);
             }
         }
-        if (rule.component == null) {
+        // empty rule? disable and bail early
+        if (rule.component == null && rule.enabler == null) {
             Log.w(TAG, "No component found for automatic rule: " + rule.conditionId);
             rule.enabled = false;
             return;
@@ -155,6 +150,8 @@
         if (current != null) {
             current.add(id);
         }
+
+        // If the rule is bound by a CPS and the CPS is alive, tell them about the rule
         if (processSubscriptions && ((trigger != null && trigger.equals(rule.component))
                 || isSystemCondition)) {
             if (DEBUG) Log.d(TAG, "Subscribing to " + rule.component);
@@ -167,40 +164,20 @@
                 if (DEBUG) Log.d(TAG, "zmc failed to subscribe");
             }
         }
-        if (rule.condition == null) {
+        // backfill the rule state from CPS backed components if it's missing
+        if (rule.component != null && rule.condition == null) {
             rule.condition = mConditionProviders.findCondition(rule.component, rule.conditionId);
             if (rule.condition != null && DEBUG) Log.d(TAG, "Found existing condition for: "
                     + rule.conditionId);
         }
     }
 
-    private boolean isAutomaticActive(ComponentName component) {
-        if (component == null) return false;
-        final ZenModeConfig config = mHelper.getConfig();
-        if (config == null) return false;
-        for (ZenRule rule : config.automaticRules.values()) {
-            if (component.equals(rule.component) && rule.isAutomaticActive()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     private boolean updateSnoozing(ZenRule rule) {
-        if (rule != null && rule.snoozing && (mFirstEvaluation || !rule.isTrueOrUnknown())) {
+        if (rule != null && rule.snoozing && !rule.isTrueOrUnknown()) {
             rule.snoozing = false;
             if (DEBUG) Log.d(TAG, "Snoozing reset for " + rule.conditionId);
             return true;
         }
         return false;
     }
-
-    private boolean updateCondition(Uri id, Condition condition, ZenRule rule) {
-        if (id == null || rule == null || rule.conditionId == null) return false;
-        if (!rule.conditionId.equals(id)) return false;
-        if (Objects.equals(condition, rule.condition)) return false;
-        rule.condition = condition;
-        return true;
-    }
-
 }
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 94d276c..afc0b72 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -26,6 +26,8 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
@@ -282,20 +284,25 @@
 
     public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) {
         if (!isSystemRule(automaticZenRule)) {
-            ServiceInfo owner = getServiceInfo(automaticZenRule.getOwner());
-            if (owner == null) {
-                throw new IllegalArgumentException("Owner is not a condition provider service");
+            PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner());
+            if (component == null) {
+                component = getActivityInfo(automaticZenRule.getConfigurationActivity());
             }
-
+            if (component == null) {
+                throw new IllegalArgumentException("Lacking enabled CPS or config activity");
+            }
             int ruleInstanceLimit = -1;
-            if (owner.metaData != null) {
-                ruleInstanceLimit = owner.metaData.getInt(
+            if (component.metaData != null) {
+                ruleInstanceLimit = component.metaData.getInt(
                         ConditionProviderService.META_DATA_RULE_INSTANCE_LIMIT, -1);
             }
-            if (ruleInstanceLimit > 0 && ruleInstanceLimit
-                    < (getCurrentInstanceCount(automaticZenRule.getOwner()) + 1)) {
+            int newRuleInstanceCount = getCurrentInstanceCount(automaticZenRule.getOwner())
+                    + getCurrentInstanceCount(automaticZenRule.getConfigurationActivity())
+                    + 1;
+            if (ruleInstanceLimit > 0 && ruleInstanceLimit < newRuleInstanceCount) {
                 throw new IllegalArgumentException("Rule instance limit exceeded");
             }
+
         }
 
         ZenModeConfig newConfig;
@@ -368,8 +375,7 @@
             newConfig = mConfig.copy();
             for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
                 ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i));
-                if (rule.component.getPackageName().equals(packageName)
-                        && canManageAutomaticZenRule(rule)) {
+                if (rule.pkg.equals(packageName) && canManageAutomaticZenRule(rule)) {
                     newConfig.automaticRules.removeAt(i);
                 }
             }
@@ -377,11 +383,73 @@
         }
     }
 
-    public int getCurrentInstanceCount(ComponentName owner) {
+    public void setAutomaticZenRuleState(String id, Condition condition) {
+        ZenModeConfig newConfig;
+        synchronized (mConfig) {
+            if (mConfig == null) return;
+
+            newConfig = mConfig.copy();
+        }
+        setAutomaticZenRuleState(newConfig, newConfig.automaticRules.get(id), condition);
+    }
+
+    public void setAutomaticZenRuleState(Uri ruleDefinition, Condition condition) {
+        ZenModeConfig newConfig;
+        synchronized (mConfig) {
+            if (mConfig == null) return;
+            newConfig = mConfig.copy();
+        }
+
+        setAutomaticZenRuleState(newConfig,
+                findMatchingRule(newConfig, ruleDefinition, condition),
+                condition);
+    }
+
+    private void setAutomaticZenRuleState(ZenModeConfig config, ZenRule rule, Condition condition) {
+        if (rule == null) return;
+
+        rule.condition = condition;
+        updateSnoozing(rule);
+        setConfigLocked(config, rule.component, "conditionChanged");
+    }
+
+    private ZenRule findMatchingRule(ZenModeConfig config, Uri id, Condition condition) {
+        if (ruleMatches(id, condition, config.manualRule)) {
+            return config.manualRule;
+        } else {
+            for (ZenRule automaticRule : config.automaticRules.values()) {
+                if (ruleMatches(id, condition, automaticRule)) {
+                    return automaticRule;
+                }
+            }
+        }
+        return null;
+    }
+
+    private boolean ruleMatches(Uri id, Condition condition, ZenRule rule) {
+        if (id == null || rule == null || rule.conditionId == null) return false;
+        if (!rule.conditionId.equals(id)) return false;
+        if (Objects.equals(condition, rule.condition)) return false;
+        return true;
+    }
+
+    private boolean updateSnoozing(ZenRule rule) {
+        if (rule != null && rule.snoozing && !rule.isTrueOrUnknown()) {
+            rule.snoozing = false;
+            if (DEBUG) Log.d(TAG, "Snoozing reset for " + rule.conditionId);
+            return true;
+        }
+        return false;
+    }
+
+    public int getCurrentInstanceCount(ComponentName cn) {
+        if (cn == null) {
+            return 0;
+        }
         int count = 0;
         synchronized (mConfig) {
             for (ZenRule rule : mConfig.automaticRules.values()) {
-                if (rule.component != null && rule.component.equals(owner)) {
+                if (cn.equals(rule.component) || cn.equals(rule.configurationActivity)) {
                     count++;
                 }
             }
@@ -401,7 +469,7 @@
             if (packages != null) {
                 final int packageCount = packages.length;
                 for (int i = 0; i < packageCount; i++) {
-                    if (packages[i].equals(rule.component.getPackageName())) {
+                    if (packages[i].equals(rule.pkg)) {
                         return true;
                     }
                 }
@@ -410,18 +478,6 @@
         }
     }
 
-    // Checks zen rule properties are the same (doesn't check creation time, name nor enabled)
-    // used to check if default rules were customized or not
-    private boolean ruleValuesEqual(AutomaticZenRule rule, ZenRule defaultRule) {
-        if (rule == null || defaultRule == null) {
-            return false;
-        }
-        return rule.getInterruptionFilter() ==
-                NotificationManager.zenModeToInterruptionFilter(defaultRule.zenMode)
-                && rule.getConditionId().equals(defaultRule.conditionId)
-                && rule.getOwner().equals(defaultRule.component);
-    }
-
     protected void updateDefaultZenRules() {
         updateDefaultAutomaticRuleNames();
         for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) {
@@ -443,7 +499,8 @@
     }
 
     private boolean isSystemRule(AutomaticZenRule rule) {
-        return ZenModeConfig.SYSTEM_AUTHORITY.equals(rule.getOwner().getPackageName());
+        return rule.getOwner() != null
+                && ZenModeConfig.SYSTEM_AUTHORITY.equals(rule.getOwner().getPackageName());
     }
 
     private ServiceInfo getServiceInfo(ComponentName owner) {
@@ -465,11 +522,31 @@
         return null;
     }
 
+    private ActivityInfo getActivityInfo(ComponentName configActivity) {
+        Intent queryIntent = new Intent();
+        queryIntent.setComponent(configActivity);
+        List<ResolveInfo> installedComponents = mPm.queryIntentActivitiesAsUser(
+                queryIntent,
+                PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA,
+                UserHandle.getCallingUserId());
+        if (installedComponents != null) {
+            for (int i = 0, count = installedComponents.size(); i < count; i++) {
+                ResolveInfo resolveInfo = installedComponents.get(i);
+                return resolveInfo.activityInfo;
+            }
+        }
+        return null;
+    }
+
     private void populateZenRule(AutomaticZenRule automaticZenRule, ZenRule rule, boolean isNew) {
         if (isNew) {
             rule.id = ZenModeConfig.newRuleId();
             rule.creationTime = System.currentTimeMillis();
             rule.component = automaticZenRule.getOwner();
+            rule.configurationActivity = automaticZenRule.getConfigurationActivity();
+            rule.pkg = (rule.component != null)
+                    ? rule.component.getPackageName()
+                    : rule.configurationActivity.getPackageName();
         }
 
         if (rule.enabled != automaticZenRule.isEnabled()) {
@@ -480,22 +557,16 @@
         rule.conditionId = automaticZenRule.getConditionId();
         rule.enabled = automaticZenRule.isEnabled();
         rule.modified = automaticZenRule.isModified();
-        if (automaticZenRule.getZenPolicy() != null) {
-            rule.zenPolicy = automaticZenRule.getZenPolicy();
-        }
+        rule.zenPolicy = automaticZenRule.getZenPolicy();
         rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
                 automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
     }
 
     protected AutomaticZenRule createAutomaticZenRule(ZenRule rule) {
-        if (rule.zenPolicy != null) {
-            return new AutomaticZenRule(rule.name, rule.component, rule.conditionId, rule.zenPolicy,
-                    rule.enabled, rule.creationTime);
-        } else {
-            return new AutomaticZenRule(rule.name, rule.component, rule.conditionId,
-                    NotificationManager.zenModeToInterruptionFilter(rule.zenMode), rule.enabled,
-                    rule.creationTime);
-        }
+        return new AutomaticZenRule(rule.name, rule.component, rule.configurationActivity,
+                rule.conditionId, rule.zenPolicy,
+                NotificationManager.zenModeToInterruptionFilter(rule.zenMode),
+                rule.enabled, rule.creationTime);
     }
 
     public void setManualZenMode(int zenMode, Uri conditionId, String caller, String reason) {
@@ -697,8 +768,9 @@
                     ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i));
                     if (RULE_INSTANCE_GRACE_PERIOD < (currentTime - rule.creationTime)) {
                         try {
-                            mPm.getPackageInfo(rule.component.getPackageName(),
-                                    PackageManager.MATCH_ANY_USER);
+                            if (rule.pkg != null) {
+                                mPm.getPackageInfo(rule.pkg, PackageManager.MATCH_ANY_USER);
+                            }
                         } catch (PackageManager.NameNotFoundException e) {
                             newConfig.automaticRules.removeAt(i);
                         }
@@ -753,11 +825,14 @@
                 if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user);
                 return true;
             }
-            // may modify config
+            // handle CPS backed conditions - danger! may modify config
             mConditions.evaluateConfig(config, null, false /*processSubscriptions*/);
+
             mConfigs.put(config.user, config);
             if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable());
             ZenLog.traceConfig(reason, mConfig, config);
+
+            // send some broadcasts
             final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),
                     getNotificationPolicy(config));
             if (!config.equals(mConfig)) {
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 6d59827..16143d3 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -60,7 +60,6 @@
 
     boolean createIdmap(@NonNull final PackageInfo targetPackage,
             @NonNull final PackageInfo overlayPackage, int userId) {
-        // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible
         if (DEBUG) {
             Slog.d(TAG, "create idmap for " + targetPackage.packageName + " and "
                     + overlayPackage.packageName);
@@ -70,16 +69,19 @@
         final String overlayPath = overlayPackage.applicationInfo.getBaseCodePath();
         try {
             if (FEATURE_FLAG_IDMAP2) {
-                mIdmap2Service.createIdmap(targetPath, overlayPath, userId);
+                if (mIdmap2Service.verifyIdmap(overlayPath, userId)) {
+                    return true;
+                }
+                return mIdmap2Service.createIdmap(targetPath, overlayPath, userId) != null;
             } else {
                 mInstaller.idmap(targetPath, overlayPath, sharedGid);
+                return true;
             }
         } catch (Exception e) {
             Slog.w(TAG, "failed to generate idmap for " + targetPath + " and "
                     + overlayPath + ": " + e.getMessage());
             return false;
         }
-        return true;
     }
 
     boolean removeIdmap(@NonNull final OverlayInfo oi, final int userId) {
@@ -88,15 +90,15 @@
         }
         try {
             if (FEATURE_FLAG_IDMAP2) {
-                mIdmap2Service.removeIdmap(oi.baseCodePath, userId);
+                return mIdmap2Service.removeIdmap(oi.baseCodePath, userId);
             } else {
                 mInstaller.removeIdmap(oi.baseCodePath);
+                return true;
             }
         } catch (Exception e) {
             Slog.w(TAG, "failed to remove idmap for " + oi.baseCodePath + ": " + e.getMessage());
             return false;
         }
-        return true;
     }
 
     boolean idmapExists(@NonNull final OverlayInfo oi) {
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index d471904..a7f1146 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -223,8 +223,8 @@
     public OverlayManagerService(@NonNull final Context context,
             @NonNull final Installer installer) {
         super(context);
-        mSettingsFile =
-            new AtomicFile(new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays");
+        mSettingsFile = new AtomicFile(
+                new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays");
         mPackageManager = new PackageManagerHelper();
         mUserManager = UserManagerService.getInstance();
         IdmapManager im = new IdmapManager(installer);
@@ -717,9 +717,9 @@
         final Map<String, List<String>> pendingChanges = new ArrayMap<>(targetPackageNames.size());
         synchronized (mLock) {
             final List<String> frameworkOverlays =
-                mImpl.getEnabledOverlayPackageNames("android", userId);
-            final int N = targetPackageNames.size();
-            for (int i = 0; i < N; i++) {
+                    mImpl.getEnabledOverlayPackageNames("android", userId);
+            final int n = targetPackageNames.size();
+            for (int i = 0; i < n; i++) {
                 final String targetPackageName = targetPackageNames.get(i);
                 List<String> list = new ArrayList<>();
                 if (!"android".equals(targetPackageName)) {
@@ -730,8 +730,8 @@
             }
         }
 
-        final int N = targetPackageNames.size();
-        for (int i = 0; i < N; i++) {
+        final int n = targetPackageNames.size();
+        for (int i = 0; i < n; i++) {
             final String targetPackageName = targetPackageNames.get(i);
             if (DEBUG) {
                 Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
@@ -789,7 +789,7 @@
             if (!mSettingsFile.getBaseFile().exists()) {
                 return;
             }
-            try (final FileInputStream stream = mSettingsFile.openRead()) {
+            try (FileInputStream stream = mSettingsFile.openRead()) {
                 mSettings.restore(stream);
 
                 // We might have data for dying users if the device was
@@ -915,8 +915,8 @@
 
             if (!verbose) {
                 int count = 0;
-                final int N = mCache.size();
-                for (int i = 0; i < N; i++) {
+                final int n = mCache.size();
+                for (int i = 0; i < n; i++) {
                     final int userId = mCache.keyAt(i);
                     count += mCache.get(userId).size();
                 }
@@ -929,8 +929,8 @@
                 return;
             }
 
-            final int N = mCache.size();
-            for (int i = 0; i < N; i++) {
+            final int n = mCache.size();
+            for (int i = 0; i < n; i++) {
                 final int userId = mCache.keyAt(i);
                 pw.println(TAB1 + "User " + userId);
                 final HashMap<String, PackageInfo> map = mCache.get(userId);
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 112059d..b0d2704 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -55,8 +55,8 @@
  */
 final class OverlayManagerServiceImpl {
     // Flags to use in conjunction with updateState.
-    private static final int FLAG_TARGET_IS_UPGRADING = 1<<0;
-    private static final int FLAG_OVERLAY_IS_UPGRADING = 1<<1;
+    private static final int FLAG_TARGET_IS_UPGRADING = 1 << 0;
+    private static final int FLAG_OVERLAY_IS_UPGRADING = 1 << 1;
 
     private final PackageManagerHelper mPackageManager;
     private final IdmapManager mIdmapManager;
@@ -89,8 +89,8 @@
         // a change in priority is only relevant for static RROs: specifically,
         // a regular RRO should not have its state reset only because a change
         // in priority
-        if (theTruth.isStaticOverlayPackage() &&
-                theTruth.overlayPriority != oldSettings.priority) {
+        if (theTruth.isStaticOverlayPackage()
+                && theTruth.overlayPriority != oldSettings.priority) {
             return true;
         }
         return false;
@@ -294,8 +294,8 @@
             final int userId, final int flags) {
         boolean modified = false;
         final List<OverlayInfo> ois = mSettings.getOverlaysForTarget(targetPackageName, userId);
-        final int N = ois.size();
-        for (int i = 0; i < N; i++) {
+        final int n = ois.size();
+        for (int i = 0; i < n; i++) {
             final OverlayInfo oi = ois.get(i);
             final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName,
                     userId);
@@ -610,8 +610,8 @@
         final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName,
                 userId);
         final List<String> paths = new ArrayList<>(overlays.size());
-        final int N = overlays.size();
-        for (int i = 0; i < N; i++) {
+        final int n = overlays.size();
+        for (int i = 0; i < n; i++) {
             final OverlayInfo oi = overlays.get(i);
             if (oi.isEnabled()) {
                 paths.add(oi.packageName);
@@ -632,8 +632,8 @@
                 userId);
 
         // Static RROs targeting to "android", ie framework-res.apk, are handled by native layers.
-        if (targetPackage != null && overlayPackage != null &&
-                !("android".equals(targetPackageName)
+        if (targetPackage != null && overlayPackage != null
+                && !("android".equals(targetPackageName)
                         && overlayPackage.isStaticOverlayPackage())) {
             mIdmapManager.createIdmap(targetPackage, overlayPackage, userId);
         }
@@ -663,7 +663,7 @@
 
     private @OverlayInfo.State int calculateNewState(@Nullable final PackageInfo targetPackage,
             @Nullable final PackageInfo overlayPackage, final int userId, final int flags)
-        throws OverlayManagerSettings.BadKeyException {
+            throws OverlayManagerSettings.BadKeyException {
 
         if ((flags & FLAG_TARGET_IS_UPGRADING) != 0) {
             return STATE_TARGET_UPGRADING;
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index 572d368..2b4ec03 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -179,13 +179,19 @@
 
     List<OverlayInfo> getOverlaysForTarget(@NonNull final String targetPackageName,
             final int userId) {
+        // Static RROs targeting "android" are loaded from AssetManager, and so they should be
+        // ignored in OverlayManagerService.
         return selectWhereTarget(targetPackageName, userId)
+                .filter((i) -> !(i.isStatic() && "android".equals(i.getTargetPackageName())))
                 .map(SettingsItem::getOverlayInfo)
                 .collect(Collectors.toList());
     }
 
     ArrayMap<String, List<OverlayInfo>> getOverlaysForUser(final int userId) {
+        // Static RROs targeting "android" are loaded from AssetManager, and so they should be
+        // ignored in OverlayManagerService.
         return selectWhereUser(userId)
+                .filter((i) -> !(i.isStatic() && "android".equals(i.getTargetPackageName())))
                 .map(SettingsItem::getOverlayInfo)
                 .collect(Collectors.groupingBy(info -> info.targetPackageName, ArrayMap::new,
                         Collectors.toList()));
@@ -290,21 +296,22 @@
             return;
         }
 
-        final int N = mItems.size();
-        for (int i = 0; i < N; i++) {
+        final int n = mItems.size();
+        for (int i = 0; i < n; i++) {
             final SettingsItem item = mItems.get(i);
             pw.println(item.mPackageName + ":" + item.getUserId() + " {");
             pw.increaseIndent();
 
-            pw.print("mPackageName.......: "); pw.println(item.mPackageName);
-            pw.print("mUserId............: "); pw.println(item.getUserId());
-            pw.print("mTargetPackageName.: "); pw.println(item.getTargetPackageName());
-            pw.print("mBaseCodePath......: "); pw.println(item.getBaseCodePath());
-            pw.print("mState.............: "); pw.println(OverlayInfo.stateToString(item.getState()));
-            pw.print("mIsEnabled.........: "); pw.println(item.isEnabled());
-            pw.print("mIsStatic..........: "); pw.println(item.isStatic());
-            pw.print("mPriority..........: "); pw.println(item.mPriority);
-            pw.print("mCategory..........: "); pw.println(item.mCategory);
+            pw.println("mPackageName.......: " + item.mPackageName);
+            pw.println("mUserId............: " + item.getUserId());
+            pw.println("mTargetPackageName.: " + item.getTargetPackageName());
+            pw.println("mBaseCodePath......: " + item.getBaseCodePath());
+            pw.println("mState.............: " + OverlayInfo.stateToString(item.getState()));
+            pw.println("mState.............: " + OverlayInfo.stateToString(item.getState()));
+            pw.println("mIsEnabled.........: " + item.isEnabled());
+            pw.println("mIsStatic..........: " + item.isStatic());
+            pw.println("mPriority..........: " + item.mPriority);
+            pw.println("mCategory..........: " + item.mCategory);
 
             pw.decreaseIndent();
             pw.println("}");
@@ -400,8 +407,8 @@
             xml.startTag(null, TAG_OVERLAYS);
             XmlUtils.writeIntAttribute(xml, ATTR_VERSION, CURRENT_VERSION);
 
-            final int N = table.size();
-            for (int i = 0; i < N; i++) {
+            final int n = table.size();
+            for (int i = 0; i < n; i++) {
                 final SettingsItem item = table.get(i);
                 persistRow(xml, item);
             }
@@ -542,8 +549,8 @@
     }
 
     private int select(@NonNull final String packageName, final int userId) {
-        final int N = mItems.size();
-        for (int i = 0; i < N; i++) {
+        final int n = mItems.size();
+        for (int i = 0; i < n; i++) {
             final SettingsItem item = mItems.get(i);
             if (item.mUserId == userId && item.mPackageName.equals(packageName)) {
                 return i;
diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
index d576d33..e2b1cba 100644
--- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
+++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
@@ -121,8 +121,8 @@
         for (final String targetPackageName : allOverlays.keySet()) {
             out.println(targetPackageName);
             List<OverlayInfo> overlaysForTarget = allOverlays.get(targetPackageName);
-            final int N = overlaysForTarget.size();
-            for (int i = 0; i < N; i++) {
+            final int n = overlaysForTarget.size();
+            for (int i = 0; i < n; i++) {
                 final OverlayInfo oi = overlaysForTarget.get(i);
                 String status;
                 switch (oi.state) {
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 7ae2271..3a7919a 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -120,7 +120,7 @@
                         android.Manifest.permission.INTERACT_ACROSS_PROFILES, callingUid,
                         -1, true);
                 if (permissionFlag != PackageManager.PERMISSION_GRANTED
-                        || !mInjector.getUserManager().isSameProfileGroup(callerUserId, userId)) {
+                        || !isSameProfileGroup(callerUserId, userId)) {
                     throw new SecurityException("Attempt to launch activity without required "
                             + android.Manifest.permission.INTERACT_ACROSS_PROFILES + " permission"
                             + " or target user is not in the same profile group.");
@@ -209,6 +209,15 @@
         }
     }
 
+    private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) {
+        final long ident = mInjector.clearCallingIdentity();
+        try {
+            return mInjector.getUserManager().isSameProfileGroup(callerUserId, userId);
+        } finally {
+            mInjector.restoreCallingIdentity(ident);
+        }
+    }
+
     /**
      * Verify that the given calling package is belong to the calling UID.
      */
diff --git a/services/core/java/com/android/server/pm/ModuleInfoProvider.java b/services/core/java/com/android/server/pm/ModuleInfoProvider.java
new file mode 100644
index 0000000..642bfa2
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ModuleInfoProvider.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.content.Context;
+import android.content.pm.IPackageManager;
+import android.content.pm.ModuleInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provides data to back {@code ModuleInfo} related APIs in the package manager. The data is stored
+ * as an XML resource in a configurable "module metadata" package.
+ */
+@VisibleForTesting
+public class ModuleInfoProvider {
+    private static final String TAG = "PackageManager.ModuleInfoProvider";
+
+    /**
+     * The key in the package's application level metadata bundle that provides a resource reference
+     * to the module metadata.
+     */
+    private static final String MODULE_METADATA_KEY = "android.content.pm.MODULE_METADATA";
+
+
+    private final Context mContext;
+    private final IPackageManager mPackageManager;
+    private final Map<String, ModuleInfo> mModuleInfo;
+
+    // TODO: Move this to an earlier boot phase if anybody requires it then.
+    private volatile boolean mMetadataLoaded;
+
+    ModuleInfoProvider(Context context, IPackageManager packageManager) {
+        mContext = context;
+        mPackageManager = packageManager;
+        mModuleInfo = new ArrayMap<>();
+    }
+
+    @VisibleForTesting
+    public ModuleInfoProvider(XmlResourceParser metadata, Resources resources) {
+        mContext = null;
+        mPackageManager = null;
+        mModuleInfo = new ArrayMap<>();
+        loadModuleMetadata(metadata, resources);
+    }
+
+    /** Called by the {@code PackageManager} when it has completed its boot sequence */
+    public void systemReady() {
+        final String packageName = mContext.getResources().getString(
+                R.string.config_defaultModuleMetadataProvider);
+        if (TextUtils.isEmpty(packageName)) {
+            Slog.w(TAG, "No configured module metadata provider.");
+            return;
+        }
+
+        final Resources packageResources;
+        final PackageInfo pi;
+        try {
+            pi = mPackageManager.getPackageInfo(packageName,
+                PackageManager.GET_META_DATA, UserHandle.USER_SYSTEM);
+
+            Context packageContext = mContext.createPackageContext(packageName, 0);
+            packageResources = packageContext.getResources();
+        } catch (RemoteException | NameNotFoundException e) {
+            Slog.w(TAG, "Unable to discover metadata package: " + packageName, e);
+            return;
+        }
+
+        XmlResourceParser parser = packageResources.getXml(
+                pi.applicationInfo.metaData.getInt(MODULE_METADATA_KEY));
+        loadModuleMetadata(parser, packageResources);
+    }
+
+    private void loadModuleMetadata(XmlResourceParser parser, Resources packageResources) {
+        try {
+            // The format for the module metadata is straightforward :
+            //
+            // The following attributes on <module> are currently defined :
+            // -- name : A resource reference to a User visible package name, maps to
+            //           ModuleInfo#getName
+            // -- packageName : The package name of the module, see ModuleInfo#getPackageName
+            // -- isHidden : Whether the module is hidden, see ModuleInfo#isHidden
+            //
+            // <module-metadata>
+            //   <module name="@string/resource" packageName="package_name" isHidden="false|true" />
+            //   <module .... />
+            // </module-metadata>
+
+            XmlUtils.beginDocument(parser, "module-metadata");
+            while (true) {
+                XmlUtils.nextElement(parser);
+                if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
+                    break;
+                }
+
+                if (!"module".equals(parser.getName())) {
+                    Slog.w(TAG, "Unexpected metadata element: " + parser.getName());
+                    mModuleInfo.clear();
+                    break;
+                }
+
+                // TODO: The module name here is fetched using the resource configuration applied
+                // at the time of parsing this information. This is probably not the best approach
+                // to dealing with this as we'll now have to listen to all config changes and
+                // regenerate the data if required. Also, is this the right way to parse a resource
+                // reference out of an XML file ?
+                final String moduleName = packageResources.getString(
+                        Integer.parseInt(parser.getAttributeValue(null, "name").substring(1)));
+                final String modulePackageName = XmlUtils.readStringAttribute(parser,
+                        "packageName");
+                final boolean isHidden = XmlUtils.readBooleanAttribute(parser, "isHidden");
+
+                ModuleInfo mi = new ModuleInfo();
+                mi.setHidden(isHidden);
+                mi.setPackageName(modulePackageName);
+                mi.setName(moduleName);
+
+                mModuleInfo.put(modulePackageName, mi);
+            }
+        } catch (XmlPullParserException | IOException e) {
+            Slog.w(TAG, "Error parsing module metadata", e);
+            mModuleInfo.clear();
+        } finally {
+            parser.close();
+            mMetadataLoaded = true;
+        }
+    }
+
+    List<ModuleInfo> getInstalledModules(int flags) {
+        if (!mMetadataLoaded) {
+            throw new IllegalStateException("Call to getInstalledModules before metadata loaded");
+        }
+
+        return new ArrayList<>(mModuleInfo.values());
+    }
+
+    ModuleInfo getModuleInfo(String packageName, int flags) {
+        if (!mMetadataLoaded) {
+            throw new IllegalStateException("Call to getModuleInfo before metadata loaded");
+        }
+
+        return mModuleInfo.get(packageName);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 093b85e..f9e31ae 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -402,12 +402,17 @@
                 + " dexoptFlags=" + printDexoptFlags(dexoptFlags)
                 + " target-filter=" + compilerFilter);
 
-        // TODO(calin): b/64530081 b/66984396. Use SKIP_SHARED_LIBRARY_CHECK for the context
-        // (instead of dexUseInfo.getClassLoaderContext()) in order to compile secondary dex files
-        // in isolation (and avoid to extract/verify the main apk if it's in the class path).
-        // Note this trades correctness for performance since the resulting slow down is
-        // unacceptable in some cases until b/64530081 is fixed.
-        String classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
+        String classLoaderContext;
+        if (dexUseInfo.isUnknownClassLoaderContext() || dexUseInfo.isVariableClassLoaderContext()) {
+            // If we have an unknown (not yet set), or a variable class loader chain, compile
+            // without a context and mark the oat file with SKIP_SHARED_LIBRARY_CHECK. Note that
+            // this might lead to a incorrect compilation.
+            // TODO(calin): We should just extract in this case.
+            classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
+        } else {
+            classLoaderContext = dexUseInfo.getClassLoaderContext();
+        }
+
         int reason = options.getCompilationReason();
         try {
             for (String isa : dexUseInfo.getLoaderIsas()) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index a95e730..7ca39df 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -128,6 +128,7 @@
 
     private final Context mContext;
     private final PackageManagerService mPm;
+    private final StagingManager mStagingManager;
     private final PermissionManagerInternal mPermissionManager;
 
     private AppOpsManager mAppOps;
@@ -163,11 +164,6 @@
     @GuardedBy("mSessions")
     private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
 
-    // STOPSHIP: This is a temporary mock implementation of staged sessions. This variable
-    //           shouldn't be needed at all.
-    @GuardedBy("mStagedSessions")
-    private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>();
-
     /** Historical sessions kept around for debugging purposes */
     @GuardedBy("mSessions")
     private final List<String> mHistoricalSessions = new ArrayList<>();
@@ -203,6 +199,8 @@
                 "package-session");
         mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions");
         mSessionsDir.mkdirs();
+
+        mStagingManager = new StagingManager(pm);
     }
 
     public void systemReady() {
@@ -211,8 +209,7 @@
         synchronized (mSessions) {
             readSessionsLocked();
 
-            reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL, false /*isInstant*/);
-            reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL, true /*isInstant*/);
+            reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL);
 
             final ArraySet<File> unclaimedIcons = newArraySet(
                     mSessionsDir.listFiles());
@@ -232,8 +229,8 @@
     }
 
     @GuardedBy("mSessions")
-    private void reconcileStagesLocked(String volumeUuid, boolean isEphemeral) {
-        final File stagingDir = buildStagingDir(volumeUuid, isEphemeral);
+    private void reconcileStagesLocked(String volumeUuid) {
+        final File stagingDir = getTmpSessionDir(volumeUuid);
         final ArraySet<File> unclaimedStages = newArraySet(
                 stagingDir.listFiles(sStageFilter));
 
@@ -254,7 +251,7 @@
 
     public void onPrivateVolumeMounted(String volumeUuid) {
         synchronized (mSessions) {
-            reconcileStagesLocked(volumeUuid, false /*isInstant*/);
+            reconcileStagesLocked(volumeUuid);
         }
     }
 
@@ -271,9 +268,9 @@
             try {
                 final int sessionId = allocateSessionIdLocked();
                 mLegacySessions.put(sessionId, true);
-                final File stageDir = buildStageDir(volumeUuid, sessionId, isEphemeral);
-                prepareStageDir(stageDir);
-                return stageDir;
+                final File sessionStageDir = buildTmpSessionDir(sessionId, volumeUuid);
+                prepareStageDir(sessionStageDir);
+                return sessionStageDir;
             } catch (IllegalStateException e) {
                 throw new IOException(e);
             }
@@ -310,7 +307,8 @@
                         final PackageInstallerSession session;
                         try {
                             session = PackageInstallerSession.readFromXml(in, mInternalCallback,
-                                    mContext, mPm, mInstallThread.getLooper(), mSessionsDir, this);
+                                    mContext, mPm, mInstallThread.getLooper(), mStagingManager,
+                                    mSessionsDir, this);
                             currentSession = session;
                         } catch (Exception e) {
                             currentSession = null;
@@ -527,25 +525,21 @@
         String stageCid = null;
         if (!params.isMultiPackage) {
             if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
-                final boolean isInstant =
-                        (params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
-                stageDir = buildStageDir(params.volumeUuid, sessionId, isInstant);
+                stageDir = buildSessionDir(sessionId, params);
             } else {
                 stageCid = buildExternalStageCid(sessionId);
             }
         }
         session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
-                mInstallThread.getLooper(), sessionId, userId, installerPackageName,
-                callingUid, params, createdMillis, stageDir, stageCid, false, false, null,
-                SessionInfo.INVALID_ID);
+                mInstallThread.getLooper(), mStagingManager, sessionId, userId,
+                installerPackageName, callingUid, params, createdMillis, stageDir, stageCid, false,
+                false, null, SessionInfo.INVALID_ID);
 
         synchronized (mSessions) {
             mSessions.put(sessionId, session);
         }
         if (params.isStaged) {
-            synchronized (mStagedSessions) {
-                mStagedSessions.put(sessionId, session);
-            }
+            mStagingManager.createSession(session);
         }
 
         mCallbacks.notifySessionCreated(session.sessionId, session.userId);
@@ -637,13 +631,21 @@
         throw new IllegalStateException("Failed to allocate session ID");
     }
 
-    private File buildStagingDir(String volumeUuid, boolean isEphemeral) {
+    private File getTmpSessionDir(String volumeUuid) {
         return Environment.getDataAppDirectory(volumeUuid);
     }
 
-    private File buildStageDir(String volumeUuid, int sessionId, boolean isEphemeral) {
-        final File stagingDir = buildStagingDir(volumeUuid, isEphemeral);
-        return new File(stagingDir, "vmdl" + sessionId + ".tmp");
+    private File buildTmpSessionDir(int sessionId, String volumeUuid) {
+        final File sessionStagingDir = getTmpSessionDir(volumeUuid);
+        return new File(sessionStagingDir, "vmdl" + sessionId + ".tmp");
+    }
+
+    private File buildSessionDir(int sessionId, SessionParams params) {
+        if (params.isStaged) {
+            final File sessionStagingDir = Environment.getDataStagingDirectory(params.volumeUuid);
+            return new File(sessionStagingDir, "session_" + sessionId);
+        }
+        return buildTmpSessionDir(sessionId, params.volumeUuid);
     }
 
     static void prepareStageDir(File stageDir) throws IOException {
@@ -678,14 +680,7 @@
 
     @Override
     public ParceledListSlice<SessionInfo> getStagedSessions() {
-        final List<SessionInfo> result = new ArrayList<>();
-        synchronized (mStagedSessions) {
-            for (int i = 0; i < mStagedSessions.size(); i++) {
-                final PackageInstallerSession session = mStagedSessions.valueAt(i);
-                result.add(session.generateInfo(false));
-            }
-        }
-        return new ParceledListSlice<>(result);
+        return mStagingManager.getSessions();
     }
 
     @Override
@@ -1130,16 +1125,9 @@
             mInstallHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    // TODO: remove this mock implementation.
                     if (session.isStaged()) {
-                        // If the session is aborted, don't keep it in memory. Only store
-                        // sessions successfully staged.
                         if (!success) {
-                            synchronized (mStagedSessions) {
-                                mStagedSessions.remove(session.sessionId);
-                            }
-                        } else {
-                            return;
+                            mStagingManager.abortSession(session);
                         }
                     }
                     synchronized (mSessions) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 206a88b..3cb7714 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -17,6 +17,7 @@
 package com.android.server.pm;
 
 import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
+import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_SIGNATURE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
@@ -105,6 +106,8 @@
 import com.android.server.LocalServices;
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
+import com.android.server.pm.dex.DexManager;
+import com.android.server.security.VerityUtils;
 
 import libcore.io.IoUtils;
 
@@ -174,6 +177,7 @@
     private final PackageManagerService mPm;
     private final Handler mHandler;
     private final PackageSessionProvider mSessionProvider;
+    private final StagingManager mStagingManager;
 
     final int sessionId;
     final int userId;
@@ -250,6 +254,15 @@
     @GuardedBy("mLock")
     private int mParentSessionId;
 
+    @GuardedBy("mLock")
+    private boolean mStagedSessionApplied;
+    @GuardedBy("mLock")
+    private boolean mStagedSessionReady;
+    @GuardedBy("mLock")
+    private boolean mStagedSessionFailed;
+    @GuardedBy("mLock")
+    private int mStagedSessionErrorCode = SessionInfo.NO_ERROR;
+
     /**
      * Path to the validated base APK for this session, which may point at an
      * APK inside the session (when the session defines the base), or it may
@@ -274,6 +287,8 @@
     private final List<String> mResolvedNativeLibPaths = new ArrayList<>();
     @GuardedBy("mLock")
     private File mInheritedFilesBase;
+    @GuardedBy("mLock")
+    private boolean mVerityFound;
 
     private static final FileFilter sAddedFilter = new FileFilter() {
         @Override
@@ -283,6 +298,7 @@
             if (file.isDirectory()) return false;
             if (file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false;
             if (DexMetadataHelper.isDexMetadataFile(file)) return false;
+            if (VerityUtils.isFsveritySignatureFile(file)) return false;
             return true;
         }
     };
@@ -387,7 +403,7 @@
 
     public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
             Context context, PackageManagerService pm,
-            PackageSessionProvider sessionProvider, Looper looper,
+            PackageSessionProvider sessionProvider, Looper looper, StagingManager stagingManager,
             int sessionId, int userId,
             String installerPackageName, int installerUid, SessionParams params, long createdMillis,
             File stageDir, String stageCid, boolean prepared, boolean sealed,
@@ -397,6 +413,7 @@
         mPm = pm;
         mSessionProvider = sessionProvider;
         mHandler = new Handler(looper, mHandlerCallback);
+        mStagingManager = stagingManager;
 
         this.sessionId = sessionId;
         this.userId = userId;
@@ -470,6 +487,10 @@
             if (info.childSessionIds == null) {
                 info.childSessionIds = EMPTY_CHILD_SESSION_ARRAY;
             }
+            info.isSessionApplied = mStagedSessionApplied;
+            info.isSessionReady = mStagedSessionReady;
+            info.isSessionFailed = mStagedSessionFailed;
+            info.setStagedSessionErrorCode(mStagedSessionErrorCode);
         }
         return info;
     }
@@ -686,9 +707,11 @@
                 switch (Binder.getCallingUid()) {
                     case android.os.Process.SHELL_UID:
                     case android.os.Process.ROOT_UID:
+                    case android.os.Process.SYSTEM_UID:
                         break;
                     default:
-                        throw new SecurityException("Reverse mode only supported from shell");
+                        throw new SecurityException(
+                                "Reverse mode only supported from shell or system");
                 }
 
                 // In "reverse" mode, we're streaming data ourselves from the
@@ -960,18 +983,18 @@
 
         // Read transfers from the original owner stay open, but as the session's data
         // cannot be modified anymore, there is no leak of information.
-        if (!params.isMultiPackage) {
+        // For staged sessions, the validation is performed by StagingManager.
+        if (!params.isMultiPackage && !params.isStaged) {
             final PackageInfo pkgInfo = mPm.getPackageInfo(
                     params.appPackageName, PackageManager.GET_SIGNATURES
                             | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
 
             resolveStageDirLocked();
 
-            // Verify that stage looks sane with respect to existing application.
-            // This currently only ensures packageName, versionCode, and certificate
-            // consistency.
             try {
                 if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
+                    // TODO(b/118865310): Remove this when APEX validation is done via
+                    //                    StagingManager.
                     validateApexInstallLocked(pkgInfo);
                 } else {
                     // Verify that stage looks sane with respect to existing application.
@@ -1044,16 +1067,17 @@
     @GuardedBy("mLock")
     private void commitLocked()
             throws PackageManagerException {
+        if (params.isStaged) {
+            mStagingManager.commitSession(this);
+            destroyInternal();
+            dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
+            return;
+        }
         final PackageManagerService.ActiveInstallSession committingSession =
                 makeSessionActiveLocked();
         if (committingSession == null) {
             return;
         }
-        if (isStaged()) {
-            // STOPSHIP: implement staged sessions
-            dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
-            return;
-        }
         if (isMultiPackage()) {
             final int[] childSessionIds = getChildSessionIds();
             List<PackageManagerService.ActiveInstallSession> childSessions =
@@ -1068,7 +1092,6 @@
                     if (activeSession != null) {
                         if ((activeSession.getSessionParams().installFlags
                                 & PackageManager.INSTALL_APEX) != 0) {
-                            // TODO(b/118865310): Add exception to this case for staged installs
                             throw new PackageManagerException(
                                     PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
                                     "Atomic install is not supported for APEX packages.");
@@ -1344,6 +1367,17 @@
         mResolvedStagedFiles.clear();
         mResolvedInheritedFiles.clear();
 
+        // Partial installs must be consistent with existing install
+        if (params.mode == SessionParams.MODE_INHERIT_EXISTING
+                && (pkgInfo == null || pkgInfo.applicationInfo == null)) {
+            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+                    "Missing existing base package");
+        }
+        // Default to require only if existing base has fs-verity.
+        mVerityFound = PackageManagerServiceUtils.isApkVerityEnabled()
+                && params.mode == SessionParams.MODE_INHERIT_EXISTING
+                && VerityUtils.hasFsverity(pkgInfo.applicationInfo.getBaseCodePath());
+
         try {
             resolveStageDirLocked();
         } catch (IOException e) {
@@ -1407,7 +1441,7 @@
             }
 
             final File targetFile = new File(mResolvedStageDir, targetName);
-            maybeRenameFile(addedFile, targetFile);
+            resolveAndStageFile(addedFile, targetFile);
 
             // Base is coming from session
             if (apk.splitName == null) {
@@ -1415,8 +1449,6 @@
                 baseApk = apk;
             }
 
-            mResolvedStagedFiles.add(targetFile);
-
             final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(addedFile);
             if (dexMetadataFile != null) {
                 if (!FileUtils.isValidExtFilename(dexMetadataFile.getName())) {
@@ -1425,8 +1457,7 @@
                 }
                 final File targetDexMetadataFile = new File(mResolvedStageDir,
                         DexMetadataHelper.buildDexMetadataPathForApk(targetName));
-                mResolvedStagedFiles.add(targetDexMetadataFile);
-                maybeRenameFile(dexMetadataFile, targetDexMetadataFile);
+                resolveAndStageFile(dexMetadataFile, targetDexMetadataFile);
             }
         }
 
@@ -1469,12 +1500,6 @@
             }
 
         } else {
-            // Partial installs must be consistent with existing install
-            if (pkgInfo == null || pkgInfo.applicationInfo == null) {
-                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
-                        "Missing existing base package for " + mPackageName);
-            }
-
             final PackageLite existing;
             final ApkLite existingBase;
             ApplicationInfo appInfo = pkgInfo.applicationInfo;
@@ -1578,12 +1603,55 @@
                 }
             }
         }
+        if (baseApk.preferCodeIntegrity) {
+            for (File file : mResolvedStagedFiles) {
+                if (file.getName().endsWith(".apk")
+                        && !DexManager.auditUncompressedCodeInApk(file.getPath())) {
+                    throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+                            "Some code are not uncompressed and aligned correctly for "
+                            + mPackageName);
+                }
+            }
+        }
         if (baseApk.isSplitRequired && stagedSplits.size() <= 1) {
             throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT,
                     "Missing split for " + mPackageName);
         }
     }
 
+    private void resolveAndStageFile(File origFile, File targetFile)
+            throws PackageManagerException {
+        mResolvedStagedFiles.add(targetFile);
+        maybeRenameFile(origFile, targetFile);
+
+        final File originalSignature = new File(
+                VerityUtils.getFsveritySignatureFilePath(origFile.getPath()));
+        // Make sure .fsv_sig exists when it should, then resolve and stage it.
+        if (originalSignature.exists()) {
+            // mVerityFound can only change from false to true here during the staging loop. Since
+            // all or none of files should have .fsv_sig, this should only happen in the first time
+            // (or never), otherwise bail out.
+            if (!mVerityFound) {
+                mVerityFound = true;
+                if (mResolvedStagedFiles.size() > 1) {
+                    throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE,
+                            "Some file is missing fs-verity signature");
+                }
+            }
+        } else {
+            if (!mVerityFound) {
+                return;
+            }
+            throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE,
+                    "Missing corresponding fs-verity signature to " + origFile);
+        }
+
+        final File stagedSignature = new File(
+                VerityUtils.getFsveritySignatureFilePath(targetFile.getPath()));
+        maybeRenameFile(originalSignature, stagedSignature);
+        mResolvedStagedFiles.add(stagedSignature);
+    }
+
     @GuardedBy("mLock")
     private void assertApkConsistentLocked(String tag, ApkLite apk)
             throws PackageManagerException {
@@ -1935,6 +2003,15 @@
         mCallback.onSessionFinished(this, success);
     }
 
+    void setStagedSessionReady() {
+        synchronized (mLock) {
+            mStagedSessionReady = true;
+            mStagedSessionApplied = false;
+            mStagedSessionFailed = false;
+            mStagedSessionErrorCode = SessionInfo.NO_ERROR;
+        }
+    }
+
     private void destroyInternal() {
         synchronized (mLock) {
             mSealed = true;
@@ -1948,7 +2025,11 @@
                 bridge.forceClose();
             }
         }
-        if (stageDir != null) {
+        // For staged sessions, we don't delete the directory where the packages have been copied,
+        // since these packages are supposed to be read on reboot. StagingManager is in charge of
+        // deleting these dirs when the staged session has reached a final state.
+        // TODO(b/118865310): Implement packageDir deletion in StagingManager.
+        if (stageDir != null && !params.isStaged) {
             try {
                 mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
             } catch (InstallerException ignored) {
@@ -2136,7 +2217,8 @@
     //                       can have a complete session for the constructor
     public static PackageInstallerSession readFromXml(@NonNull XmlPullParser in,
             @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context,
-            @NonNull PackageManagerService pm, Looper installerThread, @NonNull File sessionsDir,
+            @NonNull PackageManagerService pm, Looper installerThread,
+            @NonNull StagingManager stagingManager, @NonNull File sessionsDir,
             @NonNull PackageSessionProvider sessionProvider)
             throws IOException, XmlPullParserException {
         final int sessionId = readIntAttribute(in, ATTR_SESSION_ID);
@@ -2180,8 +2262,8 @@
             params.appIconLastModified = appIconFile.lastModified();
         }
         return new PackageInstallerSession(callback, context, pm, sessionProvider,
-                installerThread, sessionId, userId, installerPackageName, installerUid,
-                params, createdMillis, stageDir, stageCid, prepared, sealed,
+                installerThread, stagingManager, sessionId, userId, installerPackageName,
+                installerUid, params, createdMillis, stageDir, stageCid, prepared, sealed,
                 EMPTY_CHILD_SESSION_ARRAY, parentSessionId);
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6cfb846..522ab0b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -162,12 +162,14 @@
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.KeySet;
+import android.content.pm.ModuleInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageList;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
+import android.content.pm.PackageManager.ModuleInfoFlags;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageManagerInternal.CheckPermissionDelegate;
 import android.content.pm.PackageManagerInternal.PackageListObserver;
@@ -294,6 +296,7 @@
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.LockGuard;
+import com.android.server.PackageWatchdog;
 import com.android.server.ServiceThread;
 import com.android.server.SystemConfig;
 import com.android.server.SystemServerInitThreadPool;
@@ -445,6 +448,7 @@
     private static final int BLUETOOTH_UID = Process.BLUETOOTH_UID;
     private static final int SHELL_UID = Process.SHELL_UID;
     private static final int SE_UID = Process.SE_UID;
+    private static final int NETWORKSTACK_UID = Process.NETWORK_STACK_UID;
 
     static final int SCAN_NO_DEX = 1 << 0;
     static final int SCAN_UPDATE_SIGNATURE = 1 << 1;
@@ -543,6 +547,12 @@
     private static final long DEFAULT_VERIFICATION_TIMEOUT = 10 * 1000;
 
     /**
+     * The default duration to wait for rollback to be enabled in
+     * milliseconds.
+     */
+    private static final long DEFAULT_ENABLE_ROLLBACK_TIMEOUT = 10 * 1000;
+
+    /**
      * The default response for package verification timeout.
      *
      * This can be either PackageManager.VERIFICATION_ALLOW or
@@ -591,12 +601,6 @@
     public static final int REASON_LAST = REASON_SHARED;
 
     /**
-     * Version number for the package parser cache. Increment this whenever the format or
-     * extent of cached data changes. See {@code PackageParser#setCacheDir}.
-     */
-    private static final String PACKAGE_PARSER_CACHE_VERSION = "1";
-
-    /**
      * Whether the package parser cache is enabled.
      */
     private static final boolean DEFAULT_PACKAGE_PARSER_CACHE_ENABLED = true;
@@ -723,6 +727,8 @@
 
     private PackageManager mPackageManager;
 
+    private final ModuleInfoProvider mModuleInfoProvider;
+
     class PackageParserCallback implements PackageParser.Callback {
         @Override public final boolean hasFeature(String feature) {
             return PackageManagerService.this.hasSystemFeature(feature, 0);
@@ -866,6 +872,9 @@
     /** List of packages waiting for verification. */
     final SparseArray<PackageVerificationState> mPendingVerification = new SparseArray<>();
 
+    /** List of packages waiting for rollback to be enabled. */
+    final SparseArray<InstallParams> mPendingEnableRollback = new SparseArray<>();
+
     final PackageInstallerService mInstallerService;
 
     final ArtManagerService mArtManagerService;
@@ -886,10 +895,13 @@
     /** Token for keys in mPendingVerification. */
     private int mPendingVerificationToken = 0;
 
+    /** Token for keys in mPendingEnableRollback. */
+    private int mPendingEnableRollbackToken = 0;
+
     volatile boolean mSystemReady;
     volatile boolean mSafeMode;
     volatile boolean mHasSystemUidErrors;
-    private volatile boolean mWebInstantAppsDisabled;
+    private volatile SparseBooleanArray mWebInstantAppsDisabled = new SparseBooleanArray();
 
     ApplicationInfo mAndroidApplication;
     final ActivityInfo mResolveActivity = new ActivityInfo();
@@ -1064,9 +1076,6 @@
                         + verificationId + " packageName:" + packageName);
                 return;
             }
-            if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
-                    "Updating IntentFilterVerificationInfo for package " + packageName
-                            +" verificationId:" + verificationId);
 
             synchronized (mPackages) {
                 if (verified) {
@@ -1084,19 +1093,47 @@
                     int updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
                     boolean needUpdate = false;
 
-                    // We cannot override the STATUS_ALWAYS / STATUS_NEVER states if they have
-                    // already been set by the User thru the Disambiguation dialog
+                    if (DEBUG_DOMAIN_VERIFICATION) {
+                        Slog.d(TAG,
+                                "Updating IntentFilterVerificationInfo for package " + packageName
+                                + " verificationId:" + verificationId
+                                + " verified=" + verified);
+                    }
+
+                    // In a success case, we promote from undefined or ASK to ALWAYS.  This
+                    // supports a flow where the app fails validation but then ships an updated
+                    // APK that passes, and therefore deserves to be in ALWAYS.
+                    //
+                    // If validation failed, the undefined state winds up in the basic ASK behavior,
+                    // but apps that previously passed and became ALWAYS are *demoted* out of
+                    // that state, since they would not deserve the ALWAYS behavior in case of a
+                    // clean install.
                     switch (userStatus) {
+                        case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
+                            if (!verified) {
+                                // updatedStatus is already UNDEFINED
+                                needUpdate = true;
+
+                                if (DEBUG_DOMAIN_VERIFICATION) {
+                                    Slog.d(TAG, "Formerly validated but now failing; demoting");
+                                }
+                            }
+                            break;
+
                         case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
+                            // Stay in 'undefined' on verification failure
                             if (verified) {
                                 updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
-                            } else {
-                                updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
                             }
                             needUpdate = true;
+                            if (DEBUG_DOMAIN_VERIFICATION) {
+                                Slog.d(TAG, "Applying update; old=" + userStatus
+                                        + " new=" + updatedStatus);
+                            }
                             break;
 
                         case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
+                            // Keep in 'ask' on failure
                             if (verified) {
                                 updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
                                 needUpdate = true;
@@ -1112,6 +1149,8 @@
                                 packageName, updatedStatus, userId);
                         scheduleWritePackageRestrictionsLocked(userId);
                     }
+                } else {
+                    Slog.i(TAG, "autoVerify ignored when installing for all users");
                 }
             }
         }
@@ -1230,6 +1269,8 @@
     static final int INTENT_FILTER_VERIFIED = 18;
     static final int WRITE_PACKAGE_LIST = 19;
     static final int INSTANT_APP_RESOLUTION_PHASE_TWO = 20;
+    static final int ENABLE_ROLLBACK_STATUS = 21;
+    static final int ENABLE_ROLLBACK_TIMEOUT = 22;
 
     static final int WRITE_SETTINGS_DELAY = 10*1000;  // 10 seconds
 
@@ -1453,14 +1494,13 @@
                     final PackageVerificationState state = mPendingVerification.get(verificationId);
 
                     if ((state != null) && !state.timeoutExtended()) {
-                        final InstallArgs args = state.getInstallArgs();
+                        final InstallParams params = state.getInstallParams();
+                        final InstallArgs args = params.mArgs;
                         final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
 
                         Slog.i(TAG, "Verification timed out for " + originUri);
                         mPendingVerification.remove(verificationId);
 
-                        int ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
-
                         final UserHandle user = args.getUser();
                         if (getDefaultVerificationResponse(user)
                                 == PackageManager.VERIFICATION_ALLOW) {
@@ -1469,16 +1509,16 @@
                                     PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
                             broadcastPackageVerified(verificationId, originUri,
                                     PackageManager.VERIFICATION_ALLOW, user);
-                            ret = args.copyApk();
                         } else {
                             broadcastPackageVerified(verificationId, originUri,
                                     PackageManager.VERIFICATION_REJECT, user);
+                            params.setReturnCode(
+                                    PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE);
                         }
 
                         Trace.asyncTraceEnd(
                                 TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
-
-                        processPendingInstall(args, ret);
+                        params.handleVerificationFinished();
                     }
                     break;
                 }
@@ -1498,22 +1538,22 @@
                     if (state.isVerificationComplete()) {
                         mPendingVerification.remove(verificationId);
 
-                        final InstallArgs args = state.getInstallArgs();
+                        final InstallParams params = state.getInstallParams();
+                        final InstallArgs args = params.mArgs;
                         final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
 
-                        int ret;
                         if (state.isInstallAllowed()) {
                             broadcastPackageVerified(verificationId, originUri,
-                                    response.code, state.getInstallArgs().getUser());
-                            ret = args.copyApk();
+                                    response.code, args.getUser());
                         } else {
-                            ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
+                            params.setReturnCode(
+                                    PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE);
                         }
 
                         Trace.asyncTraceEnd(
                                 TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
 
-                        processPendingInstall(args, ret);
+                        params.handleVerificationFinished();
                     }
 
                     break;
@@ -1573,6 +1613,49 @@
                             (InstantAppRequest) msg.obj,
                             mInstantAppInstallerActivity,
                             mHandler);
+                    break;
+                }
+                case ENABLE_ROLLBACK_STATUS: {
+                    final int enableRollbackToken = msg.arg1;
+                    final int enableRollbackCode = msg.arg2;
+                    InstallParams params = mPendingEnableRollback.get(enableRollbackToken);
+                    if (params == null) {
+                        Slog.w(TAG, "Invalid rollback enabled token "
+                                + enableRollbackToken + " received");
+                        break;
+                    }
+
+                    mPendingEnableRollback.remove(enableRollbackToken);
+
+                    if (enableRollbackCode != PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED) {
+                        final InstallArgs args = params.mArgs;
+                        final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
+                        Slog.w(TAG, "Failed to enable rollback for " + originUri);
+                        Slog.w(TAG, "Continuing with installation of " + originUri);
+                    }
+
+                    Trace.asyncTraceEnd(
+                            TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken);
+
+                    params.handleRollbackEnabled();
+                    break;
+                }
+                case ENABLE_ROLLBACK_TIMEOUT: {
+                    final int enableRollbackToken = msg.arg1;
+                    final InstallParams params = mPendingEnableRollback.get(enableRollbackToken);
+                    if (params != null) {
+                        final InstallArgs args = params.mArgs;
+                        final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
+
+                        Slog.w(TAG, "Enable rollback timed out for " + originUri);
+                        mPendingEnableRollback.remove(enableRollbackToken);
+
+                        Slog.w(TAG, "Continuing with installation of " + originUri);
+                        Trace.asyncTraceEnd(
+                                TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken);
+                        params.handleRollbackEnabled();
+                    }
+                    break;
                 }
             }
         }
@@ -2146,6 +2229,8 @@
                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
         mSettings.addSharedUserLPw("android.uid.se", SE_UID,
                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+        mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID,
+                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
 
         String separateProcesses = SystemProperties.get("debug.separate_processes");
         if (separateProcesses != null && separateProcesses.length() > 0) {
@@ -2199,9 +2284,7 @@
             for (int i = 0; i < builtInLibCount; i++) {
                 String name = libConfig.keyAt(i);
                 SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);
-                addSharedLibraryLPw(entry.filename, null, null, name,
-                        SharedLibraryInfo.VERSION_UNDEFINED, SharedLibraryInfo.TYPE_BUILTIN,
-                        PLATFORM_PACKAGE_NAME, 0);
+                addBuiltInSharedLibraryLocked(entry.filename, name);
             }
 
             // Now that we have added all the libraries, iterate again to add dependency
@@ -2302,7 +2385,7 @@
                 }
             }
 
-            mCacheDir = preparePackageParserCache(mIsUpgrade);
+            mCacheDir = preparePackageParserCache();
 
             // Set flag to monitor and not change apk file paths when
             // scanning install directories.
@@ -3008,6 +3091,8 @@
         } // synchronized (mPackages)
         } // synchronized (mInstallLock)
 
+        mModuleInfoProvider = new ModuleInfoProvider(mContext, this);
+
         // Now after opening every single application zip, make sure they
         // are all flushed.  Not really needed, but keeps things nice and
         // tidy.
@@ -3169,7 +3254,7 @@
         setUpInstantAppInstallerActivityLP(getInstantAppInstallerLPr());
     }
 
-    private static File preparePackageParserCache(boolean isUpgrade) {
+    private static @Nullable File preparePackageParserCache() {
         if (!DEFAULT_PACKAGE_PARSER_CACHE_ENABLED) {
             return null;
         }
@@ -3190,17 +3275,25 @@
             return null;
         }
 
-        // If this is a system upgrade scenario, delete the contents of the package cache dir.
-        // This also serves to "GC" unused entries when the package cache version changes (which
-        // can only happen during upgrades).
-        if (isUpgrade) {
-            FileUtils.deleteContents(cacheBaseDir);
+        // There are several items that need to be combined together to safely
+        // identify cached items. In particular, changing the value of certain
+        // feature flags should cause us to invalidate any caches.
+        final String cacheName = SystemProperties.digestOf(
+                "ro.build.fingerprint",
+                "persist.sys.isolated_storage");
+
+        // Reconcile cache directories, keeping only what we'd actually use.
+        for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) {
+            if (Objects.equals(cacheName, cacheDir.getName())) {
+                Slog.d(TAG, "Keeping known cache " + cacheDir.getName());
+            } else {
+                Slog.d(TAG, "Destroying unknown cache " + cacheDir.getName());
+                FileUtils.deleteContentsAndDir(cacheDir);
+            }
         }
 
-
-        // Return the versioned package cache directory. This is something like
-        // "/data/system/package_cache/1"
-        File cacheDir = FileUtils.createDir(cacheBaseDir, PACKAGE_PARSER_CACHE_VERSION);
+        // Return the versioned package cache directory.
+        File cacheDir = FileUtils.createDir(cacheBaseDir, cacheName);
 
         if (cacheDir == null) {
             // Something went wrong. Attempt to delete everything and return.
@@ -3226,7 +3319,7 @@
             File frameworkDir = new File(Environment.getRootDirectory(), "framework");
             if (cacheDir.lastModified() < frameworkDir.lastModified()) {
                 FileUtils.deleteContents(cacheBaseDir);
-                cacheDir = FileUtils.createDir(cacheBaseDir, PACKAGE_PARSER_CACHE_VERSION);
+                cacheDir = FileUtils.createDir(cacheBaseDir, cacheName);
             }
         }
 
@@ -4939,6 +5032,16 @@
     }
 
     @Override
+    public ModuleInfo getModuleInfo(String packageName, @ModuleInfoFlags int flags) {
+        return mModuleInfoProvider.getModuleInfo(packageName, flags);
+    }
+
+    @Override
+    public List<ModuleInfo> getInstalledModules(int flags) {
+        return mModuleInfoProvider.getInstalledModules(flags);
+    }
+
+    @Override
     public String[] getSystemSharedLibraryNames() {
         // allow instant applications
         synchronized (mPackages) {
@@ -5524,12 +5627,18 @@
      * were updated, return true.
      */
     private boolean isCompatSignatureUpdateNeeded(PackageParser.Package scannedPkg) {
-        final VersionInfo ver = getSettingsVersionForPackage(scannedPkg);
+        return isCompatSignatureUpdateNeeded(getSettingsVersionForPackage(scannedPkg));
+    }
+
+    private static boolean isCompatSignatureUpdateNeeded(VersionInfo ver) {
         return ver.databaseVersion < DatabaseVersion.SIGNATURE_END_ENTITY;
     }
 
     private boolean isRecoverSignatureUpdateNeeded(PackageParser.Package scannedPkg) {
-        final VersionInfo ver = getSettingsVersionForPackage(scannedPkg);
+        return isRecoverSignatureUpdateNeeded(getSettingsVersionForPackage(scannedPkg));
+    }
+
+    private static boolean isRecoverSignatureUpdateNeeded(VersionInfo ver) {
         return ver.databaseVersion < DatabaseVersion.SIGNATURE_MALFORMED_RECOVER;
     }
 
@@ -5858,8 +5967,8 @@
     /**
      * Returns whether or not instant apps have been disabled remotely.
      */
-    private boolean areWebInstantAppsDisabled() {
-        return mWebInstantAppsDisabled;
+    private boolean areWebInstantAppsDisabled(int userId) {
+        return mWebInstantAppsDisabled.get(userId);
     }
 
     private boolean isInstantAppResolutionAllowed(
@@ -5890,7 +5999,7 @@
         } else {
             if (intent.getData() == null || TextUtils.isEmpty(intent.getData().getHost())) {
                 return false;
-            } else if (areWebInstantAppsDisabled()) {
+            } else if (areWebInstantAppsDisabled(userId)) {
                 return false;
             }
         }
@@ -6770,7 +6879,7 @@
     private List<ResolveInfo> applyPostResolutionFilter(List<ResolveInfo> resolveInfos,
             String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
             boolean resolveForStart, int userId, Intent intent) {
-        final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled();
+        final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId);
         for (int i = resolveInfos.size() - 1; i >= 0; i--) {
             final ResolveInfo info = resolveInfos.get(i);
             // remove locally resolved instant app web results when disabled
@@ -8293,11 +8402,12 @@
         // directory and not the APK file.
         final long lastModifiedTime = mIsPreNMR1Upgrade
                 ? new File(pkg.codePath).lastModified() : getLastModifiedTime(pkg);
+        final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(pkg);
         if (ps != null && !forceCollect
                 && ps.codePathString.equals(pkg.codePath)
                 && ps.timeStamp == lastModifiedTime
-                && !isCompatSignatureUpdateNeeded(pkg)
-                && !isRecoverSignatureUpdateNeeded(pkg)) {
+                && !isCompatSignatureUpdateNeeded(settingsVersionForPackage)
+                && !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) {
             if (ps.signatures.mSigningDetails.signatures != null
                     && ps.signatures.mSigningDetails.signatures.length != 0
                     && ps.signatures.mSigningDetails.signatureSchemeVersion
@@ -8439,16 +8549,16 @@
     }
 
     /**
-     * Returns if full apk verification can be skipped for the whole package, including the splits.
+     * Returns if forced apk verification can be skipped for the whole package, including splits.
      */
-    private boolean canSkipFullPackageVerification(PackageParser.Package pkg) {
-        if (!canSkipFullApkVerification(pkg.baseCodePath)) {
+    private boolean canSkipForcedPackageVerification(PackageParser.Package pkg) {
+        if (!canSkipForcedApkVerification(pkg.baseCodePath)) {
             return false;
         }
         // TODO: Allow base and splits to be verified individually.
         if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
             for (int i = 0; i < pkg.splitCodePaths.length; i++) {
-                if (!canSkipFullApkVerification(pkg.splitCodePaths[i])) {
+                if (!canSkipForcedApkVerification(pkg.splitCodePaths[i])) {
                     return false;
                 }
             }
@@ -8457,14 +8567,17 @@
     }
 
     /**
-     * Returns if full apk verification can be skipped, depending on current FSVerity setup and
+     * Returns if forced apk verification can be skipped, depending on current FSVerity setup and
      * whether the apk contains signed root hash.  Note that the signer's certificate still needs to
      * match one in a trusted source, and should be done separately.
      */
-    private boolean canSkipFullApkVerification(String apkPath) {
-        final byte[] rootHashObserved;
+    private boolean canSkipForcedApkVerification(String apkPath) {
+        if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) {
+            return VerityUtils.hasFsverity(apkPath);
+        }
+
         try {
-            rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
+            final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
             if (rootHashObserved == null) {
                 return false;  // APK does not contain Merkle tree root hash.
             }
@@ -8636,7 +8749,7 @@
         // in verified partition, or can be verified on access (when apk verity is enabled). In both
         // cases, only data in Signing Block is verified instead of the whole file.
         final boolean skipVerify = scanSystemPartition
-                || (forceCollect && canSkipFullPackageVerification(pkg));
+                || (forceCollect && canSkipForcedPackageVerification(pkg));
         collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify);
 
         // Reset profile if the application version is changed
@@ -8698,8 +8811,20 @@
         if (scanResult.success) {
             synchronized (mPackages) {
                 try {
+                    final String pkgName = scanResult.pkgSetting.name;
+                    final Map<String, ReconciledPackage> reconcileResult = reconcilePackagesLocked(
+                            new ReconcileRequest(
+                                    Collections.singletonMap(pkgName, scanResult),
+                                    mSharedLibraries,
+                                    mPackages,
+                                    Collections.singletonMap(
+                                            pkgName, getSettingsVersionForPackage(pkg)),
+                                    Collections.singletonMap(pkgName,
+                                            getSharedLibLatestVersionSetting(scanResult))),
+                            mSettings.mKeySetManagerService);
                     prepareScanResultLocked(scanResult);
-                    commitScanResultLocked(scanResult);
+                    commitReconciledScanResultLocked(
+                            reconcileResult.get(pkgName));
                 } catch (PackageManagerException e) {
                     unprepareScanResultLocked(scanResult);
                     throw e;
@@ -9306,8 +9431,26 @@
         }
     }
 
-    private @Nullable SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) {
-        LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name);
+    @Nullable
+    private SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) {
+        return getSharedLibraryInfo(name, version, mSharedLibraries, null);
+    }
+
+    @Nullable
+    private static SharedLibraryInfo getSharedLibraryInfo(String name, long version,
+            Map<String, LongSparseArray<SharedLibraryInfo>> existingLibraries,
+            @Nullable Map<String, LongSparseArray<SharedLibraryInfo>> newLibraries) {
+        if (newLibraries != null) {
+            final LongSparseArray<SharedLibraryInfo> versionedLib = newLibraries.get(name);
+            SharedLibraryInfo info = null;
+            if (versionedLib != null) {
+                info = versionedLib.get(version);
+            }
+            if (info != null) {
+                return info;
+            }
+        }
+        final LongSparseArray<SharedLibraryInfo> versionedLib = existingLibraries.get(name);
         if (versionedLib == null) {
             return null;
         }
@@ -9334,10 +9477,26 @@
         return null;
     }
 
+
+    @Nullable
+    private PackageSetting getSharedLibLatestVersionSetting(@NonNull ScanResult scanResult) {
+        PackageSetting sharedLibPackage = null;
+        synchronized (mPackages) {
+            final SharedLibraryInfo latestSharedLibraVersionLPr =
+                    getLatestSharedLibraVersionLPr(scanResult.pkgSetting.pkg);
+            if (latestSharedLibraVersionLPr != null) {
+                sharedLibPackage = mSettings.getPackageLPr(
+                        latestSharedLibraVersionLPr.getPackageName());
+            }
+        }
+        return sharedLibPackage;
+    }
+
     public void shutdown() {
         mPackageUsage.writeNow(mPackages);
         mCompilerStats.writeNow();
         mDexManager.writePackageDexUsageNow();
+        PackageWatchdog.getInstance(mContext).writeNow();
 
         // This is the last chance to write out pending restriction settings
         synchronized (mPackages) {
@@ -9635,35 +9794,50 @@
     @GuardedBy("mPackages")
     private void updateSharedLibrariesLPr(PackageParser.Package pkg,
             PackageParser.Package changingLib) throws PackageManagerException {
-        if (pkg == null) {
-            return;
-        }
+        final ArrayList<SharedLibraryInfo> sharedLibraryInfos =
+                collectSharedLibraryInfos(pkg, Collections.unmodifiableMap(mPackages),
+                        mSharedLibraries, null);
+        executeSharedLibrariesUpdateLPr(pkg, changingLib, sharedLibraryInfos);
+    }
 
-        // If the package provides libraries, clear their old dependencies.
-        // This method will set them up again.
-        applyDefiningSharedLibraryUpdateLocked(pkg, null, (definingLibrary, dependency) -> {
-            definingLibrary.clearDependencies();
-        });
+    private static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(PackageParser.Package pkg,
+            Map<String, PackageParser.Package> availablePackages,
+            @NonNull final Map<String, LongSparseArray<SharedLibraryInfo>> existingLibraries,
+            @Nullable final Map<String, LongSparseArray<SharedLibraryInfo>> newLibraries)
+            throws PackageManagerException {
+        if (pkg == null) {
+            return null;
+        }
         // The collection used here must maintain the order of addition (so
         // that libraries are searched in the correct order) and must have no
         // duplicates.
         ArrayList<SharedLibraryInfo> usesLibraryInfos = null;
         if (pkg.usesLibraries != null) {
-            usesLibraryInfos = addSharedLibrariesLPw(pkg.usesLibraries,
-                    null, null, pkg.packageName, true,
-                    pkg.applicationInfo.targetSdkVersion, null);
+            usesLibraryInfos = collectSharedLibraryInfos(pkg.usesLibraries, null, null,
+                    pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, null,
+                    availablePackages, existingLibraries, newLibraries);
         }
         if (pkg.usesStaticLibraries != null) {
-            usesLibraryInfos = addSharedLibrariesLPw(pkg.usesStaticLibraries,
+            usesLibraryInfos = collectSharedLibraryInfos(pkg.usesStaticLibraries,
                     pkg.usesStaticLibrariesVersions, pkg.usesStaticLibrariesCertDigests,
-                    pkg.packageName, true,
-                    pkg.applicationInfo.targetSdkVersion, usesLibraryInfos);
+                    pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, usesLibraryInfos,
+                    availablePackages, existingLibraries, newLibraries);
         }
         if (pkg.usesOptionalLibraries != null) {
-            usesLibraryInfos = addSharedLibrariesLPw(pkg.usesOptionalLibraries,
-                    null, null, pkg.packageName, false,
-                    pkg.applicationInfo.targetSdkVersion, usesLibraryInfos);
+            usesLibraryInfos = collectSharedLibraryInfos(pkg.usesOptionalLibraries,
+                    null, null, pkg.packageName, false, pkg.applicationInfo.targetSdkVersion,
+                    usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
         }
+        return usesLibraryInfos;
+    }
+
+    private void executeSharedLibrariesUpdateLPr(PackageParser.Package pkg,
+            PackageParser.Package changingLib, ArrayList<SharedLibraryInfo> usesLibraryInfos) {
+        // If the package provides libraries, clear their old dependencies.
+        // This method will set them up again.
+        applyDefiningSharedLibraryUpdateLocked(pkg, null, (definingLibrary, dependency) -> {
+            definingLibrary.clearDependencies();
+        });
         if (usesLibraryInfos != null) {
             pkg.usesLibraryInfos = usesLibraryInfos;
             // Use LinkedHashSet to preserve the order of files added to
@@ -9680,18 +9854,22 @@
     }
 
     @GuardedBy("mPackages")
-    private ArrayList<SharedLibraryInfo> addSharedLibrariesLPw(
+    private static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(
             @NonNull List<String> requestedLibraries,
             @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
             @NonNull String packageName, boolean required, int targetSdk,
-            @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries)
+            @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries,
+            @NonNull final Map<String, PackageParser.Package> availablePackages,
+            @NonNull final Map<String, LongSparseArray<SharedLibraryInfo>> existingLibraries,
+            @Nullable final Map<String, LongSparseArray<SharedLibraryInfo>> newLibraries)
             throws PackageManagerException {
         final int libCount = requestedLibraries.size();
         for (int i = 0; i < libCount; i++) {
             final String libName = requestedLibraries.get(i);
             final long libVersion = requiredVersions != null ? requiredVersions[i]
                     : SharedLibraryInfo.VERSION_UNDEFINED;
-            final SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(libName, libVersion);
+            final SharedLibraryInfo libraryInfo = getSharedLibraryInfo(libName, libVersion,
+                    existingLibraries, newLibraries);
             if (libraryInfo == null) {
                 if (required) {
                     throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
@@ -9711,7 +9889,8 @@
                                     + libraryInfo.getLongVersion() + "; failing!");
                     }
 
-                    PackageParser.Package libPkg = mPackages.get(libraryInfo.getPackageName());
+                    PackageParser.Package libPkg =
+                            availablePackages.get(libraryInfo.getPackageName());
                     if (libPkg == null) {
                         throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
                                 "Package " + packageName + " requires unavailable static shared"
@@ -9884,15 +10063,24 @@
         @Nullable public final PackageSetting pkgSetting;
         /** ABI code paths that have changed in the package scan */
         @Nullable public final List<String> changedAbiCodePath;
+
+        public final SharedLibraryInfo staticSharedLibraryInfo;
+
+        public final List<SharedLibraryInfo> dynamicSharedLibraryInfos;
+
         public ScanResult(
                 ScanRequest request, boolean success,
                 @Nullable PackageSetting pkgSetting,
-                @Nullable List<String> changedAbiCodePath, boolean existingSettingCopied) {
+                @Nullable List<String> changedAbiCodePath, boolean existingSettingCopied,
+                SharedLibraryInfo staticSharedLibraryInfo,
+                List<SharedLibraryInfo> dynamicSharedLibraryInfos) {
             this.request = request;
             this.success = success;
             this.pkgSetting = pkgSetting;
             this.changedAbiCodePath = changedAbiCodePath;
             this.existingSettingCopied = existingSettingCopied;
+            this.staticSharedLibraryInfo = staticSharedLibraryInfo;
+            this.dynamicSharedLibraryInfos = dynamicSharedLibraryInfos;
         }
     }
 
@@ -10090,29 +10278,6 @@
     }
 
 
-    private void commitSuccessfulScanResults(@NonNull List<ScanResult> results)
-            throws PackageManagerException {
-        synchronized(mPackages) {
-            for (ScanResult result : results) {
-                // failures should have been caught earlier, but in case it wasn't,
-                // let's double check
-                if (!result.success) {
-                    throw new PackageManagerException(
-                            "Scan failed for " + result.request.pkg.packageName);
-                }
-            }
-            for (ScanResult result : results) {
-                try {
-                    prepareScanResultLocked(result);
-                    commitScanResultLocked(result);
-                } catch (PackageManagerException e) {
-                    unprepareScanResultLocked(result);
-                    throw e;
-                }
-            }
-        }
-    }
-
     /** Prepares the system to commit a {@link ScanResult} in a way that will not fail. */
     private void prepareScanResultLocked(@NonNull ScanResult result)
             throws PackageManagerException {
@@ -10145,7 +10310,8 @@
      * possible and the system is not left in an inconsistent state.
      */
     @GuardedBy({"mPackages", "mInstallLock"})
-    private void commitScanResultLocked(@NonNull ScanResult result) throws PackageManagerException {
+    private void commitReconciledScanResultLocked(@NonNull ReconciledPackage reconciledPkg) {
+        final ScanResult result = reconciledPkg.scanResult;
         final ScanRequest request = result.request;
         final PackageParser.Package pkg = request.pkg;
         final PackageParser.Package oldPkg = request.oldPkg;
@@ -10153,7 +10319,6 @@
         final @ScanFlags int scanFlags = request.scanFlags;
         final PackageSetting oldPkgSetting = request.oldPkgSetting;
         final PackageSetting originalPkgSetting = request.originalPkgSetting;
-        final PackageSetting disabledPkgSetting = request.disabledPkgSetting;
         final UserHandle user = request.user;
         final String realPkgName = request.realPkgName;
         final List<String> changedAbiCodePath = result.changedAbiCodePath;
@@ -10189,130 +10354,19 @@
             mTransferedPackages.add(pkg.packageName);
         }
 
-        // THROWS: when requested libraries that can't be found. it only changes
-        // the state of the passed in pkg object, so, move to the top of the method
-        // and allow it to abort
-        if ((scanFlags & SCAN_BOOTING) == 0
-                && (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
-            // Check all shared libraries and map to their actual file path.
-            // We only do this here for apps not on a system dir, because those
-            // are the only ones that can fail an install due to this.  We
-            // will take care of the system apps by updating all of their
-            // library paths after the scan is done. Also during the initial
-            // scan don't update any libs as we do this wholesale after all
-            // apps are scanned to avoid dependency based scanning.
-            updateSharedLibrariesLPr(pkg, null);
-        }
-
-        // All versions of a static shared library are referenced with the same
-        // package name. Internally, we use a synthetic package name to allow
-        // multiple versions of the same shared library to be installed. So,
-        // we need to generate the synthetic package name of the latest shared
-        // library in order to compare signatures.
-        PackageSetting signatureCheckPs = pkgSetting;
-        if (pkg.applicationInfo.isStaticSharedLibrary()) {
-            SharedLibraryInfo libraryInfo = getLatestSharedLibraVersionLPr(pkg);
-            if (libraryInfo != null) {
-                signatureCheckPs = mSettings.getPackageLPr(libraryInfo.getPackageName());
-            }
+        if (reconciledPkg.collectedSharedLibraryInfos != null) {
+            executeSharedLibrariesUpdateLPr(pkg, null, reconciledPkg.collectedSharedLibraryInfos);
         }
 
         final KeySetManagerService ksms = mSettings.mKeySetManagerService;
-        if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
-            if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {
-                // We just determined the app is signed correctly, so bring
-                // over the latest parsed certs.
-                pkgSetting.signatures.mSigningDetails = pkg.mSigningDetails;
-            } else {
-                if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
-                    throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
-                            "Package " + pkg.packageName + " upgrade keys do not match the "
-                                    + "previously installed version");
-                } else {
-                    pkgSetting.signatures.mSigningDetails = pkg.mSigningDetails;
-                    String msg = "System package " + pkg.packageName
-                            + " signature changed; retaining data.";
-                    reportSettingsProblem(Log.WARN, msg);
-                }
-            }
-        } else {
-            try {
-                final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg);
-                final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg);
-                final boolean compatMatch = verifySignatures(signatureCheckPs, disabledPkgSetting,
-                        pkg.mSigningDetails, compareCompat, compareRecover);
-                // The new KeySets will be re-added later in the scanning process.
-                if (compatMatch) {
-                    synchronized (mPackages) {
-                        ksms.removeAppKeySetDataLPw(pkg.packageName);
-                    }
-                }
-                // We just determined the app is signed correctly, so bring
-                // over the latest parsed certs.
-                pkgSetting.signatures.mSigningDetails = pkg.mSigningDetails;
-
-
-                // if this is is a sharedUser, check to see if the new package is signed by a newer
-                // signing certificate than the existing one, and if so, copy over the new details
-                if (signatureCheckPs.sharedUser != null) {
-                    if (pkg.mSigningDetails.hasAncestor(
-                                signatureCheckPs.sharedUser.signatures.mSigningDetails)) {
-                        signatureCheckPs.sharedUser.signatures.mSigningDetails = pkg.mSigningDetails;
-                    }
-                    if (signatureCheckPs.sharedUser.signaturesChanged == null) {
-                        signatureCheckPs.sharedUser.signaturesChanged = Boolean.FALSE;
-                    }
-                }
-            } catch (PackageManagerException e) {
-                if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
-                    throw e;
-                }
-                // The signature has changed, but this package is in the system
-                // image...  let's recover!
-                pkgSetting.signatures.mSigningDetails = pkg.mSigningDetails;
-
-                // If the system app is part of a shared user we allow that shared user to change
-                // signatures as well as part of an OTA. We still need to verify that the signatures
-                // are consistent within the shared user for a given boot, so only allow updating
-                // the signatures on the first package scanned for the shared user (i.e. if the
-                // signaturesChanged state hasn't been initialized yet in SharedUserSetting).
-                if (signatureCheckPs.sharedUser != null) {
-                    if (signatureCheckPs.sharedUser.signaturesChanged != null &&
-                        compareSignatures(
-                            signatureCheckPs.sharedUser.signatures.mSigningDetails.signatures,
-                            pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH) {
-                        if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 28) {
-                            // Mismatched signatures is an error and silently skipping system
-                            // packages will likely break the device in unforeseen ways. However,
-                            // we allow the device to boot anyway because, prior to P, vendors were
-                            // not expecting the platform to crash in this situation.
-                            throw new PackageManagerException(
-                                    INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
-                                    "Signature mismatch for shared user: " + pkgSetting.sharedUser);
-                        } else {
-                            // Treat mismatched signatures on system packages using a shared UID as
-                            // fatal for the system overall, rather than just failing to install
-                            // whichever package happened to be scanned later.
-                            throw new IllegalStateException("Signature mismatch on system package "
-                                + pkg.packageName + " for shared user " + pkgSetting.sharedUser);
-                        }
-                    }
-
-                    signatureCheckPs.sharedUser.signatures.mSigningDetails = pkg.mSigningDetails;
-                    signatureCheckPs.sharedUser.signaturesChanged = Boolean.TRUE;
-                }
-                // File a report about this.
-                String msg = "System package " + pkg.packageName
-                        + " signature changed; retaining data.";
-                reportSettingsProblem(Log.WARN, msg);
-            } catch (IllegalArgumentException e) {
-                // should never happen: certs matched when checking, but not when comparing
-                // old to new for sharedUser
-                throw new RuntimeException(
-                        "Signing certificates comparison made on incomparable signing details"
-                        + " but somehow passed verifySignatures!", e);
-            }
+        if (reconciledPkg.removeAppKeySetData) {
+            ksms.removeAppKeySetDataLPw(pkg.packageName);
         }
+        if (reconciledPkg.sharedUserSignaturesChanged) {
+            pkgSetting.sharedUser.signaturesChanged = Boolean.TRUE;
+            pkgSetting.sharedUser.signatures.mSigningDetails = reconciledPkg.signingDetails;
+        }
+        pkgSetting.signatures.mSigningDetails = reconciledPkg.signingDetails;
 
         if ((scanFlags & SCAN_CHECK_ONLY) == 0 && pkg.mAdoptPermissions != null) {
             // This package wants to adopt ownership of permissions from
@@ -10350,8 +10404,8 @@
         } else {
             final int userId = user == null ? 0 : user.getIdentifier();
             // Modify state for the given package setting
-            commitPackageSettings(pkg, oldPkg, pkgSetting, user, scanFlags,
-                    (parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/);
+            commitPackageSettings(pkg, oldPkg, pkgSetting, scanFlags,
+                    (parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg);
             if (pkgSetting.getInstantApp(userId)) {
                 mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId);
             }
@@ -10522,11 +10576,10 @@
                     UserManagerService.getInstance(), usesStaticLibraries,
                     pkg.usesStaticLibrariesVersions);
         } else {
-            if (!createNewPackage) {
-                // make a deep copy to avoid modifying any existing system state.
-                pkgSetting = new PackageSetting(pkgSetting);
-                pkgSetting.pkg = pkg;
-            }
+            // make a deep copy to avoid modifying any existing system state.
+            pkgSetting = new PackageSetting(pkgSetting);
+            pkgSetting.pkg = pkg;
+
             // REMOVE SharedUserSetting from method; update in a separate call.
             //
             // TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
@@ -10745,8 +10798,21 @@
             pkgSetting.volumeUuid = volumeUuid;
         }
 
+        SharedLibraryInfo staticSharedLibraryInfo = null;
+        if (!TextUtils.isEmpty(pkg.staticSharedLibName)) {
+            staticSharedLibraryInfo = SharedLibraryInfo.createForStatic(pkg);
+        }
+        List<SharedLibraryInfo> dynamicSharedLibraryInfos = null;
+        if (!ArrayUtils.isEmpty(pkg.libraryNames)) {
+            dynamicSharedLibraryInfos = new ArrayList<>(pkg.libraryNames.size());
+            for (String name : pkg.libraryNames) {
+                dynamicSharedLibraryInfos.add(SharedLibraryInfo.createForDynamic(pkg, name));
+            }
+        }
+
         return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
-                !createNewPackage /* existingSettingCopied */);
+                !createNewPackage /* existingSettingCopied */, staticSharedLibraryInfo,
+                dynamicSharedLibraryInfos);
     }
 
     /**
@@ -11256,24 +11322,48 @@
         }
     }
 
-    private boolean addSharedLibraryLPw(String path, String apk, List<String> codePaths,
-            String name, long version, int type, String declaringPackageName,
-            long declaringVersionCode) {
+    @GuardedBy("mPackages")
+    private boolean addBuiltInSharedLibraryLocked(String path, String name) {
+        if (nonStaticSharedLibExistsLocked(name)) {
+            return false;
+        }
+
+        SharedLibraryInfo libraryInfo = new SharedLibraryInfo(path, null, null, name,
+                (long) SharedLibraryInfo.VERSION_UNDEFINED, SharedLibraryInfo.TYPE_BUILTIN,
+                new VersionedPackage(PLATFORM_PACKAGE_NAME, (long) 0),
+                null, null);
+
+        commitSharedLibraryInfoLocked(libraryInfo);
+        return true;
+    }
+
+    @GuardedBy("mPackages")
+    private boolean nonStaticSharedLibExistsLocked(String name) {
+        return sharedLibExists(name, SharedLibraryInfo.VERSION_UNDEFINED, mSharedLibraries);
+    }
+
+    private static boolean sharedLibExists(final String name, final long version,
+            Map<String, LongSparseArray<SharedLibraryInfo>> librarySource) {
+        LongSparseArray<SharedLibraryInfo> versionedLib = librarySource.get(name);
+        if (versionedLib != null && versionedLib.indexOfKey(version) >= 0) {
+            return true;
+        }
+        return false;
+    }
+
+    @GuardedBy("mPackages")
+    private void commitSharedLibraryInfoLocked(SharedLibraryInfo libraryInfo) {
+        final String name = libraryInfo.getName();
         LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name);
         if (versionedLib == null) {
             versionedLib = new LongSparseArray<>();
             mSharedLibraries.put(name, versionedLib);
-            if (type == SharedLibraryInfo.TYPE_STATIC) {
-                mStaticLibsByDeclaringPackage.put(declaringPackageName, versionedLib);
-            }
-        } else if (versionedLib.indexOfKey(version) >= 0) {
-            return false;
         }
-        SharedLibraryInfo libraryInfo = new SharedLibraryInfo(path, apk, codePaths, name,
-                version, type, new VersionedPackage(declaringPackageName, declaringVersionCode),
-                null, null);
-        versionedLib.put(version, libraryInfo);
-        return true;
+        final String declaringPackageName = libraryInfo.getDeclaringPackage().getPackageName();
+        if (libraryInfo.getType() == SharedLibraryInfo.TYPE_STATIC) {
+            mStaticLibsByDeclaringPackage.put(declaringPackageName, versionedLib);
+        }
+        versionedLib.put(libraryInfo.getLongVersion(), libraryInfo);
     }
 
     private boolean removeSharedLibraryLPw(String name, long version) {
@@ -11302,8 +11392,8 @@
      * be available for query, resolution, etc...
      */
     private void commitPackageSettings(PackageParser.Package pkg,
-            @Nullable PackageParser.Package oldPkg, PackageSetting pkgSetting, UserHandle user,
-            final @ScanFlags int scanFlags, boolean chatty) {
+            @Nullable PackageParser.Package oldPkg, PackageSetting pkgSetting,
+            final @ScanFlags int scanFlags, boolean chatty, ReconciledPackage reconciledPkg) {
         final String pkgName = pkg.packageName;
         if (mCustomResolverComponentName != null &&
                 mCustomResolverComponentName.getPackageName().equals(pkg.packageName)) {
@@ -11350,90 +11440,23 @@
         ArrayList<PackageParser.Package> clientLibPkgs = null;
         // writer
         synchronized (mPackages) {
-            boolean hasStaticSharedLibs = false;
-
-            // Any app can add new static shared libraries
-            if (pkg.staticSharedLibName != null) {
-                // Static shared libs don't allow renaming as they have synthetic package
-                // names to allow install of multiple versions, so use name from manifest.
-                if (addSharedLibraryLPw(null, pkg.packageName, pkg.getAllCodePaths(),
-                        pkg.staticSharedLibName,
-                        pkg.staticSharedLibVersion, SharedLibraryInfo.TYPE_STATIC,
-                        pkg.manifestPackageName, pkg.getLongVersionCode())) {
-                    hasStaticSharedLibs = true;
-                    // Shared libraries for the package need to be updated.
-                    try {
-                        updateSharedLibrariesLPr(pkg, null);
-                    } catch (PackageManagerException e) {
-                        Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e);
-                    }
-                } else {
-                    Slog.w(TAG, "Package " + pkg.packageName + " library "
-                                + pkg.staticSharedLibName + " already exists; skipping");
+            if (!ArrayUtils.isEmpty(reconciledPkg.allowedSharedLibraryInfos)) {
+                for (SharedLibraryInfo info : reconciledPkg.allowedSharedLibraryInfos) {
+                    commitSharedLibraryInfoLocked(info);
                 }
-                // Static shared libs cannot be updated once installed since they
-                // use synthetic package name which includes the version code, so
-                // not need to update other packages's shared lib dependencies.
+                try {
+                    // Shared libraries for the package need to be updated.
+                    updateSharedLibrariesLPr(pkg, null);
+                } catch (PackageManagerException e) {
+                    Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e);
+                }
             }
 
-            if (!hasStaticSharedLibs
-                    && (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-                // Only system apps can add new dynamic shared libraries.
-                if (pkg.libraryNames != null) {
-                    for (int i = 0; i < pkg.libraryNames.size(); i++) {
-                        String name = pkg.libraryNames.get(i);
-                        boolean allowed = false;
-                        if (pkg.isUpdatedSystemApp()) {
-                            // New library entries can only be added through the
-                            // system image.  This is important to get rid of a lot
-                            // of nasty edge cases: for example if we allowed a non-
-                            // system update of the app to add a library, then uninstalling
-                            // the update would make the library go away, and assumptions
-                            // we made such as through app install filtering would now
-                            // have allowed apps on the device which aren't compatible
-                            // with it.  Better to just have the restriction here, be
-                            // conservative, and create many fewer cases that can negatively
-                            // impact the user experience.
-                            final PackageSetting sysPs = mSettings
-                                    .getDisabledSystemPkgLPr(pkg.packageName);
-                            if (sysPs.pkg != null && sysPs.pkg.libraryNames != null) {
-                                for (int j = 0; j < sysPs.pkg.libraryNames.size(); j++) {
-                                    if (name.equals(sysPs.pkg.libraryNames.get(j))) {
-                                        allowed = true;
-                                        break;
-                                    }
-                                }
-                            }
-                        } else {
-                            allowed = true;
-                        }
-                        if (allowed) {
-                            if (!addSharedLibraryLPw(null, pkg.packageName, pkg.getAllCodePaths(),
-                                    name, SharedLibraryInfo.VERSION_UNDEFINED,
-                                    SharedLibraryInfo.TYPE_DYNAMIC,
-                                    pkg.packageName, pkg.getLongVersionCode())) {
-                                Slog.w(TAG, "Package " + pkg.packageName + " library "
-                                        + name + " already exists; skipping");
-                            }
-                            // Shared libraries for the package need to be updated.
-                            try {
-                                updateSharedLibrariesLPr(pkg, null);
-                            } catch (PackageManagerException e) {
-                                Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e);
-                            }
-                        } else {
-                            Slog.w(TAG, "Package " + pkg.packageName + " declares lib "
-                                    + name + " that is not declared on system image; skipping");
-                        }
-                    }
-
-                    if ((scanFlags & SCAN_BOOTING) == 0) {
-                        // If we are not booting, we need to update any applications
-                        // that are clients of our shared library.  If we are booting,
-                        // this will all be done once the scan is complete.
-                        clientLibPkgs = updateAllSharedLibrariesLPw(pkg);
-                    }
-                }
+            if (reconciledPkg.hasDynamicSharedLibraries() && (scanFlags & SCAN_BOOTING) == 0) {
+                // If we are not booting, we need to update any applications
+                // that are clients of our shared library.  If we are booting,
+                // this will all be done once the scan is complete.
+                clientLibPkgs = updateAllSharedLibrariesLPw(pkg);
             }
         }
 
@@ -13003,20 +13026,26 @@
     }
 
     @Override
-    public boolean canSuspendPackageForUser(String packageName, int userId) {
+    public String[] getUnsuspendablePackagesForUser(String[] packageNames, int userId) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
-                "canSuspendPackageForUser");
+                "getUnsuspendablePackagesForUser");
         final int callingUid = Binder.getCallingUid();
         if (UserHandle.getUserId(callingUid) != userId) {
             throw new SecurityException("Calling uid " + callingUid
-                    + " cannot query canSuspendPackageForUser for user " + userId);
+                    + " cannot query getUnsuspendablePackagesForUser for user " + userId);
         }
+        final ArraySet<String> unactionablePackages = new ArraySet<>();
         final long identity = Binder.clearCallingIdentity();
         try {
-            return canSuspendPackageForUserInternal(packageName, userId);
+            for (String packageName : packageNames) {
+                if (!canSuspendPackageForUserInternal(packageName, userId)) {
+                    unactionablePackages.add(packageName);
+                }
+            }
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
+        return unactionablePackages.toArray(new String[unactionablePackages.size()]);
     }
 
     private boolean canSuspendPackageForUserInternal(String packageName, int userId) {
@@ -13261,6 +13290,13 @@
         }
     }
 
+    private void setEnableRollbackCode(int token, int enableRollbackCode) {
+        final Message msg = mHandler.obtainMessage(ENABLE_ROLLBACK_STATUS);
+        msg.arg1 = token;
+        msg.arg2 = enableRollbackCode;
+        mHandler.sendMessage(msg);
+    }
+
     @Override
     public void finishPackageInstall(int token, boolean didLaunch) {
         enforceSystemOrRoot("Only the system is allowed to finish installs");
@@ -13912,7 +13948,7 @@
         @NonNull
         private final ArrayList<InstallParams> mChildParams;
         @NonNull
-        private final Map<InstallArgs, Integer> mVerifiedState;
+        private final Map<InstallArgs, Integer> mCurrentState;
 
         MultiPackageInstallParams(
                 @NonNull UserHandle user,
@@ -13928,7 +13964,7 @@
                 childParams.mParentInstallParams = this;
                 this.mChildParams.add(childParams);
             }
-            this.mVerifiedState = new ArrayMap<>(mChildParams.size());
+            this.mCurrentState = new ArrayMap<>(mChildParams.size());
         }
 
         @Override
@@ -13954,12 +13990,12 @@
         }
 
         void tryProcessInstallRequest(InstallArgs args, int currentStatus) {
-            mVerifiedState.put(args, currentStatus);
+            mCurrentState.put(args, currentStatus);
             boolean success = true;
-            if (mVerifiedState.size() != mChildParams.size()) {
+            if (mCurrentState.size() != mChildParams.size()) {
                 return;
             }
-            for (Integer status : mVerifiedState.values()) {
+            for (Integer status : mCurrentState.values()) {
                 if (status == PackageManager.INSTALL_UNKNOWN) {
                     return;
                 } else if (status != PackageManager.INSTALL_SUCCEEDED) {
@@ -13967,8 +14003,8 @@
                     break;
                 }
             }
-            final List<InstallRequest> installRequests = new ArrayList<>(mVerifiedState.size());
-            for (Map.Entry<InstallArgs, Integer> entry : mVerifiedState.entrySet()) {
+            final List<InstallRequest> installRequests = new ArrayList<>(mCurrentState.size());
+            for (Map.Entry<InstallArgs, Integer> entry : mCurrentState.entrySet()) {
                 installRequests.add(new InstallRequest(entry.getKey(),
                         createPackageInstalledInfo(entry.getValue())));
             }
@@ -13985,6 +14021,8 @@
         int installFlags;
         final String installerPackageName;
         final String volumeUuid;
+        private boolean mVerificationCompleted;
+        private boolean mEnableRollbackCompleted;
         private InstallArgs mArgs;
         int mRet;
         final String packageAbiOverride;
@@ -14234,6 +14272,8 @@
             }
 
             final InstallArgs args = createInstallArgs(this);
+            mVerificationCompleted = true;
+            mEnableRollbackCompleted = true;
             mArgs = args;
 
             if (ret == PackageManager.INSTALL_SUCCEEDED) {
@@ -14313,7 +14353,7 @@
                     }
 
                     final PackageVerificationState verificationState = new PackageVerificationState(
-                            requiredUid, args);
+                            requiredUid, this);
 
                     mPendingVerification.append(verificationId, verificationState);
 
@@ -14375,25 +14415,80 @@
 
                         /*
                          * We don't want the copy to proceed until verification
-                         * succeeds, so null out this field.
+                         * succeeds.
                          */
-                        mArgs = null;
+                        mVerificationCompleted = false;
                     }
-                } else {
-                    /*
-                     * No package verification is enabled, so immediately start
-                     * the remote call to initiate copy using temporary file.
-                     */
-                    ret = args.copyApk();
+                }
+
+                if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
+                    // TODO(ruhler) b/112431924: Don't do this in case of 'move'?
+                    final int enableRollbackToken = mPendingEnableRollbackToken++;
+                    Trace.asyncTraceBegin(
+                            TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken);
+                    mPendingEnableRollback.append(enableRollbackToken, this);
+
+                    // TODO(ruhler) b/112431924: What user? Test for multi-user.
+                    Intent enableRollbackIntent = new Intent(Intent.ACTION_PACKAGE_ENABLE_ROLLBACK);
+                    enableRollbackIntent.putExtra(
+                            PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN,
+                            enableRollbackToken);
+                    enableRollbackIntent.putExtra(
+                            PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALL_FLAGS,
+                            installFlags);
+                    enableRollbackIntent.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),
+                            PACKAGE_MIME_TYPE);
+                    enableRollbackIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+                    mContext.sendOrderedBroadcastAsUser(enableRollbackIntent, getUser(),
+                            android.Manifest.permission.PACKAGE_ROLLBACK_AGENT,
+                            new BroadcastReceiver() {
+                                @Override
+                                public void onReceive(Context context, Intent intent) {
+                                    // TODO(ruhler) b/112431924 Have a configurable setting to
+                                    // allow changing the timeout and fall back to the default
+                                    // if no such specified.
+                                    final Message msg = mHandler.obtainMessage(
+                                            ENABLE_ROLLBACK_TIMEOUT);
+                                    msg.arg1 = enableRollbackToken;
+                                    mHandler.sendMessageDelayed(msg,
+                                            DEFAULT_ENABLE_ROLLBACK_TIMEOUT);
+                                }
+                            }, null, 0, null, null);
+
+                    mEnableRollbackCompleted = false;
                 }
             }
 
             mRet = ret;
         }
 
+        void setReturnCode(int ret) {
+            if (mRet == PackageManager.INSTALL_SUCCEEDED) {
+                // Only update mRet if it was previously INSTALL_SUCCEEDED to
+                // ensure we do not overwrite any previous failure results.
+                mRet = ret;
+            }
+        }
+
+        void handleVerificationFinished() {
+            mVerificationCompleted = true;
+            handleReturnCode();
+        }
+
+        void handleRollbackEnabled() {
+            // TODO(ruhler) b/112431924: Consider halting the install if we
+            // couldn't enable rollback.
+            mEnableRollbackCompleted = true;
+            handleReturnCode();
+        }
+
         @Override
         void handleReturnCode() {
-            if (mArgs != null) {
+            if (mVerificationCompleted && mEnableRollbackCompleted) {
+                if (mRet == PackageManager.INSTALL_SUCCEEDED) {
+                    mRet = mArgs.copyApk();
+                }
                 processPendingInstall(mArgs, mRet);
             }
         }
@@ -15189,20 +15284,40 @@
     private static class ReconcileRequest {
         public final Map<String, ScanResult> scannedPackages;
 
-        // TODO: Remove install-specific details from reconcile request; make them generic types
-        //       that can be used for scanDir for example.
+        public final Map<String, PackageParser.Package> allPackages;
+        public final Map<String, LongSparseArray<SharedLibraryInfo>> sharedLibrarySource;
         public final Map<String, InstallArgs> installArgs;
         public final Map<String, PackageInstalledInfo> installResults;
         public final Map<String, PrepareResult> preparedPackages;
+        public final Map<String, VersionInfo> versionInfos;
+        public final Map<String, PackageSetting> lastStaticSharedLibSettings;
 
         private ReconcileRequest(Map<String, ScanResult> scannedPackages,
                 Map<String, InstallArgs> installArgs,
                 Map<String, PackageInstalledInfo> installResults,
-                Map<String, PrepareResult> preparedPackages) {
+                Map<String, PrepareResult> preparedPackages,
+                Map<String, LongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
+                Map<String, PackageParser.Package> allPackages,
+                Map<String, VersionInfo> versionInfos,
+                Map<String, PackageSetting> lastStaticSharedLibSettings) {
             this.scannedPackages = scannedPackages;
             this.installArgs = installArgs;
             this.installResults = installResults;
             this.preparedPackages = preparedPackages;
+            this.sharedLibrarySource = sharedLibrarySource;
+            this.allPackages = allPackages;
+            this.versionInfos = versionInfos;
+            this.lastStaticSharedLibSettings = lastStaticSharedLibSettings;
+        }
+
+        private ReconcileRequest(Map<String, ScanResult> scannedPackages,
+                Map<String, LongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
+                Map<String, PackageParser.Package> allPackages,
+                Map<String, VersionInfo> versionInfos,
+                Map<String, PackageSetting> lastStaticSharedLibSettings) {
+            this(scannedPackages, Collections.emptyMap(), Collections.emptyMap(),
+                    Collections.emptyMap(), sharedLibrarySource, allPackages, versionInfos,
+                    lastStaticSharedLibSettings);
         }
     }
     private static class ReconcileFailure extends PackageManagerException {
@@ -15212,6 +15327,9 @@
         ReconcileFailure(int reason, String message) {
             super(reason, "Reconcile failed: " + message);
         }
+        ReconcileFailure(PackageManagerException e) {
+            this(e.error, e.getMessage());
+        }
     }
 
     /**
@@ -15221,55 +15339,97 @@
     private static class ReconciledPackage {
         public final PackageSetting pkgSetting;
         public final ScanResult scanResult;
-        public final UserHandle installForUser;
-        public final String volumeUuid;
         // TODO: Remove install-specific details from the reconcile result
         public final PackageInstalledInfo installResult;
-        public final PrepareResult prepareResult;
-        @PackageManager.InstallFlags
-        public final int installFlags;
-        public final InstallArgs installArgs;
+        @Nullable public final PrepareResult prepareResult;
+        @Nullable public final InstallArgs installArgs;
         public final DeletePackageAction deletePackageAction;
+        public final List<SharedLibraryInfo> allowedSharedLibraryInfos;
+        public final SigningDetails signingDetails;
+        public final boolean sharedUserSignaturesChanged;
+        public ArrayList<SharedLibraryInfo> collectedSharedLibraryInfos;
+        public final boolean removeAppKeySetData;
 
         private ReconciledPackage(InstallArgs installArgs, PackageSetting pkgSetting,
-                UserHandle installForUser, PackageInstalledInfo installResult, int installFlags,
-                String volumeUuid, PrepareResult prepareResult, ScanResult scanResult,
-                DeletePackageAction deletePackageAction) {
+                PackageInstalledInfo installResult,
+                PrepareResult prepareResult, ScanResult scanResult,
+                DeletePackageAction deletePackageAction,
+                List<SharedLibraryInfo> allowedSharedLibraryInfos,
+                SigningDetails signingDetails,
+                boolean sharedUserSignaturesChanged,
+                boolean removeAppKeySetData) {
             this.installArgs = installArgs;
             this.pkgSetting = pkgSetting;
-            this.installForUser = installForUser;
             this.installResult = installResult;
-            this.installFlags = installFlags;
-            this.volumeUuid = volumeUuid;
             this.prepareResult = prepareResult;
             this.scanResult = scanResult;
             this.deletePackageAction = deletePackageAction;
+            this.allowedSharedLibraryInfos = allowedSharedLibraryInfos;
+            this.signingDetails = signingDetails;
+            this.sharedUserSignaturesChanged = sharedUserSignaturesChanged;
+            this.removeAppKeySetData = removeAppKeySetData;
+        }
+
+        public boolean hasDynamicSharedLibraries() {
+            return !ArrayUtils.isEmpty(allowedSharedLibraryInfos)
+                    && allowedSharedLibraryInfos.get(0).getType() != SharedLibraryInfo.TYPE_STATIC;
         }
     }
 
     @GuardedBy("mPackages")
     private static Map<String, ReconciledPackage> reconcilePackagesLocked(
-            final ReconcileRequest request)
+            final ReconcileRequest request, KeySetManagerService ksms)
             throws ReconcileFailure {
-        Map<String, ReconciledPackage> result = new ArrayMap<>(request.scannedPackages.size());
-        for (String installPackageName : request.installArgs.keySet()) {
-            final ScanResult scanResult = request.scannedPackages.get(installPackageName);
+        final Map<String, ScanResult> scannedPackages = request.scannedPackages;
+
+        final Map<String, ReconciledPackage> result = new ArrayMap<>(scannedPackages.size());
+
+        // make a copy of the existing set of packages so we can combine them with incoming packages
+        final ArrayMap<String, PackageParser.Package> combinedPackages =
+                new ArrayMap<>(request.allPackages.size() + scannedPackages.size());
+        combinedPackages.putAll(request.allPackages);
+
+        final Map<String, LongSparseArray<SharedLibraryInfo>> incomingSharedLibraries =
+                new ArrayMap<>();
+
+        for (String installPackageName : scannedPackages.keySet()) {
+            final ScanResult scanResult = scannedPackages.get(installPackageName);
+
+            // add / replace existing with incoming packages
+            combinedPackages.put(scanResult.pkgSetting.name, scanResult.request.pkg);
+
+            // in the first pass, we'll build up the set of incoming shared libraries
+            final List<SharedLibraryInfo> allowedSharedLibInfos =
+                    getAllowedSharedLibInfos(scanResult, request.sharedLibrarySource);
+            final SharedLibraryInfo staticLib = scanResult.staticSharedLibraryInfo;
+            if (allowedSharedLibInfos != null) {
+                for (SharedLibraryInfo info : allowedSharedLibInfos) {
+                    if (!addSharedLibraryToPackageVersionMap(incomingSharedLibraries, info)) {
+                        throw new ReconcileFailure("Static Shared Library " + staticLib.getName()
+                                + " is being installed twice in this set!");
+                    }
+                }
+            }
+
+            // the following may be null if we're just reconciling on boot (and not during install)
             final InstallArgs installArgs = request.installArgs.get(installPackageName);
             final PackageInstalledInfo res = request.installResults.get(installPackageName);
             final PrepareResult prepareResult = request.preparedPackages.get(installPackageName);
-            if (scanResult == null || installArgs == null || res == null) {
-                throw new ReconcileFailure(
-                        "inputs not balanced; missing argument for " + installPackageName);
+            final boolean isInstall = installArgs != null;
+            if (isInstall && (res == null || prepareResult == null)) {
+                throw new ReconcileFailure("Reconcile arguments are not balanced for "
+                        + installPackageName + "!");
             }
+
             final DeletePackageAction deletePackageAction;
             // we only want to try to delete for non system apps
-            if (prepareResult.replace && !prepareResult.system) {
+            if (isInstall && prepareResult.replace && !prepareResult.system) {
                 final boolean killApp = (scanResult.request.scanFlags & SCAN_DONT_KILL_APP) == 0;
                 final int deleteFlags = PackageManager.DELETE_KEEP_DATA
                         | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
                 deletePackageAction = mayDeletePackageLocked(res.removedInfo,
                         prepareResult.originalPs, prepareResult.disabledPs,
-                        prepareResult.childPackageSettings, deleteFlags, installArgs.user);
+                        prepareResult.childPackageSettings, deleteFlags, null /* all users */);
                 if (deletePackageAction == null) {
                     throw new ReconcileFailure(
                             PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE,
@@ -15278,17 +15438,261 @@
             } else {
                 deletePackageAction = null;
             }
+
+            final int scanFlags = scanResult.request.scanFlags;
+            final int parseFlags = scanResult.request.parseFlags;
+            final PackageParser.Package pkg = scanResult.request.pkg;
+
+            final PackageSetting disabledPkgSetting = scanResult.request.disabledPkgSetting;
+            final PackageSetting lastStaticSharedLibSetting =
+                    request.lastStaticSharedLibSettings.get(installPackageName);
+            final PackageSetting signatureCheckPs =
+                    (prepareResult != null && lastStaticSharedLibSetting != null)
+                            ? lastStaticSharedLibSetting
+                            : scanResult.pkgSetting;
+            boolean removeAppKeySetData = false;
+            boolean sharedUserSignaturesChanged = false;
+            SigningDetails signingDetails = null;
+            if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
+                if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {
+                    // We just determined the app is signed correctly, so bring
+                    // over the latest parsed certs.
+                } else {
+                    if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+                        throw new ReconcileFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+                                "Package " + pkg.packageName + " upgrade keys do not match the "
+                                        + "previously installed version");
+                    } else {
+                        String msg = "System package " + pkg.packageName
+                                + " signature changed; retaining data.";
+                        reportSettingsProblem(Log.WARN, msg);
+                    }
+                }
+                signingDetails = pkg.mSigningDetails;
+            } else {
+                try {
+                    final VersionInfo versionInfo = request.versionInfos.get(installPackageName);
+                    final boolean compareCompat = isCompatSignatureUpdateNeeded(versionInfo);
+                    final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo);
+                    final boolean compatMatch = verifySignatures(signatureCheckPs,
+                            disabledPkgSetting, pkg.mSigningDetails, compareCompat, compareRecover);
+                    // The new KeySets will be re-added later in the scanning process.
+                    if (compatMatch) {
+                        removeAppKeySetData = true;
+                    }
+                    // We just determined the app is signed correctly, so bring
+                    // over the latest parsed certs.
+                    signingDetails = pkg.mSigningDetails;
+
+
+                    // if this is is a sharedUser, check to see if the new package is signed by a
+                    // newer
+                    // signing certificate than the existing one, and if so, copy over the new
+                    // details
+                    if (signatureCheckPs.sharedUser != null) {
+                        if (pkg.mSigningDetails.hasAncestor(
+                                signatureCheckPs.sharedUser.signatures.mSigningDetails)) {
+                            signatureCheckPs.sharedUser.signatures.mSigningDetails =
+                                    pkg.mSigningDetails;
+                        }
+                        if (signatureCheckPs.sharedUser.signaturesChanged == null) {
+                            signatureCheckPs.sharedUser.signaturesChanged = Boolean.FALSE;
+                        }
+                    }
+                } catch (PackageManagerException e) {
+                    if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+                        throw new ReconcileFailure(e);
+                    }
+                    signingDetails = pkg.mSigningDetails;
+
+                    // If the system app is part of a shared user we allow that shared user to
+                    // change
+                    // signatures as well as part of an OTA. We still need to verify that the
+                    // signatures
+                    // are consistent within the shared user for a given boot, so only allow
+                    // updating
+                    // the signatures on the first package scanned for the shared user (i.e. if the
+                    // signaturesChanged state hasn't been initialized yet in SharedUserSetting).
+                    if (signatureCheckPs.sharedUser != null) {
+                        final Signature[] sharedUserSignatures =
+                                signatureCheckPs.sharedUser.signatures.mSigningDetails.signatures;
+                        if (signatureCheckPs.sharedUser.signaturesChanged != null
+                                && compareSignatures(sharedUserSignatures,
+                                        pkg.mSigningDetails.signatures)
+                                        != PackageManager.SIGNATURE_MATCH) {
+                            if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 28) {
+                                // Mismatched signatures is an error and silently skipping system
+                                // packages will likely break the device in unforeseen ways.
+                                // However,
+                                // we allow the device to boot anyway because, prior to P,
+                                // vendors were
+                                // not expecting the platform to crash in this situation.
+                                throw new ReconcileFailure(
+                                        INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+                                        "Signature mismatch for shared user: "
+                                                + scanResult.pkgSetting.sharedUser);
+                            } else {
+                                // Treat mismatched signatures on system packages using a shared
+                                // UID as
+                                // fatal for the system overall, rather than just failing to install
+                                // whichever package happened to be scanned later.
+                                throw new IllegalStateException(
+                                        "Signature mismatch on system package "
+                                                + pkg.packageName + " for shared user "
+                                                + scanResult.pkgSetting.sharedUser);
+                            }
+                        }
+
+                        sharedUserSignaturesChanged = true;
+                        signatureCheckPs.sharedUser.signatures.mSigningDetails =
+                                pkg.mSigningDetails;
+                        signatureCheckPs.sharedUser.signaturesChanged = Boolean.TRUE;
+                    }
+                    // File a report about this.
+                    String msg = "System package " + pkg.packageName
+                            + " signature changed; retaining data.";
+                    reportSettingsProblem(Log.WARN, msg);
+                } catch (IllegalArgumentException e) {
+                    // should never happen: certs matched when checking, but not when comparing
+                    // old to new for sharedUser
+                    throw new RuntimeException(
+                            "Signing certificates comparison made on incomparable signing details"
+                                    + " but somehow passed verifySignatures!", e);
+                }
+            }
+
             result.put(installPackageName,
-                    new ReconciledPackage(installArgs, scanResult.pkgSetting, installArgs.getUser(),
-                            res, installArgs.installFlags, installArgs.volumeUuid,
-                            request.preparedPackages.get(installPackageName), scanResult,
-                            deletePackageAction));
+                    new ReconciledPackage(installArgs, scanResult.pkgSetting,
+                            res, request.preparedPackages.get(installPackageName), scanResult,
+                            deletePackageAction, allowedSharedLibInfos, signingDetails,
+                            sharedUserSignaturesChanged, removeAppKeySetData));
         }
+
+        for (String installPackageName : scannedPackages.keySet()) {
+            // Check all shared libraries and map to their actual file path.
+            // We only do this here for apps not on a system dir, because those
+            // are the only ones that can fail an install due to this.  We
+            // will take care of the system apps by updating all of their
+            // library paths after the scan is done. Also during the initial
+            // scan don't update any libs as we do this wholesale after all
+            // apps are scanned to avoid dependency based scanning.
+            final ScanResult scanResult = scannedPackages.get(installPackageName);
+            if ((scanResult.request.scanFlags & SCAN_BOOTING) != 0
+                    || (scanResult.request.parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
+                continue;
+            }
+            try {
+                result.get(installPackageName).collectedSharedLibraryInfos =
+                        collectSharedLibraryInfos(scanResult.request.pkg, combinedPackages,
+                                request.sharedLibrarySource, incomingSharedLibraries);
+
+            } catch (PackageManagerException e) {
+                throw new ReconcileFailure(e.error, e.getMessage());
+            }
+        }
+
         return result;
     }
 
+    /**
+     * Compare the newly scanned package with current system state to see which of its declared
+     * shared libraries should be allowed to be added to the system.
+     */
+    private static List<SharedLibraryInfo> getAllowedSharedLibInfos(
+            ScanResult scanResult,
+            Map<String, LongSparseArray<SharedLibraryInfo>> existingSharedLibraries) {
+        // Let's used the parsed package as scanResult.pkgSetting may be null
+        final PackageParser.Package pkg = scanResult.request.pkg;
+        if (scanResult.staticSharedLibraryInfo == null
+                && scanResult.dynamicSharedLibraryInfos == null) {
+            return null;
+        }
+
+        // Any app can add new static shared libraries
+        if (scanResult.staticSharedLibraryInfo != null) {
+            return Collections.singletonList(scanResult.staticSharedLibraryInfo);
+        }
+        final boolean hasDynamicLibraries =
+                (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
+                        && scanResult.dynamicSharedLibraryInfos != null;
+        if (!hasDynamicLibraries) {
+            return null;
+        }
+        final ArrayList<SharedLibraryInfo> infos =
+                new ArrayList<>(scanResult.dynamicSharedLibraryInfos.size());
+        final boolean updatedSystemApp = pkg.isUpdatedSystemApp();
+        for (SharedLibraryInfo info : scanResult.dynamicSharedLibraryInfos) {
+            String name = info.getName();
+            boolean allowed = false;
+            if (updatedSystemApp) {
+                // New library entries can only be added through the
+                // system image.  This is important to get rid of a lot
+                // of nasty edge cases: for example if we allowed a non-
+                // system update of the app to add a library, then uninstalling
+                // the update would make the library go away, and assumptions
+                // we made such as through app install filtering would now
+                // have allowed apps on the device which aren't compatible
+                // with it.  Better to just have the restriction here, be
+                // conservative, and create many fewer cases that can negatively
+                // impact the user experience. We may not yet have disabled the
+                // updated package yet, so be sure to grab the current setting if
+                // that's the case.
+                final PackageSetting sysPs = scanResult.request.disabledPkgSetting == null
+                        ? scanResult.request.oldPkgSetting
+                        : scanResult.request.disabledPkgSetting;
+                if (sysPs.pkg != null && sysPs.pkg.libraryNames != null) {
+                    for (int j = 0; j < sysPs.pkg.libraryNames.size(); j++) {
+                        if (name.equals(sysPs.pkg.libraryNames.get(j))) {
+                            allowed = true;
+                            break;
+                        }
+                    }
+                }
+            } else {
+                allowed = true;
+            }
+            if (allowed) {
+                if (sharedLibExists(
+                        name, SharedLibraryInfo.VERSION_UNDEFINED, existingSharedLibraries)) {
+                    Slog.w(TAG, "Package " + pkg.packageName + " library "
+                            + name + " already exists; skipping");
+                    continue;
+                }
+                infos.add(info);
+            } else {
+                Slog.w(TAG, "Package " + pkg.packageName + " declares lib "
+                        + name + " that is not declared on system image; skipping");
+                continue;
+            }
+        }
+        return infos;
+    }
+
+    /**
+     * Returns false if the adding shared library already exists in the map and so could not be
+     * added.
+     */
+    private static boolean addSharedLibraryToPackageVersionMap(
+            Map<String, LongSparseArray<SharedLibraryInfo>> target,
+            SharedLibraryInfo library) {
+        final String name = library.getName();
+        if (target.containsKey(name)) {
+            if (library.getType() != SharedLibraryInfo.TYPE_STATIC) {
+                // We've already added this non-version-specific library to the map.
+                return false;
+            } else if (target.get(name).indexOfKey(library.getLongVersion()) >= 0) {
+                // We've already added this version of a version-specific library to the map.
+                return false;
+            }
+        } else {
+            target.put(name, new LongSparseArray<>());
+        }
+        target.get(name).put(library.getLongVersion(), library);
+        return true;
+    }
+
     @GuardedBy("mPackages")
-    private boolean commitPackagesLocked(final CommitRequest request) {
+    private void commitPackagesLocked(final CommitRequest request) {
         // TODO: remove any expected failures from this method; this should only be able to fail due
         //       to unavoidable errors (I/O, etc.)
         for (ReconciledPackage reconciledPkg : request.reconciledPackages.values()) {
@@ -15424,19 +15828,9 @@
                 }
             }
 
-
-
-            try {
-                prepareScanResultLocked(scanResult);
-                commitScanResultLocked(scanResult);
-            } catch (PackageManagerException e) {
-                unprepareScanResultLocked(scanResult);
-                res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR);
-                res.setError("Package couldn't be installed in " + pkg.codePath, e);
-                return false;
-            }
+            commitReconciledScanResultLocked(reconciledPkg);
             updateSettingsLI(pkg, reconciledPkg.installArgs.installerPackageName, request.mAllUsers,
-                    res, reconciledPkg.installForUser, reconciledPkg.installArgs.installReason);
+                    res, reconciledPkg.installArgs.user, reconciledPkg.installArgs.installReason);
 
             final PackageSetting ps = mSettings.mPackages.get(packageName);
             if (ps != null) {
@@ -15459,7 +15853,6 @@
                 updateInstantAppInstallerLocked(packageName);
             }
         }
-        return true;
     }
 
     /**
@@ -15481,12 +15874,16 @@
      *
      * Failure at any phase will result in a full failure to install all packages.
      */
-    @GuardedBy({"mInstallLock", "mPackages"})
+    @GuardedBy("mInstallLock")
     private void installPackagesLI(List<InstallRequest> requests) {
-        final Map<String, ScanResult> scans = new ArrayMap<>(requests.size());
+        final Map<String, ScanResult> preparedScans = new ArrayMap<>(requests.size());
         final Map<String, InstallArgs> installArgs = new ArrayMap<>(requests.size());
         final Map<String, PackageInstalledInfo> installResults = new ArrayMap<>(requests.size());
         final Map<String, PrepareResult> prepareResults = new ArrayMap<>(requests.size());
+        final Map<String, VersionInfo> versionInfos = new ArrayMap<>(requests.size());
+        final Map<String, PackageSetting> lastStaticSharedLibSettings =
+                new ArrayMap<>(requests.size());
+        boolean success = false;
         try {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
             for (InstallRequest request : requests) {
@@ -15518,33 +15915,47 @@
                             prepareResult.scanFlags, System.currentTimeMillis(),
                             request.args.user);
                     for (ScanResult result : scanResults) {
-                        if (null != scans.put(result.pkgSetting.pkg.packageName, result)) {
+                        if (null != preparedScans.put(result.pkgSetting.pkg.packageName, result)) {
                             request.installResult.setError(
                                     PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE,
                                     "Duplicate package " + result.pkgSetting.pkg.packageName
                                             + " in multi-package install request.");
                             return;
                         }
+                        prepareScanResultLocked(result);
+                        versionInfos.put(result.pkgSetting.pkg.packageName,
+                                getSettingsVersionForPackage(result.pkgSetting.pkg));
+                        if (result.staticSharedLibraryInfo != null) {
+                            final PackageSetting sharedLibLatestVersionSetting =
+                                    getSharedLibLatestVersionSetting(result);
+                            if (sharedLibLatestVersionSetting != null) {
+                                lastStaticSharedLibSettings.put(result.pkgSetting.pkg.packageName,
+                                        sharedLibLatestVersionSetting);
+                            }
+                        }
+                        prepareScanResultLocked(result);
                     }
                 } catch (PackageManagerException e) {
                     request.installResult.setError("Scanning Failed.", e);
                     return;
                 }
             }
-            ReconcileRequest reconcileRequest = new ReconcileRequest(scans, installArgs,
+            ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
                     installResults,
-                    prepareResults);
+                    prepareResults,
+                    mSharedLibraries,
+                    Collections.unmodifiableMap(mPackages), versionInfos,
+                    lastStaticSharedLibSettings);
             CommitRequest commitRequest = null;
             synchronized (mPackages) {
                 Map<String, ReconciledPackage> reconciledPackages;
                 try {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
-                    reconciledPackages = reconcilePackagesLocked(reconcileRequest);
+                    reconciledPackages = reconcilePackagesLocked(
+                            reconcileRequest, mSettings.mKeySetManagerService);
                 } catch (ReconcileFailure e) {
                     for (InstallRequest request : requests) {
-                        // TODO(b/109941548): add more concrete failure reasons
                         request.installResult.setError("Reconciliation failed...", e);
-                        // TODO: return any used system resources
                     }
                     return;
                 } finally {
@@ -15554,10 +15965,8 @@
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages");
                     commitRequest = new CommitRequest(reconciledPackages,
                             sUserManager.getUserIds());
-                    if (!commitPackagesLocked(commitRequest)) {
-                        cleanUpCommitFailuresLocked(commitRequest);
-                        return;
-                    }
+                    commitPackagesLocked(commitRequest);
+                    success = true;
                 } finally {
                     for (PrepareResult result : prepareResults.values()) {
                         if (result.freezer != null) {
@@ -15569,6 +15978,11 @@
             }
             executePostCommitSteps(commitRequest);
         } finally {
+            if (!success) {
+                for (ScanResult result : preparedScans.values()) {
+                    unprepareScanResultLocked(result);
+                }
+            }
             for (PrepareResult result : prepareResults.values()) {
                 if (result.freezer != null) {
                     result.freezer.close();
@@ -15585,8 +15999,8 @@
      */
     private void executePostCommitSteps(CommitRequest commitRequest) {
         for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
-            final boolean instantApp =
-                    ((reconciledPkg.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
+            final boolean instantApp = ((reconciledPkg.scanResult.request.scanFlags
+                            & PackageManagerService.SCAN_AS_INSTANT_APP) != 0);
             final PackageParser.Package pkg = reconciledPkg.pkgSetting.pkg;
             final String packageName = pkg.packageName;
             prepareAppDataAfterInstallLIF(pkg);
@@ -15604,7 +16018,7 @@
             // can be used for optimizations.
             mArtManagerService.prepareAppProfiles(
                     pkg,
-                    resolveUserIds(reconciledPkg.installForUser.getIdentifier()),
+                    resolveUserIds(reconciledPkg.installArgs.user.getIdentifier()),
                     /* updateReferenceProfileContent= */ true);
 
             // Check whether we need to dexopt the app.
@@ -15655,109 +16069,6 @@
             // TODO: Layering violation
             BackgroundDexOptService.notifyPackageChanged(packageName);
         }
-
-    }
-
-    private void cleanUpCommitFailuresLocked(CommitRequest request) {
-        final Map<String, ReconciledPackage> reconciledPackages = request.reconciledPackages;
-        final int[] allUsers = request.mAllUsers;
-        for (ReconciledPackage reconciledPackage : reconciledPackages.values()) {
-            final String pkgName1 = reconciledPackage.pkgSetting.pkg.packageName;
-            if (reconciledPackage.installResult.returnCode == PackageManager.INSTALL_SUCCEEDED) {
-                reconciledPackage.installResult.setError(
-                        PackageManager.INSTALL_FAILED_INTERNAL_ERROR, "Commit failed...");
-            }
-            final PackageParser.Package oldPackage =
-                    reconciledPackage.prepareResult.existingPackage;
-            final PackageParser.Package newPackage = reconciledPackage.pkgSetting.pkg;
-            if (reconciledPackage.prepareResult.system) {
-                // Re installation failed. Restore old information
-                // Remove new pkg information
-                if (newPackage != null) {
-                    removeInstalledPackageLI(newPackage, true);
-                }
-                // Add back the old system package
-                PackageParser.Package restoredPkg = null;
-                try {
-                    final List<ScanResult> restoreResults = scanPackageTracedLI(oldPackage,
-                            reconciledPackage.prepareResult.parseFlags, SCAN_UPDATE_SIGNATURE, 0,
-                            reconciledPackage.installForUser);
-                    commitSuccessfulScanResults(restoreResults);
-                    restoredPkg = restoreResults.get(0).pkgSetting.pkg;
-                } catch (PackageManagerException e) {
-                    Slog.e(TAG, "Failed to restore original package: " + e.getMessage());
-                }
-                synchronized (mPackages) {
-                    final boolean disabledSystem;
-                    // Remove existing system package
-                    removePackageLI(reconciledPackage.scanResult.pkgSetting.pkg, true);
-                    disabledSystem = disableSystemPackageLPw(
-                            reconciledPackage.scanResult.pkgSetting.pkg, restoredPkg);
-                    if (disabledSystem) {
-                        enableSystemPackageLPw(restoredPkg);
-                    }
-                    // Ensure the installer package name up to date
-                    setInstallerPackageNameLPw(reconciledPackage.scanResult.pkgSetting.pkg,
-                            reconciledPackage.installArgs.installerPackageName);
-                    // Update permissions for restored package
-                    mPermissionManager.updatePermissions(
-                            restoredPkg.packageName, restoredPkg, false, mPackages.values(),
-                            mPermissionCallback);
-                    mSettings.writeLPr();
-                }
-                Slog.i(TAG, "Successfully restored package : " + restoredPkg.packageName
-                        + " after failed upgrade");
-            } else if (reconciledPackage.prepareResult.replace) {
-                if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, rolling pack: " + pkgName1);
-
-                // Revert all internal state mutations and added folders for the failed install
-                boolean deletedPkg = deletePackageLIF(pkgName1, null, true,
-                        allUsers, /*TODO: deleteFlags*/ 0,
-                        reconciledPackage.installResult.removedInfo, true, null);
-
-                // Restore the old package
-                if (deletedPkg) {
-                    if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + oldPackage);
-                    File restoreFile = new File(oldPackage.codePath);
-                    // Parse old package
-                    boolean oldExternal = isExternal(oldPackage);
-                    int oldParseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
-                            | (oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
-                    int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;
-                    try {
-                        scanPackageTracedLI(restoreFile, oldParseFlags, oldScanFlags,
-                                /* origUpdateTime */ System.currentTimeMillis(), null);
-                    } catch (PackageManagerException e) {
-                        Slog.e(TAG, "Failed to restore package : " + pkgName1
-                                + " after failed upgrade: "
-                                + e.getMessage());
-                        return;
-                    }
-
-                    synchronized (mPackages) {
-                        // Ensure the installer package name up to date
-                        setInstallerPackageNameLPw(oldPackage,
-                                reconciledPackage.installArgs.installerPackageName);
-
-                        // Update permissions for restored package
-                        mPermissionManager.updatePermissions(
-                                oldPackage.packageName, oldPackage, false, mPackages.values(),
-                                mPermissionCallback);
-
-                        mSettings.writeLPr();
-                    }
-
-                    Slog.i(TAG, "Successfully restored package : " + pkgName1
-                            + " after failed upgrade");
-                }
-            } else {
-                // Remove package from internal structures, but keep around any
-                // data that might have already existed
-                deletePackageLIF(pkgName1, UserHandle.ALL, false, null,
-                        PackageManager.DELETE_KEEP_DATA,
-                        reconciledPackage.installResult.removedInfo, true, null);
-            }
-        }
     }
 
     /**
@@ -16238,59 +16549,17 @@
                 throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
                         "Error deriving application ABI");
             }
-
-            // Shared libraries for the package need to be updated.
-            synchronized (mPackages) {
-                try {
-                    updateSharedLibrariesLPr(pkg, null);
-                } catch (PackageManagerException e) {
-                    Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
-                }
-            }
         }
 
         if (!args.doRename(res.returnCode, pkg)) {
             throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
         }
 
-        if (PackageManagerServiceUtils.isApkVerityEnabled()) {
-            String apkPath = null;
-            synchronized (mPackages) {
-                // Note that if the attacker managed to skip verify setup, for example by tampering
-                // with the package settings, upon reboot we will do full apk verification when
-                // verity is not detected.
-                final PackageSetting ps = mSettings.mPackages.get(pkgName);
-                if (ps != null && ps.isPrivileged()) {
-                    apkPath = pkg.baseCodePath;
-                }
-            }
-            if (apkPath != null) {
-                final VerityUtils.SetupResult result =
-                        VerityUtils.generateApkVeritySetupData(apkPath, null /* signaturePath */,
-                                true /* skipSigningBlock */);
-                if (result.isOk()) {
-                    if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling apk verity to " + apkPath);
-                    FileDescriptor fd = result.getUnownedFileDescriptor();
-                    try {
-                        final byte[] signedRootHash =
-                                VerityUtils.generateApkVerityRootHash(apkPath);
-                        mInstaller.installApkVerity(apkPath, fd, result.getContentSize());
-                        mInstaller.assertFsverityRootHashMatches(apkPath, signedRootHash);
-                    } catch (InstallerException | IOException | DigestException |
-                            NoSuchAlgorithmException e) {
-                        throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
-                                "Failed to set up verity: " + e);
-                    } finally {
-                        IoUtils.closeQuietly(fd);
-                    }
-                } else if (result.isFailed()) {
-                    throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
-                            "Failed to generate verity");
-                } else {
-                    // Do nothing if verity is skipped. Will fall back to full apk verification on
-                    // reboot.
-                }
-            }
+        try {
+            setUpFsVerityIfPossible(pkg);
+        } catch (InstallerException | IOException | DigestException | NoSuchAlgorithmException e) {
+            throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
+                    "Failed to set up verity: " + e);
         }
 
         if (!instantApp) {
@@ -16588,6 +16857,86 @@
         }
     }
 
+    /**
+     * Set up fs-verity for the given package if possible.  This requires a feature flag of system
+     * property to be enabled only if the kernel supports fs-verity.
+     *
+     * <p>When the feature flag is set to legacy mode, only APK is supported (with some experimental
+     * kernel patches). In normal mode, all file format can be supported.
+     */
+    private void setUpFsVerityIfPossible(PackageParser.Package pkg) throws InstallerException,
+            PrepareFailure, IOException, DigestException, NoSuchAlgorithmException {
+        final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled();
+        final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled();
+        if (!standardMode && !legacyMode) {
+            return;
+        }
+
+        // Collect files we care for fs-verity setup.
+        ArrayMap<String, String> fsverityCandidates = new ArrayMap<>();
+        if (legacyMode) {
+            synchronized (mPackages) {
+                final PackageSetting ps = mSettings.mPackages.get(pkg.packageName);
+                if (ps != null && ps.isPrivileged()) {
+                    fsverityCandidates.put(pkg.baseCodePath, null);
+                    if (pkg.splitCodePaths != null) {
+                        for (String splitPath : pkg.splitCodePaths) {
+                            fsverityCandidates.put(splitPath, null);
+                        }
+                    }
+                }
+            }
+        } else {
+            // NB: These files will become only accessible if the signing key is loaded in kernel's
+            // .fs-verity keyring.
+            fsverityCandidates.put(pkg.baseCodePath,
+                    VerityUtils.getFsveritySignatureFilePath(pkg.baseCodePath));
+
+            final String dmPath = DexMetadataHelper.buildDexMetadataPathForApk(pkg.baseCodePath);
+            if (new File(dmPath).exists()) {
+                fsverityCandidates.put(dmPath, VerityUtils.getFsveritySignatureFilePath(dmPath));
+            }
+
+            if (pkg.splitCodePaths != null) {
+                for (String path : pkg.splitCodePaths) {
+                    fsverityCandidates.put(path, VerityUtils.getFsveritySignatureFilePath(path));
+
+                    final String splitDmPath = DexMetadataHelper.buildDexMetadataPathForApk(path);
+                    if (new File(splitDmPath).exists()) {
+                        fsverityCandidates.put(splitDmPath,
+                                VerityUtils.getFsveritySignatureFilePath(splitDmPath));
+                    }
+                }
+            }
+        }
+
+        for (Map.Entry<String, String> entry : fsverityCandidates.entrySet()) {
+            final String filePath = entry.getKey();
+            final String signaturePath = entry.getValue();
+
+            final VerityUtils.SetupResult result = VerityUtils.generateApkVeritySetupData(
+                    filePath, signaturePath, legacyMode);
+            if (result.isOk()) {
+                if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling verity to " + filePath);
+                final FileDescriptor fd = result.getUnownedFileDescriptor();
+                try {
+                    mInstaller.installApkVerity(filePath, fd, result.getContentSize());
+
+                    // In legacy mode, fs-verity can only be enabled by process with CAP_SYS_ADMIN.
+                    if (legacyMode) {
+                        final byte[] rootHash = VerityUtils.generateApkVerityRootHash(filePath);
+                        mInstaller.assertFsverityRootHashMatches(filePath, rootHash);
+                    }
+                } finally {
+                    IoUtils.closeQuietly(fd);
+                }
+            } else if (result.isFailed()) {
+                throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
+                        "Failed to generate verity");
+            }
+        }
+    }
+
     private void startIntentFilterVerifications(int userId, boolean replacing,
             PackageParser.Package pkg) {
         if (mIntentFilterVerifierComponent == null) {
@@ -16708,6 +17057,7 @@
         int status = ivi.getStatus();
         switch (status) {
             case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
             case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
                 return true;
 
@@ -17356,28 +17706,22 @@
      * make sure this flag is set for partially installed apps. If not its meaningless to
      * delete a partially installed application.
      */
-    private void removePackageDataLIF(PackageSetting ps, int[] allUserHandles,
+    private void removePackageDataLIF(final PackageSetting deletedPs, int[] allUserHandles,
             PackageRemovedInfo outInfo, int flags, boolean writeSettings) {
-        String packageName = ps.name;
-        if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + ps);
+        String packageName = deletedPs.name;
+        if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + deletedPs);
         // Retrieve object to delete permissions for shared user later on
-        final PackageParser.Package deletedPkg;
-        final PackageSetting deletedPs;
-        // reader
-        synchronized (mPackages) {
-            deletedPkg = mPackages.get(packageName);
-            deletedPs = mSettings.mPackages.get(packageName);
-            if (outInfo != null) {
-                outInfo.removedPackage = packageName;
-                outInfo.installerPackageName = ps.installerPackageName;
-                outInfo.isStaticSharedLib = deletedPkg != null
-                        && deletedPkg.staticSharedLibName != null;
-                outInfo.populateUsers(deletedPs == null ? null
-                        : deletedPs.queryInstalledUsers(sUserManager.getUserIds(), true), deletedPs);
-            }
+        final PackageParser.Package deletedPkg = deletedPs.pkg;
+        if (outInfo != null) {
+            outInfo.removedPackage = packageName;
+            outInfo.installerPackageName = deletedPs.installerPackageName;
+            outInfo.isStaticSharedLib = deletedPkg != null
+                    && deletedPkg.staticSharedLibName != null;
+            outInfo.populateUsers(deletedPs == null ? null
+                    : deletedPs.queryInstalledUsers(sUserManager.getUserIds(), true), deletedPs);
         }
 
-        removePackageLI(ps.name, (flags & PackageManager.DELETE_CHATTY) != 0);
+        removePackageLI(deletedPs.name, (flags & PackageManager.DELETE_CHATTY) != 0);
 
         if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
             final PackageParser.Package resolvedPkg;
@@ -17386,8 +17730,8 @@
             } else {
                 // We don't have a parsed package when it lives on an ejected
                 // adopted storage device, so fake something together
-                resolvedPkg = new PackageParser.Package(ps.name);
-                resolvedPkg.setVolumeUuid(ps.volumeUuid);
+                resolvedPkg = new PackageParser.Package(deletedPs.name);
+                resolvedPkg.setVolumeUuid(deletedPs.volumeUuid);
             }
             destroyAppDataLIF(resolvedPkg, UserHandle.USER_ALL,
                     StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
@@ -17447,10 +17791,10 @@
                         if (DEBUG_REMOVE) {
                             Slog.d(TAG, "    user " + userId + " => " + installed);
                         }
-                        if (installed != ps.getInstalled(userId)) {
+                        if (installed != deletedPs.getInstalled(userId)) {
                             installedStateChanged = true;
                         }
-                        ps.setInstalled(installed, userId);
+                        deletedPs.setInstalled(installed, userId);
                     }
                 }
             }
@@ -17460,7 +17804,7 @@
                 mSettings.writeLPr();
             }
             if (installedStateChanged) {
-                mSettings.writeKernelMappingLPr(ps);
+                mSettings.writeKernelMappingLPr(deletedPs);
             }
         }
         if (removedAppId != -1) {
@@ -17530,12 +17874,12 @@
     /*
      * Tries to delete system package.
      */
-    private void deleteSystemPackageLIF(DeletePackageAction action,
-            PackageParser.Package deletedPkg, PackageSetting deletedPs, int[] allUserHandles,
-            int flags, PackageRemovedInfo outInfo, boolean writeSettings)
+    private void deleteSystemPackageLIF(DeletePackageAction action, PackageSetting deletedPs,
+            int[] allUserHandles, int flags, PackageRemovedInfo outInfo, boolean writeSettings)
             throws SystemDeleteException {
         final boolean applyUserRestrictions
                 = (allUserHandles != null) && (outInfo.origUsers != null);
+        final PackageParser.Package deletedPkg = deletedPs.pkg;
         // Confirm if the system package has been updated
         // An updated system app can be deleted. This will also have to restore
         // the system pkg from system partition
@@ -17815,21 +18159,14 @@
         public final PackageRemovedInfo outInfo;
         public final int flags;
         public final UserHandle user;
-        /**
-         * True if this package is an unupdated system app that may be deleted by the system.
-         * When true, disabledPs will be null.
-         */
-        public final boolean mayDeleteUnupdatedSystemApp;
 
         private DeletePackageAction(PackageSetting deletingPs, PackageSetting disabledPs,
-                PackageRemovedInfo outInfo, int flags, UserHandle user,
-                boolean mayDeleteUnupdatedSystemApp) {
+                PackageRemovedInfo outInfo, int flags, UserHandle user) {
             this.deletingPs = deletingPs;
             this.disabledPs = disabledPs;
             this.outInfo = outInfo;
             this.flags = flags;
             this.user = user;
-            this.mayDeleteUnupdatedSystemApp = mayDeleteUnupdatedSystemApp;
         }
     }
 
@@ -17845,24 +18182,22 @@
         if (ps == null) {
             return null;
         }
-        boolean mayDeleteUnupdatedSystemApp = false;
         if (isSystemApp(ps)) {
             if (ps.parentPackageName != null) {
                 Slog.w(TAG, "Attempt to delete child system package " + ps.pkg.packageName);
                 return null;
             }
 
-            if (((flags & PackageManager.DELETE_SYSTEM_APP) != 0) && user != null
-                    && user.getIdentifier() != UserHandle.USER_ALL) {
-                mayDeleteUnupdatedSystemApp = true;
-            } else if (disabledPs == null) {
-                // Confirmed if the system package has been updated
-                // An updated system app can be deleted. This will also have to restore
-                // the system pkg from system partition
-                // reader
+            final boolean deleteSystem = (flags & PackageManager.DELETE_SYSTEM_APP) != 0;
+            final boolean deleteAllUsers =
+                    user == null || user.getIdentifier() == UserHandle.USER_ALL;
+            if ((!deleteSystem || deleteAllUsers) && disabledPs == null) {
                 Slog.w(TAG, "Attempt to delete unknown system package " + ps.pkg.packageName);
                 return null;
             }
+            // Confirmed if the system package has been updated
+            // An updated system app can be deleted. This will also have to restore
+            // the system pkg from system partition reader
         }
         final int parentReferenceCount =
                 (ps.childPackageNames != null) ? ps.childPackageNames.size() : 0;
@@ -17877,8 +18212,7 @@
                 }
             }
         }
-        return new DeletePackageAction(ps, disabledPs, outInfo, flags, user,
-                mayDeleteUnupdatedSystemApp);
+        return new DeletePackageAction(ps, disabledPs, outInfo, flags, user);
     }
 
     /*
@@ -17895,16 +18229,18 @@
             PackageSetting[] children = mSettings.getChildSettingsLPr(ps);
             action = mayDeletePackageLocked(outInfo, ps, disabledPs, children, flags, user);
         }
+        if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: " + packageName + " user " + user);
         if (null == action) {
+            if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: action was null");
             return false;
         }
 
-        if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: " + packageName + " user " + user);
 
         try {
             executeDeletePackageLIF(action, packageName, deleteCodeAndResources,
                     allUserHandles, writeSettings, replacingPackage);
         } catch (SystemDeleteException e) {
+            if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: system deletion failure", e);
             return false;
         }
         return true;
@@ -17950,43 +18286,43 @@
         if (ps.getPermissionsState().hasPermission(Manifest.permission.SUSPEND_APPS, userId)) {
             unsuspendForSuspendingPackage(packageName, userId);
         }
-
-
-        if (!systemApp || action.mayDeleteUnupdatedSystemApp) {
+        if ((!systemApp || (flags & PackageManager.DELETE_SYSTEM_APP) != 0)
+                && userId != UserHandle.USER_ALL) {
             // The caller is asking that the package only be deleted for a single
             // user.  To do this, we just mark its uninstalled state and delete
             // its data. If this is a system app, we only allow this to happen if
             // they have set the special DELETE_SYSTEM_APP which requests different
             // semantics than normal for uninstalling system apps.
-            markPackageUninstalledForUserLPw(ps, user);
-
-            if (!systemApp) {
-                // Do not uninstall the APK if an app should be cached
-                boolean keepUninstalledPackage = shouldKeepUninstalledPackageLPr(packageName);
-                if (ps.isAnyInstalled(sUserManager.getUserIds()) || keepUninstalledPackage) {
-                    // Other user still have this package installed, so all
+            synchronized (mPackages) {
+                markPackageUninstalledForUserLPw(ps, user);
+                if (!systemApp) {
+                    // Do not uninstall the APK if an app should be cached
+                    boolean keepUninstalledPackage = shouldKeepUninstalledPackageLPr(packageName);
+                    if (ps.isAnyInstalled(sUserManager.getUserIds()) || keepUninstalledPackage) {
+                        // Other users still have this package installed, so all
+                        // we need to do is clear this user's data and save that
+                        // it is uninstalled.
+                        if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users");
+                        clearPackageStateForUserLIF(ps, userId, outInfo, flags);
+                        scheduleWritePackageRestrictionsLocked(user);
+                        return;
+                    } else {
+                        // We need to set it back to 'installed' so the uninstall
+                        // broadcasts will be sent correctly.
+                        if (DEBUG_REMOVE) Slog.d(TAG, "Not installed by other users, full delete");
+                        ps.setInstalled(true, userId);
+                        mSettings.writeKernelMappingLPr(ps);
+                    }
+                } else {
+                    // This is a system app, so we assume that the
+                    // other users still have this package installed, so all
                     // we need to do is clear this user's data and save that
                     // it is uninstalled.
-                    if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users");
-                    clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo, flags);
+                    if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app");
+                    clearPackageStateForUserLIF(ps, userId, outInfo, flags);
                     scheduleWritePackageRestrictionsLocked(user);
                     return;
-                } else {
-                    // We need to set it back to 'installed' so the uninstall
-                    // broadcasts will be sent correctly.
-                    if (DEBUG_REMOVE) Slog.d(TAG, "Not installed by other users, full delete");
-                    ps.setInstalled(true, user.getIdentifier());
-                    mSettings.writeKernelMappingLPr(ps);
                 }
-            } else {
-                // This is a system app, so we assume that the
-                // other users still have this package installed, so all
-                // we need to do is clear this user's data and save that
-                // it is uninstalled.
-                if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app");
-                clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo, flags);
-                scheduleWritePackageRestrictionsLocked(user);
-                return;
             }
         }
 
@@ -18015,8 +18351,7 @@
             if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package: " + ps.name);
             // When an updated system application is deleted we delete the existing resources
             // as well and fall back to existing code in system partition
-            deleteSystemPackageLIF(
-                    action, ps.pkg, ps, allUserHandles, flags, outInfo, writeSettings);
+            deleteSystemPackageLIF(action, ps, allUserHandles, flags, outInfo, writeSettings);
         } else {
             if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.name);
             deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles,
@@ -19436,6 +19771,17 @@
         return getHomeActivitiesAsUser(allHomeCandidates, UserHandle.getCallingUserId());
     }
 
+    /**
+     * Send a {@code PackageInstaller.ACTION_SESSION_UPDATED} broadcast intent, containing
+     * the {@code sessionInfo} in the extra field {@code PackageInstaller.EXTRA_SESSION}.
+     */
+    public void sendSessionUpdatedBroadcast(PackageInstaller.SessionInfo sessionInfo,
+            int userId) {
+        Intent sessionUpdatedIntent = new Intent(PackageInstaller.ACTION_SESSION_UPDATED)
+                .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo);
+        mContext.sendBroadcastAsUser(sessionUpdatedIntent, UserHandle.of(userId));
+    }
+
     public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId) {
         UserManagerService ums = UserManagerService.getInstance();
         if (ums != null) {
@@ -20076,16 +20422,21 @@
         ContentObserver co = new ContentObserver(mHandler) {
             @Override
             public void onChange(boolean selfChange) {
-                mWebInstantAppsDisabled =
-                        (Global.getInt(resolver, Global.ENABLE_EPHEMERAL_FEATURE, 1) == 0) ||
-                                (Secure.getInt(resolver, Secure.INSTANT_APPS_ENABLED, 1) == 0);
+                final boolean ephemeralFeatureDisabled =
+                        Global.getInt(resolver, Global.ENABLE_EPHEMERAL_FEATURE, 1) == 0;
+                for (int userId : UserManagerService.getInstance().getUserIds()) {
+                    final boolean instantAppsDisabledForUser =
+                            ephemeralFeatureDisabled || Secure.getIntForUser(resolver,
+                                    Secure.INSTANT_APPS_ENABLED, 1, userId) == 0;
+                    mWebInstantAppsDisabled.put(userId, instantAppsDisabledForUser);
+                }
             }
         };
         mContext.getContentResolver().registerContentObserver(android.provider.Settings.Global
                         .getUriFor(Global.ENABLE_EPHEMERAL_FEATURE),
-                false, co, UserHandle.USER_SYSTEM);
+                false, co, UserHandle.USER_ALL);
         mContext.getContentResolver().registerContentObserver(android.provider.Settings.Secure
-                        .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_SYSTEM);
+                        .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_ALL);
         co.onChange(true);
 
         // Disable any carrier apps. We do this very early in boot to prevent the apps from being
@@ -20210,6 +20561,8 @@
                 }
             }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
         }
+
+        mModuleInfoProvider.systemReady();
     }
 
     public void waitForAppDataPrepared() {
@@ -23289,6 +23642,11 @@
                 return setting.getEnabled(userId);
             }
         }
+
+        @Override
+        public void setEnableRollbackCode(int token, int enableRollbackCode) {
+            PackageManagerService.this.setEnableRollbackCode(token, enableRollbackCode);
+        }
     }
 
     @GuardedBy("mPackages")
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 36948fc..6134d30 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -543,14 +543,31 @@
         }
     }
 
-    /** Returns true if APK Verity is enabled. */
+    /** Default is to not use fs-verity since it depends on kernel support. */
+    private static final int FSVERITY_DISABLED = 0;
+
+    /**
+     * Experimental implementation targeting priv apps, with Android specific kernel patches to
+     * extend fs-verity.
+     */
+    private static final int FSVERITY_LEGACY = 1;
+
+    /** Standard fs-verity. */
+    private static final int FSVERITY_ENABLED = 2;
+
+    /** Returns true if standard APK Verity is enabled. */
     static boolean isApkVerityEnabled() {
-        return SystemProperties.getInt("ro.apk_verity.mode", 0) != 0;
+        return SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) == FSVERITY_ENABLED;
+    }
+
+    static boolean isLegacyApkVerityEnabled() {
+        return SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) == FSVERITY_LEGACY;
     }
 
     /** Returns true to force apk verification if the updated package (in /data) is a priv app. */
     static boolean isApkVerificationForced(@Nullable PackageSetting disabledPs) {
-        return disabledPs != null && disabledPs.isPrivileged() && isApkVerityEnabled();
+        return disabledPs != null && disabledPs.isPrivileged() && (
+                isApkVerityEnabled() || isLegacyApkVerityEnabled());
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 37a35a2..2e9d26a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -630,9 +630,13 @@
                     pw.print("=");
                 }
                 pw.print(info.packageName);
-                if (showVersionCode && !isApex) {
+                if (showVersionCode) {
                     pw.print(" versionCode:");
-                    pw.print(info.applicationInfo.versionCode);
+                    if (info.applicationInfo != null) {
+                        pw.print(info.applicationInfo.versionCode);
+                    } else {
+                        pw.print(info.versionCode);
+                    }
                 }
                 if (listInstaller && !isApex) {
                     pw.print("  installer=");
@@ -2289,6 +2293,9 @@
                     break;
                 case "--apex":
                     sessionParams.installFlags |= PackageManager.INSTALL_APEX;
+                    // TODO(b/118865310): APEX packages should always imply
+                    //                    sessionParams.isStaged(). Enforce this when the staged
+                    //                    install workflow is complete.
                     break;
                 case "--multi-package":
                     sessionParams.setMultiPackage();
@@ -2296,6 +2303,9 @@
                 case "--staged":
                     sessionParams.setStaged();
                     break;
+                case "--enable-rollback":
+                    sessionParams.installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK;
+                    break;
                 default:
                     throw new IllegalArgumentException("Unknown option " + opt);
             }
@@ -2581,7 +2591,7 @@
         try {
             session = new PackageInstaller.Session(
                     mInterface.getPackageInstaller().openSession(sessionId));
-            if (!session.isMultiPackage()) {
+            if (!session.isMultiPackage() && !session.isStaged()) {
                 // Sanity check that all .dm files match an apk.
                 // (The installer does not support standalone .dm files and will not process them.)
                 try {
@@ -2779,7 +2789,7 @@
         pw.println("    Prints all system libraries.");
         pw.println("");
         pw.println("  list packages [-f] [-d] [-e] [-s] [-3] [-i] [-l] [-u] [-U] ");
-        pw.println("      [--apex-only] [--uid UID] [--user USER_ID] [FILTER]");
+        pw.println("      [--show-versioncode] [--apex-only] [--uid UID] [--user USER_ID] [FILTER]");
         pw.println("    Prints all packages; optionally only those whose name contains");
         pw.println("    the text in FILTER.  Options are:");
         pw.println("      -f: see their associated file");
@@ -2792,6 +2802,7 @@
         pw.println("      -l: ignored (used for compatibility with older releases)");
         pw.println("      -U: also show the package UID");
         pw.println("      -u: also include uninstalled packages");
+        pw.println("      --show-versioncode: also show the version code");
         pw.println("      --apex-only: only show APEX packages");
         pw.println("      --uid UID: filter to only show packages with the given UID");
         pw.println("      --user USER_ID: only list packages belonging to the given user");
@@ -2828,6 +2839,7 @@
         pw.println("       [--install-reason 0/1/2/3/4] [--originating-uri URI]");
         pw.println("       [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
         pw.println("       [--preload] [--instantapp] [--full] [--dont-kill]");
+        pw.println("       [--enable-rollback]");
         pw.println("       [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES] [--apex]");
         pw.println("       [PATH|-]");
         pw.println("    Install an application.  Must provide the apk data to install, either as a");
diff --git a/services/core/java/com/android/server/pm/PackageVerificationState.java b/services/core/java/com/android/server/pm/PackageVerificationState.java
index 3214e88..c50bf59 100644
--- a/services/core/java/com/android/server/pm/PackageVerificationState.java
+++ b/services/core/java/com/android/server/pm/PackageVerificationState.java
@@ -16,11 +16,11 @@
 
 package com.android.server.pm;
 
-import com.android.server.pm.PackageManagerService.InstallArgs;
-
 import android.content.pm.PackageManager;
 import android.util.SparseBooleanArray;
 
+import com.android.server.pm.PackageManagerService.InstallParams;
+
 /**
  * Tracks the package verification state for a particular package. Each package
  * verification has a required verifier and zero or more sufficient verifiers.
@@ -29,7 +29,7 @@
  * then package verification is considered complete.
  */
 class PackageVerificationState {
-    private final InstallArgs mArgs;
+    private final InstallParams mParams;
 
     private final SparseBooleanArray mSufficientVerifierUids;
 
@@ -53,15 +53,15 @@
      * @param requiredVerifierUid user ID of required package verifier
      * @param args
      */
-    public PackageVerificationState(int requiredVerifierUid, InstallArgs args) {
+    PackageVerificationState(int requiredVerifierUid, InstallParams params) {
         mRequiredVerifierUid = requiredVerifierUid;
-        mArgs = args;
+        mParams = params;
         mSufficientVerifierUids = new SparseBooleanArray();
         mExtendedTimeout = false;
     }
 
-    public InstallArgs getInstallArgs() {
-        return mArgs;
+    InstallParams getInstallParams() {
+        return mParams;
     }
 
     /**
@@ -69,7 +69,7 @@
      *
      * @param uid user ID of sufficient verifier
      */
-    public void addSufficientVerifier(int uid) {
+    void addSufficientVerifier(int uid) {
         mSufficientVerifierUids.put(uid, true);
     }
 
@@ -80,7 +80,7 @@
      * @param uid user ID of the verifying agent
      * @return {@code true} if the verifying agent actually exists in our list
      */
-    public boolean setVerifierResponse(int uid, int code) {
+    boolean setVerifierResponse(int uid, int code) {
         if (uid == mRequiredVerifierUid) {
             mRequiredVerificationComplete = true;
             switch (code) {
@@ -120,7 +120,7 @@
      *
      * @return {@code true} when verification is considered complete
      */
-    public boolean isVerificationComplete() {
+    boolean isVerificationComplete() {
         if (!mRequiredVerificationComplete) {
             return false;
         }
@@ -138,7 +138,7 @@
      *
      * @return {@code true} if installation should be allowed
      */
-    public boolean isInstallAllowed() {
+    boolean isInstallAllowed() {
         if (!mRequiredVerificationPassed) {
             return false;
         }
@@ -153,7 +153,7 @@
     /**
      * Extend the timeout for this Package to be verified.
      */
-    public void extendTimeout() {
+    void extendTimeout() {
         if (!mExtendedTimeout) {
             mExtendedTimeout = true;
         }
@@ -164,7 +164,7 @@
      *
      * @return {@code true} if a timeout was already extended.
      */
-    public boolean timeoutExtended() {
+    boolean timeoutExtended() {
         return mExtendedTimeout;
     }
 }
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
new file mode 100644
index 0000000..3beda6a
--- /dev/null
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.NonNull;
+import android.content.pm.PackageInstaller;
+import android.content.pm.ParceledListSlice;
+import android.os.Handler;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class handles staged install sessions, i.e. install sessions that require packages to
+ * be installed only after a reboot.
+ */
+public class StagingManager {
+
+    private static final String TAG = "StagingManager";
+
+    private final PackageManagerService mPm;
+    private final Handler mBgHandler;
+
+    // STOPSHIP: This is a temporary mock implementation of staged sessions. This variable
+    //           shouldn't be needed at all.
+    // TODO(b/118865310): Implement staged sessions logic.
+    @GuardedBy("mStagedSessions")
+    private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>();
+
+    StagingManager(PackageManagerService pm) {
+        mPm = pm;
+        mBgHandler = BackgroundThread.getHandler();
+    }
+
+    private void updateStoredSession(@NonNull PackageInstallerSession sessionInfo) {
+        synchronized (mStagedSessions) {
+            PackageInstallerSession storedSession = mStagedSessions.get(sessionInfo.sessionId);
+            // storedSession might be null if a call to abortSession was made before the session
+            // is updated.
+            if (storedSession != null) {
+                mStagedSessions.put(sessionInfo.sessionId, sessionInfo);
+            }
+        }
+    }
+
+    ParceledListSlice<PackageInstaller.SessionInfo> getSessions() {
+        final List<PackageInstaller.SessionInfo> result = new ArrayList<>();
+        synchronized (mStagedSessions) {
+            for (int i = 0; i < mStagedSessions.size(); i++) {
+                result.add(mStagedSessions.valueAt(i).generateInfo(false));
+            }
+        }
+        return new ParceledListSlice<>(result);
+    }
+
+    void commitSession(@NonNull PackageInstallerSession sessionInfo) {
+        updateStoredSession(sessionInfo);
+
+        mBgHandler.post(() -> {
+            // TODO(b/118865310): Dispatch the session to apexd/PackageManager for verification. For
+            //                    now we directly mark it as ready.
+            sessionInfo.setStagedSessionReady();
+            mPm.sendSessionUpdatedBroadcast(sessionInfo.generateInfo(), sessionInfo.userId);
+        });
+    }
+
+    void createSession(@NonNull PackageInstallerSession sessionInfo) {
+        synchronized (mStagedSessions) {
+            mStagedSessions.append(sessionInfo.sessionId, sessionInfo);
+        }
+    }
+
+    void abortSession(@NonNull PackageInstallerSession sessionInfo) {
+        updateStoredSession(sessionInfo);
+        synchronized (mStagedSessions) {
+            mStagedSessions.remove(sessionInfo.sessionId);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index d1d5818..4f20590 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2691,10 +2691,12 @@
                 if (!isGuest && !isManagedProfile && !isDemo && isUserLimitReached()) {
                     // If we're not adding a guest/demo user or a managed profile and the limit has
                     // been reached, cannot add a user.
+                    Log.e(LOG_TAG, "Cannot add user. Maximum user limit is reached.");
                     return null;
                 }
                 // If we're adding a guest and there already exists one, bail.
                 if (isGuest && findCurrentGuestUser() != null) {
+                    Log.e(LOG_TAG, "Cannot add guest user. Guest user already exists.");
                     return null;
                 }
                 // In legacy mode, restricted profile's parent can only be the owner user
@@ -2937,13 +2939,26 @@
             final UserData userData;
             int currentUser = ActivityManager.getCurrentUser();
             if (currentUser == userHandle) {
-                Log.w(LOG_TAG, "Current user cannot be removed");
+                Log.w(LOG_TAG, "Current user cannot be removed.");
                 return false;
             }
             synchronized (mPackagesLock) {
                 synchronized (mUsersLock) {
                     userData = mUsers.get(userHandle);
-                    if (userHandle == 0 || userData == null || mRemovingUserIds.get(userHandle)) {
+                    if (userHandle == UserHandle.USER_SYSTEM) {
+                        Log.e(LOG_TAG, "System user cannot be removed.");
+                        return false;
+                    }
+
+                    if (userData == null) {
+                        Log.e(LOG_TAG, String.format(
+                                "Cannot remove user %d, invalid user id provided.", userHandle));
+                        return false;
+                    }
+
+                    if (mRemovingUserIds.get(userHandle)) {
+                        Log.e(LOG_TAG, String.format(
+                                "User %d is already scheduled for removal.", userHandle));
                         return false;
                     }
 
@@ -2962,7 +2977,7 @@
             try {
                 mAppOpsService.removeUser(userHandle);
             } catch (RemoteException e) {
-                Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user", e);
+                Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user.", e);
             }
 
             if (userData.info.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
@@ -2986,6 +3001,7 @@
                             }
                         });
             } catch (RemoteException e) {
+                Log.w(LOG_TAG, "Failed to stop user during removal.", e);
                 return false;
             }
             return res == ActivityManager.USER_OP_SUCCESS;
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 25ef767..e57d9d7 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -785,10 +785,10 @@
      * files that can be direclty mapped.
      */
     private static void logIfPackageHasUncompressedCode(PackageParser.Package pkg) {
-        logIfApkHasUncompressedCode(pkg.baseCodePath);
+        auditUncompressedCodeInApk(pkg.baseCodePath);
         if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
             for (int i = 0; i < pkg.splitCodePaths.length; i++) {
-                logIfApkHasUncompressedCode(pkg.splitCodePaths[i]);
+                auditUncompressedCodeInApk(pkg.splitCodePaths[i]);
             }
         }
     }
@@ -797,34 +797,41 @@
      * Generates log if the archive located at {@code fileName} has uncompressed dex file and so
      * files that can be direclty mapped.
      */
-    private static void logIfApkHasUncompressedCode(String fileName) {
+    public static boolean auditUncompressedCodeInApk(String fileName) {
         StrictJarFile jarFile = null;
         try {
             jarFile = new StrictJarFile(fileName,
                     false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/);
             Iterator<ZipEntry> it = jarFile.iterator();
+            boolean allCorrect = true;
             while (it.hasNext()) {
                 ZipEntry entry = it.next();
                 if (entry.getName().endsWith(".dex")) {
                     if (entry.getMethod() != ZipEntry.STORED) {
+                        allCorrect = false;
                         Slog.w(TAG, "APK " + fileName + " has compressed dex code " +
                                 entry.getName());
                     } else if ((entry.getDataOffset() & 0x3) != 0) {
+                        allCorrect = false;
                         Slog.w(TAG, "APK " + fileName + " has unaligned dex code " +
                                 entry.getName());
                     }
                 } else if (entry.getName().endsWith(".so")) {
                     if (entry.getMethod() != ZipEntry.STORED) {
+                        allCorrect = false;
                         Slog.w(TAG, "APK " + fileName + " has compressed native code " +
                                 entry.getName());
                     } else if ((entry.getDataOffset() & (0x1000 - 1)) != 0) {
+                        allCorrect = false;
                         Slog.w(TAG, "APK " + fileName + " has unaligned native code " +
                                 entry.getName());
                     }
                 }
             }
+            return allCorrect;
         } catch (IOException ignore) {
             Slog.wtf(TAG, "Error when parsing APK " + fileName);
+            return false;
         } finally {
             try {
                 if (jarFile != null) {
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index 519a20d..e68c238 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -16,9 +16,9 @@
 
 package com.android.server.pm.dex;
 
+import android.os.Build;
 import android.util.AtomicFile;
 import android.util.Slog;
-import android.os.Build;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -26,26 +26,27 @@
 import com.android.server.pm.AbstractStatsBase;
 import com.android.server.pm.PackageManagerServiceUtils;
 
+import dalvik.system.VMRuntime;
+
+import libcore.io.IoUtils;
+
 import java.io.BufferedReader;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
-import java.io.InputStreamReader;
 import java.io.IOException;
+import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.io.Reader;
 import java.io.StringWriter;
 import java.io.Writer;
 import java.util.Collections;
-import java.util.Iterator;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
-import dalvik.system.VMRuntime;
-import libcore.io.IoUtils;
-
 /**
  * Stat file which store usage information about dex files.
  */
@@ -86,6 +87,13 @@
     private static final String UNSUPPORTED_CLASS_LOADER_CONTEXT =
             "=UnsupportedClassLoaderContext=";
 
+    /**
+     * Limit on how many secondary DEX paths we store for a single owner, to avoid one app causing
+     * unbounded memory consumption.
+     */
+    @VisibleForTesting
+    /* package */ static final int MAX_SECONDARY_FILES_PER_OWNER = 100;
+
     // Map which structures the information we have on a package.
     // Maps package name to package data (which stores info about UsedByOtherApps and
     // secondary dex files.).
@@ -164,8 +172,12 @@
                     DexUseInfo existingData = packageUseInfo.mDexUseInfoMap.get(dexPath);
                     if (existingData == null) {
                         // It's the first time we see this dex file.
-                        packageUseInfo.mDexUseInfoMap.put(dexPath, newData);
-                        return true;
+                        if (packageUseInfo.mDexUseInfoMap.size() < MAX_SECONDARY_FILES_PER_OWNER) {
+                            packageUseInfo.mDexUseInfoMap.put(dexPath, newData);
+                            return true;
+                        } else {
+                            return updateLoadingPackages;
+                        }
                     } else {
                         if (ownerUserId != existingData.mOwnerUserId) {
                             // Oups, this should never happen, the DexManager who calls this should
@@ -863,15 +875,13 @@
 
         public String getClassLoaderContext() { return mClassLoaderContext; }
 
-        @VisibleForTesting
-        /* package */ boolean isUnknownClassLoaderContext() {
+        public boolean isUnknownClassLoaderContext() {
             // The class loader context may be unknown if we loaded the data from a previous version
             // which didn't save the context.
             return UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
         }
 
-        @VisibleForTesting
-        /* package */ boolean isVariableClassLoaderContext() {
+        public boolean isVariableClassLoaderContext() {
             return VARIABLE_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
         }
     }
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 51619cf..789664d 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -143,6 +143,13 @@
         LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
     }
 
+    private static final Set<String> ALWAYS_LOCATION_PERMISSIONS = new ArraySet<>();
+    static {
+        ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION);
+        ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
+        ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
+    }
+
     private static final Set<String> ACTIVITY_RECOGNITION_PERMISSIONS = new ArraySet<>();
     static {
         ACTIVITY_RECOGNITION_PERMISSIONS.add(Manifest.permission.ACTIVITY_RECOGNITION);
@@ -690,7 +697,7 @@
         // Companion devices
         grantSystemFixedPermissionsToSystemPackage(
                 CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, userId,
-                LOCATION_PERMISSIONS);
+                ALWAYS_LOCATION_PERMISSIONS);
 
         // Ringtone Picker
         grantSystemFixedPermissionsToSystemPackage(
@@ -1186,9 +1193,9 @@
                             if (pm.checkPermission(fgPerm, pkg.packageName)
                                     == PackageManager.PERMISSION_GRANTED) {
                                 // Upgrade the app-op state of the fg permission to allow bg access
-                                mContext.getSystemService(AppOpsManager.class).setMode(
+                                mContext.getSystemService(AppOpsManager.class).setUidMode(
                                         AppOpsManager.permissionToOp(fgPerm), uid,
-                                        pkg.packageName, AppOpsManager.MODE_ALLOWED);
+                                        AppOpsManager.MODE_ALLOWED);
 
                                 break;
                             }
@@ -1198,8 +1205,8 @@
                     String bgPerm = getBackgroundPermission(permission);
                     if (bgPerm == null) {
                         if (op != null) {
-                            mContext.getSystemService(AppOpsManager.class).setMode(op, uid,
-                                    pkg.packageName, AppOpsManager.MODE_ALLOWED);
+                            mContext.getSystemService(AppOpsManager.class).setUidMode(op, uid,
+                                    AppOpsManager.MODE_ALLOWED);
                         }
                     } else {
                         int mode;
@@ -1210,8 +1217,7 @@
                             mode = AppOpsManager.MODE_FOREGROUND;
                         }
 
-                        mContext.getSystemService(AppOpsManager.class).setMode(op, uid,
-                                pkg.packageName, mode);
+                        mContext.getSystemService(AppOpsManager.class).setUidMode(op, uid, mode);
                     }
 
                     if (DEBUG) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index b58c811..93964cb 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -27,7 +27,6 @@
 import static android.app.AppOpsManager.OP_NONE;
 import static android.app.AppOpsManager.permissionToOp;
 import static android.app.AppOpsManager.permissionToOpCode;
-import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
@@ -1073,9 +1072,8 @@
         AppOpsManagerInternal appOpsInternal = LocalServices.getService(
                 AppOpsManagerInternal.class);
 
-        appOpsInternal.setMode(permissionToOpCode(permission),
-                getUid(userId, getAppId(pkg.applicationInfo.uid)), pkg.packageName, mode,
-                (pkg.applicationInfo.privateFlags & PRIVATE_FLAG_PRIVILEGED) != 0);
+        appOpsInternal.setUidMode(permissionToOpCode(permission),
+                getUid(userId, getAppId(pkg.applicationInfo.uid)), mode);
     }
 
     /**
@@ -1345,8 +1343,12 @@
                                     sourcePermNum++) {
                                 String sourcePerm = sourcePerms.valueAt(sourcePermNum);
 
-                                if (appOpsManager.unsafeCheckOpNoThrow(permissionToOp(sourcePerm),
-                                        getUid(userId, getAppId(pkg.applicationInfo.uid)), pkgName)
+                                if (ps.hasRuntimePermission(sourcePerm, userId)
+                                        && ps.getRuntimePermissionState(sourcePerm, userId)
+                                        .isGranted()
+                                        && appOpsManager.unsafeCheckOpNoThrow(
+                                                permissionToOp(sourcePerm), getUid(userId,
+                                                getAppId(pkg.applicationInfo.uid)), pkgName)
                                         == MODE_ALLOWED) {
                                     setAppOpMode(sourcePerm, pkg, userId, MODE_FOREGROUND);
                                 }
diff --git a/services/core/java/com/android/server/pm/permission/TEST_MAPPING b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
new file mode 100644
index 0000000..076c94c
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+    "presubmit": [
+        {
+            "name": "GtsPermissionTestCases",
+            "options": [
+                {
+                    "include-filter": "com.google.android.permission.gts.DefaultPermissionGrantPolicyTest"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 3ba1155..2060aef 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -24,6 +24,7 @@
 import static android.content.Context.CONTEXT_RESTRICTED;
 import static android.content.Context.DISPLAY_SERVICE;
 import static android.content.Context.WINDOW_SERVICE;
+import static android.content.pm.PackageManager.FEATURE_HDMI_CEC;
 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
@@ -126,6 +127,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
 import android.hardware.display.DisplayManager;
+import android.hardware.hdmi.HdmiAudioSystemClient;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiPlaybackClient;
 import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
@@ -195,6 +197,7 @@
 import com.android.internal.accessibility.AccessibilityShortcutController;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.os.RoSystemProperties;
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.IShortcutService;
 import com.android.internal.policy.PhoneWindow;
@@ -373,6 +376,7 @@
     private ScreenshotHelper mScreenshotHelper;
     private boolean mHasFeatureWatch;
     private boolean mHasFeatureLeanback;
+    private boolean mHasFeatureHdmiCec;
 
     // Assigned on main thread, accessed on UI thread
     volatile VrManagerInternal mVrManagerInternal;
@@ -599,6 +603,9 @@
 
     private boolean mAodShowing;
 
+    private boolean mPerDisplayFocusEnabled = false;
+    private int mTopFocusedDisplayId = INVALID_DISPLAY;
+
     private static final int MSG_ENABLE_POINTER_LOCATION = 1;
     private static final int MSG_DISABLE_POINTER_LOCATION = 2;
     private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3;
@@ -841,7 +848,7 @@
     }
 
     private void interceptBackKeyDown() {
-        MetricsLogger.count(mContext, "key_back_down", 1);
+        mLogger.count("key_back_down", 1);
         // Reset back key state for long press
         mBackKeyHandled = false;
 
@@ -855,6 +862,7 @@
 
     // returns true if the key was handled and should not be passed to the user
     private boolean interceptBackKeyUp(KeyEvent event) {
+        mLogger.count("key_back_up", 1);
         // Cache handled state
         boolean handled = mBackKeyHandled;
 
@@ -1449,7 +1457,7 @@
      */
     private HdmiControl getHdmiControl() {
         if (null == mHdmiControl) {
-            if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) {
+            if (!mHasFeatureHdmiCec) {
                 return null;
             }
             HdmiControlManager manager = (HdmiControlManager) mContext.getSystemService(
@@ -1685,6 +1693,7 @@
         mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
         mHasFeatureWatch = mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH);
         mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK);
+        mHasFeatureHdmiCec = mContext.getPackageManager().hasSystemFeature(FEATURE_HDMI_CEC);
         mAccessibilityShortcutController =
                 new AccessibilityShortcutController(mContext, new Handler(), mCurrentUserId);
         mLogger = new MetricsLogger();
@@ -1811,6 +1820,9 @@
         mHandleVolumeKeysInWM = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_handleVolumeKeysInWindowManager);
 
+        mPerDisplayFocusEnabled = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_perDisplayFocusEnabled);
+
         readConfigurationDependentBehaviors();
 
         mAccessibilityManager = (AccessibilityManager) context.getSystemService(
@@ -2542,6 +2554,23 @@
     /** {@inheritDoc} */
     @Override
     public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
+        final long result = interceptKeyBeforeDispatchingInner(win, event, policyFlags);
+        final int eventDisplayId = event.getDisplayId();
+        if (result == 0 && !mPerDisplayFocusEnabled
+                && eventDisplayId != INVALID_DISPLAY && eventDisplayId != mTopFocusedDisplayId) {
+            // Someone tries to send a key event to a display which doesn't have a focused window.
+            // We drop the event here, or it will cause ANR.
+            // TODO (b/121057974): The user may be confused about why the key doesn't work, so we
+            // may need to deal with this problem.
+            Slog.i(TAG, "Dropping this event targeting display #" + eventDisplayId
+                    + " because the focus is on display #" + mTopFocusedDisplayId);
+            return -1;
+        }
+        return result;
+    }
+
+    private long interceptKeyBeforeDispatchingInner(WindowState win, KeyEvent event,
+            int policyFlags) {
         final boolean keyguardOn = keyguardOn();
         final int keyCode = event.getKeyCode();
         final int repeatCount = event.getRepeatCount();
@@ -3123,6 +3152,11 @@
     }
 
     @Override
+    public void setTopFocusedDisplay(int displayId) {
+        mTopFocusedDisplayId = displayId;
+    }
+
+    @Override
     public void registerShortcutKey(long shortcutCode, IShortcutService shortcutService)
             throws RemoteException {
         synchronized (mLock) {
@@ -4087,6 +4121,19 @@
     }
 
     private void dispatchDirectAudioEvent(KeyEvent event) {
+        // When System Audio Mode is off, volume keys received by AVR can be either consumed by AVR
+        // or forwarded to the TV. It's up to Amplifier manufacturer’s implementation.
+        HdmiControlManager hdmiControlManager = getHdmiControlManager();
+        if (null != hdmiControlManager
+                && !hdmiControlManager.getSystemAudioMode()
+                && shouldCecAudioDeviceForwardVolumeKeysSystemAudioModeOff()) {
+            HdmiAudioSystemClient audioSystemClient = hdmiControlManager.getAudioSystemClient();
+            if (audioSystemClient != null) {
+                audioSystemClient.sendKeyEvent(
+                        event.getKeyCode(), event.getAction() == KeyEvent.ACTION_DOWN);
+                return;
+            }
+        }
         if (event.getAction() != KeyEvent.ACTION_DOWN) {
             return;
         }
@@ -4094,6 +4141,7 @@
         int flags = AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_PLAY_SOUND
                 | AudioManager.FLAG_FROM_KEY;
         String pkgName = mContext.getOpPackageName();
+
         switch (keyCode) {
             case KeyEvent.KEYCODE_VOLUME_UP:
                 try {
@@ -4125,6 +4173,18 @@
         }
     }
 
+    @Nullable
+    private HdmiControlManager getHdmiControlManager() {
+        if (!mHasFeatureHdmiCec) {
+            return null;
+        }
+        return (HdmiControlManager) mContext.getSystemService(HdmiControlManager.class);
+    }
+
+    private boolean shouldCecAudioDeviceForwardVolumeKeysSystemAudioModeOff() {
+        return RoSystemProperties.CEC_AUDIO_DEVICE_FORWARD_VOLUME_KEYS_SYSTEM_AUDIO_MODE_OFF;
+    }
+
     void dispatchMediaKeyWithWakeLock(KeyEvent event) {
         if (DEBUG_INPUT) {
             Slog.d(TAG, "dispatchMediaKeyWithWakeLock: " + event);
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 3d474e3..3da325c 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -1034,6 +1034,13 @@
     public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags);
 
     /**
+     * Called when the top focused display is changed.
+     *
+     * @param displayId The ID of the top focused display.
+     */
+    void setTopFocusedDisplay(int displayId);
+
+    /**
      * Apply the keyguard policy to a specific window.
      *
      * @param win The window to apply the keyguard policy.
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
index f262f6d..a60f16d 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -15,8 +15,14 @@
  */
 package com.android.server.power.batterysaver;
 
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.os.Handler;
 import android.os.PowerManager;
@@ -26,6 +32,7 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
@@ -46,6 +53,8 @@
  */
 public class BatterySaverStateMachine {
     private static final String TAG = "BatterySaverStateMachine";
+    private static final String DYNAMIC_MODE_NOTIF_CHANNEL_ID = "dynamic_mode_notification";
+    private static final int DYNAMIC_MODE_NOTIFICATION_ID = 1992;
     private final Object mLock;
 
     private static final boolean DEBUG = BatterySaverPolicy.DEBUG;
@@ -484,6 +493,13 @@
         }
         mBatterySaverController.enableBatterySaver(enable, intReason);
 
+        // Handle triggering the notification to show/hide when appropriate
+        if (intReason == BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON) {
+            runOnBgThread(this::triggerDynamicModeNotification);
+        } else if (!enable) {
+            runOnBgThread(this::hideDynamicModeNotification);
+        }
+
         if (DEBUG) {
             Slog.d(TAG, "Battery saver: Enabled=" + enable
                     + " manual=" + manual
@@ -491,6 +507,44 @@
         }
     }
 
+    private void triggerDynamicModeNotification() {
+        NotificationManager manager = mContext.getSystemService(NotificationManager.class);
+        ensureNotificationChannelExists(manager);
+
+        manager.notify(DYNAMIC_MODE_NOTIFICATION_ID, buildNotification());
+    }
+
+    private void ensureNotificationChannelExists(NotificationManager manager) {
+        NotificationChannel channel = new NotificationChannel(
+                DYNAMIC_MODE_NOTIF_CHANNEL_ID,
+                mContext.getText(
+                        R.string.dynamic_mode_notification_channel_name),
+                NotificationManager.IMPORTANCE_DEFAULT);
+        channel.setSound(null, null);
+        manager.createNotificationChannel(channel);
+    }
+
+    private Notification buildNotification() {
+        Resources res = mContext.getResources();
+        Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        PendingIntent batterySaverIntent = PendingIntent.getActivity(
+                mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+        return new Notification.Builder(mContext, DYNAMIC_MODE_NOTIF_CHANNEL_ID)
+                .setSmallIcon(R.drawable.ic_battery)
+                .setContentTitle(res.getString(R.string.dynamic_mode_notification_title))
+                .setContentText(res.getString(R.string.dynamic_mode_notification_summary))
+                .setContentIntent(batterySaverIntent)
+                .setOnlyAlertOnce(true)
+                .build();
+    }
+
+    private void hideDynamicModeNotification() {
+        NotificationManager manager = mContext.getSystemService(NotificationManager.class);
+        manager.cancel(DYNAMIC_MODE_NOTIFICATION_ID);
+    }
+
     @GuardedBy("mLock")
     private void updateSnoozingLocked(boolean snoozing, String reason) {
         if (mBatterySaverSnoozing == snoozing) {
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index 35013de..f37ca12 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -503,6 +503,18 @@
             return userState.removeRoleHolder(roleName, packageName);
         }
 
+        @Override
+        public List<String> getHeldRolesFromController(@NonNull String packageName) {
+            Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
+            getContext().enforceCallingOrSelfPermission(
+                    RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
+                    "getRolesHeldFromController");
+
+            int userId = UserHandle.getCallingUserId();
+            RoleUserState userState = getOrCreateUserState(userId);
+            return userState.getHeldRoles(packageName);
+        }
+
         @CheckResult
         private int handleIncomingUser(@UserIdInt int userId, @NonNull String name) {
             return ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
diff --git a/services/core/java/com/android/server/role/RoleManagerShellCommand.java b/services/core/java/com/android/server/role/RoleManagerShellCommand.java
index 336b311..b245e98 100644
--- a/services/core/java/com/android/server/role/RoleManagerShellCommand.java
+++ b/services/core/java/com/android/server/role/RoleManagerShellCommand.java
@@ -23,6 +23,7 @@
 import android.os.RemoteException;
 import android.os.ShellCommand;
 import android.os.UserHandle;
+import android.util.Log;
 
 import java.io.PrintWriter;
 import java.util.concurrent.CompletableFuture;
@@ -47,7 +48,8 @@
                 mResult.get(5, TimeUnit.SECONDS);
                 return 0;
             } catch (Exception e) {
-                getErrPrintWriter().println("Error: " + e.toString());
+                getErrPrintWriter().println("Error: see logcat for details.\n"
+                        + Log.getStackTraceString(e));
                 return -1;
             }
         }
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index d55e261..630a39c 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -47,6 +47,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 
@@ -315,6 +316,21 @@
     }
 
     /**
+     * @see android.app.role.RoleManager#getHeldRolesFromController
+     */
+    @NonNull
+    public List<String> getHeldRoles(@NonNull String packageName) {
+        ArrayList<String> result = new ArrayList<>();
+        int size = mRoles.size();
+        for (int i = 0; i < size; i++) {
+            if (mRoles.valueAt(i).contains(packageName)) {
+                result.add(mRoles.keyAt(i));
+            }
+        }
+        return result;
+    }
+
+    /**
      * Schedule writing the state to file.
      */
     @GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/rollback/PackageRollbackData.java b/services/core/java/com/android/server/rollback/PackageRollbackData.java
new file mode 100644
index 0000000..15d1242
--- /dev/null
+++ b/services/core/java/com/android/server/rollback/PackageRollbackData.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.rollback;
+
+import android.content.rollback.PackageRollbackInfo;
+
+import java.io.File;
+import java.time.Instant;
+
+/**
+ * Information about a rollback available for a particular package.
+ * This is similar to {@link PackageRollbackInfo}, but extended with
+ * additional information for internal bookkeeping.
+ */
+class PackageRollbackData {
+    public final PackageRollbackInfo info;
+
+    /**
+     * The directory where the apk backup is stored.
+     */
+    public final File backupDir;
+
+    /**
+     * The time when the upgrade occurred, for purposes of expiring
+     * rollback data.
+     */
+    public final Instant timestamp;
+
+    PackageRollbackData(PackageRollbackInfo info,
+            File backupDir, Instant timestamp) {
+        this.info = info;
+        this.backupDir = backupDir;
+        this.timestamp = timestamp;
+    }
+}
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerService.java b/services/core/java/com/android/server/rollback/RollbackManagerService.java
new file mode 100644
index 0000000..4b5e764
--- /dev/null
+++ b/services/core/java/com/android/server/rollback/RollbackManagerService.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.rollback;
+
+import android.content.Context;
+
+import com.android.server.SystemService;
+
+/**
+ * Service that manages APK level rollbacks. Publishes
+ * Context.ROLLBACK_SERVICE.
+ *
+ * @hide
+ */
+public final class RollbackManagerService extends SystemService {
+
+    private RollbackManagerServiceImpl mService;
+
+    public RollbackManagerService(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void onStart() {
+        mService = new RollbackManagerServiceImpl(getContext());
+        publishBinderService(Context.ROLLBACK_SERVICE, mService);
+    }
+}
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
new file mode 100644
index 0000000..0c21312
--- /dev/null
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -0,0 +1,832 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.rollback;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageParser;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.StringParceledListSlice;
+import android.content.rollback.IRollbackManager;
+import android.content.rollback.PackageRollbackInfo;
+import android.content.rollback.RollbackInfo;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
+import com.android.server.pm.PackageManagerServiceUtils;
+
+import libcore.io.IoUtils;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.time.Instant;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Implementation of service that manages APK level rollbacks.
+ */
+class RollbackManagerServiceImpl extends IRollbackManager.Stub {
+
+    private static final String TAG = "RollbackManager";
+
+    // Rollbacks expire after 48 hours.
+    // TODO: How to test rollback expiration works properly?
+    private static final long ROLLBACK_LIFETIME_DURATION_MILLIS = 48 * 60 * 60 * 1000;
+
+    // Lock used to synchronize accesses to in-memory rollback data
+    // structures. By convention, methods with the suffix "Locked" require
+    // mLock is held when they are called.
+    private final Object mLock = new Object();
+
+    // Package rollback data available to be used for rolling back a package.
+    // This list is null until the rollback data has been loaded.
+    @GuardedBy("mLock")
+    private List<PackageRollbackData> mAvailableRollbacks;
+
+    // The list of recently executed rollbacks.
+    // This list is null until the rollback data has been loaded.
+    @GuardedBy("mLock")
+    private List<RollbackInfo> mRecentlyExecutedRollbacks;
+
+    // Data for available rollbacks and recently executed rollbacks is
+    // persisted in storage. Assuming the rollback data directory is
+    // /data/rollback, we use the following directory structure
+    // to store this data:
+    //   /data/rollback/
+    //      available/
+    //          com.package.A-XXX/
+    //              base.apk
+    //              rollback.json
+    //          com.package.B-YYY/
+    //              base.apk
+    //              rollback.json
+    //      recently_executed.json
+    // TODO: Use AtomicFile for rollback.json and recently_executed.json.
+    private final File mRollbackDataDir;
+    private final File mAvailableRollbacksDir;
+    private final File mRecentlyExecutedRollbacksFile;
+
+    private final Context mContext;
+    private final HandlerThread mHandlerThread;
+
+    RollbackManagerServiceImpl(Context context) {
+        mContext = context;
+        mHandlerThread = new HandlerThread("RollbackManagerServiceHandler");
+        mHandlerThread.start();
+
+        mRollbackDataDir = new File(Environment.getDataDirectory(), "rollback");
+        mAvailableRollbacksDir = new File(mRollbackDataDir, "available");
+        mRecentlyExecutedRollbacksFile = new File(mRollbackDataDir, "recently_executed.json");
+
+        // Kick off loading of the rollback data from strorage in a background
+        // thread.
+        // TODO: Consider loading the rollback data directly here instead, to
+        // avoid the need to call ensureRollbackDataLoaded every time before
+        // accessing the rollback data?
+        // TODO: Test that this kicks off initial scheduling of rollback
+        // expiration.
+        getHandler().post(() -> ensureRollbackDataLoaded());
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+        filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
+        filter.addDataScheme("package");
+        mContext.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                String action = intent.getAction();
+                if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
+                    String packageName = intent.getData().getSchemeSpecificPart();
+                    onPackageReplaced(packageName);
+                }
+                if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
+                    String packageName = intent.getData().getSchemeSpecificPart();
+                    onPackageFullyRemoved(packageName);
+                }
+            }
+        }, filter, null, getHandler());
+
+        IntentFilter enableRollbackFilter = new IntentFilter();
+        enableRollbackFilter.addAction(Intent.ACTION_PACKAGE_ENABLE_ROLLBACK);
+        try {
+            enableRollbackFilter.addDataType("application/vnd.android.package-archive");
+        } catch (IntentFilter.MalformedMimeTypeException e) {
+            Log.e(TAG, "addDataType", e);
+        }
+
+        mContext.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (Intent.ACTION_PACKAGE_ENABLE_ROLLBACK.equals(intent.getAction())) {
+                    int token = intent.getIntExtra(
+                            PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN, -1);
+                    int installFlags = intent.getIntExtra(
+                            PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALL_FLAGS, 0);
+                    File newPackageCodePath = new File(intent.getData().getPath());
+
+                    getHandler().post(() -> {
+                        boolean success = enableRollback(installFlags, newPackageCodePath);
+                        int ret = PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED;
+                        if (!success) {
+                            ret = PackageManagerInternal.ENABLE_ROLLBACK_FAILED;
+                        }
+
+                        PackageManagerInternal pm = LocalServices.getService(
+                                PackageManagerInternal.class);
+                        pm.setEnableRollbackCode(token, ret);
+                    });
+
+                    // We're handling the ordered broadcast. Abort the
+                    // broadcast because there is no need for it to go to
+                    // anyone else.
+                    abortBroadcast();
+                }
+            }
+        }, enableRollbackFilter, null, getHandler());
+    }
+
+    @Override
+    public RollbackInfo getAvailableRollback(String packageName) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_ROLLBACKS,
+                "getAvailableRollback");
+
+        PackageRollbackInfo.PackageVersion installedVersion =
+                getInstalledPackageVersion(packageName);
+        if (installedVersion == null) {
+            return null;
+        }
+
+        synchronized (mLock) {
+            // TODO: Have ensureRollbackDataLoadedLocked return the list of
+            // available rollbacks, to hopefully avoid forgetting to call it?
+            ensureRollbackDataLoadedLocked();
+            for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
+                PackageRollbackData data = mAvailableRollbacks.get(i);
+                if (data.info.packageName.equals(packageName)
+                        && data.info.higherVersion.equals(installedVersion)) {
+                    // TODO: For atomic installs, check all dependent packages
+                    // for available rollbacks and include that info here.
+                    return new RollbackInfo(data.info);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public StringParceledListSlice getPackagesWithAvailableRollbacks() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_ROLLBACKS,
+                "getPackagesWithAvailableRollbacks");
+
+        // TODO: This may return packages whose rollback is out of date or
+        // expired.  Presumably that's okay because the package rollback could
+        // be expired anyway between when the caller calls this method and
+        // when the caller calls getAvailableRollback for more details.
+        final Set<String> packageNames = new HashSet<>();
+        synchronized (mLock) {
+            ensureRollbackDataLoadedLocked();
+            for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
+                PackageRollbackData data = mAvailableRollbacks.get(i);
+                packageNames.add(data.info.packageName);
+            }
+        }
+        return new StringParceledListSlice(new ArrayList<>(packageNames));
+    }
+
+    @Override
+    public ParceledListSlice<RollbackInfo> getRecentlyExecutedRollbacks() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_ROLLBACKS,
+                "getRecentlyExecutedRollbacks");
+
+        synchronized (mLock) {
+            ensureRollbackDataLoadedLocked();
+            List<RollbackInfo> rollbacks = new ArrayList<>(mRecentlyExecutedRollbacks);
+            return new ParceledListSlice<>(rollbacks);
+        }
+    }
+
+    @Override
+    public void executeRollback(RollbackInfo rollback, String callerPackageName,
+            IntentSender statusReceiver) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_ROLLBACKS,
+                "executeRollback");
+
+        final int callingUid = Binder.getCallingUid();
+        AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+        appOps.checkPackage(callingUid, callerPackageName);
+
+        getHandler().post(() ->
+                executeRollbackInternal(rollback, callerPackageName, statusReceiver));
+    }
+
+    /**
+     * Performs the actual work to execute a rollback.
+     * The work is done on the current thread. This may be a long running
+     * operation.
+     */
+    private void executeRollbackInternal(RollbackInfo rollback,
+            String callerPackageName, IntentSender statusReceiver) {
+        String packageName = rollback.targetPackage.packageName;
+        Log.i(TAG, "Initiating rollback of " + packageName);
+
+        PackageRollbackInfo.PackageVersion installedVersion =
+                getInstalledPackageVersion(packageName);
+        if (installedVersion == null) {
+            // TODO: Test this case
+            sendFailure(statusReceiver, "Target package to roll back is not installed");
+            return;
+        }
+
+        if (!rollback.targetPackage.higherVersion.equals(installedVersion)) {
+            // TODO: Test this case
+            sendFailure(statusReceiver, "Target package version to roll back not installed.");
+            return;
+        }
+
+        // TODO: We assume that between now and the time we commit the
+        // downgrade install, the currently installed package version does not
+        // change. This is not safe to assume, particularly in the case of a
+        // rollback racing with a roll-forward fix of a buggy package.
+        // Figure out how to ensure we don't commit the rollback if
+        // roll forward happens at the same time.
+        PackageRollbackData data = null;
+        synchronized (mLock) {
+            ensureRollbackDataLoadedLocked();
+            for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
+                PackageRollbackData available = mAvailableRollbacks.get(i);
+                // TODO: Check if available.info.lowerVersion matches
+                // rollback.targetPackage.lowerVersion?
+                if (available.info.packageName.equals(packageName)
+                        && available.info.higherVersion.equals(installedVersion)) {
+                    data = available;
+                    break;
+                }
+            }
+        }
+
+        if (data == null) {
+            sendFailure(statusReceiver, "Rollback not available");
+            return;
+        }
+
+        // Get a context for the caller to use to install the downgraded
+        // version of the package.
+        Context context = null;
+        try {
+            context = mContext.createPackageContext(callerPackageName, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            sendFailure(statusReceiver, "Invalid callerPackageName");
+            return;
+        }
+
+        PackageManager pm = context.getPackageManager();
+        try {
+            PackageInstaller.Session session = null;
+
+            PackageInstaller packageInstaller = pm.getPackageInstaller();
+            PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+                    PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+            params.setAllowDowngrade(true);
+            int sessionId = packageInstaller.createSession(params);
+            session = packageInstaller.openSession(sessionId);
+
+            // TODO: Will it always be called "base.apk"? What about splits?
+            File baseApk = new File(data.backupDir, "base.apk");
+            try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(baseApk,
+                    ParcelFileDescriptor.MODE_READ_ONLY)) {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    session.write("base.apk", 0, baseApk.length(), fd);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+
+            final LocalIntentReceiver receiver = new LocalIntentReceiver();
+            session.commit(receiver.getIntentSender());
+
+            Intent result = receiver.getResult();
+            int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                    PackageInstaller.STATUS_FAILURE);
+            if (status != PackageInstaller.STATUS_SUCCESS) {
+                sendFailure(statusReceiver, "Rollback downgrade install failed: "
+                        + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE));
+                return;
+            }
+
+            addRecentlyExecutedRollback(rollback);
+            sendSuccess(statusReceiver);
+
+            Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED,
+                    Uri.fromParts("package", packageName, Manifest.permission.MANAGE_ROLLBACKS));
+
+            // TODO: This call emits the warning "Calling a method in the
+            // system process without a qualified user". Fix that.
+            mContext.sendBroadcast(broadcast);
+        } catch (IOException e) {
+            Log.e(TAG, "Unable to roll back " + packageName, e);
+            sendFailure(statusReceiver, "IOException: " + e.toString());
+            return;
+        }
+    }
+
+    @Override
+    public void reloadPersistedData() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_ROLLBACKS,
+                "reloadPersistedData");
+
+        synchronized (mLock) {
+            mAvailableRollbacks = null;
+            mRecentlyExecutedRollbacks = null;
+        }
+        getHandler().post(() -> ensureRollbackDataLoaded());
+    }
+
+    @Override
+    public void expireRollbackForPackage(String packageName) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_ROLLBACKS,
+                "expireRollbackForPackage");
+
+        // TODO: Should this take a package version number in addition to
+        // package name? For now, just remove all rollbacks matching the
+        // package name. This method is only currently used to facilitate
+        // testing anyway.
+        synchronized (mLock) {
+            ensureRollbackDataLoadedLocked();
+            Iterator<PackageRollbackData> iter = mAvailableRollbacks.iterator();
+            while (iter.hasNext()) {
+                PackageRollbackData data = iter.next();
+                if (data.info.packageName.equals(packageName)) {
+                    iter.remove();
+                    removeFile(data.backupDir);
+                }
+            }
+        }
+    }
+
+    /**
+     * Load rollback data from storage if it has not already been loaded.
+     * After calling this funciton, mAvailableRollbacks and
+     * mRecentlyExecutedRollbacks will be non-null.
+     */
+    private void ensureRollbackDataLoaded() {
+        synchronized (mLock) {
+            ensureRollbackDataLoadedLocked();
+        }
+    }
+
+    /**
+     * Load rollback data from storage if it has not already been loaded.
+     * After calling this function, mAvailableRollbacks and
+     * mRecentlyExecutedRollbacks will be non-null.
+     */
+    @GuardedBy("mLock")
+    private void ensureRollbackDataLoadedLocked() {
+        if (mAvailableRollbacks == null) {
+            loadRollbackDataLocked();
+        }
+    }
+
+    /**
+     * Load rollback data from storage.
+     * Note: We do potentially heavy IO here while holding mLock, because we
+     * have to have the rollback data loaded before we can do anything else
+     * meaningful.
+     */
+    @GuardedBy("mLock")
+    private void loadRollbackDataLocked() {
+        mAvailableRollbacksDir.mkdirs();
+        mAvailableRollbacks = new ArrayList<>();
+        for (File rollbackDir : mAvailableRollbacksDir.listFiles()) {
+            if (rollbackDir.isDirectory()) {
+                // TODO: How to detect and clean up an invalid rollback
+                // directory? We don't know if it's invalid because something
+                // went wrong, or if it's only temporarily invalid because
+                // it's in the process of being created.
+                try {
+                    File jsonFile = new File(rollbackDir, "rollback.json");
+                    String jsonString = IoUtils.readFileAsString(jsonFile.getAbsolutePath());
+                    JSONObject jsonObject = new JSONObject(jsonString);
+                    String packageName = jsonObject.getString("packageName");
+                    long higherVersionCode = jsonObject.getLong("higherVersionCode");
+                    long lowerVersionCode = jsonObject.getLong("lowerVersionCode");
+                    Instant timestamp = Instant.parse(jsonObject.getString("timestamp"));
+                    PackageRollbackData data = new PackageRollbackData(
+                            new PackageRollbackInfo(packageName,
+                                new PackageRollbackInfo.PackageVersion(higherVersionCode),
+                                new PackageRollbackInfo.PackageVersion(lowerVersionCode)),
+                            rollbackDir, timestamp);
+                    mAvailableRollbacks.add(data);
+                } catch (IOException | JSONException | DateTimeParseException e) {
+                    Log.e(TAG, "Unable to read rollback data at " + rollbackDir, e);
+                }
+            }
+        }
+
+        mRecentlyExecutedRollbacks = new ArrayList<>();
+        if (mRecentlyExecutedRollbacksFile.exists()) {
+            try {
+                // TODO: How to cope with changes to the format of this file from
+                // when RollbackStore is updated in the future?
+                String jsonString = IoUtils.readFileAsString(
+                        mRecentlyExecutedRollbacksFile.getAbsolutePath());
+                JSONObject object = new JSONObject(jsonString);
+                JSONArray array = object.getJSONArray("recentlyExecuted");
+                for (int i = 0; i < array.length(); ++i) {
+                    JSONObject element = array.getJSONObject(i);
+                    String packageName = element.getString("packageName");
+                    long higherVersionCode = element.getLong("higherVersionCode");
+                    long lowerVersionCode = element.getLong("lowerVersionCode");
+                    PackageRollbackInfo target = new PackageRollbackInfo(packageName,
+                            new PackageRollbackInfo.PackageVersion(higherVersionCode),
+                            new PackageRollbackInfo.PackageVersion(lowerVersionCode));
+                    RollbackInfo rollback = new RollbackInfo(target);
+                    mRecentlyExecutedRollbacks.add(rollback);
+                }
+            } catch (IOException | JSONException e) {
+                // TODO: What to do here? Surely we shouldn't just forget about
+                // everything after the point of exception?
+                Log.e(TAG, "Failed to read recently executed rollbacks", e);
+            }
+        }
+
+        scheduleExpiration(0);
+    }
+
+    /**
+     * Called when a package has been replaced with a different version.
+     * Removes all backups for the package not matching the currently
+     * installed package version.
+     */
+    private void onPackageReplaced(String packageName) {
+        // TODO: Could this end up incorrectly deleting a rollback for a
+        // package that is about to be installed?
+        PackageRollbackInfo.PackageVersion installedVersion =
+                getInstalledPackageVersion(packageName);
+
+        synchronized (mLock) {
+            ensureRollbackDataLoadedLocked();
+            Iterator<PackageRollbackData> iter = mAvailableRollbacks.iterator();
+            while (iter.hasNext()) {
+                PackageRollbackData data = iter.next();
+                if (data.info.packageName.equals(packageName)
+                        && !data.info.higherVersion.equals(installedVersion)) {
+                    iter.remove();
+                    removeFile(data.backupDir);
+                }
+            }
+        }
+    }
+
+    /**
+     * Called when a package has been completely removed from the device.
+     * Removes all backups and rollback history for the given package.
+     */
+    private void onPackageFullyRemoved(String packageName) {
+        expireRollbackForPackage(packageName);
+
+        synchronized (mLock) {
+            ensureRollbackDataLoadedLocked();
+            Iterator<RollbackInfo> iter = mRecentlyExecutedRollbacks.iterator();
+            boolean changed = false;
+            while (iter.hasNext()) {
+                RollbackInfo rollback = iter.next();
+                if (packageName.equals(rollback.targetPackage.packageName)) {
+                    iter.remove();
+                    changed = true;
+                }
+            }
+
+            if (changed) {
+                saveRecentlyExecutedRollbacksLocked();
+            }
+        }
+    }
+
+    /**
+     * Write the list of recently executed rollbacks to storage.
+     * Note: This happens while mLock is held, which should be okay because we
+     * expect executed rollbacks to be modified only in exceptional cases.
+     */
+    @GuardedBy("mLock")
+    private void saveRecentlyExecutedRollbacksLocked() {
+        try {
+            JSONObject json = new JSONObject();
+            JSONArray array = new JSONArray();
+            json.put("recentlyExecuted", array);
+
+            for (int i = 0; i < mRecentlyExecutedRollbacks.size(); ++i) {
+                RollbackInfo rollback = mRecentlyExecutedRollbacks.get(i);
+                JSONObject element = new JSONObject();
+                element.put("packageName", rollback.targetPackage.packageName);
+                element.put("higherVersionCode", rollback.targetPackage.higherVersion.versionCode);
+                element.put("lowerVersionCode", rollback.targetPackage.lowerVersion.versionCode);
+                array.put(element);
+            }
+
+            PrintWriter pw = new PrintWriter(mRecentlyExecutedRollbacksFile);
+            pw.println(json.toString());
+            pw.close();
+        } catch (IOException | JSONException e) {
+            // TODO: What to do here?
+            Log.e(TAG, "Failed to save recently executed rollbacks", e);
+        }
+    }
+
+    /**
+     * Records that the given package has been recently rolled back.
+     */
+    private void addRecentlyExecutedRollback(RollbackInfo rollback) {
+        // TODO: if the list of rollbacks gets too big, trim it to only those
+        // that are necessary to keep track of.
+        synchronized (mLock) {
+            ensureRollbackDataLoadedLocked();
+            mRecentlyExecutedRollbacks.add(rollback);
+            saveRecentlyExecutedRollbacksLocked();
+        }
+    }
+
+    /**
+     * Notifies an IntentSender of failure.
+     *
+     * @param statusReceiver where to send the failure
+     * @param message the failure message.
+     */
+    private void sendFailure(IntentSender statusReceiver, String message) {
+        Log.e(TAG, message);
+        try {
+            // TODO: More context on which rollback failed?
+            // TODO: More refined failure code?
+            final Intent fillIn = new Intent();
+            fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
+            fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, message);
+            statusReceiver.sendIntent(mContext, 0, fillIn, null, null);
+        } catch (IntentSender.SendIntentException e) {
+            // Nowhere to send the result back to, so don't bother.
+        }
+    }
+
+    /**
+     * Notifies an IntentSender of success.
+     */
+    private void sendSuccess(IntentSender statusReceiver) {
+        try {
+            final Intent fillIn = new Intent();
+            fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_SUCCESS);
+            statusReceiver.sendIntent(mContext, 0, fillIn, null, null);
+        } catch (IntentSender.SendIntentException e) {
+            // Nowhere to send the result back to, so don't bother.
+        }
+    }
+
+    // Check to see if anything needs expiration, and if so, expire it.
+    // Schedules future expiration as appropriate.
+    // TODO: Handle cases where the user changes time on the device.
+    private void runExpiration() {
+        Instant now = Instant.now();
+        Instant oldest = null;
+        synchronized (mLock) {
+            ensureRollbackDataLoadedLocked();
+
+            Iterator<PackageRollbackData> iter = mAvailableRollbacks.iterator();
+            while (iter.hasNext()) {
+                PackageRollbackData data = iter.next();
+                if (!now.isBefore(data.timestamp.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS))) {
+                    iter.remove();
+                    removeFile(data.backupDir);
+                } else if (oldest == null || oldest.isAfter(data.timestamp)) {
+                    oldest = data.timestamp;
+                }
+            }
+        }
+
+        if (oldest != null) {
+            scheduleExpiration(now.until(oldest.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS),
+                        ChronoUnit.MILLIS));
+        }
+    }
+
+    /**
+     * Schedules an expiration check to be run after the given duration in
+     * milliseconds has gone by.
+     */
+    private void scheduleExpiration(long duration) {
+        getHandler().postDelayed(() -> runExpiration(), duration);
+    }
+
+    private Handler getHandler() {
+        return mHandlerThread.getThreadHandler();
+    }
+
+    /**
+     * Called via broadcast by the package manager when a package is being
+     * staged for install with rollback enabled. Called before the package has
+     * been installed.
+     *
+     * @param id the id of the enable rollback request
+     * @param installFlags information about what is being installed.
+     * @param newPackageCodePath path to the package about to be installed.
+     * @return true if enabling the rollback succeeds, false otherwise.
+     */
+    private boolean enableRollback(int installFlags, File newPackageCodePath) {
+        if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
+            Log.e(TAG, "Rollbacks not supported for instant app install");
+            return false;
+        }
+        if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
+            Log.e(TAG, "Rollbacks not supported for apex install");
+            return false;
+        }
+
+        // Get information about the package to be installed.
+        PackageParser.PackageLite newPackage = null;
+        try {
+            newPackage = PackageParser.parsePackageLite(newPackageCodePath, 0);
+        } catch (PackageParser.PackageParserException e) {
+            Log.e(TAG, "Unable to parse new package", e);
+            return false;
+        }
+
+        String packageName = newPackage.packageName;
+        Log.i(TAG, "Enabling rollback for install of " + packageName);
+
+        PackageRollbackInfo.PackageVersion newVersion =
+                new PackageRollbackInfo.PackageVersion(newPackage.versionCode);
+
+        // Get information about the currently installed package.
+        PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
+        PackageParser.Package installedPackage = pm.getPackage(packageName);
+        if (installedPackage == null) {
+            // TODO: Support rolling back fresh package installs rather than
+            // fail here. Test this case.
+            Log.e(TAG, packageName + " is not installed");
+            return false;
+        }
+        PackageRollbackInfo.PackageVersion installedVersion =
+                new PackageRollbackInfo.PackageVersion(installedPackage.getLongVersionCode());
+
+        File backupDir;
+        try {
+            backupDir = Files.createTempDirectory(
+                mAvailableRollbacksDir.toPath(), packageName + "-").toFile();
+        } catch (IOException e) {
+            Log.e(TAG, "Unable to create rollback for " + packageName, e);
+            return false;
+        }
+
+        // TODO: Should the timestamp be for when we commit the install, not
+        // when we create the pending one?
+        Instant timestamp = Instant.now();
+        try {
+            JSONObject json = new JSONObject();
+            json.put("packageName", packageName);
+            json.put("higherVersionCode", newVersion.versionCode);
+            json.put("lowerVersionCode", installedVersion.versionCode);
+            json.put("timestamp", timestamp.toString());
+
+            File jsonFile = new File(backupDir, "rollback.json");
+            PrintWriter pw = new PrintWriter(jsonFile);
+            pw.println(json.toString());
+            pw.close();
+        } catch (IOException | JSONException e) {
+            Log.e(TAG, "Unable to create rollback for " + packageName, e);
+            removeFile(backupDir);
+            return false;
+        }
+
+        // TODO: Copy by hard link instead to save on cpu and storage space?
+        int status = PackageManagerServiceUtils.copyPackage(installedPackage.codePath, backupDir);
+        if (status != PackageManager.INSTALL_SUCCEEDED) {
+            Log.e(TAG, "Unable to copy package for rollback for " + packageName);
+            removeFile(backupDir);
+            return false;
+        }
+
+        PackageRollbackData data = new PackageRollbackData(
+                new PackageRollbackInfo(packageName, newVersion, installedVersion),
+                backupDir, timestamp);
+
+        synchronized (mLock) {
+            ensureRollbackDataLoadedLocked();
+            mAvailableRollbacks.add(data);
+        }
+
+        return true;
+    }
+
+    // TODO: Don't copy this from PackageManagerShellCommand like this?
+    private static class LocalIntentReceiver {
+        private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>();
+
+        private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+            @Override
+            public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+                    IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+                try {
+                    mResult.offer(intent, 5, TimeUnit.SECONDS);
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        };
+
+        public IntentSender getIntentSender() {
+            return new IntentSender((IIntentSender) mLocalSender);
+        }
+
+        public Intent getResult() {
+            try {
+                return mResult.take();
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    /**
+     * Deletes a file completely.
+     * If the file is a directory, its contents are deleted as well.
+     * Has no effect if the directory does not exist.
+     */
+    private void removeFile(File file) {
+        if (file.isDirectory()) {
+            for (File child : file.listFiles()) {
+                removeFile(child);
+            }
+        }
+        if (file.exists()) {
+            file.delete();
+        }
+    }
+
+    /**
+     * Gets the version of the package currently installed.
+     * Returns null if the package is not currently installed.
+     */
+    private PackageRollbackInfo.PackageVersion getInstalledPackageVersion(String packageName) {
+        PackageManager pm = mContext.getPackageManager();
+        PackageInfo pkgInfo = null;
+        try {
+            pkgInfo = pm.getPackageInfo(packageName, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            return null;
+        }
+
+        return new PackageRollbackInfo.PackageVersion(pkgInfo.getLongVersionCode());
+    }
+}
diff --git a/services/core/java/com/android/server/rollback/TEST_MAPPING b/services/core/java/com/android/server/rollback/TEST_MAPPING
new file mode 100644
index 0000000..c1d95ac
--- /dev/null
+++ b/services/core/java/com/android/server/rollback/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "RollbackTest"
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index 514dfed..839ed30 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -16,13 +16,11 @@
 
 package com.android.server.security;
 
-import static android.system.OsConstants.PROT_READ;
-import static android.system.OsConstants.PROT_WRITE;
-
 import android.annotation.NonNull;
 import android.os.SharedMemory;
 import android.system.ErrnoException;
 import android.system.Os;
+import android.system.OsConstants;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.apk.ApkSignatureVerifier;
@@ -32,6 +30,7 @@
 
 import libcore.util.HexEncoding;
 
+import java.io.File;
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.RandomAccessFile;
@@ -51,11 +50,42 @@
 abstract public class VerityUtils {
     private static final String TAG = "VerityUtils";
 
+    /**
+     * File extension of the signature file. For example, foo.apk.fsv_sig is the signature file of
+     * foo.apk.
+     */
+    public static final String FSVERITY_SIGNATURE_FILE_EXTENSION = ".fsv_sig";
+
     /** The maximum size of signature file.  This is just to avoid potential abuse. */
     private static final int MAX_SIGNATURE_FILE_SIZE_BYTES = 8192;
 
     private static final boolean DEBUG = false;
 
+    /** Returns true if the given file looks like containing an fs-verity signature. */
+    public static boolean isFsveritySignatureFile(File file) {
+        return file.getName().endsWith(FSVERITY_SIGNATURE_FILE_EXTENSION);
+    }
+
+    /** Returns the fs-verity signature file path of the given file. */
+    public static String getFsveritySignatureFilePath(String filePath) {
+        return filePath + FSVERITY_SIGNATURE_FILE_EXTENSION;
+    }
+
+    /** Returns whether the file has fs-verity enabled. */
+    public static boolean hasFsverity(@NonNull String filePath) {
+        // NB: only measure but not check the actual measurement here. As long as this succeeds,
+        // the file is on readable if the measurement can be verified against a trusted key, and
+        // this is good enough for installed apps.
+        int errno = measureFsverityNative(filePath);
+        if (errno != 0) {
+            if (errno != OsConstants.ENODATA) {
+                Slog.e(TAG, "Failed to measure fs-verity, errno " + errno + ": " + filePath);
+            }
+            return false;
+        }
+        return true;
+    }
+
     /**
      * Generates Merkle tree and fs-verity metadata.
      *
@@ -213,6 +243,7 @@
         return md.digest();
     }
 
+    private static native int measureFsverityNative(@NonNull String filePath);
     private static native byte[] constructFsveritySignedDataNative(@NonNull byte[] measurement);
     private static native byte[] constructFsverityDescriptorNative(long fileSize);
     private static native byte[] constructFsverityExtensionNative(short extensionId,
@@ -249,7 +280,7 @@
         if (shm == null) {
             throw new IllegalStateException("Failed to generate verity tree into shared memory");
         }
-        if (!shm.setProtect(PROT_READ)) {
+        if (!shm.setProtect(OsConstants.PROT_READ)) {
             throw new SecurityException("Failed to set up shared memory correctly");
         }
         return Pair.create(shm, contentSize);
@@ -323,7 +354,7 @@
                     throw new IllegalStateException("Multiple instantiation from this factory");
                 }
                 mShm = SharedMemory.create("apkverity", capacity);
-                if (!mShm.setProtect(PROT_READ | PROT_WRITE)) {
+                if (!mShm.setProtect(OsConstants.PROT_READ | OsConstants.PROT_WRITE)) {
                     throw new SecurityException("Failed to set protection");
                 }
                 mBuffer = mShm.mapReadWrite();
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java b/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java
similarity index 86%
rename from services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
rename to services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java
index 4908964..438c303 100644
--- a/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
+++ b/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java
@@ -30,7 +30,7 @@
 import java.util.Map;
 import java.util.Set;
 
-class SignedConfigApplicator {
+class GlobalSettingsConfigApplicator {
 
     private static final String TAG = "SignedConfig";
 
@@ -68,7 +68,7 @@
     private final String mSourcePackage;
     private final SignatureVerifier mVerifier;
 
-    SignedConfigApplicator(Context context, String sourcePackage) {
+    GlobalSettingsConfigApplicator(Context context, String sourcePackage) {
         mContext = context;
         mSourcePackage = sourcePackage;
         mVerifier = new SignatureVerifier();
@@ -102,7 +102,7 @@
 
     void applyConfig(String configStr, String signature) {
         if (!checkSignature(configStr, signature)) {
-            Slog.e(TAG, "Signature check on signed configuration in package " + mSourcePackage
+            Slog.e(TAG, "Signature check on global settings in package " + mSourcePackage
                     + " failed; ignoring");
             return;
         }
@@ -110,26 +110,26 @@
         try {
             config = SignedConfig.parse(configStr, ALLOWED_KEYS, KEY_VALUE_MAPPERS);
         } catch (InvalidConfigException e) {
-            Slog.e(TAG, "Failed to parse config from package " + mSourcePackage, e);
+            Slog.e(TAG, "Failed to parse global settings from package " + mSourcePackage, e);
             return;
         }
         int currentVersion = getCurrentConfigVersion();
         if (currentVersion >= config.version) {
-            Slog.i(TAG, "Config from package " + mSourcePackage + " is older than existing: "
-                    + config.version + "<=" + currentVersion);
+            Slog.i(TAG, "Global settings from package " + mSourcePackage
+                    + " is older than existing: " + config.version + "<=" + currentVersion);
             return;
         }
         // We have new config!
-        Slog.i(TAG, "Got new signed config from package " + mSourcePackage + ": version "
+        Slog.i(TAG, "Got new global settings from package " + mSourcePackage + ": version "
                 + config.version + " replacing existing version " + currentVersion);
         SignedConfig.PerSdkConfig matchedConfig =
                 config.getMatchingConfig(Build.VERSION.SDK_INT);
         if (matchedConfig == null) {
-            Slog.i(TAG, "Config is not applicable to current SDK version; ignoring");
+            Slog.i(TAG, "Settings is not applicable to current SDK version; ignoring");
             return;
         }
 
-        Slog.i(TAG, "Updating signed config to version " + config.version);
+        Slog.i(TAG, "Updating global settings to version " + config.version);
         updateCurrentConfig(config.version, matchedConfig.values);
     }
 }
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigService.java b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
index 84ce93f..6bcee14 100644
--- a/services/core/java/com/android/server/signedconfig/SignedConfigService.java
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
@@ -42,8 +42,8 @@
     private static final String TAG = "SignedConfig";
 
     // TODO should these be elsewhere? In a public API?
-    private static final String KEY_CONFIG = "android.signedconfig";
-    private static final String KEY_CONFIG_SIGNATURE = "android.signedconfig.signature";
+    private static final String KEY_GLOBAL_SETTINGS = "android.settings.global";
+    private static final String KEY_GLOBAL_SETTINGS_SIGNATURE = "android.settings.global.signature";
 
     private static class UpdateReceiver extends BroadcastReceiver {
         @Override
@@ -80,25 +80,25 @@
             if (DBG) Slog.d(TAG, "handlePackageBroadcast: no metadata");
             return;
         }
-        if (metaData.containsKey(KEY_CONFIG)
-                && metaData.containsKey(KEY_CONFIG_SIGNATURE)) {
-            String config = metaData.getString(KEY_CONFIG);
-            String signature = metaData.getString(KEY_CONFIG_SIGNATURE);
+        if (metaData.containsKey(KEY_GLOBAL_SETTINGS)
+                && metaData.containsKey(KEY_GLOBAL_SETTINGS_SIGNATURE)) {
+            String config = metaData.getString(KEY_GLOBAL_SETTINGS);
+            String signature = metaData.getString(KEY_GLOBAL_SETTINGS_SIGNATURE);
             try {
                 // Base64 encoding is standard (not URL safe) encoding: RFC4648
                 config = new String(Base64.getDecoder().decode(config), StandardCharsets.UTF_8);
             } catch (IllegalArgumentException iae) {
-                Slog.e(TAG, "Failed to base64 decode config from " + packageName);
+                Slog.e(TAG, "Failed to base64 decode global settings config from " + packageName);
                 return;
             }
             if (DBG) {
-                Slog.d(TAG, "Got signed config: " + config);
-                Slog.d(TAG, "Got config signature: " + signature);
+                Slog.d(TAG, "Got global settings config: " + config);
+                Slog.d(TAG, "Got global settings signature: " + signature);
             }
-            new SignedConfigApplicator(mContext, packageName).applyConfig(
+            new GlobalSettingsConfigApplicator(mContext, packageName).applyConfig(
                     config, signature);
         } else {
-            if (DBG) Slog.d(TAG, "Package has no config/signature.");
+            if (DBG) Slog.d(TAG, "Package has no global settings config/signature.");
         }
     }
 
diff --git a/services/core/java/com/android/server/slice/SliceClientPermissions.java b/services/core/java/com/android/server/slice/SliceClientPermissions.java
index e461e0d..ab94a59 100644
--- a/services/core/java/com/android/server/slice/SliceClientPermissions.java
+++ b/services/core/java/com/android/server/slice/SliceClientPermissions.java
@@ -282,9 +282,12 @@
         public synchronized void writeTo(XmlSerializer out) throws IOException {
             final int N = mPaths.size();
             for (int i = 0; i < N; i++) {
-                out.startTag(NAMESPACE, TAG_PATH);
-                out.text(encodeSegments(mPaths.valueAt(i)));
-                out.endTag(NAMESPACE, TAG_PATH);
+                final String[] segments = mPaths.valueAt(i);
+                if (segments != null) {
+                    out.startTag(NAMESPACE, TAG_PATH);
+                    out.text(encodeSegments(segments));
+                    out.endTag(NAMESPACE, TAG_PATH);
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/slice/SlicePermissionManager.java b/services/core/java/com/android/server/slice/SlicePermissionManager.java
index 780bc96..315d5e3 100644
--- a/services/core/java/com/android/server/slice/SlicePermissionManager.java
+++ b/services/core/java/com/android/server/slice/SlicePermissionManager.java
@@ -315,7 +315,8 @@
         return new AtomicFile(new File(mSliceDir, fileName));
     }
 
-    private void handlePersist() {
+    @VisibleForTesting
+    void handlePersist() {
         synchronized (this) {
             for (Persistable persistable : mDirty) {
                 AtomicFile file = getFile(persistable.getFileName());
@@ -335,7 +336,7 @@
 
                     out.flush();
                     file.finishWrite(stream);
-                } catch (IOException | XmlPullParserException e) {
+                } catch (IOException | XmlPullParserException | RuntimeException e) {
                     Slog.w(TAG, "Failed to save access file, restoring backup", e);
                     file.failWrite(stream);
                 }
@@ -344,6 +345,12 @@
         }
     }
 
+    // use addPersistableDirty(); this is just for tests
+    @VisibleForTesting
+    void addDirtyImmediate(Persistable obj) {
+        mDirty.add(obj);
+    }
+
     private void handleRemove(PkgUser pkgUser) {
         getFile(SliceClientPermissions.getFileName(pkgUser)).delete();
         getFile(SliceProviderPermissions.getFileName(pkgUser)).delete();
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index f0ebb75..30aa528 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -74,6 +74,7 @@
 import android.os.StatsLogEventWrapper;
 import android.os.SynchronousResultReceiver;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.os.Temperature;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -1140,7 +1141,8 @@
             e.writeLong(rssHighWaterMarkInBytes);
             pulledData.add(e);
         }
-        // TODO(b/119598534): Reset HWM counters here.
+        // Invoke rss_hwm_reset binary to reset RSS HWM counters for all processes.
+        SystemProperties.set("sys.rss_hwm_reset.on", "1");
     }
 
     private void pullBinderCallsStats(
@@ -1561,11 +1563,16 @@
         return mBatteryStatsHelper;
     }
 
+    private long milliAmpHrsToNanoAmpSecs(double mAh) {
+        final long MILLI_AMP_HR_TO_NANO_AMP_SECS = 1_000_000L * 3600L;
+        return (long) (mAh * MILLI_AMP_HR_TO_NANO_AMP_SECS + 0.5);
+    }
+
     private void pullDeviceCalculatedPowerUse(int tagId,
             long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
         BatteryStatsHelper bsHelper = getBatteryStatsHelper();
         StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-        e.writeFloat((float) bsHelper.getComputedPower());
+        e.writeLong(milliAmpHrsToNanoAmpSecs(bsHelper.getComputedPower()));
         pulledData.add(e);
     }
 
@@ -1581,7 +1588,7 @@
             }
             StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(bs.uidObj.getUid());
-            e.writeFloat((float) bs.totalPowerMah);
+            e.writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah));
             pulledData.add(e);
         }
     }
@@ -1601,7 +1608,7 @@
             }
             StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(bs.drainType.ordinal());
-            e.writeFloat((float) bs.totalPowerMah);
+            e.writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah));
             pulledData.add(e);
         }
     }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 6e4c00e..fc21adb 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -718,12 +718,12 @@
             disabledData += " }";
             final UiState state = getUiState(displayId);
 
-            Log.d(TAG, "disabledlocked (b/113914868): displayId=" + displayId + "net1=" + net1
+            Log.d(TAG, "disabledlocked (b/113914868): displayId=" + displayId + ", net1=" + net1
                     + ", mDisabled1=" + state.mDisabled1 + ", token=" + token
                     + ", mDisableRecords=" + mDisableRecords.size() + " => " + disabledData);
         }
         final UiState state = getUiState(displayId);
-        if (state.disableEquals(net1, net2)) {
+        if (!state.disableEquals(net1, net2)) {
             state.setDisabled(net1, net2);
             mHandler.post(() -> mNotificationDelegate.onSetDisabled(net1));
             if (mBar != null) {
@@ -1254,12 +1254,13 @@
     }
 
     @Override
-    public void onNotificationSmartRepliesAdded(String key, int replyCount)
-            throws RemoteException {
+    public void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount,
+            int smartActionCount, boolean generatedByAssistant) {
         enforceStatusBarService();
         long identity = Binder.clearCallingIdentity();
         try {
-            mNotificationDelegate.onNotificationSmartRepliesAdded(key, replyCount);
+            mNotificationDelegate.onNotificationSmartSuggestionsAdded(key, smartReplyCount,
+                    smartActionCount, generatedByAssistant);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/services/core/java/com/android/server/textservices/TextServicesManagerInternal.java b/services/core/java/com/android/server/textservices/TextServicesManagerInternal.java
new file mode 100644
index 0000000..56bcdd9
--- /dev/null
+++ b/services/core/java/com/android/server/textservices/TextServicesManagerInternal.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.textservices;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.view.textservice.SpellCheckerInfo;
+
+import com.android.server.LocalServices;
+
+/**
+ * Local interface of {@link TextServicesManagerService} inside system server process.
+ */
+public abstract class TextServicesManagerInternal {
+    /**
+     * Returns the list of installed input methods for the specified user.
+     *
+     * <p>CAVEAT: This method is not fully implemented yet. This may return an empty list if
+     * {@code userId} for a background user is specified. Check the implementation before starting
+     * this method.</p>
+     *
+     * @param userId The user ID to be queried.
+     * @return {@link SpellCheckerInfo} that is currently selected {@code userId}.
+     */
+    @Nullable
+    public abstract SpellCheckerInfo getCurrentSpellCheckerForUser(@UserIdInt int userId);
+
+    /**
+     * Fake implementation of {@link TextServicesManagerInternal}.  All the methods do nothing.
+     */
+    private static final TextServicesManagerInternal NOP =
+            new TextServicesManagerInternal() {
+                @Override
+                public SpellCheckerInfo getCurrentSpellCheckerForUser(@UserIdInt int userId) {
+                    return null;
+                }
+            };
+
+    /**
+     * @return Global instance if exists.  Otherwise, a dummy no-op instance.
+     */
+    @NonNull
+    public static TextServicesManagerInternal get() {
+        final TextServicesManagerInternal instance =
+                LocalServices.getService(TextServicesManagerInternal.class);
+        return instance != null ? instance : NOP;
+    }
+}
diff --git a/services/core/java/com/android/server/textservices/TextServicesManagerService.java b/services/core/java/com/android/server/textservices/TextServicesManagerService.java
index 23c29f8..65d5b10 100644
--- a/services/core/java/com/android/server/textservices/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/textservices/TextServicesManagerService.java
@@ -18,20 +18,6 @@
 
 import static android.view.textservice.TextServicesManager.DISABLE_PER_PROFILE_SPELL_CHECKER;
 
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.content.PackageMonitor;
-import com.android.internal.inputmethod.SubtypeLocaleUtils;
-import com.android.internal.textservice.ISpellCheckerService;
-import com.android.internal.textservice.ISpellCheckerServiceCallback;
-import com.android.internal.textservice.ISpellCheckerSession;
-import com.android.internal.textservice.ISpellCheckerSessionListener;
-import com.android.internal.textservice.ITextServicesManager;
-import com.android.internal.textservice.ITextServicesSessionListener;
-import com.android.internal.util.DumpUtils;
-import com.android.server.SystemService;
-
-import org.xmlpull.v1.XmlPullParserException;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -60,12 +46,27 @@
 import android.view.textservice.SpellCheckerInfo;
 import android.view.textservice.SpellCheckerSubtype;
 
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.inputmethod.SubtypeLocaleUtils;
+import com.android.internal.textservice.ISpellCheckerService;
+import com.android.internal.textservice.ISpellCheckerServiceCallback;
+import com.android.internal.textservice.ISpellCheckerSession;
+import com.android.internal.textservice.ISpellCheckerSessionListener;
+import com.android.internal.textservice.ITextServicesManager;
+import com.android.internal.textservice.ITextServicesSessionListener;
+import com.android.internal.util.DumpUtils;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
-import java.util.Arrays;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
@@ -278,6 +279,14 @@
 
         @Override
         public void onStart() {
+            LocalServices.addService(TextServicesManagerInternal.class,
+                    new TextServicesManagerInternal() {
+                        @Override
+                        public SpellCheckerInfo getCurrentSpellCheckerForUser(
+                                @UserIdInt int userId) {
+                            return mService.getCurrentSpellCheckerForUser(userId);
+                        }
+                    });
             publishBinderService(Context.TEXT_SERVICES_MANAGER_SERVICE, mService);
         }
 
@@ -493,6 +502,15 @@
         return spellCheckerList.get(0);
     }
 
+    @Nullable
+    private SpellCheckerInfo getCurrentSpellCheckerForUser(@UserIdInt int userId) {
+        synchronized (mLock) {
+            final int spellCheckerOwnerUserId = mSpellCheckerOwnerUserIdMap.get(userId);
+            final TextServicesData data = mUserData.get(spellCheckerOwnerUserId);
+            return data != null ? data.getCurrentSpellChecker() : null;
+        }
+    }
+
     // TODO: Save SpellCheckerService by supported languages. Currently only one spell
     // checker is saved.
     @Override
diff --git a/services/core/java/com/android/server/utils/UserTokenWatcher.java b/services/core/java/com/android/server/utils/UserTokenWatcher.java
new file mode 100644
index 0000000..a3e58f8
--- /dev/null
+++ b/services/core/java/com/android/server/utils/UserTokenWatcher.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.utils;
+
+import android.annotation.UserIdInt;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.TokenWatcher;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
+
+/**
+ * Multi-user aware {@link TokenWatcher}.
+ *
+ * {@link UserTokenWatcher} is thread-safe.
+ */
+public final class UserTokenWatcher {
+
+    private final Callback mCallback;
+    private final Handler mHandler;
+    private final String mTag;
+
+    @GuardedBy("mWatchers")
+    private final SparseArray<TokenWatcher> mWatchers = new SparseArray<>(1);
+
+    public UserTokenWatcher(Callback callback, Handler handler, String tag) {
+        mCallback = callback;
+        mHandler = handler;
+        mTag = tag;
+    }
+
+    /**
+     * Record that this token has been acquired for the given user.  When acquire is called, and
+     * the user's count goes from 0 to 1, the acquired callback is called on the given
+     * handler.
+     *
+     * Note that the same {@code token} can only be acquired once per user. If this
+     * {@code token} has already been acquired for the given user, no action is taken. The first
+     * subsequent call to {@link #release} will release this {@code token}
+     * immediately.
+     *
+     * @param token  An IBinder object.
+     * @param tag    A string used by the {@link #dump} method for debugging,
+     *               to see who has references.
+     * @param userId A user id
+     */
+    public void acquire(IBinder token, String tag, @UserIdInt int userId) {
+        synchronized (mWatchers) {
+            TokenWatcher watcher = mWatchers.get(userId);
+            if (watcher == null) {
+                watcher = new InnerTokenWatcher(userId, mHandler, mTag);
+                mWatchers.put(userId, watcher);
+            }
+            watcher.acquire(token, tag);
+        }
+    }
+
+    /**
+     * Record that this token has been released for the given user.  When release is called, and
+     * the user's count goes from 1 to 0, the released callback is called on the given
+     * handler.
+     *
+     * @param token  An IBinder object.
+     * @param userId A user id
+     */
+    public void release(IBinder token, @UserIdInt int userId) {
+        synchronized (mWatchers) {
+            TokenWatcher watcher = mWatchers.get(userId);
+            if (watcher != null) {
+                watcher.release(token);
+            }
+        }
+    }
+
+    /**
+     * Returns whether the given user has any registered tokens that have not been cleaned up.
+     *
+     * @return true, if the given user has registered tokens.
+     */
+    public boolean isAcquired(@UserIdInt int userId) {
+        synchronized (mWatchers) {
+            TokenWatcher watcher = mWatchers.get(userId);
+            return watcher != null && watcher.isAcquired();
+        }
+    }
+
+    /**
+     * Dumps the current state.
+     */
+    public void dump(PrintWriter pw) {
+        synchronized (mWatchers) {
+            for (int i = 0; i < mWatchers.size(); i++) {
+                int userId = mWatchers.keyAt(i);
+                TokenWatcher watcher = mWatchers.valueAt(i);
+                if (watcher.isAcquired()) {
+                    pw.print("User ");
+                    pw.print(userId);
+                    pw.println(":");
+                    watcher.dump(new IndentingPrintWriter(pw, " "));
+                }
+            }
+        }
+    }
+
+    /**
+     * Callback for {@link UserTokenWatcher}.
+     */
+    public interface Callback {
+
+        /**
+         * Reports that the first token has been acquired for the given user.
+         */
+        void acquired(@UserIdInt int userId);
+
+        /**
+         * Reports that the last token has been release for the given user.
+         */
+        void released(@UserIdInt int userId);
+    }
+
+    private final class InnerTokenWatcher extends TokenWatcher {
+        private final int mUserId;
+
+        private InnerTokenWatcher(int userId, Handler handler, String tag) {
+            super(handler, tag);
+            this.mUserId = userId;
+        }
+
+        @Override
+        public void acquired() {
+            // We MUST NOT hold any locks while invoking the callbacks.
+            mCallback.acquired(mUserId);
+        }
+
+        @Override
+        public void released() {
+            // We MUST NOT hold any locks while invoking the callbacks.
+            mCallback.released(mUserId);
+
+            synchronized (mWatchers) {
+                final TokenWatcher watcher = mWatchers.get(mUserId);
+                if (watcher != null && !watcher.isAcquired()) {
+                    mWatchers.remove(mUserId);
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 64ff9cf..01bff07 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -60,6 +60,7 @@
 import android.hardware.display.DisplayManager;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Debug;
 import android.os.Environment;
 import android.os.FileObserver;
 import android.os.FileUtils;
@@ -85,6 +86,7 @@
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.util.Xml;
 import android.view.Display;
 import android.view.IWindowManager;
@@ -120,12 +122,13 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.function.Consumer;
+import java.util.function.Predicate;
 
 public class WallpaperManagerService extends IWallpaperManager.Stub
         implements IWallpaperManagerService {
-    static final String TAG = "WallpaperManagerService";
-    static final boolean DEBUG = false;
-    static final boolean DEBUG_LIVE = DEBUG || true;
+    private static final String TAG = "WallpaperManagerService";
+    private static final boolean DEBUG = false;
+    private static final boolean DEBUG_LIVE = true;
 
     public static class Lifecycle extends SystemService {
         private IWallpaperManagerService mService;
@@ -163,14 +166,14 @@
         }
     }
 
-    final Object mLock = new Object();
+    private final Object mLock = new Object();
 
     /**
      * Minimum time between crashes of a wallpaper service for us to consider
      * restarting it vs. just reverting to the static wallpaper.
      */
-    static final long MIN_WALLPAPER_CRASH_TIME = 10000;
-    static final int MAX_WALLPAPER_COMPONENT_LOG_LENGTH = 128;
+    private static final long MIN_WALLPAPER_CRASH_TIME = 10000;
+    private static final int MAX_WALLPAPER_COMPONENT_LOG_LENGTH = 128;
     static final String WALLPAPER = "wallpaper_orig";
     static final String WALLPAPER_CROP = "wallpaper";
     static final String WALLPAPER_LOCK_ORIG = "wallpaper_lock_orig";
@@ -178,7 +181,7 @@
     static final String WALLPAPER_INFO = "wallpaper_info.xml";
 
     // All the various per-user state files we need to be aware of
-    static final String[] sPerUserFiles = new String[] {
+    private static final String[] sPerUserFiles = new String[] {
         WALLPAPER, WALLPAPER_CROP,
         WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP,
         WALLPAPER_INFO
@@ -335,7 +338,7 @@
         }
     }
 
-    void notifyLockWallpaperChanged() {
+    private void notifyLockWallpaperChanged() {
         final IWallpaperManagerCallback cb = mKeyguardListener;
         if (cb != null) {
             try {
@@ -347,12 +350,34 @@
     }
 
     private void notifyWallpaperColorsChanged(@NonNull WallpaperData wallpaper, int which) {
+        if (wallpaper.connection != null) {
+            wallpaper.connection.forEachDisplayConnector(connector -> {
+                notifyWallpaperColorsChangedOnDisplay(wallpaper, which, connector.mDisplayId);
+            });
+        } else { // Lock wallpaper does not have WallpaperConnection.
+            notifyWallpaperColorsChangedOnDisplay(wallpaper, which, DEFAULT_DISPLAY);
+        }
+    }
+
+    private RemoteCallbackList<IWallpaperManagerCallback> getWallpaperCallbacks(int userId,
+            int displayId) {
+        RemoteCallbackList<IWallpaperManagerCallback> listeners = null;
+        final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> displayListeners =
+                mColorsChangedListeners.get(userId);
+        if (displayListeners != null) {
+            listeners = displayListeners.get(displayId);
+        }
+        return listeners;
+    }
+
+    private void notifyWallpaperColorsChangedOnDisplay(@NonNull WallpaperData wallpaper, int which,
+            int displayId) {
         boolean needsExtraction;
         synchronized (mLock) {
             final RemoteCallbackList<IWallpaperManagerCallback> currentUserColorListeners =
-                    mColorsChangedListeners.get(wallpaper.userId);
+                    getWallpaperCallbacks(wallpaper.userId, displayId);
             final RemoteCallbackList<IWallpaperManagerCallback> userAllColorListeners =
-                    mColorsChangedListeners.get(UserHandle.USER_ALL);
+                    getWallpaperCallbacks(UserHandle.USER_ALL, displayId);
             // No-op until someone is listening to it.
             if (emptyCallbackList(currentUserColorListeners)  &&
                     emptyCallbackList(userAllColorListeners)) {
@@ -360,7 +385,7 @@
             }
 
             if (DEBUG) {
-                Slog.v(TAG, "notifyWallpaperColorsChanged " + which);
+                Slog.v(TAG, "notifyWallpaperColorsChangedOnDisplay " + which);
             }
 
             needsExtraction = wallpaper.primaryColors == null;
@@ -368,7 +393,7 @@
 
         // Let's notify the current values, it's fine if it's null, it just means
         // that we don't know yet.
-        notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId);
+        notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId);
 
         if (needsExtraction) {
             extractColors(wallpaper);
@@ -378,7 +403,7 @@
                     return;
                 }
             }
-            notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId);
+            notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId);
         }
     }
 
@@ -387,14 +412,14 @@
     }
 
     private void notifyColorListeners(@NonNull WallpaperColors wallpaperColors, int which,
-            int userId) {
+            int userId, int displayId) {
         final IWallpaperManagerCallback keyguardListener;
         final ArrayList<IWallpaperManagerCallback> colorListeners = new ArrayList<>();
         synchronized (mLock) {
             final RemoteCallbackList<IWallpaperManagerCallback> currentUserColorListeners =
-                    mColorsChangedListeners.get(userId);
+                    getWallpaperCallbacks(userId, displayId);
             final RemoteCallbackList<IWallpaperManagerCallback> userAllColorListeners =
-                    mColorsChangedListeners.get(UserHandle.USER_ALL);
+                    getWallpaperCallbacks(UserHandle.USER_ALL, displayId);
             keyguardListener = mKeyguardListener;
 
             if (currentUserColorListeners != null) {
@@ -424,7 +449,8 @@
             }
         }
 
-        if (keyguardListener != null) {
+        // Only shows Keyguard on default display
+        if (keyguardListener != null && displayId == DEFAULT_DISPLAY) {
             try {
                 keyguardListener.onWallpaperColorsChanged(wallpaperColors, which, userId);
             } catch (RemoteException e) {
@@ -443,6 +469,11 @@
         String cropFile = null;
         int wallpaperId;
 
+        if (wallpaper.equals(mFallbackWallpaper)) {
+            extractDefaultImageWallpaperColors();
+            return;
+        }
+
         synchronized (mLock) {
             // Not having a wallpaperComponent means it's a lock screen wallpaper.
             final boolean imageWallpaper = mImageWallpaper.equals(wallpaper.wallpaperComponent)
@@ -479,6 +510,39 @@
         }
     }
 
+    private void extractDefaultImageWallpaperColors() {
+        synchronized (mLock) {
+            if (mFallbackWallpaper.primaryColors != null) return;
+        }
+
+        if (DEBUG) Slog.d(TAG, "Extract default image wallpaper colors");
+        WallpaperColors colors = null;
+        final InputStream is = WallpaperManager.openDefaultWallpaper(mContext, FLAG_SYSTEM);
+        if (is != null) {
+            try {
+                final BitmapFactory.Options options = new BitmapFactory.Options();
+                final Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
+                if (bitmap != null) {
+                    colors = WallpaperColors.fromBitmap(bitmap);
+                    bitmap.recycle();
+                }
+            } catch (OutOfMemoryError e) {
+                Slog.w(TAG, "Can't decode default wallpaper stream", e);
+            } finally {
+                IoUtils.closeQuietly(is);
+            }
+        }
+
+        if (colors == null) {
+            Slog.e(TAG, "Extract default image wallpaper colors failed");
+            return;
+        }
+
+        synchronized (mLock) {
+            mFallbackWallpaper.primaryColors = colors;
+        }
+    }
+
     /**
      * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped
      * for display.
@@ -487,7 +551,7 @@
         boolean success = false;
 
         // Only generate crop for default display.
-        final WallpaperData.DisplayData wpData = getDisplayDataOrCreate(wallpaper, DEFAULT_DISPLAY);
+        final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
         Rect cropHint = new Rect(wallpaper.cropHint);
 
         if (DEBUG) {
@@ -640,11 +704,11 @@
         }
     }
 
-    final Context mContext;
-    final IWindowManager mIWindowManager;
-    final IPackageManager mIPackageManager;
-    final MyPackageMonitor mMonitor;
-    final AppOpsManager mAppOpsManager;
+    private final Context mContext;
+    private final IWindowManager mIWindowManager;
+    private final IPackageManager mIPackageManager;
+    private final MyPackageMonitor mMonitor;
+    private final AppOpsManager mAppOpsManager;
 
     private final DisplayManager mDisplayManager;
     private final DisplayManager.DisplayListener mDisplayListener =
@@ -654,11 +718,23 @@
         public void onDisplayAdded(int displayId) {
             synchronized (mLock) {
                 if (mLastWallpaper != null) {
-                    final WallpaperConnection.DisplayConnector connector =
-                            mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
-                    if (connector == null) return;
-
-                    connector.connectLocked(mLastWallpaper.connection, mLastWallpaper);
+                    if (supportsMultiDisplay(mLastWallpaper.connection)) {
+                        final WallpaperConnection.DisplayConnector connector =
+                                mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
+                        if (connector == null) return;
+                        connector.connectLocked(mLastWallpaper.connection, mLastWallpaper);
+                        return;
+                    }
+                    // System wallpaper does not support multiple displays, attach this display to
+                    // the fallback wallpaper.
+                    if (mFallbackWallpaper != null) {
+                        final WallpaperConnection.DisplayConnector connector = mFallbackWallpaper
+                                .connection.getDisplayConnectorOrCreate(displayId);
+                        if (connector == null) return;
+                        connector.connectLocked(mFallbackWallpaper.connection, mFallbackWallpaper);
+                    } else {
+                        Slog.w(TAG, "No wallpaper can be added to the new display");
+                    }
                 }
             }
         }
@@ -667,12 +743,24 @@
         public void onDisplayRemoved(int displayId) {
             synchronized (mLock) {
                 if (mLastWallpaper != null) {
-                    final WallpaperConnection.DisplayConnector connector =
-                            mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
+                    WallpaperData targetWallpaper = null;
+                    if (mLastWallpaper.connection.containsDisplay(displayId)) {
+                        targetWallpaper = mLastWallpaper;
+                    } else if (mFallbackWallpaper.connection.containsDisplay(displayId)) {
+                        targetWallpaper = mFallbackWallpaper;
+                    }
+                    if (targetWallpaper == null) return;
+                    WallpaperConnection.DisplayConnector connector =
+                            targetWallpaper.connection.getDisplayConnectorOrCreate(displayId);
                     if (connector == null) return;
                     connector.disconnectLocked();
-                    mLastWallpaper.connection.removeDisplayConnector(displayId);
-                    mLastWallpaper.removeDisplayData(displayId);
+                    targetWallpaper.connection.removeDisplayConnector(displayId);
+                    removeDisplayData(displayId);
+                }
+                for (int i = mColorsChangedListeners.size() - 1; i >= 0; i--) {
+                    final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> callbacks =
+                            mColorsChangedListeners.valueAt(i);
+                    callbacks.delete(displayId);
                 }
             }
         }
@@ -684,37 +772,44 @@
 
     /**
      * Map of color listeners per user id.
-     * The key will be the id of a user or UserHandle.USER_ALL - for wildcard listeners.
+     * The first key will be the id of a user or UserHandle.USER_ALL - for wildcard listeners.
+     * The secondary key will be the display id, which means which display the listener is
+     * interested in.
      */
-    final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> mColorsChangedListeners;
-    WallpaperData mLastWallpaper;
-    IWallpaperManagerCallback mKeyguardListener;
-    boolean mWaitingForUnlock;
-    boolean mShuttingDown;
+    private final SparseArray<SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>>
+            mColorsChangedListeners;
+    private WallpaperData mLastWallpaper;
+    private IWallpaperManagerCallback mKeyguardListener;
+    private boolean mWaitingForUnlock;
+    private boolean mShuttingDown;
 
     /**
      * ID of the current wallpaper, changed every time anything sets a wallpaper.
      * This is used for external detection of wallpaper update activity.
      */
-    int mWallpaperId;
+    private int mWallpaperId;
 
     /**
      * Name of the component used to display bitmap wallpapers from either the gallery or
      * built-in wallpapers.
      */
-    final ComponentName mImageWallpaper;
+    private final ComponentName mImageWallpaper;
 
     /**
      * Name of the default wallpaper component; might be different from mImageWallpaper
      */
-    final ComponentName mDefaultWallpaperComponent;
+    private final ComponentName mDefaultWallpaperComponent;
 
-    final SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
-    final SparseArray<WallpaperData> mLockWallpaperMap = new SparseArray<WallpaperData>();
+    private final SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
+    private final SparseArray<WallpaperData> mLockWallpaperMap = new SparseArray<WallpaperData>();
 
-    final SparseArray<Boolean> mUserRestorecon = new SparseArray<Boolean>();
-    int mCurrentUserId = UserHandle.USER_NULL;
-    boolean mInAmbientMode;
+    private SparseArray<DisplayData> mDisplayDatas = new SparseArray<>();
+
+    private WallpaperData mFallbackWallpaper;
+
+    private final SparseBooleanArray mUserRestorecon = new SparseBooleanArray();
+    private int mCurrentUserId = UserHandle.USER_NULL;
+    private boolean mInAmbientMode;
 
     static class WallpaperData {
 
@@ -780,18 +875,6 @@
         private RemoteCallbackList<IWallpaperManagerCallback> callbacks
                 = new RemoteCallbackList<IWallpaperManagerCallback>();
 
-        private static final class DisplayData {
-            int mWidth = -1;
-            int mHeight = -1;
-            final Rect mPadding = new Rect(0, 0, 0, 0);
-            final int mDisplayId;
-
-            DisplayData(int displayId) {
-                mDisplayId = displayId;
-            }
-        }
-        private SparseArray<DisplayData> mDisplayDatas = new SparseArray<>();
-
         /**
          * The crop hint supplied for displaying a subset of the source image
          */
@@ -812,24 +895,34 @@
         boolean sourceExists() {
             return wallpaperFile.exists();
         }
+    }
 
-        void removeDisplayData(int displayId) {
-            mDisplayDatas.remove(displayId);
+    private static final class DisplayData {
+        int mWidth = -1;
+        int mHeight = -1;
+        final Rect mPadding = new Rect(0, 0, 0, 0);
+        final int mDisplayId;
+
+        DisplayData(int displayId) {
+            mDisplayId = displayId;
         }
     }
 
-    private WallpaperData.DisplayData getDisplayDataOrCreate(WallpaperData data, int displayId) {
-        WallpaperData.DisplayData wpdData = data.mDisplayDatas.get(displayId);
+    private void removeDisplayData(int displayId) {
+        mDisplayDatas.remove(displayId);
+    }
+
+    private DisplayData getDisplayDataOrCreate(int displayId) {
+        DisplayData wpdData = mDisplayDatas.get(displayId);
         if (wpdData == null) {
-            wpdData = new WallpaperData.DisplayData(displayId);
+            wpdData = new DisplayData(displayId);
             ensureSaneWallpaperDisplaySize(wpdData, displayId);
-            data.mDisplayDatas.append(displayId, wpdData);
+            mDisplayDatas.append(displayId, wpdData);
         }
         return wpdData;
     }
 
-    private void ensureSaneWallpaperDisplaySize(WallpaperData.DisplayData wpdData,
-            int displayId) {
+    private void ensureSaneWallpaperDisplaySize(DisplayData wpdData, int displayId) {
         // We always want to have some reasonable width hint.
         final int baseSize = getMaximumSizeDimension(displayId);
         if (wpdData.mWidth < baseSize) {
@@ -842,12 +935,16 @@
 
     private int getMaximumSizeDimension(int displayId) {
         Display display = mDisplayManager.getDisplay(displayId);
+        if (display == null) {
+            Slog.w(TAG, "Invalid displayId=" + displayId + " " + Debug.getCallers(4));
+            display = mDisplayManager.getDisplay(DEFAULT_DISPLAY);
+        }
         return display.getMaximumSizeDimension();
     }
 
-    void forEachDisplayData(WallpaperData data, Consumer<WallpaperData.DisplayData> action) {
-        for (int i = data.mDisplayDatas.size() - 1; i >= 0; i--) {
-            final WallpaperData.DisplayData wpdData = data.mDisplayDatas.valueAt(i);
+    void forEachDisplayData(Consumer<DisplayData> action) {
+        for (int i = mDisplayDatas.size() - 1; i >= 0; i--) {
+            final DisplayData wpdData = mDisplayDatas.valueAt(i);
             action.accept(wpdData);
         }
     }
@@ -859,6 +956,43 @@
         return mWallpaperId;
     }
 
+    private boolean supportsMultiDisplay(WallpaperConnection connection) {
+        if (connection != null) {
+            return connection.mInfo == null // This is image wallpaper
+                    || connection.mInfo.supportsMultipleDisplays();
+        }
+        return false;
+    }
+
+    private void updateFallbackConnection() {
+        if (mLastWallpaper == null || mFallbackWallpaper == null) return;
+        final WallpaperConnection systemConnection = mLastWallpaper.connection;
+        final WallpaperConnection fallbackConnection = mFallbackWallpaper.connection;
+        if (fallbackConnection == null) {
+            Slog.w(TAG, "Fallback wallpaper connection has not been created yet!!");
+            return;
+        }
+        if (supportsMultiDisplay(systemConnection)
+                && fallbackConnection.mDisplayConnector.size() != 0) {
+            fallbackConnection.forEachDisplayConnector(connector -> {
+                if (connector.mEngine != null) {
+                    connector.disconnectLocked();
+                }
+            });
+            fallbackConnection.mDisplayConnector.clear();
+        } else {
+            fallbackConnection.appendConnectorWithCondition(display ->
+                    fallbackConnection.isUsableDisplay(display)
+                            && display.getDisplayId() != DEFAULT_DISPLAY
+                            && !fallbackConnection.containsDisplay(display.getDisplayId()));
+            fallbackConnection.forEachDisplayConnector(connector -> {
+                if (connector.mEngine == null) {
+                    connector.connectLocked(fallbackConnection, mFallbackWallpaper);
+                }
+            });
+        }
+    }
+
     class WallpaperConnection extends IWallpaperConnection.Stub
             implements ServiceConnection {
 
@@ -877,8 +1011,7 @@
             }
 
             void ensureStatusHandled() {
-                final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(mWallpaper,
-                        mDisplayId);
+                final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId);
                 if (mDimensionsChanged) {
                     try {
                         mEngine.setDesiredSize(wpdData.mWidth, wpdData.mHeight);
@@ -898,6 +1031,10 @@
             }
 
             void connectLocked(WallpaperConnection connection, WallpaperData wallpaper) {
+                if (connection.mService == null) {
+                    Slog.w(TAG, "WallpaperService is not connected yet");
+                    return;
+                }
                 if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
                 try {
                     mIWindowManager.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId);
@@ -906,15 +1043,14 @@
                     return;
                 }
 
-                final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
-                        mDisplayId);
+                final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId);
                 try {
                     connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
                             wpdData.mWidth, wpdData.mHeight,
                             wpdData.mPadding, mDisplayId);
                 } catch (RemoteException e) {
                     Slog.w(TAG, "Failed attaching wallpaper on display", e);
-                    if (mLastWallpaper != null && !mLastWallpaper.wallpaperUpdating
+                    if (wallpaper != null && !wallpaper.wallpaperUpdating
                             && connection.getConnectedEngineSize() == 0) {
                         bindWallpaperComponentLocked(null /* componentName */, false /* force */,
                                 false /* fromUser */, wallpaper, null /* reply */);
@@ -982,19 +1118,36 @@
         }
 
         private void initDisplayState() {
-            final Display[] displays = mDisplayManager.getDisplays();
-            for (Display display : displays) {
-                if (isUsableDisplay(display)) {
-                    final int displayId = display.getDisplayId();
-                    mDisplayConnector.append(displayId, new DisplayConnector(displayId));
+            // Do not initialize fallback wallpaper
+            if (!mWallpaper.equals(mFallbackWallpaper)) {
+                if (supportsMultiDisplay(this)) {
+                    // The system wallpaper is image wallpaper or it can supports multiple displays.
+                    appendConnectorWithCondition(this::isUsableDisplay);
+                } else {
+                    // The system wallpaper does not support multiple displays, so just attach it on
+                    // default display.
+                    mDisplayConnector.append(DEFAULT_DISPLAY,
+                            new DisplayConnector(DEFAULT_DISPLAY));
                 }
             }
         }
 
-        // TODO(b/115486823) Support the system decorations change at runtime.
+        private void appendConnectorWithCondition(Predicate<Display> tester) {
+            final Display[] displays = mDisplayManager.getDisplays();
+            for (Display display : displays) {
+                if (tester.test(display)) {
+                    final int displayId = display.getDisplayId();
+                    final DisplayConnector connector = mDisplayConnector.get(displayId);
+                    if (connector == null) {
+                        mDisplayConnector.append(displayId,
+                                new DisplayConnector(displayId));
+                    }
+                }
+            }
+        }
+
         private boolean isUsableDisplay(Display display) {
             return display != null &&  display.hasAccess(mClientUid)
-                    // TODO(b/114338689) Use WindowManager.supportsSystemDecorations when ready
                     && (display.supportsSystemDecorations()
                             || display.getDisplayId() == DEFAULT_DISPLAY);
         }
@@ -1027,6 +1180,10 @@
             return connector;
         }
 
+        boolean containsDisplay(int displayId) {
+            return mDisplayConnector.get(displayId) != null;
+        }
+
         void removeDisplayConnector(int displayId) {
             final DisplayConnector connector = mDisplayConnector.get(displayId);
             if (connector != null) {
@@ -1044,7 +1201,9 @@
                     // when we have an engine, but I'm not sure about
                     // locking there and anyway we always need to be able to
                     // recover if there is something wrong.
-                    saveSettingsLocked(mWallpaper.userId);
+                    if (!mWallpaper.equals(mFallbackWallpaper)) {
+                        saveSettingsLocked(mWallpaper.userId);
+                    }
                     FgThread.getHandler().removeCallbacks(mResetRunnable);
                 }
             }
@@ -1137,9 +1296,10 @@
         /**
          * Called by a live wallpaper if its colors have changed.
          * @param primaryColors representation of wallpaper primary colors
+         * @param displayId for which display
          */
         @Override
-        public void onWallpaperColorsChanged(WallpaperColors primaryColors) {
+        public void onWallpaperColorsChanged(WallpaperColors primaryColors, int displayId) {
             int which;
             synchronized (mLock) {
                 // Do not broadcast changes on ImageWallpaper since it's handled
@@ -1152,14 +1312,16 @@
 
                 // Live wallpapers always are system wallpapers.
                 which = FLAG_SYSTEM;
-                // It's also the lock screen wallpaper when we don't have a bitmap in there
-                WallpaperData lockedWallpaper = mLockWallpaperMap.get(mWallpaper.userId);
-                if (lockedWallpaper == null) {
-                    which |= FLAG_LOCK;
+                // It's also the lock screen wallpaper when we don't have a bitmap in there.
+                if (displayId == DEFAULT_DISPLAY) {
+                    final WallpaperData lockedWallpaper = mLockWallpaperMap.get(mWallpaper.userId);
+                    if (lockedWallpaper == null) {
+                        which |= FLAG_LOCK;
+                    }
                 }
             }
             if (which != 0) {
-                notifyWallpaperColorsChanged(mWallpaper, which);
+                notifyWallpaperColorsChangedOnDisplay(mWallpaper, which, displayId);
             }
         }
 
@@ -1186,16 +1348,12 @@
                         Slog.w(TAG, "Failed to set ambient mode state", e);
                     }
                 }
-                // TODO(multi-display) So far, we have shared the same wallpaper on each display.
-                // Once we have multiple wallpapers on multiple displays, please complete here.
-                if (displayId == DEFAULT_DISPLAY) {
-                    try {
-                        // This will trigger onComputeColors in the wallpaper engine.
-                        // It's fine to be locked in here since the binder is oneway.
-                        connector.mEngine.requestWallpaperColors();
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "Failed to request wallpaper colors", e);
-                    }
+                try {
+                    // This will trigger onComputeColors in the wallpaper engine.
+                    // It's fine to be locked in here since the binder is oneway.
+                    connector.mEngine.requestWallpaperColors();
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Failed to request wallpaper colors", e);
                 }
             }
         }
@@ -1533,8 +1691,8 @@
                 // This corrects for mislabeling bugs that might have arisen from move-to
                 // operations involving the wallpaper files.  This isn't timing-critical,
                 // so we do it in the background to avoid holding up the user unlock operation.
-                if (mUserRestorecon.get(userId) != Boolean.TRUE) {
-                    mUserRestorecon.put(userId, Boolean.TRUE);
+                if (!mUserRestorecon.get(userId)) {
+                    mUserRestorecon.put(userId, true);
                     Runnable relabeler = new Runnable() {
                         @Override
                         public void run() {
@@ -1562,7 +1720,7 @@
             for (String filename : sPerUserFiles) {
                 new File(wallpaperDir, filename).delete();
             }
-            mUserRestorecon.remove(userId);
+            mUserRestorecon.delete(userId);
         }
     }
 
@@ -1590,6 +1748,7 @@
         FgThread.getHandler().post(() -> {
             notifyWallpaperColorsChanged(systemWallpaper, FLAG_SYSTEM);
             notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK);
+            notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
         });
     }
 
@@ -1652,6 +1811,7 @@
         // When clearing a wallpaper, broadcast new valid colors
         if (data != null) {
             notifyWallpaperColorsChanged(data, which);
+            notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
         }
     }
 
@@ -1789,7 +1949,7 @@
                 throw new IllegalArgumentException("Cannot find display with id=" + displayId);
             }
 
-            final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, displayId);
+            final DisplayData wpdData = getDisplayDataOrCreate(displayId);
             if (width != wpdData.mWidth || height != wpdData.mHeight) {
                 wpdData.mWidth = width;
                 wpdData.mHeight = height;
@@ -1826,8 +1986,7 @@
             }
             WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
             if (wallpaper != null) {
-                final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
-                        displayId);
+                final DisplayData wpdData = getDisplayDataOrCreate(displayId);
                 return wpdData.mWidth;
             } else {
                 return 0;
@@ -1845,8 +2004,7 @@
             }
             WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
             if (wallpaper != null) {
-                final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
-                        displayId);
+                final DisplayData wpdData = getDisplayDataOrCreate(displayId);
                 return wpdData.mHeight;
             } else {
                 return 0;
@@ -1872,7 +2030,7 @@
                 throw new IllegalArgumentException("padding must be positive: " + padding);
             }
 
-            final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, displayId);
+            final DisplayData wpdData = getDisplayDataOrCreate(displayId);
             if (!padding.equals(wpdData.mPadding)) {
                 wpdData.mPadding.set(padding);
                 if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId);
@@ -1940,8 +2098,7 @@
                 return null;
             }
             // Only for default display.
-            final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
-                    DEFAULT_DISPLAY);
+            final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
             try {
                 if (outParams != null) {
                     outParams.putInt("width", wpdData.mWidth);
@@ -1996,35 +2153,47 @@
     }
 
     @Override
-    public void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId) {
+    public void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId,
+            int displayId) {
         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, true, true, "registerWallpaperColorsCallback", null);
         synchronized (mLock) {
-            RemoteCallbackList<IWallpaperManagerCallback> userColorsChangedListeners =
-                    mColorsChangedListeners.get(userId);
-            if (userColorsChangedListeners == null) {
-                userColorsChangedListeners = new RemoteCallbackList<>();
-                mColorsChangedListeners.put(userId, userColorsChangedListeners);
+            SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>
+                    userDisplayColorsChangedListeners = mColorsChangedListeners.get(userId);
+            if (userDisplayColorsChangedListeners == null) {
+                userDisplayColorsChangedListeners = new SparseArray<>();
+                mColorsChangedListeners.put(userId, userDisplayColorsChangedListeners);
             }
-            userColorsChangedListeners.register(cb);
+            RemoteCallbackList<IWallpaperManagerCallback> displayChangedListeners =
+                    userDisplayColorsChangedListeners.get(displayId);
+            if (displayChangedListeners == null) {
+                displayChangedListeners = new RemoteCallbackList<>();
+                userDisplayColorsChangedListeners.put(displayId, displayChangedListeners);
+            }
+            displayChangedListeners.register(cb);
         }
     }
 
     @Override
-    public void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId) {
+    public void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId,
+            int displayId) {
         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, true, true, "unregisterWallpaperColorsCallback", null);
         synchronized (mLock) {
-            final RemoteCallbackList<IWallpaperManagerCallback> userColorsChangedListeners =
-                    mColorsChangedListeners.get(userId);
-            if (userColorsChangedListeners != null) {
-                userColorsChangedListeners.unregister(cb);
+            SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>
+                    userDisplayColorsChangedListeners = mColorsChangedListeners.get(userId);
+            if (userDisplayColorsChangedListeners != null) {
+                RemoteCallbackList<IWallpaperManagerCallback> displayChangedListeners =
+                        userDisplayColorsChangedListeners.get(displayId);
+                if (displayChangedListeners != null) {
+                    displayChangedListeners.unregister(cb);
+                }
             }
         }
     }
 
     /**
-     * TODO(b/115486823) Extends this method with specific display.
+     * TODO(multi-display) Extends this method with specific display.
      * Propagate ambient state to wallpaper engine.
      *
      * @param inAmbientMode {@code true} when in ambient mode, {@code false} otherwise.
@@ -2037,7 +2206,7 @@
             final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
             if (data != null && data.connection != null && data.connection.mInfo != null
                     && data.connection.mInfo.supportsAmbientMode()) {
-                // TODO(b/115486823) Extends this method with specific display.
+                // TODO(multi-display) Extends this method with specific display.
                 engine = data.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
             } else {
                 engine = null;
@@ -2063,7 +2232,8 @@
     }
 
     @Override
-    public WallpaperColors getWallpaperColors(int which, int userId) throws RemoteException {
+    public WallpaperColors getWallpaperColors(int which, int userId, int displayId)
+            throws RemoteException {
         if (which != FLAG_LOCK && which != FLAG_SYSTEM) {
             throw new IllegalArgumentException("which should be either FLAG_LOCK or FLAG_SYSTEM");
         }
@@ -2081,7 +2251,7 @@
             // Try to get the system wallpaper anyway since it might
             // also be the lock screen wallpaper
             if (wallpaperData == null) {
-                wallpaperData = mWallpaperMap.get(userId);
+                wallpaperData = findWallpaperAtDisplay(userId, displayId);
             }
 
             if (wallpaperData == null) {
@@ -2099,6 +2269,15 @@
         }
     }
 
+    private WallpaperData findWallpaperAtDisplay(int userId, int displayId) {
+        if (mFallbackWallpaper != null && mFallbackWallpaper.connection != null
+                && mFallbackWallpaper.connection.containsDisplay(displayId)) {
+            return mFallbackWallpaper;
+        } else {
+            return mWallpaperMap.get(userId);
+        }
+    }
+
     @Override
     public ParcelFileDescriptor setWallpaper(String name, String callingPackage,
             Rect cropHint, boolean allowBackup, Bundle extras, int which,
@@ -2173,14 +2352,8 @@
         // We know a-priori that there is no lock-only wallpaper currently
         WallpaperData lockWP = new WallpaperData(userId,
                 WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
-        final WallpaperData.DisplayData lockWPDData = getDisplayDataOrCreate(lockWP,
-                DEFAULT_DISPLAY);
-        final WallpaperData.DisplayData sysWPDData = getDisplayDataOrCreate(sysWP,
-                DEFAULT_DISPLAY);
         lockWP.wallpaperId = sysWP.wallpaperId;
         lockWP.cropHint.set(sysWP.cropHint);
-        lockWPDData.mWidth = sysWPDData.mWidth;
-        lockWPDData.mHeight = sysWPDData.mHeight;
         lockWP.allowBackup = sysWP.allowBackup;
         lockWP.primaryColors = sysWP.primaryColors;
 
@@ -2300,6 +2473,7 @@
 
         if (shouldNotifyColors) {
             notifyWallpaperColorsChanged(wallpaper, which);
+            notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
         }
     }
 
@@ -2320,7 +2494,7 @@
         return false;
     }
 
-    boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
+    private boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
             boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
         if (DEBUG_LIVE) {
             Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
@@ -2443,15 +2617,17 @@
                 Slog.w(TAG, msg);
                 return false;
             }
-            if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null) {
+            if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null
+                    && !wallpaper.equals(mFallbackWallpaper)) {
                 detachWallpaperLocked(mLastWallpaper);
             }
             wallpaper.wallpaperComponent = componentName;
             wallpaper.connection = newConn;
             newConn.mReply = reply;
-            if (wallpaper.userId == mCurrentUserId) {
+            if (wallpaper.userId == mCurrentUserId && !wallpaper.equals(mFallbackWallpaper)) {
                 mLastWallpaper = wallpaper;
             }
+            updateFallbackConnection();
         } catch (RemoteException e) {
             String msg = "Remote exception for " + componentName + "\n" + e;
             if (fromUser) {
@@ -2463,7 +2639,7 @@
         return true;
     }
 
-    void detachWallpaperLocked(WallpaperData wallpaper) {
+    private void detachWallpaperLocked(WallpaperData wallpaper) {
         if (wallpaper.connection != null) {
             if (wallpaper.connection.mReply != null) {
                 try {
@@ -2473,7 +2649,8 @@
                 wallpaper.connection.mReply = null;
             }
             mContext.unbindService(wallpaper.connection);
-            wallpaper.connection.forEachDisplayConnector(connector -> connector.disconnectLocked());
+            wallpaper.connection.forEachDisplayConnector(
+                    WallpaperConnection.DisplayConnector::disconnectLocked);
             wallpaper.connection.mService = null;
             wallpaper.connection.mDisplayConnector.clear();
             wallpaper.connection = null;
@@ -2481,12 +2658,12 @@
         }
     }
 
-    void clearWallpaperComponentLocked(WallpaperData wallpaper) {
+    private void clearWallpaperComponentLocked(WallpaperData wallpaper) {
         wallpaper.wallpaperComponent = null;
         detachWallpaperLocked(wallpaper);
     }
 
-    void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
+    private void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
         conn.forEachDisplayConnector(connector-> connector.connectLocked(conn, wallpaper));
     }
 
@@ -2596,8 +2773,7 @@
         if (DEBUG) {
             Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId);
         }
-        final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
-                DEFAULT_DISPLAY);
+        final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
         out.startTag(null, tag);
         out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
         out.attribute(null, "width", Integer.toString(wpdData.mWidth));
@@ -2755,10 +2931,10 @@
                     Slog.i(TAG, "No static wallpaper imagery; defaults will be shown");
                 }
             }
+            initializeFallbackWallpaper();
         }
         boolean success = false;
-        final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
-                DEFAULT_DISPLAY);
+        final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
         try {
             stream = new FileInputStream(file);
             XmlPullParser parser = Xml.newPullParser();
@@ -2845,8 +3021,19 @@
         }
     }
 
+    private void initializeFallbackWallpaper() {
+        if (mFallbackWallpaper == null) {
+            if (DEBUG) Slog.d(TAG, "Initialize fallback wallpaper");
+            mFallbackWallpaper = new WallpaperData(
+                    UserHandle.USER_SYSTEM, WALLPAPER, WALLPAPER_CROP);
+            mFallbackWallpaper.allowBackup = false;
+            mFallbackWallpaper.wallpaperId = makeWallpaperIdLocked();
+            bindWallpaperComponentLocked(mImageWallpaper, true, false, mFallbackWallpaper, null);
+        }
+    }
+
     private void ensureSaneWallpaperData(WallpaperData wallpaper, int displayId) {
-        final WallpaperData.DisplayData size = getDisplayDataOrCreate(wallpaper, displayId);
+        final DisplayData size = getDisplayDataOrCreate(displayId);
 
         if (displayId == DEFAULT_DISPLAY) {
             // crop, if not previously specified
@@ -2869,7 +3056,7 @@
             wallpaper.wallpaperId = makeWallpaperIdLocked();
         }
 
-        final WallpaperData.DisplayData wpData = getDisplayDataOrCreate(wallpaper, DEFAULT_DISPLAY);
+        final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
 
         if (!keepDimensionHints) {
             wpData.mWidth = Integer.parseInt(parser.getAttributeValue(null, "width"));
@@ -2963,7 +3150,7 @@
     }
 
     // Restore the named resource bitmap to both source + crop files
-    boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
+    private boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
         if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) {
             String resName = wallpaper.name.substring(4);
 
@@ -3048,8 +3235,9 @@
             for (int i = 0; i < mWallpaperMap.size(); i++) {
                 WallpaperData wallpaper = mWallpaperMap.valueAt(i);
                 pw.print(" User "); pw.print(wallpaper.userId);
-                    pw.print(": id="); pw.println(wallpaper.wallpaperId);
-                forEachDisplayData(wallpaper, wpSize -> {
+                pw.print(": id="); pw.println(wallpaper.wallpaperId);
+                pw.println(" Display state:");
+                forEachDisplayData(wpSize -> {
                     pw.print("  displayId=");
                     pw.println(wpSize.mDisplayId);
                     pw.print("  mWidth=");
@@ -3072,11 +3260,11 @@
                         pw.println(conn.mInfo.getComponent());
                     }
                     conn.forEachDisplayConnector(connector -> {
-                        pw.print("    mDisplayId=");
+                        pw.print("     mDisplayId=");
                         pw.println(connector.mDisplayId);
-                        pw.print("    mToken=");
+                        pw.print("     mToken=");
                         pw.println(connector.mToken);
-                        pw.print("    mEngine=");
+                        pw.print("     mEngine=");
                         pw.println(connector.mEngine);
                     });
                     pw.print("    mService=");
@@ -3090,18 +3278,38 @@
                 WallpaperData wallpaper = mLockWallpaperMap.valueAt(i);
                 pw.print(" User "); pw.print(wallpaper.userId);
                 pw.print(": id="); pw.println(wallpaper.wallpaperId);
-                forEachDisplayData(wallpaper, wpSize -> {
-                    pw.print("  displayId=");
-                    pw.println(wpSize.mDisplayId);
-                    pw.print("  mWidth="); pw.print(wpSize.mWidth);
-                    pw.print("  mHeight="); pw.println(wpSize.mHeight);
-                    pw.print("  mPadding="); pw.println(wpSize.mPadding);
-                });
                 pw.print("  mCropHint="); pw.println(wallpaper.cropHint);
                 pw.print("  mName=");  pw.println(wallpaper.name);
                 pw.print("  mAllowBackup="); pw.println(wallpaper.allowBackup);
             }
-
+            pw.println("Fallback wallpaper state:");
+            pw.print(" User "); pw.print(mFallbackWallpaper.userId);
+            pw.print(": id="); pw.println(mFallbackWallpaper.wallpaperId);
+            pw.print("  mCropHint="); pw.println(mFallbackWallpaper.cropHint);
+            pw.print("  mName=");  pw.println(mFallbackWallpaper.name);
+            pw.print("  mAllowBackup="); pw.println(mFallbackWallpaper.allowBackup);
+            if (mFallbackWallpaper.connection != null) {
+                WallpaperConnection conn = mFallbackWallpaper.connection;
+                pw.print("  Fallback Wallpaper connection ");
+                pw.print(conn);
+                pw.println(":");
+                if (conn.mInfo != null) {
+                    pw.print("    mInfo.component=");
+                    pw.println(conn.mInfo.getComponent());
+                }
+                conn.forEachDisplayConnector(connector -> {
+                    pw.print("     mDisplayId=");
+                    pw.println(connector.mDisplayId);
+                    pw.print("     mToken=");
+                    pw.println(connector.mToken);
+                    pw.print("     mEngine=");
+                    pw.println(connector.mEngine);
+                });
+                pw.print("    mService=");
+                pw.println(conn.mService);
+                pw.print("    mLastDiedTime=");
+                pw.println(mFallbackWallpaper.lastDiedTime - SystemClock.uptimeMillis());
+            }
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index fe0b5c2..caebf15 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1066,93 +1066,19 @@
 
                 final int visibleWindowCount = visibleWindows.size();
                 HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>();
-                for (int i = visibleWindowCount - 1; i >= 0; i--) {
+
+                // Iterate until we figure out what is touchable for the entire screen.
+                for (int i = visibleWindowCount - 1; i >= 0 && !unaccountedSpace.isEmpty(); i--) {
                     final WindowState windowState = visibleWindows.valueAt(i);
-                    final int flags = windowState.mAttrs.flags;
-                    final Task task = windowState.getTask();
 
-                    // If the window is part of a task that we're finished with - ignore.
-                    if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) {
-                        continue;
-                    }
-
-                    // Ignore non-touchable windows, except the split-screen divider, which is
-                    // occasionally non-touchable but still useful for identifying split-screen
-                    // mode.
-                    if (((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
-                            && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)) {
-                        continue;
-                    }
-
-                    // Compute the bounds in the screen.
                     final Rect boundsInScreen = mTempRect;
                     computeWindowBoundsInScreen(windowState, boundsInScreen);
 
-                    // If the window is completely covered by other windows - ignore.
-                    if (unaccountedSpace.quickReject(boundsInScreen)) {
-                        continue;
-                    }
-
-                    // Add windows of certain types not covered by modal windows.
-                    if (isReportedWindowType(windowState.mAttrs.type)) {
-                        // Add the window to the ones to be reported.
+                    if (windowMattersToAccessibility(windowState, boundsInScreen, unaccountedSpace,
+                            skipRemainingWindowsForTasks)) {
                         addPopulatedWindowInfo(windowState, boundsInScreen, windows, addedWindows);
-                        if (windowState.isFocused()) {
-                            focusedWindowAdded = true;
-                        }
-                    }
-
-                    if (windowState.mAttrs.type !=
-                            WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
-
-                        // Account for the space this window takes if the window
-                        // is not an accessibility overlay which does not change
-                        // the reported windows.
-                        unaccountedSpace.op(boundsInScreen, unaccountedSpace,
-                                Region.Op.REVERSE_DIFFERENCE);
-
-                        // If a window is modal it prevents other windows from being touched
-                        if ((flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
-                            // Account for all space in the task, whether the windows in it are
-                            // touchable or not. The modal window blocks all touches from the task's
-                            // area.
-                            unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
-                                    Region.Op.REVERSE_DIFFERENCE);
-
-                            if (task != null) {
-                                // If the window is associated with a particular task, we can skip the
-                                // rest of the windows for that task.
-                                skipRemainingWindowsForTasks.add(task.mTaskId);
-                                continue;
-                            } else {
-                                // If the window is not associated with a particular task, then it is
-                                // globally modal. In this case we can skip all remaining windows.
-                                break;
-                            }
-                        }
-                    }
-
-                    // We figured out what is touchable for the entire screen - done.
-                    if (unaccountedSpace.isEmpty()) {
-                        break;
-                    }
-                }
-
-                // Always report the focused window.
-                if (!focusedWindowAdded) {
-                    for (int i = visibleWindowCount - 1; i >= 0; i--) {
-                        WindowState windowState = visibleWindows.valueAt(i);
-                        if (windowState.isFocused()) {
-                            // Compute the bounds in the screen.
-                            Rect boundsInScreen = mTempRect;
-                            computeWindowBoundsInScreen(windowState, boundsInScreen);
-
-                            // Add the window to the ones to be reported.
-                            addPopulatedWindowInfo(
-                                    windowState, boundsInScreen, windows, addedWindows);
-                            break;
-                        }
+                        updateUnaccountedSpace(windowState, boundsInScreen, unaccountedSpace,
+                                skipRemainingWindowsForTasks);
                     }
                 }
 
@@ -1221,6 +1147,73 @@
             clearAndRecycleWindows(windows);
         }
 
+        private boolean windowMattersToAccessibility(WindowState windowState, Rect boundsInScreen,
+                Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks) {
+            if (windowState.isFocused()) {
+                return true;
+            }
+
+            // If the window is part of a task that we're finished with - ignore.
+            final Task task = windowState.getTask();
+            if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) {
+                return false;
+            }
+
+            // Ignore non-touchable windows, except the split-screen divider, which is
+            // occasionally non-touchable but still useful for identifying split-screen
+            // mode.
+            if (((windowState.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
+                    && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)) {
+                return false;
+            }
+
+            // If the window is completely covered by other windows - ignore.
+            if (unaccountedSpace.quickReject(boundsInScreen)) {
+                return false;
+            }
+
+            // Add windows of certain types not covered by modal windows.
+            if (isReportedWindowType(windowState.mAttrs.type)) {
+                return true;
+            }
+
+            return false;
+        }
+
+        private void updateUnaccountedSpace(WindowState windowState, Rect boundsInScreen,
+                Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks) {
+            if (windowState.mAttrs.type
+                    != WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
+
+                // Account for the space this window takes if the window
+                // is not an accessibility overlay which does not change
+                // the reported windows.
+                unaccountedSpace.op(boundsInScreen, unaccountedSpace,
+                        Region.Op.REVERSE_DIFFERENCE);
+
+                // If a window is modal it prevents other windows from being touched
+                if ((windowState.mAttrs.flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
+                    // Account for all space in the task, whether the windows in it are
+                    // touchable or not. The modal window blocks all touches from the task's
+                    // area.
+                    unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
+                            Region.Op.REVERSE_DIFFERENCE);
+
+                    final Task task = windowState.getTask();
+                    if (task != null) {
+                        // If the window is associated with a particular task, we can skip the
+                        // rest of the windows for that task.
+                        skipRemainingWindowsForTasks.add(task.mTaskId);
+                    } else {
+                        // If the window is not associated with a particular task, then it is
+                        // globally modal. In this case we can skip all remaining windows.
+                        unaccountedSpace.setEmpty();
+                    }
+                }
+            }
+        }
+
         private void computeWindowBoundsInScreen(WindowState windowState, Rect outBounds) {
             // Get the touchable frame.
             Region touchableRegion = mTempRegion1;
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index 10542d5..1f638c7 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -37,6 +37,7 @@
 import static com.android.server.am.ActivityDisplayProto.FOCUSED_STACK_ID;
 import static com.android.server.am.ActivityDisplayProto.ID;
 import static com.android.server.am.ActivityDisplayProto.RESUMED_ACTIVITY;
+import static com.android.server.am.ActivityDisplayProto.SINGLE_TASK_INSTANCE;
 import static com.android.server.am.ActivityDisplayProto.STACKS;
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
 import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
@@ -120,6 +121,9 @@
      */
     private boolean mRemoved;
 
+    /** The display can only contain one task. */
+    private boolean mSingleTaskInstance;
+
     /**
      * A focusable stack that is purposely to be positioned at the top. Although the stack may not
      * have the topmost index, it is used as a preferred candidate to prevent being unable to resume
@@ -212,7 +216,7 @@
         removeStackReferenceIfNeeded(stack);
         releaseSelfIfNeeded();
         mService.updateSleepIfNeededLocked();
-        onStackOrderChanged();
+        onStackOrderChanged(stack);
     }
 
     void positionChildAtTop(ActivityStack stack, boolean includingParents) {
@@ -244,6 +248,10 @@
         final ActivityStack prevFocusedStack = updateLastFocusedStackReason != null
                 ? getFocusedStack() : null;
         final boolean wasContained = mStacks.remove(stack);
+        if (mSingleTaskInstance && getChildCount() > 0) {
+            throw new IllegalStateException(
+                    "positionChildAt: Can only have one child on display=" + this);
+        }
         final int insertPosition = getTopInsertPosition(stack, position);
         mStacks.add(insertPosition, stack);
 
@@ -280,7 +288,7 @@
         if (!wasContained) {
             stack.setParent(this);
         }
-        onStackOrderChanged();
+        onStackOrderChanged(stack);
     }
 
     private int getTopInsertPosition(ActivityStack stack, int candidatePosition) {
@@ -403,6 +411,14 @@
      */
     <T extends ActivityStack> T createStack(int windowingMode, int activityType, boolean onTop) {
 
+        if (mSingleTaskInstance && getChildCount() > 0) {
+            // Create stack on default display instead since this display can only contain 1 stack.
+            // TODO: Kinda a hack, but better that having the decision at each call point. Hoping
+            // this goes away once ActivityView is no longer using virtual displays.
+            return mRootActivityContainer.getDefaultDisplay().createStack(
+                    windowingMode, activityType, onTop);
+        }
+
         if (activityType == ACTIVITY_TYPE_UNDEFINED) {
             // Can't have an undefined stack type yet...so re-map to standard. Anyone that wants
             // anything else should be passing it in anyways...
@@ -1309,9 +1325,13 @@
         mStackOrderChangedCallbacks.remove(listener);
     }
 
-    private void onStackOrderChanged() {
+    /**
+     * Notifies of a stack order change
+     * @param stack The stack which triggered the order change
+     */
+    private void onStackOrderChanged(ActivityStack stack) {
         for (int i = mStackOrderChangedCallbacks.size() - 1; i >= 0; i--) {
-            mStackOrderChangedCallbacks.get(i).onStackOrderChanged();
+            mStackOrderChangedCallbacks.get(i).onStackOrderChanged(stack);
         }
     }
 
@@ -1333,8 +1353,31 @@
         }
     }
 
+    void setDisplayToSingleTaskInstance() {
+        final int childCount = getChildCount();
+        if (childCount > 1) {
+            throw new IllegalArgumentException("Display already has multiple stacks. display="
+                    + this);
+        }
+        if (childCount > 0) {
+            final ActivityStack stack = getChildAt(0);
+            if (stack.getChildCount() > 1) {
+                throw new IllegalArgumentException("Display stack already has multiple tasks."
+                        + " display=" + this + " stack=" + stack);
+            }
+        }
+
+        mSingleTaskInstance = true;
+    }
+
+    /** Returns true if the display can only contain one task */
+    boolean isSingleTaskInstance() {
+        return mSingleTaskInstance;
+    }
+
     public void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + "displayId=" + mDisplayId + " stacks=" + mStacks.size());
+        pw.println(prefix + "displayId=" + mDisplayId + " stacks=" + mStacks.size()
+                + (mSingleTaskInstance ? " mSingleTaskInstance" : ""));
         final String myPrefix = prefix + " ";
         if (mHomeStack != null) {
             pw.println(myPrefix + "mHomeStack=" + mHomeStack);
@@ -1369,6 +1412,7 @@
         final long token = proto.start(fieldId);
         super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
         proto.write(ID, mDisplayId);
+        proto.write(SINGLE_TASK_INSTANCE, mSingleTaskInstance);
         final ActivityStack focusedStack = getFocusedStack();
         if (focusedStack != null) {
             proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
@@ -1390,6 +1434,6 @@
      * Callback for when the order of the stacks in the display changes.
      */
     interface OnStackOrderChangedListener {
-        void onStackOrderChanged();
+        void onStackOrderChanged(ActivityStack stack);
     }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4373675..6f8f85f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -42,6 +42,7 @@
 import static android.content.Intent.ACTION_MAIN;
 import static android.content.Intent.CATEGORY_HOME;
 import static android.content.Intent.CATEGORY_LAUNCHER;
+import static android.content.Intent.CATEGORY_SECONDARY_HOME;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NO_HISTORY;
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
@@ -86,6 +87,7 @@
 import static android.os.Process.SYSTEM_UID;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
 
 import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
 import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
@@ -269,7 +271,10 @@
     boolean fullscreen; // The activity is opaque and fills the entire space of this task.
     // TODO: See if it possible to combine this with the fullscreen field.
     final boolean hasWallpaper; // Has a wallpaper window as a background.
-    final boolean noDisplay;  // activity is not displayed?
+    @VisibleForTesting
+    boolean noDisplay;  // activity is not displayed?
+    @VisibleForTesting
+    int mHandoverLaunchDisplayId = INVALID_DISPLAY; // Handover launch display id to next activity.
     private final boolean componentSpecified;  // did caller specify an explicit component?
     final boolean rootVoiceInteraction;  // was this the root activity of a voice interaction?
 
@@ -583,6 +588,9 @@
             if (info.maxAspectRatio != 0) {
                 pw.println(prefix + "maxAspectRatio=" + info.maxAspectRatio);
             }
+            if (info.minAspectRatio != 0) {
+                pw.println(prefix + "minAspectRatio=" + info.minAspectRatio);
+            }
         }
     }
 
@@ -720,9 +728,10 @@
             mLastReportedMultiWindowMode = inPictureInPictureMode;
             final Configuration newConfig = new Configuration();
             if (targetStackBounds != null && !targetStackBounds.isEmpty()) {
-                task.computeResolvedOverrideConfiguration(newConfig,
-                        task.getParent().getConfiguration(),
-                        task.getRequestedOverrideConfiguration());
+                newConfig.setTo(task.getRequestedOverrideConfiguration());
+                Rect outBounds = newConfig.windowConfiguration.getBounds();
+                task.adjustForMinimalTaskDimensions(outBounds, outBounds);
+                task.computeConfigResourceOverrides(newConfig, task.getParent().getConfiguration());
             }
             schedulePictureInPictureModeChanged(newConfig);
             scheduleMultiWindowModeChanged(newConfig);
@@ -1014,6 +1023,8 @@
             if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) {
                 lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
             }
+            // Gets launch display id from options. It returns INVALID_DISPLAY if not set.
+            mHandoverLaunchDisplayId = options.getLaunchDisplayId();
         }
     }
 
@@ -1174,7 +1185,8 @@
 
     private boolean isHomeIntent(Intent intent) {
         return ACTION_MAIN.equals(intent.getAction())
-                && intent.hasCategory(CATEGORY_HOME)
+                && (intent.hasCategory(CATEGORY_HOME)
+                || intent.hasCategory(CATEGORY_SECONDARY_HOME))
                 && intent.getCategories().size() == 1
                 && intent.getData() == null
                 && intent.getType() == null;
@@ -2480,36 +2492,21 @@
     }
 
     void setRequestedOrientation(int requestedOrientation) {
-        final int displayId = getDisplayId();
-        final Configuration displayConfig =
-                mRootActivityContainer.getDisplayOverrideConfiguration(displayId);
-
-        final Configuration config = setOrientation(requestedOrientation,
-                displayId, displayConfig, mayFreezeScreenLocked(app));
-        if (config != null) {
-            frozenBeforeDestroy = true;
-            if (!mAtmService.updateDisplayOverrideConfigurationLocked(config, this,
-                    false /* deferResume */, displayId)) {
-                mRootActivityContainer.resumeFocusedStacksTopActivities();
-            }
-        }
+        setOrientation(requestedOrientation, mayFreezeScreenLocked(app));
         mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
                 task.taskId, requestedOrientation);
     }
 
-    Configuration setOrientation(int requestedOrientation, int displayId,
-            Configuration displayConfig, boolean freezeScreenIfNeeded) {
+    private void setOrientation(int requestedOrientation, boolean freezeScreenIfNeeded) {
         if (mAppWindowToken == null) {
             Slog.w(TAG_WM,
                     "Attempted to set orientation of non-existing app token: " + appToken);
-            return null;
+            return;
         }
 
-        mAppWindowToken.setOrientation(requestedOrientation);
-
-        final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null;
-        return mAtmService.mWindowManager.updateOrientationFromAppTokens(displayConfig, binder,
-                displayId);
+        final IBinder binder =
+                (freezeScreenIfNeeded && appToken != null) ? appToken.asBinder() : null;
+        mAppWindowToken.setOrientation(requestedOrientation, binder, this);
     }
 
     int getOrientation() {
@@ -2552,7 +2549,6 @@
 
     // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
     private void updateOverrideConfiguration() {
-        mTmpConfig.unset();
         computeBounds(mTmpBounds);
 
         if (mTmpBounds.equals(getRequestedOverrideBounds())) {
@@ -2563,8 +2559,10 @@
 
         // Bounds changed...update configuration to match.
         if (!matchParentBounds()) {
-            task.computeResolvedOverrideConfiguration(mTmpConfig,
-                    task.getParent().getConfiguration(), getRequestedOverrideConfiguration());
+            mTmpConfig.setTo(getRequestedOverrideConfiguration());
+            task.computeConfigResourceOverrides(mTmpConfig, task.getParent().getConfiguration());
+        } else {
+            mTmpConfig.unset();
         }
 
         onRequestedOverrideConfigurationChanged(mTmpConfig);
@@ -2593,7 +2591,10 @@
         outBounds.setEmpty();
         final float maxAspectRatio = info.maxAspectRatio;
         final ActivityStack stack = getActivityStack();
-        if (task == null || stack == null || task.inMultiWindowMode() || maxAspectRatio == 0
+        final float minAspectRatio = info.minAspectRatio;
+
+        if (task == null || stack == null || task.inMultiWindowMode()
+                || (maxAspectRatio == 0 && minAspectRatio == 0)
                 || isInVrUiMode(getConfiguration())) {
             // We don't set override configuration if that activity task isn't fullscreen. I.e. the
             // activity is in multi-window mode. Or, there isn't a max aspect ratio specified for
@@ -2608,20 +2609,35 @@
         final Rect appBounds = getParent().getWindowConfiguration().getAppBounds();
         final int containingAppWidth = appBounds.width();
         final int containingAppHeight = appBounds.height();
-        int maxActivityWidth = containingAppWidth;
-        int maxActivityHeight = containingAppHeight;
+        final float containingRatio = Math.max(containingAppWidth, containingAppHeight)
+                / (float) Math.min(containingAppWidth, containingAppHeight);
 
-        if (containingAppWidth < containingAppHeight) {
-            // Width is the shorter side, so we use that to figure-out what the max. height
-            // should be given the aspect ratio.
-            maxActivityHeight = (int) ((maxActivityWidth * maxAspectRatio) + 0.5f);
-        } else {
-            // Height is the shorter side, so we use that to figure-out what the max. width
-            // should be given the aspect ratio.
-            maxActivityWidth = (int) ((maxActivityHeight * maxAspectRatio) + 0.5f);
+        int activityWidth = containingAppWidth;
+        int activityHeight = containingAppHeight;
+
+        if (containingRatio > maxAspectRatio && maxAspectRatio != 0) {
+            if (containingAppWidth < containingAppHeight) {
+                // Width is the shorter side, so we use that to figure-out what the max. height
+                // should be given the aspect ratio.
+                activityHeight = (int) ((activityWidth * maxAspectRatio) + 0.5f);
+            } else {
+                // Height is the shorter side, so we use that to figure-out what the max. width
+                // should be given the aspect ratio.
+                activityWidth = (int) ((activityHeight * maxAspectRatio) + 0.5f);
+            }
+        } else if (containingRatio < minAspectRatio && minAspectRatio != 0) {
+            if (containingAppWidth < containingAppHeight) {
+                // Width is the shorter side, so we use the height to figure-out what the max. width
+                // should be given the aspect ratio.
+                activityWidth = (int) ((activityHeight / minAspectRatio) + 0.5f);
+            } else {
+                // Height is the shorter side, so we use the width to figure-out what the max.
+                // height should be given the aspect ratio.
+                activityHeight = (int) ((activityWidth / minAspectRatio) + 0.5f);
+            }
         }
 
-        if (containingAppWidth <= maxActivityWidth && containingAppHeight <= maxActivityHeight) {
+        if (containingAppWidth <= activityWidth && containingAppHeight <= activityHeight) {
             // The display matches or is less than the activity aspect ratio, so nothing else to do.
             // Return the existing bounds. If this method is running for the first time,
             // {@link #getRequestedOverrideBounds()} will be empty (representing no override). If
@@ -2636,12 +2652,21 @@
         // Also account for the left / top insets (e.g. from display cutouts), which will be clipped
         // away later in StackWindowController.adjustConfigurationForBounds(). Otherwise, the app
         // bounds would end up too small.
-        outBounds.set(0, 0, maxActivityWidth + appBounds.left, maxActivityHeight + appBounds.top);
+        outBounds.set(0, 0, activityWidth + appBounds.left, activityHeight + appBounds.top);
 
-        if (mAtmService.mWindowManager.getNavBarPosition(getDisplayId()) == NAV_BAR_LEFT) {
+        final int navBarPosition = mAtmService.mWindowManager.getNavBarPosition(getDisplayId());
+        if (navBarPosition == NAV_BAR_LEFT) {
             // Position the activity frame on the opposite side of the nav bar.
-            outBounds.left = appBounds.right - maxActivityWidth;
+            outBounds.left = appBounds.right - activityWidth;
             outBounds.right = appBounds.right;
+        } else if (navBarPosition == NAV_BAR_RIGHT) {
+            // Position the activity frame on the opposite side of the nav bar.
+            outBounds.left = 0;
+            outBounds.right = activityWidth + appBounds.left;
+        } else {
+            // Horizontally center the frame.
+            outBounds.left = appBounds.left + (containingAppWidth - activityWidth) / 2;
+            outBounds.right = outBounds.left + activityWidth;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 2663d99..4581a0f 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -66,6 +66,8 @@
 import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList;
+import static com.android.server.wm.ActivityStackSupervisor.printThisActivity;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APP;
@@ -309,7 +311,7 @@
      * The first entry in the list is the least recently used.
      * It contains HistoryRecord objects.
      */
-    final ArrayList<ActivityRecord> mLRUActivities = new ArrayList<>();
+    private final ArrayList<ActivityRecord> mLRUActivities = new ArrayList<>();
 
     /**
      * When we are in the process of pausing an activity, before starting the
@@ -1142,6 +1144,12 @@
         }
     }
 
+    /** @return true if the stack can only contain one task */
+    boolean isSingleTaskInstance() {
+        final ActivityDisplay display = getDisplay();
+        return display != null && display.isSingleTaskInstance();
+    }
+
     final void removeActivitiesFromLRUListLocked(TaskRecord task) {
         for (ActivityRecord r : task.mActivities) {
             mLRUActivities.remove(r);
@@ -4914,7 +4922,6 @@
 
         // Update override configurations of all tasks in the stack.
         final Rect taskBounds = tempTaskBounds != null ? tempTaskBounds : bounds;
-        final Rect insetBounds = tempTaskInsetBounds != null ? tempTaskInsetBounds : taskBounds;
 
         mTmpBounds.clear();
         mTmpInsetBounds.clear();
@@ -4922,7 +4929,7 @@
         for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
             final TaskRecord task = mTaskHistory.get(i);
             if (task.isResizeable()) {
-                task.updateOverrideConfiguration(taskBounds, insetBounds);
+                task.updateOverrideConfiguration(taskBounds, tempTaskInsetBounds);
             }
 
             if (task.hasDisplayedBounds()) {
@@ -5154,6 +5161,47 @@
         }
     }
 
+    boolean dump(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient,
+            String dumpPackage, boolean needSep) {
+        pw.println("  Stack #" + mStackId
+                + ": type=" + activityTypeToString(getActivityType())
+                + " mode=" + windowingModeToString(getWindowingMode()));
+        pw.println("  isSleeping=" + shouldSleepActivities());
+        pw.println("  mBounds=" + getRequestedOverrideBounds());
+
+        boolean printed = dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage,
+                needSep);
+
+        printed |= dumpHistoryList(fd, pw, mLRUActivities, "    ", "Run", false,
+                !dumpAll, false, dumpPackage, true,
+                "    Running activities (most recent first):", null);
+
+        needSep = printed;
+        boolean pr = printThisActivity(pw, mPausingActivity, dumpPackage, needSep,
+                "    mPausingActivity: ");
+        if (pr) {
+            printed = true;
+            needSep = false;
+        }
+        pr = printThisActivity(pw, getResumedActivity(), dumpPackage, needSep,
+                "    mResumedActivity: ");
+        if (pr) {
+            printed = true;
+            needSep = false;
+        }
+        if (dumpAll) {
+            pr = printThisActivity(pw, mLastPausedActivity, dumpPackage, needSep,
+                    "    mLastPausedActivity: ");
+            if (pr) {
+                printed = true;
+                needSep = true;
+            }
+            printed |= printThisActivity(pw, mLastNoHistoryActivity, dumpPackage,
+                    needSep, "    mLastNoHistoryActivity: ");
+        }
+        return printed;
+    }
+
     boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
             boolean dumpClient, String dumpPackage, boolean needSep) {
 
@@ -5173,7 +5221,7 @@
             pw.println(prefix + "mLastNonFullscreenBounds=" + task.mLastNonFullscreenBounds);
             pw.println(prefix + "* " + task);
             task.dump(pw, prefix + "  ");
-            ActivityStackSupervisor.dumpHistoryList(fd, pw, mTaskHistory.get(taskNdx).mActivities,
+            dumpHistoryList(fd, pw, mTaskHistory.get(taskNdx).mActivities,
                     prefix, "Hist", true, !dumpAll, dumpClient, dumpPackage, false, null, task);
         }
         return true;
@@ -5242,11 +5290,6 @@
      *             {@link #REMOVE_TASK_MODE_MOVING}, {@link #REMOVE_TASK_MODE_MOVING_TO_TOP}.
      */
     void removeTask(TaskRecord task, String reason, int mode) {
-        // TODO(b/119259346): Move some logic below to TaskRecord. See bug for more context.
-        for (ActivityRecord record : task.mActivities) {
-            onActivityRemovedFromStack(record);
-        }
-
         final boolean removed = mTaskHistory.remove(task);
 
         if (removed) {
@@ -5256,25 +5299,8 @@
         removeActivitiesFromLRUListLocked(task);
         updateTaskMovement(task, true);
 
-        if (mode == REMOVE_TASK_MODE_DESTROYING && task.mActivities.isEmpty()) {
-            // This task is going away, so save the last state if necessary.
-            task.saveLaunchingStateIfNeeded();
-
-            // TODO: VI what about activity?
-            final boolean isVoiceSession = task.voiceSession != null;
-            if (isVoiceSession) {
-                try {
-                    task.voiceSession.taskFinished(task.intent, task.taskId);
-                } catch (RemoteException e) {
-                }
-            }
-            if (task.autoRemoveFromRecents() || isVoiceSession) {
-                // Task creator asked to remove this when done, or this task was a voice
-                // interaction, so it should not remain on the recent tasks list.
-                mStackSupervisor.mRecentTasks.remove(task);
-            }
-
-            task.removeWindowContainer();
+        if (mode == REMOVE_TASK_MODE_DESTROYING) {
+            task.cleanUpResourcesForDestroy();
         }
 
         if (mTaskHistory.isEmpty()) {
@@ -5349,6 +5375,10 @@
             String reason) {
         // TODO: Is this remove really needed? Need to look into the call path for the other addTask
         mTaskHistory.remove(task);
+        if (isSingleTaskInstance() && !mTaskHistory.isEmpty()) {
+            throw new IllegalStateException("Can only have one child on stack=" + this);
+        }
+
         position = getAdjustedPositionForTask(task, position, null /* starting */);
         final boolean toTop = position >= mTaskHistory.size();
         final ActivityStack prevStack = preAddTask(task, reason, toTop);
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index f58b83d..a50ae84 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -947,6 +947,7 @@
         final WindowProcessController wpc =
                 mService.getProcessController(r.processName, r.info.applicationInfo.uid);
 
+        boolean knownToBeDead = false;
         if (wpc != null && wpc.hasThread()) {
             try {
                 if ((r.info.flags & ActivityInfo.FLAG_MULTIPROCESS) == 0
@@ -965,6 +966,7 @@
 
             // If a dead object exception was thrown -- fall through to
             // restart the application.
+            knownToBeDead = true;
         }
 
         // Suppress transition until the new activity becomes ready, otherwise the keyguard can
@@ -978,7 +980,7 @@
         // ATMS lock held.
         final Message msg = PooledLambda.obtainMessage(
                 ActivityManagerInternal::startProcess, mService.mAmInternal, r.processName,
-                r.info.applicationInfo, true, "activity", r.intent.getComponent());
+                r.info.applicationInfo, knownToBeDead, "activity", r.intent.getComponent());
         mService.mH.sendMessage(msg);
     }
 
@@ -2657,7 +2659,8 @@
             return mService.getActivityStartController().startActivityInPackage(
                     task.mCallingUid, callingPid, callingUid, callingPackage, intent, null, null,
                     null, 0, 0, options, userId, task, "startActivityFromRecents",
-                    false /* validateIncomingUser */, null /* originatingPendingIntent */);
+                    false /* validateIncomingUser */, null /* originatingPendingIntent */,
+                    false /* allowBackgroundActivityStart */);
         } finally {
             if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && task != null) {
                 // If we are launching the task in the docked stack, put it into resizing mode so
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 904d9dd..0859683 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -258,7 +258,7 @@
             String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
             String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
             int userId, TaskRecord inTask, String reason, boolean validateIncomingUser,
-            PendingIntentRecord originatingPendingIntent) {
+            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
 
         userId = checkTargetUser(userId, validateIncomingUser, realCallingPid, realCallingUid,
                 reason);
@@ -278,6 +278,7 @@
                 .setMayWait(userId)
                 .setInTask(inTask)
                 .setOriginatingPendingIntent(originatingPendingIntent)
+                .setAllowBackgroundActivityStart(allowBackgroundActivityStart)
                 .execute();
     }
 
@@ -294,7 +295,8 @@
      */
     final int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
             String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
-            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent) {
+            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
+            boolean allowBackgroundActivityStart) {
 
         final String reason = "startActivityInPackage";
 
@@ -303,12 +305,13 @@
 
         // TODO: Switch to user app stacks here.
         return startActivities(null, uid, callingPackage, intents, resolvedTypes, resultTo, options,
-                userId, reason, originatingPendingIntent);
+                userId, reason, originatingPendingIntent, allowBackgroundActivityStart);
     }
 
     int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
             Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options,
-            int userId, String reason, PendingIntentRecord originatingPendingIntent) {
+            int userId, String reason, PendingIntentRecord originatingPendingIntent,
+            boolean allowBackgroundActivityStart) {
         if (intents == null) {
             throw new NullPointerException("intents is null");
         }
@@ -355,7 +358,6 @@
                     ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i], 0,
                             null, userId, ActivityStarter.computeResolveFilterUid(
                                     callingUid, realCallingUid, UserHandle.USER_NULL));
-                    // TODO: New, check if this is correct
                     aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId);
 
                     if (aInfo != null &&
@@ -388,6 +390,7 @@
                             // top one as otherwise an activity below might consume it.
                             .setAllowPendingRemoteAnimationRegistryLookup(top /* allowLookup*/)
                             .setOriginatingPendingIntent(originatingPendingIntent)
+                            .setAllowBackgroundActivityStart(allowBackgroundActivityStart)
                             .execute();
 
                     if (res < 0) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 57bfc29..b100ecd 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -73,6 +73,8 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
+import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
+import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY;
 import static com.android.server.wm.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
 import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
 
@@ -321,6 +323,7 @@
         WaitResult waitResult;
         int filterCallingUid;
         PendingIntentRecord originatingPendingIntent;
+        boolean allowBackgroundActivityStart;
 
         /**
          * If set to {@code true}, allows this activity start to look into
@@ -378,6 +381,7 @@
             allowPendingRemoteAnimationRegistryLookup = true;
             filterCallingUid = UserHandle.USER_NULL;
             originatingPendingIntent = null;
+            allowBackgroundActivityStart = false;
         }
 
         /**
@@ -417,6 +421,7 @@
                     = request.allowPendingRemoteAnimationRegistryLookup;
             filterCallingUid = request.filterCallingUid;
             originatingPendingIntent = request.originatingPendingIntent;
+            allowBackgroundActivityStart = request.allowBackgroundActivityStart;
         }
     }
 
@@ -502,7 +507,7 @@
                         mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId,
                         mRequest.inTask, mRequest.reason,
                         mRequest.allowPendingRemoteAnimationRegistryLookup,
-                        mRequest.originatingPendingIntent);
+                        mRequest.originatingPendingIntent, mRequest.allowBackgroundActivityStart);
             } else {
                 return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent,
                         mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo,
@@ -513,7 +518,7 @@
                         mRequest.ignoreTargetSecurity, mRequest.componentSpecified,
                         mRequest.outActivity, mRequest.inTask, mRequest.reason,
                         mRequest.allowPendingRemoteAnimationRegistryLookup,
-                        mRequest.originatingPendingIntent);
+                        mRequest.originatingPendingIntent, mRequest.allowBackgroundActivityStart);
             }
         } finally {
             onExecutionComplete();
@@ -546,7 +551,7 @@
             SafeActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
             ActivityRecord[] outActivity, TaskRecord inTask, String reason,
             boolean allowPendingRemoteAnimationRegistryLookup,
-            PendingIntentRecord originatingPendingIntent) {
+            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
 
         if (TextUtils.isEmpty(reason)) {
             throw new IllegalArgumentException("Need to specify a reason.");
@@ -559,7 +564,8 @@
                 aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,
                 callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
                 options, ignoreTargetSecurity, componentSpecified, mLastStartActivityRecord,
-                inTask, allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent);
+                inTask, allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent,
+                allowBackgroundActivityStart);
 
         if (outActivity != null) {
             // mLastStartActivityRecord[0] is set in the call to startActivity above.
@@ -590,7 +596,7 @@
             SafeActivityOptions options,
             boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
             TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup,
-            PendingIntentRecord originatingPendingIntent) {
+            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
         int err = ActivityManager.START_SUCCESS;
         // Pull the optional Ephemeral Installer-only bundle out of the options early.
         final Bundle verificationBundle
@@ -740,7 +746,7 @@
         // on START_ABORTED
         if (!abort) {
             abort |= shouldAbortBackgroundActivityStart(callingUid, callingPackage, realCallingUid,
-                    callerApp);
+                    callerApp, originatingPendingIntent, allowBackgroundActivityStart);
         }
 
         // Merge the two options bundles, while realCallerOptions takes precedence.
@@ -888,7 +894,8 @@
     }
 
     private boolean shouldAbortBackgroundActivityStart(int callingUid, final String callingPackage,
-            int realCallingUid, WindowProcessController callerApp) {
+            int realCallingUid, WindowProcessController callerApp,
+            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
         if (mService.isBackgroundActivityStartsEnabled()) {
             return false;
         }
@@ -900,12 +907,25 @@
         if (callerApp != null && callerApp.hasForegroundActivities()) {
             return false;
         }
-        // don't abort if the callingUid is in the foreground
-        if (isUidForeground(callingUid)) {
+        // don't abort if the callingUid is in the foreground or is a persistent system process
+        if (isUidForeground(callingUid) || isUidPersistentSystemProcess(callingUid)) {
             return false;
         }
-        // don't abort if the realCallingUid is in the foreground and callingUid isn't
-        if ((realCallingUid != callingUid) && isUidForeground(realCallingUid)) {
+        // take realCallingUid into consideration
+        if (realCallingUid != callingUid) {
+            // don't abort if the realCallingUid is in the foreground and callingUid isn't
+            if (isUidForeground(realCallingUid)) {
+                return false;
+            }
+            // if the realCallingUid is a persistent system process, abort if the IntentSender
+            // wasn't whitelisted to start an activity
+            if (isUidPersistentSystemProcess(realCallingUid) && (originatingPendingIntent != null)
+                    && allowBackgroundActivityStart) {
+                return false;
+            }
+        }
+        // don't abort if the caller is currently temporarily whitelisted
+        if (callerApp != null && callerApp.areBackgroundActivityStartsAllowed()) {
             return false;
         }
         // don't abort if the caller has the same uid as the recents component
@@ -922,12 +942,17 @@
         return true;
     }
 
-    /** Returns true if uid has a visible window or its process is in top or persistent state. */
+    /** Returns true if uid has a visible window or its process is in a top state. */
     private boolean isUidForeground(int uid) {
-        return (mService.getUidStateLocked(uid) <= ActivityManager.PROCESS_STATE_TOP)
+        return (mService.getUidStateLocked(uid) == ActivityManager.PROCESS_STATE_TOP)
             || mService.mWindowManager.isAnyWindowVisibleForUid(uid);
     }
 
+    /** Returns true if uid is in a persistent state. */
+    private boolean isUidPersistentSystemProcess(int uid) {
+        return (mService.getUidStateLocked(uid) <= ActivityManager.PROCESS_STATE_PERSISTENT_UI);
+    }
+
     private void maybeLogActivityStart(int callingUid, String callingPackage, int realCallingUid,
             Intent intent, WindowProcessController callerApp, ActivityRecord r,
             PendingIntentRecord originatingPendingIntent, boolean abortedStart) {
@@ -1047,7 +1072,7 @@
             Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity,
             int userId, TaskRecord inTask, String reason,
             boolean allowPendingRemoteAnimationRegistryLookup,
-            PendingIntentRecord originatingPendingIntent) {
+            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
         // Refuse possible leaked file descriptors
         if (intent != null && intent.hasFileDescriptors()) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -1193,7 +1218,8 @@
                     voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid,
                     callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options,
                     ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason,
-                    allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent);
+                    allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent,
+                    allowBackgroundActivityStart);
 
             Binder.restoreCallingIdentity(origId);
 
@@ -1339,6 +1365,21 @@
                 voiceInteractor);
         final int preferredWindowingMode = mLaunchParams.mWindowingMode;
 
+        computeLaunchingTaskFlags();
+
+        computeSourceStack();
+
+        mIntent.setFlags(mLaunchFlags);
+
+        ActivityRecord reusedActivity = getReusableIntentActivity();
+
+        mSupervisor.getLaunchParamsController().calculate(
+                reusedActivity != null ? reusedActivity.getTaskRecord() : mInTask,
+                r.info.windowLayout, r, sourceRecord, options, PHASE_BOUNDS, mLaunchParams);
+        mPreferredDisplayId =
+                mLaunchParams.hasPreferredDisplay() ? mLaunchParams.mPreferredDisplayId
+                        : DEFAULT_DISPLAY;
+
         // Do not start home activity if it cannot be launched on preferred display. We are not
         // doing this in ActivityStackSupervisor#canPlaceEntityOnDisplay because it might
         // fallback to launch on other displays.
@@ -1348,14 +1389,6 @@
             return START_CANCELED;
         }
 
-        computeLaunchingTaskFlags();
-
-        computeSourceStack();
-
-        mIntent.setFlags(mLaunchFlags);
-
-        ActivityRecord reusedActivity = getReusableIntentActivity();
-
         if (reusedActivity != null) {
             // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
             // still needs to be a lock task mode violation since the task gets cleared out and
@@ -1651,14 +1684,13 @@
 
         mLaunchParams.reset();
 
+        // Preferred display id is the only state we need for now and it could be updated again
+        // after we located a reusable task (which might be resided in another display).
         mSupervisor.getLaunchParamsController().calculate(inTask, r.info.windowLayout, r,
-                sourceRecord, options, mLaunchParams);
-
-        if (mLaunchParams.hasPreferredDisplay()) {
-            mPreferredDisplayId = mLaunchParams.mPreferredDisplayId;
-        } else {
-            mPreferredDisplayId = DEFAULT_DISPLAY;
-        }
+                sourceRecord, options, PHASE_DISPLAY, mLaunchParams);
+        mPreferredDisplayId =
+                mLaunchParams.hasPreferredDisplay() ? mLaunchParams.mPreferredDisplayId
+                        : DEFAULT_DISPLAY;
 
         mLaunchMode = r.launchMode;
 
@@ -2502,14 +2534,9 @@
 
         if (((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) == 0)
                  || mPreferredDisplayId != DEFAULT_DISPLAY) {
-            // We don't pass in the default display id into the get launch stack call so it can do a
-            // full resolution.
-            mLaunchParams.mPreferredDisplayId =
-                    mPreferredDisplayId != DEFAULT_DISPLAY ? mPreferredDisplayId : INVALID_DISPLAY;
             final boolean onTop = aOptions == null || !aOptions.getAvoidMoveToFront();
             final ActivityStack stack =
                     mRootActivityContainer.getLaunchStack(r, aOptions, task, onTop, mLaunchParams);
-            mLaunchParams.mPreferredDisplayId = mPreferredDisplayId;
             return stack;
         }
         // Otherwise handle adjacent launch.
@@ -2728,6 +2755,11 @@
         return this;
     }
 
+    ActivityStarter setAllowBackgroundActivityStart(boolean allowBackgroundActivityStart) {
+        mRequest.allowBackgroundActivityStart = allowBackgroundActivityStart;
+        return this;
+    }
+
     void dump(PrintWriter pw, String prefix) {
         prefix = prefix + "  ";
         pw.print(prefix);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 0fc890a..0f286ce 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -197,16 +197,19 @@
      * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
      * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
      *        null if not originated by PendingIntent
+     * @param allowBackgroundActivityStart Whether the background activity start should be allowed
+     *        from originatingPendingIntent
      */
     public abstract int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
             String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
-            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent);
+            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
+            boolean allowBackgroundActivityStart);
 
     public abstract int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
             String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
             String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
             int userId, TaskRecord inTask, String reason, boolean validateIncomingUser,
-            PendingIntentRecord originatingPendingIntent);
+            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart);
 
     /**
      * Start activity {@code intent} without calling user-id check.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 9861157..42121ca 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -639,7 +639,8 @@
         }
     }
 
-    ActivityTaskManagerService(Context context) {
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public ActivityTaskManagerService(Context context) {
         mContext = context;
         mFactoryTest = FactoryTest.getMode();
         mSystemThread = ActivityThread.currentActivityThread();
@@ -960,7 +961,7 @@
         // TODO: Switch to user app stacks here.
         return getActivityStartController().startActivities(caller, -1, callingPackage, intents,
                 resolvedTypes, resultTo, SafeActivityOptions.fromBundle(bOptions), userId, reason,
-                null /* originatingPendingIntent */);
+                null /* originatingPendingIntent */, false /* allowBackgroundActivityStart */);
     }
 
     @Override
@@ -1426,6 +1427,42 @@
     }
 
     /**
+     * Public API to check if the client is allowed to start an activity on specified display.
+     *
+     * If the target display is private or virtual, some restrictions will apply.
+     *
+     * @param displayId Target display id.
+     * @param intent Intent used to launch the activity.
+     * @param resolvedType The MIME type of the intent.
+     * @param userId The id of the user for whom the call is made.
+     * @return {@code true} if a call to start an activity on the target display should succeed and
+     *         no {@link SecurityException} will be thrown, {@code false} otherwise.
+     */
+    @Override
+    public final boolean isActivityStartAllowedOnDisplay(int displayId, Intent intent,
+            String resolvedType, int userId) {
+        final int callingUid = Binder.getCallingUid();
+        final int callingPid = Binder.getCallingPid();
+        final long origId = Binder.clearCallingIdentity();
+
+        try {
+            // Collect information about the target of the Intent.
+            ActivityInfo aInfo = mStackSupervisor.resolveActivity(intent, resolvedType,
+                    0 /* startFlags */, null /* profilerInfo */, userId,
+                    ActivityStarter.computeResolveFilterUid(callingUid, callingUid,
+                            UserHandle.USER_NULL));
+            aInfo = mAmInternal.getActivityInfoForUser(aInfo, userId);
+
+            synchronized (mGlobalLock) {
+                return mStackSupervisor.canPlaceEntityOnDisplay(displayId, callingPid, callingUid,
+                        aInfo);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    /**
      * This is the internal entry point for handling Activity.finish().
      *
      * @param token The Binder token referencing the Activity we want to finish.
@@ -4533,6 +4570,26 @@
         }
     }
 
+    /**
+     * Makes the display with the given id a single task instance display. I.e the display can only
+     * contain one task.
+     */
+    @Override
+    public void setDisplayToSingleTaskInstance(int displayId) {
+        mAmInternal.enforceCallingPermission(Manifest.permission.MANAGE_ACTIVITY_STACKS,
+                "setDisplayToSingleTaskInstance");
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            final ActivityDisplay display =
+                    mRootActivityContainer.getActivityDisplayOrCreate(displayId);
+            if (display != null) {
+                display.setDisplayToSingleTaskInstance();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
     void dumpLastANRLocked(PrintWriter pw) {
         pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)");
         if (mLastANRState == null) {
@@ -5475,6 +5532,31 @@
         return intent;
     }
 
+    /**
+     * Return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME} to resolve secondary home
+     * activities.
+     *
+     * @param preferredPackage Specify a preferred package name, otherwise use secondary home
+     *                        component defined in config_secondaryHomeComponent.
+     * @return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME}
+     */
+    Intent getSecondaryHomeIntent(String preferredPackage) {
+        final Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
+        if (preferredPackage == null) {
+            // Using the component stored in config if no package name.
+            final String secondaryHomeComponent = mContext.getResources().getString(
+                    com.android.internal.R.string.config_secondaryHomeComponent);
+            intent.setComponent(ComponentName.unflattenFromString(secondaryHomeComponent));
+        } else {
+            intent.setPackage(preferredPackage);
+        }
+        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
+        if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
+            intent.addCategory(Intent.CATEGORY_SECONDARY_HOME);
+        }
+        return intent;
+    }
+
     ApplicationInfo getAppInfoForUser(ApplicationInfo info, int userId) {
         if (info == null) return null;
         ApplicationInfo newInfo = new ApplicationInfo(info);
@@ -5726,18 +5808,20 @@
                         packageUid, packageName,
                         intents, resolvedTypes, null /* resultTo */,
                         SafeActivityOptions.fromBundle(bOptions), userId,
-                        false /* validateIncomingUser */, null /* originatingPendingIntent */);
+                        false /* validateIncomingUser */, null /* originatingPendingIntent */,
+                        false /* allowBackgroundActivityStart */);
             }
         }
 
         @Override
         public int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
                 String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
-                boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent) {
+                boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
+                boolean allowBackgroundActivityStart) {
             synchronized (mGlobalLock) {
                 return getActivityStartController().startActivitiesInPackage(uid, callingPackage,
                         intents, resolvedTypes, resultTo, options, userId, validateIncomingUser,
-                        originatingPendingIntent);
+                        originatingPendingIntent, allowBackgroundActivityStart);
             }
         }
 
@@ -5746,12 +5830,14 @@
                 String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
                 String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
                 int userId, TaskRecord inTask, String reason, boolean validateIncomingUser,
-                PendingIntentRecord originatingPendingIntent) {
+                PendingIntentRecord originatingPendingIntent,
+                boolean allowBackgroundActivityStart) {
             synchronized (mGlobalLock) {
                 return getActivityStartController().startActivityInPackage(uid, realCallingPid,
                         realCallingUid, callingPackage, intent, resolvedType, resultTo, resultWho,
                         requestCode, startFlags, options, userId, inTask, reason,
-                        validateIncomingUser, originatingPendingIntent);
+                        validateIncomingUser, originatingPendingIntent,
+                        allowBackgroundActivityStart);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index ca306aa..2157ef6 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -91,6 +91,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Debug;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -733,6 +734,17 @@
     }
 
     boolean windowsAreFocusable() {
+        if (mTargetSdk < Build.VERSION_CODES.Q) {
+            final int pid = mActivityRecord != null
+                    ? (mActivityRecord.app != null ? mActivityRecord.app.getPid() : 0) : 0;
+            final AppWindowToken topFocusedAppOfMyProcess =
+                    mWmService.mRoot.mTopFocusedAppByProcess.get(pid);
+            if (topFocusedAppOfMyProcess != null && topFocusedAppOfMyProcess != this) {
+                // For the apps below Q, there can be only one app which has the focused window per
+                // process, because legacy apps may not be ready for a multi-focus system.
+                return false;
+            }
+        }
         return getWindowConfiguration().canReceiveKeys() || mAlwaysFocusable;
     }
 
@@ -1826,23 +1838,23 @@
                     return false;
                 }
             }
-
-            if (transferStartingWindow(transferFrom)) {
-                return true;
-            }
-
-            // There is no existing starting window, and we don't want to create a splash screen, so
-            // that's it!
-            if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
-                return false;
-            }
-
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
-            startingData = new SplashScreenStartingData(mWmService, pkg,
-                    theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
-                    getMergedOverrideConfiguration());
-            scheduleAddStartingWindow();
         }
+
+        if (transferStartingWindow(transferFrom)) {
+            return true;
+        }
+
+        // There is no existing starting window, and we don't want to create a splash screen, so
+        // that's it!
+        if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
+            return false;
+        }
+
+        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
+        startingData = new SplashScreenStartingData(mWmService, pkg,
+                theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
+                getMergedOverrideConfiguration());
+        scheduleAddStartingWindow();
         return true;
     }
 
@@ -1938,6 +1950,11 @@
         } else if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
             return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
         } else if (taskSwitch && allowTaskSnapshot) {
+            if (mWmService.mLowRamTaskSnapshots) {
+                // For low RAM devices, we use the splash screen starting window instead of the
+                // task snapshot starting window.
+                return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+            }
             return snapshot == null ? STARTING_WINDOW_TYPE_NONE
                     : snapshotOrientationSameAsTask(snapshot) || fromRecents
                             ? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java
index 5b20af3..9bc8462 100644
--- a/services/core/java/com/android/server/wm/BarController.java
+++ b/services/core/java/com/android/server/wm/BarController.java
@@ -62,7 +62,8 @@
     private StatusBarManagerInternal mStatusBarInternal;
 
     protected WindowState mWin;
-    private int mState = StatusBarManager.WINDOW_STATE_SHOWING;
+    private @StatusBarManager.WindowVisibleState int mState =
+            StatusBarManager.WINDOW_STATE_SHOWING;
     private int mTransientBarState;
     private boolean mPendingShow;
     private long mLastTranslucent;
@@ -199,7 +200,8 @@
         return !mWin.isDrawnLw();
     }
 
-    private int computeStateLw(boolean wasVis, boolean wasAnim, WindowState win, boolean change) {
+    private @StatusBarManager.WindowVisibleState int computeStateLw(
+            boolean wasVis, boolean wasAnim, WindowState win, boolean change) {
         if (win.isDrawnLw()) {
             final boolean vis = win.isVisibleLw();
             final boolean anim = win.isAnimatingLw();
@@ -218,7 +220,7 @@
         return mState;
     }
 
-    private boolean updateStateLw(final int state) {
+    private boolean updateStateLw(@StatusBarManager.WindowVisibleState final int state) {
         if (mWin != null && state != mState) {
             mState = state;
             if (DEBUG) Slog.d(mTag, "mState: " + StatusBarManager.windowStateToString(state));
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index 731ebb8..f9980be 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -128,9 +128,10 @@
         mAnimationHandler = animationHandler;
         if (animationHandler != null) {
             // If an animation handler is provided, then ensure that it runs on the sf vsync tick
-            handler.runWithScissors(() -> mChoreographer = Choreographer.getSfInstance(),
-                    0 /* timeout */);
-            animationHandler.setProvider(new SfVsyncFrameCallbackProvider(mChoreographer));
+            handler.post(() -> {
+                mChoreographer = Choreographer.getSfInstance();
+                animationHandler.setProvider(new SfVsyncFrameCallbackProvider(mChoreographer));
+            });
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2f4c5ca..740d472 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -61,9 +61,9 @@
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
-
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
@@ -103,6 +103,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.CUSTOM_SCREEN_ROTATION;
+import static com.android.server.wm.WindowManagerService.H.REPORT_FOCUS_CHANGE;
 import static com.android.server.wm.WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE;
 import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
 import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
@@ -169,6 +170,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.internal.util.function.TriConsumer;
+import com.android.server.AnimationThread;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.wm.utils.DisplayRotationUtil;
 import com.android.server.wm.utils.RotationCache;
@@ -395,10 +397,11 @@
     private final Matrix mTmpMatrix = new Matrix();
     private final Region mTmpRegion = new Region();
 
-
     /** Used for handing back size of display */
     private final Rect mTmpBounds = new Rect();
 
+    private final Configuration mTmpConfiguration = new Configuration();
+
     /** Remove this display when animation on it has completed. */
     private boolean mDeferredRemoval;
 
@@ -861,7 +864,7 @@
 
         AnimationHandler animationHandler = new AnimationHandler();
         mBoundsAnimationController = new BoundsAnimationController(service.mContext,
-                mAppTransition, SurfaceAnimationThread.getHandler(), animationHandler);
+                mAppTransition, AnimationThread.getHandler(), animationHandler);
 
         if (mWmService.mInputManager != null) {
             final InputChannel inputChannel = mWmService.mInputManager.monitorInput("Display "
@@ -1154,6 +1157,36 @@
         mWmService.mH.obtainMessage(SEND_NEW_CONFIGURATION, this).sendToTarget();
     }
 
+    @Override
+    boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
+            ConfigurationContainer requestingContainer) {
+        final Configuration config = updateOrientationFromAppTokens(
+                getRequestedOverrideConfiguration(), freezeDisplayToken, false);
+        // If display rotation class tells us that it doesn't consider app requested orientation,
+        // this display won't rotate just because of an app changes its requested orientation. Thus
+        // it indicates that this display chooses not to handle this request.
+        final boolean handled = getDisplayRotation().respectAppRequestedOrientation();
+        if (config == null) {
+            return handled;
+        }
+
+        if (handled && requestingContainer instanceof ActivityRecord) {
+            final ActivityRecord activityRecord = (ActivityRecord) requestingContainer;
+            final boolean kept = mWmService.mAtmService.updateDisplayOverrideConfigurationLocked(
+                    config, activityRecord, false /* deferResume */, getDisplayId());
+            activityRecord.frozenBeforeDestroy = true;
+            if (!kept) {
+                mWmService.mAtmService.mRootActivityContainer.resumeFocusedStacksTopActivities();
+            }
+        } else {
+            // We have a new configuration to push so we need to update ATMS for now.
+            // TODO: Clean up display configuration push between ATMS and WMS after unification.
+            mWmService.mAtmService.updateDisplayOverrideConfigurationLocked(
+                    config, null /* starting */, false /* deferResume */, getDisplayId());
+        }
+        return handled;
+    }
+
     /**
      * Determine the new desired orientation of this display.
      *
@@ -1167,7 +1200,56 @@
         return updateOrientationFromAppTokens(false /* forceUpdate */);
     }
 
-    boolean updateOrientationFromAppTokens(boolean forceUpdate) {
+    /**
+     * Update orientation of the target display, returning a non-null new Configuration if it has
+     * changed from the current orientation. If a non-null configuration is returned, someone must
+     * call {@link WindowManagerService#setNewDisplayOverrideConfiguration(Configuration,
+     * DisplayContent)} to tell the window manager it can unfreeze the screen. This will typically
+     * be done by calling {@link WindowManagerService#sendNewConfiguration(int)}.
+     */
+    Configuration updateOrientationFromAppTokens(Configuration currentConfig,
+            IBinder freezeDisplayToken, boolean forceUpdate) {
+        if (!mDisplayReady) {
+            return null;
+        }
+
+        Configuration config = null;
+        if (updateOrientationFromAppTokens(forceUpdate)) {
+            // If we changed the orientation but mOrientationChangeComplete is already true,
+            // we used seamless rotation, and we don't need to freeze the screen.
+            if (freezeDisplayToken != null && !mWmService.mRoot.mOrientationChangeComplete) {
+                final AppWindowToken atoken = getAppWindowToken(freezeDisplayToken);
+                if (atoken != null) {
+                    atoken.startFreezingScreen();
+                }
+            }
+            config = new Configuration();
+            computeScreenConfiguration(config);
+        } else if (currentConfig != null) {
+            // No obvious action we need to take, but if our current state mismatches the
+            // activity manager's, update it, disregarding font scale, which should remain set
+            // to the value of the previous configuration.
+            // Here we're calling Configuration#unset() instead of setToDefaults() because we
+            // need to keep override configs clear of non-empty values (e.g. fontSize).
+            mTmpConfiguration.unset();
+            mTmpConfiguration.updateFrom(currentConfig);
+            computeScreenConfiguration(mTmpConfiguration);
+            if (currentConfig.diff(mTmpConfiguration) != 0) {
+                mWaitingForConfig = true;
+                setLayoutNeeded();
+                int[] anim = new int[2];
+                getDisplayPolicy().selectRotationAnimationLw(anim);
+
+                mWmService.startFreezingDisplayLocked(anim[0], anim[1], this);
+                config = new Configuration(mTmpConfiguration);
+            }
+        }
+
+        return config;
+    }
+
+
+    private boolean updateOrientationFromAppTokens(boolean forceUpdate) {
         final int req = getOrientation();
         if (req != mLastOrientation || forceUpdate) {
             mLastOrientation = req;
@@ -2424,6 +2506,7 @@
             mWmService.mAnimator.removeDisplayLocked(mDisplayId);
             mWindowingLayer.release();
             mOverlayLayer.release();
+            mInputMonitor.onDisplayRemoved();
         } finally {
             mDisplayReady = false;
             mRemovingDisplay = false;
@@ -2832,6 +2915,11 @@
         forAllWindows(mScheduleToastTimeout, false /* traverseTopToBottom */);
     }
 
+    WindowState findFocusedWindowIfNeeded() {
+        return (mWmService.mPerDisplayFocusEnabled
+                || mWmService.mRoot.mTopFocusedAppByProcess.isEmpty()) ? findFocusedWindow() : null;
+    }
+
     WindowState findFocusedWindow() {
         mTmpWindow = null;
 
@@ -2844,7 +2932,6 @@
         return mTmpWindow;
     }
 
-
     /**
      * Update the focused window and make some adjustments if the focus has changed.
      *
@@ -2856,31 +2943,31 @@
      * @param updateInputWindows Whether to sync the window information to the input module.
      * @return {@code true} if the focused window has changed.
      */
-    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows, boolean focusFound) {
-        final WindowState newFocus = findFocusedWindow();
+    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
+        WindowState newFocus = findFocusedWindowIfNeeded();
         if (mCurrentFocus == newFocus) {
             return false;
         }
         boolean imWindowChanged = false;
-        // TODO (b/111080190): Multi-Session IME
-        if (!focusFound) {
-            final WindowState imWindow = mInputMethodWindow;
-            if (imWindow != null) {
-                final WindowState prevTarget = mInputMethodTarget;
+        final WindowState imWindow = mInputMethodWindow;
+        if (imWindow != null) {
+            final WindowState prevTarget = mInputMethodTarget;
+            final WindowState newTarget = computeImeTarget(true /* updateImeTarget*/);
+            imWindowChanged = prevTarget != newTarget;
 
-                final WindowState newTarget = computeImeTarget(true /* updateImeTarget*/);
-                imWindowChanged = prevTarget != newTarget;
-
-                if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
-                        && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
-                    assignWindowLayers(false /* setLayoutNeeded */);
-                }
+            if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
+                    && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
+                assignWindowLayers(false /* setLayoutNeeded */);
             }
         }
 
         if (imWindowChanged) {
             mWmService.mWindowsChanged = true;
             setLayoutNeeded();
+            newFocus = findFocusedWindowIfNeeded();
+        }
+        if (mCurrentFocus != newFocus) {
+            mWmService.mH.obtainMessage(REPORT_FOCUS_CHANGE, this).sendToTarget();
         }
 
         if (DEBUG_FOCUS_LIGHT || mWmService.localLOGV) Slog.v(TAG_WM, "Changing focus from "
@@ -3569,6 +3656,12 @@
         }
     }
 
+    /** @returns the orientation of the display when it's rotation is ROTATION_0. */
+    int getNaturalOrientation() {
+        return mBaseDisplayWidth < mBaseDisplayHeight
+                ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+    }
+
     void performLayout(boolean initial, boolean updateInputWindows) {
         if (!isLayoutNeeded()) {
             return;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 8b8cadc..6d3c693 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -505,23 +505,6 @@
         // TODO: Make it can take screenshot on external display
         mScreenshotHelper = displayContent.isDefaultDisplay
                 ? new ScreenshotHelper(mContext) : null;
-    }
-
-    void systemReady() {
-        mSystemGestures.systemReady();
-    }
-
-    private int getDisplayId() {
-        return mDisplayContent.getDisplayId();
-    }
-
-    void onDisplayRemoved() {
-        mDisplayContent.unregisterPointerEventListener(mSystemGestures);
-    }
-
-    void configure(int width, int height, int shortSizeDp) {
-        // Allow the navigation bar to move on non-square small devices (phones).
-        mNavigationBarCanMove = width != height && shortSizeDp < 600;
 
         if (mDisplayContent.isDefaultDisplay) {
             mHasStatusBar = true;
@@ -541,6 +524,23 @@
         }
     }
 
+    void systemReady() {
+        mSystemGestures.systemReady();
+    }
+
+    private int getDisplayId() {
+        return mDisplayContent.getDisplayId();
+    }
+
+    void onDisplayRemoved() {
+        mDisplayContent.unregisterPointerEventListener(mSystemGestures);
+    }
+
+    void configure(int width, int height, int shortSizeDp) {
+        // Allow the navigation bar to move on non-square small devices (phones).
+        mNavigationBarCanMove = width != height && shortSizeDp < 600;
+    }
+
     void updateConfigurationDependentBehaviors() {
         mNavBarOpacityMode = mContext.getResources().getInteger(R.integer.config_navBarOpacityMode);
     }
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 7aabc15..bcc7be4 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -329,6 +329,15 @@
         return mFixedToUserRotation;
     }
 
+    /**
+     * Returns {@code true} if this display rotation takes app requested orientation into
+     * consideration; {@code false} otherwise. For the time being the only case where this is {@code
+     * false} is when {@link #isFixedToUserRotation()} is {@code true}.
+     */
+    boolean respectAppRequestedOrientation() {
+        return !mFixedToUserRotation;
+    }
+
     public int getLandscapeRotation() {
         return mLandscapeRotation;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index 45d77de..5cfa7de 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -326,15 +326,11 @@
 
     void applySettingsToDisplayLocked(DisplayContent dc) {
         final DisplayInfo displayInfo = dc.getDisplayInfo();
-        final Entry entry = getEntry(displayInfo);
+        final Entry entry = getOrCreateEntry(displayInfo);
 
         // Setting windowing mode first, because it may override overscan values later.
         dc.setWindowingMode(getWindowingModeLocked(entry, dc.getDisplayId()));
 
-        if (entry == null) {
-            return;
-        }
-
         displayInfo.overscanLeft = entry.mOverscanLeft;
         displayInfo.overscanTop = entry.mOverscanTop;
         displayInfo.overscanRight = entry.mOverscanRight;
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index f9c9d33..639ed02 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -1,6 +1,5 @@
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
@@ -10,6 +9,7 @@
 import android.os.Debug;
 import android.os.IBinder;
 import android.util.Slog;
+import android.view.InputApplicationHandle;
 import android.view.KeyEvent;
 import android.view.WindowManager;
 
@@ -204,37 +204,6 @@
                 + WindowManagerService.TYPE_LAYER_OFFSET;
     }
 
-    /** Callback to get pointer display id. */
-    @Override
-    public int getPointerDisplayId() {
-        synchronized (mService.mGlobalLock) {
-            // If desktop mode is not enabled, show on the default display.
-            if (!mService.mForceDesktopModeOnExternalDisplays) {
-                return DEFAULT_DISPLAY;
-            }
-
-            // Look for the topmost freeform display.
-            int firstExternalDisplayId = DEFAULT_DISPLAY;
-            for (int i = mService.mRoot.mChildren.size() - 1; i >= 0; --i) {
-                final DisplayContent displayContent = mService.mRoot.mChildren.get(i);
-                // Heuristic solution here. Currently when "Freeform windows" developer option is
-                // enabled we automatically put secondary displays in freeform mode and emulating
-                // "desktop mode". It also makes sense to show the pointer on the same display.
-                if (displayContent.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
-                    return displayContent.getDisplayId();
-                }
-
-                if (firstExternalDisplayId == DEFAULT_DISPLAY
-                        && displayContent.getDisplayId() != DEFAULT_DISPLAY) {
-                    firstExternalDisplayId = displayContent.getDisplayId();
-                }
-            }
-
-            // Look for the topmost non-default display
-            return firstExternalDisplayId;
-        }
-    }
-
     /** Waits until the built-in input devices have been configured. */
     public boolean waitForInputDevicesReady(long timeoutMillis) {
         synchronized (mInputDevicesReadyMonitor) {
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index c1e9a73..632db38 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -32,6 +32,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.graphics.Rect;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Process;
@@ -45,11 +46,12 @@
 import android.view.InputEventReceiver;
 import android.view.InputWindowHandle;
 import android.view.SurfaceControl;
+import android.view.animation.Animation;
 
+import com.android.server.AnimationThread;
 import com.android.server.policy.WindowManagerPolicy;
 
 import java.io.PrintWriter;
-import java.util.Arrays;
 import java.util.Set;
 import java.util.function.Consumer;
 
@@ -61,6 +63,7 @@
 
     // When true, need to call updateInputWindowsLw().
     private boolean mUpdateInputWindowsNeeded = true;
+    private boolean mUpdateInputWindowsPending;
 
     // Currently focused input window handle.
     private InputWindowHandle mFocusedInputWindowHandle;
@@ -70,9 +73,12 @@
     private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer =
             new UpdateInputForAllWindowsConsumer();
 
-    private int mDisplayId;
+    private final int mDisplayId;
+    private final DisplayContent mDisplayContent;
+    private boolean mDisplayRemoved;
 
-    SurfaceControl.Transaction mInputTransaction = new SurfaceControl.Transaction();
+    private final SurfaceControl.Transaction mInputTransaction;
+    private final Handler mHandler;
 
     /**
      * The set of input consumer added to the window manager by name, which consumes input events
@@ -106,9 +112,64 @@
         }
     }
 
+    private final Runnable mUpdateInputWindows = new Runnable() {
+        @Override
+        public void run() {
+            synchronized (mService.mGlobalLock) {
+                mUpdateInputWindowsPending = false;
+                mUpdateInputWindowsNeeded = false;
+
+                if (mDisplayRemoved) {
+                    return;
+                }
+
+                // Populate the input window list with information about all of the windows that
+                // could potentially receive input.
+                // As an optimization, we could try to prune the list of windows but this turns
+                // out to be difficult because only the native code knows for sure which window
+                // currently has touch focus.
+
+                // If there's a drag in flight, provide a pseudo-window to catch drag input
+                final boolean inDrag = mService.mDragDropController.dragDropActiveLocked();
+                if (inDrag) {
+                    if (DEBUG_DRAG) {
+                        Log.d(TAG_WM, "Inserting drag window");
+                    }
+                    mService.mDragDropController.showInputSurface(mInputTransaction, mDisplayId);
+                } else {
+                    mService.mDragDropController.hideInputSurface(mInputTransaction, mDisplayId);
+                }
+
+                final boolean inPositioning =
+                        mService.mTaskPositioningController.isPositioningLocked();
+                if (inPositioning) {
+                    if (DEBUG_TASK_POSITIONING) {
+                        Log.d(TAG_WM, "Inserting window handle for repositioning");
+                    }
+                    mService.mTaskPositioningController.showInputSurface(mInputTransaction,
+                            mDisplayId);
+                } else {
+                    mService.mTaskPositioningController.hideInputSurface(mInputTransaction,
+                            mDisplayId);
+                }
+
+                // Add all windows on the default display.
+                mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag);
+            }
+        }
+    };
+
     public InputMonitor(WindowManagerService service, int displayId) {
         mService = service;
+        mDisplayContent = mService.mRoot.getDisplayContent(displayId);
         mDisplayId = displayId;
+        mInputTransaction = mDisplayContent.getPendingTransaction();
+        mHandler = AnimationThread.getHandler();
+    }
+
+    void onDisplayRemoved() {
+        mHandler.removeCallbacks(mUpdateInputWindows);
+        mDisplayRemoved = true;
     }
 
     private void addInputConsumer(String name, InputConsumerImpl consumer) {
@@ -248,41 +309,18 @@
         if (!force && !mUpdateInputWindowsNeeded) {
             return;
         }
-        mUpdateInputWindowsNeeded = false;
+        scheduleUpdateInputWindows();
+    }
 
-        if (false) Slog.d(TAG_WM, ">>>>>> ENTERED updateInputWindowsLw");
-
-        // Populate the input window list with information about all of the windows that
-        // could potentially receive input.
-        // As an optimization, we could try to prune the list of windows but this turns
-        // out to be difficult because only the native code knows for sure which window
-        // currently has touch focus.
-
-        // If there's a drag in flight, provide a pseudo-window to catch drag input
-        final boolean inDrag = mService.mDragDropController.dragDropActiveLocked();
-        if (inDrag) {
-            if (DEBUG_DRAG) {
-                Log.d(TAG_WM, "Inserting drag window");
-            }
-            mService.mDragDropController.showInputSurface(mInputTransaction, mDisplayId);
-        } else {
-            mService.mDragDropController.hideInputSurface(mInputTransaction, mDisplayId);
+    private void scheduleUpdateInputWindows() {
+        if (mDisplayRemoved) {
+            return;
         }
 
-        final boolean inPositioning = mService.mTaskPositioningController.isPositioningLocked();
-        if (inPositioning) {
-            if (DEBUG_TASK_POSITIONING) {
-                Log.d(TAG_WM, "Inserting window handle for repositioning");
-            }
-            mService.mTaskPositioningController.showInputSurface(mInputTransaction, mDisplayId);
-        } else {
-            mService.mTaskPositioningController.hideInputSurface(mInputTransaction, mDisplayId);
+        if (!mUpdateInputWindowsPending) {
+            mUpdateInputWindowsPending = true;
+            mHandler.post(mUpdateInputWindows);
         }
-
-        // Add all windows on the default display.
-        mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag);
-
-        if (false) Slog.d(TAG_WM, "<<<<<<< EXITED updateInputWindowsLw");
     }
 
     /* Called when the current input focus changes.
@@ -385,19 +423,18 @@
             mTmpRect.setEmpty();
             mDisableWallpaperTouchEvents = false;
             this.inDrag = inDrag;
-            final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
-            wallpaperController = dc.mWallpaperController;
+            wallpaperController = mDisplayContent.mWallpaperController;
 
             resetInputConsumers(mInputTransaction);
 
-            dc.forAllWindows(this,
+            mDisplayContent.forAllWindows(this,
                     true /* traverseTopToBottom */);
 
             if (mAddWallpaperInputConsumerHandle) {
                 wallpaperInputConsumer.show(mInputTransaction, 0);
             }
 
-            mInputTransaction.apply();
+            mDisplayContent.scheduleAnimation();
 
             Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
         }
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 282838f..d6f1616 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -18,15 +18,17 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.proto.ProtoOutputStream;
+import android.view.InsetsState;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.InsetsSource;
 import android.view.InsetsSourceControl;
+import android.view.ViewRootImpl;
 
 import com.android.internal.util.function.TriConsumer;
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 
 import java.io.PrintWriter;
@@ -47,8 +49,18 @@
     private WindowState mWin;
     private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider;
 
+    /** The visibility override from the current controlling window. */
+    private boolean mClientVisible;
+
+    /**
+     * Whether the window is available and considered visible as in {@link WindowState#isVisible}.
+     */
+    private boolean mServerVisible;
+
+
     InsetsSourceProvider(InsetsSource source, InsetsStateController stateController,
             DisplayContent displayContent) {
+        mClientVisible = InsetsState.getDefaultVisibly(source.getType());
         mSource = source;
         mDisplayContent = displayContent;
         mStateController = stateController;
@@ -73,10 +85,9 @@
         mWin = win;
         mFrameProvider = frameProvider;
         if (win == null) {
-            mSource.setVisible(false);
+            setServerVisible(false);
             mSource.setFrame(new Rect());
         } else {
-            mSource.setVisible(true);
             mWin.setInsetProvider(this);
         }
     }
@@ -96,8 +107,8 @@
             mTmpRect.inset(mWin.mGivenContentInsets);
         }
         mSource.setFrame(mTmpRect);
-        mSource.setVisible(mWin.isVisible() && !mWin.mGivenInsetsPending);
-
+        setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.mPolicyVisibility
+                && !mWin.mGivenInsetsPending);
     }
 
     void updateControlForTarget(@Nullable WindowState target) {
@@ -105,26 +116,50 @@
             return;
         }
         if (target == null) {
-            revokeControl();
+            // Cancelling the animation will invoke onAnimationCancelled, resetting all the fields.
+            mWin.cancelAnimation();
             return;
         }
         mAdapter = new ControlAdapter();
         mWin.startAnimation(mDisplayContent.getPendingTransaction(), mAdapter,
-                false /* TODO hidden */);
+                !mClientVisible /* hidden */);
         mControllingWin = target;
         mControl = new InsetsSourceControl(mSource.getType(), mAdapter.mCapturedLeash);
     }
 
+    boolean onInsetsModified(WindowState caller, InsetsSource modifiedSource) {
+        if (mControllingWin != caller || modifiedSource.isVisible() == mClientVisible) {
+            return false;
+        }
+        setClientVisible(modifiedSource.isVisible());
+        return true;
+    }
+
+    private void setClientVisible(boolean clientVisible) {
+        if (mClientVisible == clientVisible) {
+            return;
+        }
+        mClientVisible = clientVisible;
+        mDisplayContent.mWmService.mH.sendMessage(PooledLambda.obtainMessage(
+                DisplayContent::layoutAndAssignWindowLayersIfNeeded, mDisplayContent));
+        updateVisibility();
+    }
+
+    private void setServerVisible(boolean serverVisible) {
+        mServerVisible = serverVisible;
+        updateVisibility();
+    }
+
+    private void updateVisibility() {
+        mSource.setVisible(mServerVisible && mClientVisible);
+    }
+
     InsetsSourceControl getControl() {
         return mControl;
     }
 
-    void revokeControl() {
-        if (mControllingWin != null) {
-
-            // Cancelling the animation will invoke onAnimationCancelled, resetting all the fields.
-            mWin.cancelAnimation();
-        }
+    boolean isClientVisible() {
+        return !ViewRootImpl.USE_NEW_INSETS || mClientVisible;
     }
 
     private class ControlAdapter implements AnimationAdapter {
@@ -152,6 +187,7 @@
         public void onAnimationCancelled(SurfaceControl animationLeash) {
             if (mAdapter == this) {
                 mStateController.notifyControlRevoked(mControllingWin, InsetsSourceProvider.this);
+                setClientVisible(InsetsState.getDefaultVisibly(mSource.getType()));
                 mControl = null;
                 mControllingWin = null;
                 mAdapter = null;
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 592b7fb..cc57b93 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -25,6 +25,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.SparseArray;
+import android.view.InsetsSource;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.ViewRootImpl;
@@ -117,6 +118,21 @@
         }
     }
 
+    void onInsetsModified(WindowState windowState, InsetsState state) {
+        boolean changed = false;
+        for (int i = state.getSourcesCount() - 1; i >= 0; i--) {
+            final InsetsSource source = state.sourceAt(i);
+            final InsetsSourceProvider provider = mControllers.get(source.getType());
+            if (provider == null) {
+                continue;
+            }
+            changed |= provider.onInsetsModified(windowState, source);
+        }
+        if (changed) {
+            notifyInsetsChanged();
+        }
+    }
+
     void onImeTargetChanged(@Nullable WindowState imeTarget) {
         onControlChanged(TYPE_IME, imeTarget);
         notifyPendingInsetsControlChanged();
@@ -202,5 +218,11 @@
     void dump(String prefix, PrintWriter pw) {
         pw.println(prefix + "WindowInsetsStateController");
         mState.dump(prefix + "  ", pw);
+        pw.println(prefix + "  " + "Control map:");
+        for (int i = mTypeWinControlMap.size() - 1; i >= 0; i--) {
+            pw.print(prefix + "  ");
+            pw.println(InsetsState.typeToString(mTypeWinControlMap.keyAt(i)) + " -> "
+                    + mTypeWinControlMap.valueAt(i));
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/KeyguardDisableHandler.java b/services/core/java/com/android/server/wm/KeyguardDisableHandler.java
index 4a20f1a..c9173a6 100644
--- a/services/core/java/com/android/server/wm/KeyguardDisableHandler.java
+++ b/services/core/java/com/android/server/wm/KeyguardDisableHandler.java
@@ -19,113 +19,142 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
-import android.app.ActivityManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.TokenWatcher;
-import android.util.Log;
-import android.util.Pair;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManagerInternal;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
 import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.utils.UserTokenWatcher;
+import com.android.server.wm.LockTaskController.LockTaskToken;
 
-public class KeyguardDisableHandler extends Handler {
+class KeyguardDisableHandler {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "KeyguardDisableHandler" : TAG_WM;
 
-    private static final int ALLOW_DISABLE_YES = 1;
-    private static final int ALLOW_DISABLE_NO = 0;
-    private static final int ALLOW_DISABLE_UNKNOWN = -1; // check with DevicePolicyManager
-    private int mAllowDisableKeyguard = ALLOW_DISABLE_UNKNOWN; // sync'd by mKeyguardTokenWatcher
+    private final UserTokenWatcher mAppTokenWatcher;
+    private final UserTokenWatcher mSystemTokenWatcher;
 
-    // Message.what constants
-    static final int KEYGUARD_DISABLE = 1;
-    static final int KEYGUARD_REENABLE = 2;
-    static final int KEYGUARD_POLICY_CHANGED = 3;
+    private int mCurrentUser = UserHandle.USER_SYSTEM;
+    private Injector mInjector;
 
-    final Context mContext;
-    final WindowManagerPolicy mPolicy;
-    KeyguardTokenWatcher mKeyguardTokenWatcher;
-
-    public KeyguardDisableHandler(final Context context, final WindowManagerPolicy policy) {
-        mContext = context;
-        mPolicy = policy;
+    @VisibleForTesting
+    KeyguardDisableHandler(Injector injector, Handler handler) {
+        mInjector = injector;
+        mAppTokenWatcher = new UserTokenWatcher(mCallback, handler, TAG);
+        mSystemTokenWatcher = new UserTokenWatcher(mCallback, handler, TAG);
     }
 
-    @SuppressWarnings("unchecked")
-    @Override
-    public void handleMessage(Message msg) {
-        if (mKeyguardTokenWatcher == null) {
-            mKeyguardTokenWatcher = new KeyguardTokenWatcher(this);
-        }
-
-        switch (msg.what) {
-            case KEYGUARD_DISABLE:
-                final Pair<IBinder, String> pair = (Pair<IBinder, String>)msg.obj;
-                mKeyguardTokenWatcher.acquire(pair.first, pair.second);
-                break;
-
-            case KEYGUARD_REENABLE:
-                mKeyguardTokenWatcher.release((IBinder)msg.obj);
-                break;
-
-            case KEYGUARD_POLICY_CHANGED:
-                mAllowDisableKeyguard = ALLOW_DISABLE_UNKNOWN;
-                if (mKeyguardTokenWatcher.isAcquired()) {
-                    // If we are currently disabled we need to know if the keyguard
-                    // should be re-enabled, so determine the allow state immediately.
-                    mKeyguardTokenWatcher.updateAllowState();
-                    if (mAllowDisableKeyguard != ALLOW_DISABLE_YES) {
-                        mPolicy.enableKeyguard(true);
-                    }
-                } else {
-                    // lazily evaluate this next time we're asked to disable keyguard
-                    mPolicy.enableKeyguard(true);
-                }
-                break;
+    public void setCurrentUser(int user) {
+        synchronized (this) {
+            mCurrentUser = user;
+            updateKeyguardEnabledLocked(UserHandle.USER_ALL);
         }
     }
 
-    class KeyguardTokenWatcher extends TokenWatcher {
-
-        public KeyguardTokenWatcher(final Handler handler) {
-            super(handler, TAG);
+    void updateKeyguardEnabled(int userId) {
+        synchronized (this) {
+            updateKeyguardEnabledLocked(userId);
         }
+    }
 
-        public void updateAllowState() {
-            // We fail safe and prevent disabling keyguard in the unlikely event this gets
-            // called before DevicePolicyManagerService has started.
-            DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
-                    Context.DEVICE_POLICY_SERVICE);
-            if (dpm != null) {
-                try {
-                    mAllowDisableKeyguard = dpm.getPasswordQuality(null,
-                            ActivityManager.getService().getCurrentUser().id)
-                            == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED ?
-                                    ALLOW_DISABLE_YES : ALLOW_DISABLE_NO;
-                } catch (RemoteException re) {
-                    // Nothing much we can do
-                }
-            }
+    private void updateKeyguardEnabledLocked(int userId) {
+        if (mCurrentUser == userId || userId == UserHandle.USER_ALL) {
+            mInjector.enableKeyguard(shouldKeyguardBeEnabled(mCurrentUser));
+        }
+    }
+
+    void disableKeyguard(IBinder token, String tag, int callingUid, int userId) {
+        UserTokenWatcher watcherForCaller = watcherForCallingUid(token, callingUid);
+        watcherForCaller.acquire(token, tag, mInjector.getProfileParentId(userId));
+    }
+
+    void reenableKeyguard(IBinder token, int callingUid, int userId) {
+        UserTokenWatcher watcherForCaller = watcherForCallingUid(token, callingUid);
+        watcherForCaller.release(token, mInjector.getProfileParentId(userId));
+    }
+
+    private UserTokenWatcher watcherForCallingUid(IBinder token, int callingUid) {
+        if (Process.isApplicationUid(callingUid)) {
+            return mAppTokenWatcher;
+        } else if (callingUid == Process.SYSTEM_UID && token instanceof LockTaskToken) {
+            // We allow the lock task token here as a legacy case, because it enforces its own
+            // security guarantees.
+            // NOTE: DO NOT add new usages of this API in system server. It is deprecated and
+            // easily misused.
+            return mSystemTokenWatcher;
+        } else {
+            throw new UnsupportedOperationException("Only apps can use the KeyguardLock API");
+        }
+    }
+
+    private boolean shouldKeyguardBeEnabled(int userId) {
+        final boolean dpmRequiresPassword = mInjector.dpmRequiresPassword(mCurrentUser);
+        final boolean keyguardSecure = mInjector.isKeyguardSecure(mCurrentUser);
+
+        final boolean allowedFromApps = !dpmRequiresPassword && !keyguardSecure;
+        // The system can disable the keyguard for lock task mode even if the keyguard is secure,
+        // because it enforces its own security guarantees.
+        final boolean allowedFromSystem = !dpmRequiresPassword;
+
+        final boolean shouldBeDisabled = allowedFromApps && mAppTokenWatcher.isAcquired(userId)
+                        || allowedFromSystem && mSystemTokenWatcher.isAcquired(userId);
+        return !shouldBeDisabled;
+    }
+
+    // Callback happens on mHandler thread.
+    private final UserTokenWatcher.Callback mCallback = new UserTokenWatcher.Callback() {
+        @Override
+        public void acquired(int userId) {
+            updateKeyguardEnabled(userId);
         }
 
         @Override
-        public void acquired() {
-            if (mAllowDisableKeyguard == ALLOW_DISABLE_UNKNOWN) {
-                updateAllowState();
-            }
-            if (mAllowDisableKeyguard == ALLOW_DISABLE_YES) {
-                mPolicy.enableKeyguard(false);
-            } else {
-                Log.v(TAG, "Not disabling keyguard since device policy is enforced");
-            }
+        public void released(int userId) {
+            updateKeyguardEnabled(userId);
         }
+    };
 
-        @Override
-        public void released() {
-            mPolicy.enableKeyguard(true);
-        }
+    static KeyguardDisableHandler create(Context context, WindowManagerPolicy policy,
+            Handler handler) {
+        final UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class);
+        return new KeyguardDisableHandler(new Injector() {
+            @Override
+            public boolean dpmRequiresPassword(int userId) {
+                DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
+                        Context.DEVICE_POLICY_SERVICE);
+                return dpm == null || dpm.getPasswordQuality(null, userId)
+                        != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+            }
+
+            @Override
+            public boolean isKeyguardSecure(int userId) {
+                return policy.isKeyguardSecure(userId);
+            }
+
+            @Override
+            public int getProfileParentId(int userId) {
+                return userManager.getProfileParentId(userId);
+            }
+
+            @Override
+            public void enableKeyguard(boolean enabled) {
+                policy.enableKeyguard(enabled);
+            }
+        }, handler);
+    }
+
+    interface Injector {
+        boolean dpmRequiresPassword(int userId);
+
+        boolean isKeyguardSecure(int userId);
+
+        int getProfileParentId(int userId);
+
+        void enableKeyguard(boolean enabled);
     }
 }
diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java
index 0947577..59c02f7 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsController.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsController.java
@@ -20,6 +20,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 
+import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
@@ -74,7 +75,7 @@
      * @param result    The resulting params.
      */
     void calculate(TaskRecord task, WindowLayout layout, ActivityRecord activity,
-                   ActivityRecord source, ActivityOptions options, LaunchParams result) {
+                   ActivityRecord source, ActivityOptions options, int phase, LaunchParams result) {
         result.reset();
 
         if (task != null || activity != null) {
@@ -89,7 +90,7 @@
             mTmpResult.reset();
             final LaunchParamsModifier modifier = mModifiers.get(i);
 
-            switch(modifier.onCalculate(task, layout, activity, source, options, mTmpCurrent,
+            switch(modifier.onCalculate(task, layout, activity, source, options, phase, mTmpCurrent,
                     mTmpResult)) {
                 case RESULT_SKIP:
                     // Do not apply any results when we are told to skip
@@ -125,7 +126,7 @@
 
     boolean layoutTask(TaskRecord task, WindowLayout layout, ActivityRecord activity,
             ActivityRecord source, ActivityOptions options) {
-        calculate(task, layout, activity, source, options, mTmpParams);
+        calculate(task, layout, activity, source, options, PHASE_BOUNDS, mTmpParams);
 
         // No changes, return.
         if (mTmpParams.isEmpty()) {
@@ -259,6 +260,25 @@
          */
         int RESULT_CONTINUE = 2;
 
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef({PHASE_DISPLAY, PHASE_WINDOWING_MODE, PHASE_BOUNDS})
+        @interface Phase {}
+
+        /**
+         * Stops once we are done with preferred display calculation.
+         */
+        int PHASE_DISPLAY = 0;
+
+        /**
+         * Stops once we are done with windowing mode calculation.
+         */
+        int PHASE_WINDOWING_MODE = 1;
+
+        /**
+         * Stops once we are done with window bounds calculation.
+         */
+        int PHASE_BOUNDS = 2;
+
         /**
          * Returns the launch params that the provided activity launch params should be overridden
          * to. {@link LaunchParamsModifier} can use this for various purposes, including: 1)
@@ -277,6 +297,7 @@
          *                      launched should have this be non-null.
          * @param source        the Activity that launched a new task. Could be {@code null}.
          * @param options       {@link ActivityOptions} used to start the activity with.
+         * @param phase         the calculation phase, see {@link LaunchParamsModifier.Phase}
          * @param currentParams launching params after the process of last {@link
          *                      LaunchParamsModifier}.
          * @param outParams     the result params to be set.
@@ -284,7 +305,7 @@
          */
         @Result
         int onCalculate(TaskRecord task, WindowLayout layout, ActivityRecord activity,
-                ActivityRecord source, ActivityOptions options, LaunchParams currentParams,
-                LaunchParams outParams);
+                ActivityRecord source, ActivityOptions options, @Phase int phase,
+                LaunchParams currentParams, LaunchParams outParams);
     }
 }
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 3b66f7d..e6e6275 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -54,6 +54,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.telecom.TelecomManager;
 import android.util.Pair;
@@ -126,7 +127,7 @@
     /** Tag used for disabling of keyguard */
     private static final String LOCK_TASK_TAG = "Lock-to-App";
 
-    private final IBinder mToken = new Binder();
+    private final IBinder mToken = new LockTaskToken();
     private final ActivityStackSupervisor mSupervisor;
     private final Context mContext;
 
@@ -180,6 +181,17 @@
      */
     private final Handler mHandler;
 
+    /**
+     * Stores the user for which we're trying to dismiss the keyguard and then subsequently
+     * disable it.
+     *
+     * Tracking this ensures we don't mistakenly disable the keyguard if we've stopped trying to
+     * between the dismiss request and when it succeeds.
+     *
+     * Must only be accessed from the Handler thread.
+     */
+    private int mPendingDisableFromDismiss = UserHandle.USER_NULL;
+
     LockTaskController(Context context, ActivityStackSupervisor supervisor,
             Handler handler) {
         mContext = context;
@@ -740,16 +752,18 @@
      * Should only be called on the handler thread to avoid race.
      */
     private void setKeyguardState(int lockTaskModeState, int userId) {
+        mPendingDisableFromDismiss = UserHandle.USER_NULL;
         if (lockTaskModeState == LOCK_TASK_MODE_NONE) {
-            mWindowManager.reenableKeyguard(mToken);
+            mWindowManager.reenableKeyguard(mToken, userId);
 
         } else if (lockTaskModeState == LOCK_TASK_MODE_LOCKED) {
             if (isKeyguardAllowed(userId)) {
-                mWindowManager.reenableKeyguard(mToken);
+                mWindowManager.reenableKeyguard(mToken, userId);
             } else {
                 // If keyguard is not secure and it is locked, dismiss the keyguard before
                 // disabling it, which avoids the platform to think the keyguard is still on.
                 if (mWindowManager.isKeyguardLocked() && !mWindowManager.isKeyguardSecure()) {
+                    mPendingDisableFromDismiss = userId;
                     mWindowManager.dismissKeyguard(new IKeyguardDismissCallback.Stub() {
                         @Override
                         public void onDismissError() throws RemoteException {
@@ -759,7 +773,13 @@
                         @Override
                         public void onDismissSucceeded() throws RemoteException {
                             mHandler.post(
-                                    () -> mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG));
+                                    () -> {
+                                        if (mPendingDisableFromDismiss == userId) {
+                                            mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG,
+                                                    userId);
+                                            mPendingDisableFromDismiss = UserHandle.USER_NULL;
+                                        }
+                                    });
                         }
 
                         @Override
@@ -768,12 +788,12 @@
                         }
                     }, null);
                 } else {
-                    mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
+                    mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG, userId);
                 }
             }
 
         } else { // lockTaskModeState == LOCK_TASK_MODE_PINNED
-            mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
+            mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG, userId);
         }
     }
 
@@ -898,4 +918,10 @@
             default: return "unknown=" + mLockTaskModeState;
         }
     }
+
+    /** Marker class for the token used to disable keyguard. */
+    static class LockTaskToken extends Binder {
+        private LockTaskToken() {
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index fff42c5..8dda485 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -2,6 +2,6 @@
 jjaggi@google.com
 racarr@google.com
 chaviw@google.com
-brycelee@google.com
+vishnun@google.com
 akulian@google.com
 roosa@google.com
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 42cd8e8..cb9cbd6 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -134,10 +134,8 @@
 
         mWindowManager.deferSurfaceLayout();
         try {
-            final int userId = mService.getCurrentUserId();
-
             // Kick off the assist data request in the background before showing the target activity
-            requestAssistData(recentsComponent, recentsUid, assistDataReceiver, userId);
+            requestAssistData(recentsComponent, recentsUid, assistDataReceiver);
 
             if (hasExistingActivity) {
                 // Move the recents activity into place for the animation if it is not top most
@@ -164,7 +162,7 @@
                         .setCallingUid(recentsUid)
                         .setCallingPackage(recentsComponent.getPackageName())
                         .setActivityOptions(SafeActivityOptions.fromBundle(options.toBundle()))
-                        .setMayWait(userId)
+                        .setMayWait(mService.getCurrentUserId())
                         .execute();
 
                 // Move the recents activity into place for the animation
@@ -221,7 +219,7 @@
      * Requests assist data for the top visible activities.
      */
     private void requestAssistData(ComponentName recentsComponent, int recentsUid,
-            @Deprecated IAssistDataReceiver assistDataReceiver, int userId) {
+            @Deprecated IAssistDataReceiver assistDataReceiver) {
         final AppOpsManager appOpsManager = (AppOpsManager)
                 mService.mContext.getSystemService(Context.APP_OPS_SERVICE);
         final List<IBinder> topActivities =
@@ -237,8 +235,9 @@
                     final ContentCaptureManagerInternal imService =
                             LocalServices.getService(ContentCaptureManagerInternal.class);
                     final IBinder activityToken = topActivities.get(activityIndex);
-                    if (imService == null
-                            || !imService.sendActivityAssistData(userId, activityToken, data)) {
+                    final ActivityRecord r = ActivityRecord.forTokenLocked(activityToken);
+                    if (r != null && (imService == null
+                            || !imService.sendActivityAssistData(r.mUserId, activityToken, data))) {
                         // Otherwise, use the provided assist data receiver
                         super.onAssistDataReceivedLocked(data, activityIndex, activityCount);
                     }
@@ -263,7 +262,10 @@
                         int activityCount) {
                     // Try to notify the intelligence service
                     final IBinder activityToken = topActivities.get(activityIndex);
-                    imService.sendActivityAssistData(userId, activityToken, data);
+                    final ActivityRecord r = ActivityRecord.forTokenLocked(activityToken);
+                    if (r != null) {
+                        imService.sendActivityAssistData(r.mUserId, activityToken, data);
+                    }
                 }
             };
         }
@@ -387,7 +389,13 @@
     }
 
     @Override
-    public void onStackOrderChanged() {
+    public void onStackOrderChanged(ActivityStack stack) {
+        if (DEBUG) Slog.d(TAG, "onStackOrderChanged(): stack=" + stack);
+        if (mDefaultDisplay.getIndexOf(stack) == -1 || !stack.shouldBeVisible(null)) {
+            // The stack is not visible, so ignore this change
+            return;
+        }
+
         // If the activity display stack order changes, cancel any running recents animation in
         // place
         mWindowManager.cancelRecentsAnimationSynchronously(REORDER_KEEP_IN_PLACE,
@@ -429,7 +437,7 @@
         }
 
         for (int i = targetStack.getChildCount() - 1; i >= 0; i--) {
-            final TaskRecord task = (TaskRecord) targetStack.getChildAt(i);
+            final TaskRecord task = targetStack.getChildAt(i);
             if (task.getBaseIntent().getComponent().equals(component)) {
                 return task.getTopActivity();
             }
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index 84a32fc..f55c7c9 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -29,8 +29,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.app.WindowConfiguration.activityTypeToString;
-import static android.app.WindowConfiguration.windowingModeToString;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
@@ -90,17 +88,18 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.power.V1_0.PowerHint;
-import android.os.Build;
 import android.os.FactoryTest;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.IntArray;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
@@ -110,6 +109,7 @@
 import android.view.DisplayInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.ResolverActivity;
 import com.android.server.LocalServices;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.am.AppTimeTracker;
@@ -346,35 +346,53 @@
     }
 
     /**
-     * This starts home activity on displays that can have system decorations and only if the
-     * home activity can have multiple instances.
+     * This starts home activity on displays that can have system decorations based on displayId -
+     * Default display always use primary home component.
+     * For Secondary displays, the home activity must have category SECONDARY_HOME and then resolves
+     * according to the priorities listed below.
+     *  - If default home is not set, always use the secondary home defined in the config.
+     *  - Use currently selected primary home activity.
+     *  - Use the activity in the same package as currently selected primary home activity.
+     *    If there are multiple activities matched, use first one.
+     *  - Use the secondary home defined in the config.
      */
     boolean startHomeOnDisplay(int userId, String reason, int displayId) {
-        final Intent homeIntent = mService.getHomeIntent();
-        final ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent);
+        Intent homeIntent;
+        ActivityInfo aInfo;
+        if (displayId == DEFAULT_DISPLAY) {
+            homeIntent = mService.getHomeIntent();
+            aInfo = resolveHomeActivity(userId, homeIntent);
+        } else {
+            Pair<ActivityInfo, Intent> info = resolveSecondaryHomeActivity(userId, displayId);
+            aInfo = info.first;
+            homeIntent = info.second;
+        }
         if (aInfo == null) {
             return false;
         }
 
-        if (!canStartHomeOnDisplay(aInfo, displayId,
-                false /* allowInstrumenting */)) {
+        if (!canStartHomeOnDisplay(aInfo, displayId, false /* allowInstrumenting */)) {
             return false;
         }
 
+        // Updates the home component of the intent.
+        homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
+        homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
         // Update the reason for ANR debugging to verify if the user activity is the one that
         // actually launched.
         final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(
-                aInfo.applicationInfo.uid);
+                aInfo.applicationInfo.uid) + ":" + displayId;
         mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
                 displayId);
         return true;
     }
 
     /**
-     * This resolves the home activity info and updates the home component of the given intent.
+     * This resolves the home activity info.
      * @return the home activity info if any.
      */
-    private ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
+    @VisibleForTesting
+    ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
         final int flags = ActivityManagerService.STOCK_PM_FLAGS;
         final ComponentName comp = homeIntent.getComponent();
         ActivityInfo aInfo = null;
@@ -400,13 +418,82 @@
             return null;
         }
 
-        homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
         aInfo = new ActivityInfo(aInfo);
         aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
-        homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
         return aInfo;
     }
 
+    @VisibleForTesting
+    Pair<ActivityInfo, Intent> resolveSecondaryHomeActivity(int userId, int displayId) {
+        if (displayId == DEFAULT_DISPLAY) {
+            throw new IllegalArgumentException(
+                    "resolveSecondaryHomeActivity: Should not be DEFAULT_DISPLAY");
+        }
+        // Resolve activities in the same package as currently selected primary home activity.
+        Intent homeIntent = mService.getHomeIntent();
+        ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent);
+        if (aInfo != null) {
+            if (ResolverActivity.class.getName().equals(aInfo.name)) {
+                // Always fallback to secondary home component if default home is not set.
+                aInfo = null;
+            } else {
+                // Look for secondary home activities in the currently selected default home
+                // package.
+                homeIntent = mService.getSecondaryHomeIntent(aInfo.applicationInfo.packageName);
+                final List<ResolveInfo> resolutions = resolveActivities(userId, homeIntent);
+                final int size = resolutions.size();
+                final String targetName = aInfo.name;
+                aInfo = null;
+                for (int i = 0; i < size; i++) {
+                    ResolveInfo resolveInfo = resolutions.get(i);
+                    // We need to traverse all resolutions to check if the currently selected
+                    // default home activity is present.
+                    if (resolveInfo.activityInfo.name.equals(targetName)) {
+                        aInfo = resolveInfo.activityInfo;
+                        break;
+                    }
+                }
+                if (aInfo == null && size > 0) {
+                    // First one is the best.
+                    aInfo = resolutions.get(0).activityInfo;
+                }
+            }
+        }
+
+        if (aInfo != null) {
+            if (!canStartHomeOnDisplay(aInfo, displayId, false /* allowInstrumenting */)) {
+                aInfo = null;
+            }
+        }
+
+        // Fallback to secondary home component.
+        if (aInfo == null) {
+            homeIntent = mService.getSecondaryHomeIntent(null);
+            aInfo = resolveHomeActivity(userId, homeIntent);
+        }
+        return Pair.create(aInfo, homeIntent);
+    }
+
+    /**
+     * Retrieve all activities that match the given intent.
+     * The list should already ordered from best to worst matched.
+     * {@link android.content.pm.PackageManager#queryIntentActivities}
+     */
+    @VisibleForTesting
+    List<ResolveInfo> resolveActivities(int userId, Intent homeIntent) {
+        List<ResolveInfo> resolutions;
+        try {
+            final String resolvedType =
+                    homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
+            resolutions = AppGlobals.getPackageManager().queryIntentActivities(homeIntent,
+                    resolvedType, ActivityManagerService.STOCK_PM_FLAGS, userId).getList();
+
+        } catch (RemoteException e) {
+            resolutions = new ArrayList<>();
+        }
+        return resolutions;
+    }
+
     boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) {
         if (!mService.isBooting() && !mService.isBooted()) {
             // Not ready yet!
@@ -457,6 +544,14 @@
             return true;
         }
 
+        final boolean deviceProvisioned = Settings.Global.getInt(
+                mService.mContext.getContentResolver(),
+                Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+        if (displayId != DEFAULT_DISPLAY && displayId != INVALID_DISPLAY && !deviceProvisioned) {
+            // Can't launch home on secondary display before device is provisioned.
+            return false;
+        }
+
         final ActivityDisplay display = getActivityDisplay(displayId);
         if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) {
             // Can't launch home on display that doesn't support system decorations.
@@ -464,13 +559,9 @@
         }
 
         final boolean supportMultipleInstance = homeInfo.launchMode != LAUNCH_SINGLE_TASK
-                && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE
-                && homeInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q;
+                && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE;
         if (!supportMultipleInstance) {
-            // Can't launch home on other displays if it requested to be single instance. Also we
-            // don't allow home applications that target before Q to have multiple home activity
-            // instances because they may not be expected to have multiple home scenario and
-            // haven't explicitly request for single instance.
+            // Can't launch home on secondary displays if it requested to be single instance.
             return false;
         }
 
@@ -504,11 +595,15 @@
 
         // Force-update the orientation from the WindowManager, since we need the true configuration
         // to send to the client now.
-        final Configuration config = mWindowManager.updateOrientationFromAppTokens(
-                getDisplayOverrideConfiguration(displayId),
-                starting != null && starting.mayFreezeScreenLocked(starting.app)
-                        ? starting.appToken : null,
-                displayId, true /* forceUpdate */);
+        final DisplayContent displayContent = mRootWindowContainer.getDisplayContent(displayId);
+        Configuration config = null;
+        if (displayContent != null) {
+            config = displayContent.updateOrientationFromAppTokens(
+                    getDisplayOverrideConfiguration(displayId),
+                    starting != null && starting.mayFreezeScreenLocked(starting.app)
+                            ? starting.appToken : null,
+                    true /* forceUpdate */);
+        }
         if (starting != null && markFrozenIfConfigChanged && config != null) {
             starting.frozenBeforeDestroy = true;
         }
@@ -818,6 +913,13 @@
                     + " to its current displayId=" + displayId);
         }
 
+        if (activityDisplay.isSingleTaskInstance() && activityDisplay.getChildCount() > 0) {
+            // We don't allow moving stacks to single instance display that already has a child.
+            Slog.e(TAG, "Can not move stack=" + stack
+                    + " to single task instance display=" + activityDisplay);
+            return;
+        }
+
         stack.reparent(activityDisplay, onTop, false /* displayRemoved */);
         // TODO(multi-display): resize stacks properly if moved from split-screen.
     }
@@ -1426,6 +1528,13 @@
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ActivityDisplay display = mActivityDisplays.get(displayNdx);
             for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                // Stacks and activities could be removed while putting activities to sleep if
+                // the app process was gone. This prevents us getting exception by accessing an
+                // invalid stack index.
+                if (stackNdx >= display.getChildCount()) {
+                    continue;
+                }
+
                 final ActivityStack stack = display.getChildAt(stackNdx);
                 if (allowDelay) {
                     allSleep &= stack.goToSleepIfPossible(shuttingDown);
@@ -1615,33 +1724,35 @@
             return candidateTask.getStack();
         }
 
+        int windowingMode;
+        if (launchParams != null) {
+            // When launch params is not null, we always defer to its windowing mode. Sometimes
+            // it could be unspecified, which indicates it should inherit windowing mode from
+            // display.
+            windowingMode = launchParams.mWindowingMode;
+        } else {
+            windowingMode = options != null ? options.getLaunchWindowingMode()
+                    : r.getWindowingMode();
+        }
+        windowingMode = activityDisplay.validateWindowingMode(windowingMode, r, candidateTask,
+                r.getActivityType());
+
         // Return the topmost valid stack on the display.
         for (int i = activityDisplay.getChildCount() - 1; i >= 0; --i) {
             final ActivityStack stack = activityDisplay.getChildAt(i);
-            if (isValidLaunchStack(stack, r)) {
+            if (isValidLaunchStack(stack, r, windowingMode)) {
                 return stack;
             }
         }
 
         // If there is no valid stack on the external display - check if new dynamic stack will do.
         if (displayId != DEFAULT_DISPLAY) {
-            final int windowingMode;
-            if (launchParams != null) {
-                // When launch params is not null, we always defer to its windowing mode. Sometimes
-                // it could be unspecified, which indicates it should inherit windowing mode from
-                // display.
-                windowingMode = launchParams.mWindowingMode;
-            } else {
-                windowingMode = options != null ? options.getLaunchWindowingMode()
-                        : r.getWindowingMode();
-            }
             final int activityType =
                     options != null && options.getLaunchActivityType() != ACTIVITY_TYPE_UNDEFINED
                             ? options.getLaunchActivityType() : r.getActivityType();
             return activityDisplay.createStack(windowingMode, activityType, true /*onTop*/);
         }
 
-        Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId);
         return null;
     }
 
@@ -1653,7 +1764,7 @@
     }
 
     // TODO: Can probably be consolidated into getLaunchStack()...
-    private boolean isValidLaunchStack(ActivityStack stack, ActivityRecord r) {
+    private boolean isValidLaunchStack(ActivityStack stack, ActivityRecord r, int windowingMode) {
         switch (stack.getActivityType()) {
             case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome();
             case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
@@ -1661,11 +1772,13 @@
         }
         // There is a 1-to-1 relationship between stack and task when not in
         // primary split-windowing mode.
-        if (stack.getWindowingMode() != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            return false;
-        } else {
-            return r.supportsSplitScreenWindowingMode();
+        if (stack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+                && r.supportsSplitScreenWindowingMode()
+                && (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+                || windowingMode == WINDOWING_MODE_UNDEFINED)) {
+            return true;
         }
+        return false;
     }
 
     int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
@@ -2193,42 +2306,7 @@
             for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
                 final ActivityStack stack = display.getChildAt(stackNdx);
                 pw.println();
-                pw.println("  Stack #" + stack.mStackId
-                        + ": type=" + activityTypeToString(stack.getActivityType())
-                        + " mode=" + windowingModeToString(stack.getWindowingMode()));
-                pw.println("  isSleeping=" + stack.shouldSleepActivities());
-                pw.println("  mBounds=" + stack.getRequestedOverrideBounds());
-
-                printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage,
-                        needSep);
-
-                printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, "    ", "Run", false,
-                        !dumpAll, false, dumpPackage, true,
-                        "    Running activities (most recent first):", null);
-
-                needSep = printed;
-                boolean pr = printThisActivity(pw, stack.mPausingActivity, dumpPackage, needSep,
-                        "    mPausingActivity: ");
-                if (pr) {
-                    printed = true;
-                    needSep = false;
-                }
-                pr = printThisActivity(pw, stack.getResumedActivity(), dumpPackage, needSep,
-                        "    mResumedActivity: ");
-                if (pr) {
-                    printed = true;
-                    needSep = false;
-                }
-                if (dumpAll) {
-                    pr = printThisActivity(pw, stack.mLastPausedActivity, dumpPackage, needSep,
-                            "    mLastPausedActivity: ");
-                    if (pr) {
-                        printed = true;
-                        needSep = true;
-                    }
-                    printed |= printThisActivity(pw, stack.mLastNoHistoryActivity, dumpPackage,
-                            needSep, "    mLastNoHistoryActivity: ");
-                }
+                printed = stack.dump(fd, pw, dumpAll, dumpClient, dumpPackage, needSep);
                 needSep = printed;
             }
             printThisActivity(pw, activityDisplay.getResumedActivity(), dumpPackage, needSep,
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 3bbef92..4e70bbc 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -79,6 +79,7 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.function.Consumer;
 
 /** Root {@link WindowContainer} for the device. */
@@ -122,7 +123,10 @@
 
     // The ID of the display which is responsible for receiving display-unspecified key and pointer
     // events.
-    int mTopFocusedDisplayId = INVALID_DISPLAY;
+    private int mTopFocusedDisplayId = INVALID_DISPLAY;
+
+    // Map from the PID to the top most app which has a focused window of the process.
+    final HashMap<Integer, AppWindowToken> mTopFocusedAppByProcess = new HashMap<>();
 
     // Only a separate transaction until we separate the apply surface changes
     // transaction from the global transaction.
@@ -157,50 +161,33 @@
     }
 
     boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
+        mTopFocusedAppByProcess.clear();
         boolean changed = false;
         int topFocusedDisplayId = INVALID_DISPLAY;
-
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             final DisplayContent dc = mChildren.get(i);
-            changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows,
-                    topFocusedDisplayId != INVALID_DISPLAY /* focusFound */);
-            if (topFocusedDisplayId == INVALID_DISPLAY && dc.mCurrentFocus != null) {
-                topFocusedDisplayId = dc.getDisplayId();
+            changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows);
+            final WindowState newFocus = dc.mCurrentFocus;
+            if (newFocus != null) {
+                final int pidOfNewFocus = newFocus.mSession.mPid;
+                if (mTopFocusedAppByProcess.get(pidOfNewFocus) == null) {
+                    mTopFocusedAppByProcess.put(pidOfNewFocus, newFocus.mAppToken);
+                }
+                if (topFocusedDisplayId == INVALID_DISPLAY) {
+                    topFocusedDisplayId = dc.getDisplayId();
+                }
             }
         }
         if (topFocusedDisplayId == INVALID_DISPLAY) {
             topFocusedDisplayId = DEFAULT_DISPLAY;
         }
-        // TODO(b/118865114): Review if need callback top focus display change to view component.
-        // (i.e. Activity or View)
-        // Currently we only tracked topFocusedDisplayChanged for notifying InputMethodManager via
-        // ViewRootImpl.windowFocusChanged to refocus IME window when top display focus changed
-        // but window focus remain the same case.
-        // It may need to review if any use case that need to add new callback for reporting
-        // this change.
-        final boolean topFocusedDisplayChanged =
-                mTopFocusedDisplayId != topFocusedDisplayId && mode == UPDATE_FOCUS_NORMAL;
         if (mTopFocusedDisplayId != topFocusedDisplayId) {
             mTopFocusedDisplayId = topFocusedDisplayId;
-            mWmService.mInputManager.setFocusedDisplay(mTopFocusedDisplayId);
+            mWmService.mInputManager.setFocusedDisplay(topFocusedDisplayId);
+            mWmService.mPolicy.setTopFocusedDisplay(topFocusedDisplayId);
             if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "New topFocusedDisplayId="
-                    + mTopFocusedDisplayId);
+                    + topFocusedDisplayId);
         }
-
-        // Report window focus or top display focus changed through REPORT_FOCUS_CHANGE.
-        forAllDisplays((dc) -> {
-            final boolean windowFocusChanged =
-                    dc.mCurrentFocus != null && dc.mCurrentFocus != dc.mLastFocus;
-            final boolean isTopFocusedDisplay =
-                    topFocusedDisplayChanged && dc.getDisplayId() == mTopFocusedDisplayId;
-            if (windowFocusChanged || isTopFocusedDisplay) {
-                final Message msg = mWmService.mH.obtainMessage(
-                        WindowManagerService.H.REPORT_FOCUS_CHANGE, dc);
-                msg.arg1 = topFocusedDisplayChanged ? 1 : 0;
-                mWmService.mH.sendMessage(msg);
-            }
-        });
-
         return changed;
     }
 
@@ -211,7 +198,8 @@
 
     @Override
     void onChildPositionChanged() {
-        mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateInputWindows */);
+        mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
+                !mWmService.mPerDisplayFocusEnabled /* updateInputWindows */);
     }
 
     DisplayContent getDisplayContent(int displayId) {
@@ -807,8 +795,9 @@
     private void handleResizingWindows() {
         for (int i = mWmService.mResizingWindows.size() - 1; i >= 0; i--) {
             WindowState win = mWmService.mResizingWindows.get(i);
-            if (win.mAppFreezing) {
-                // Don't remove this window until rotation has completed.
+            if (win.mAppFreezing || win.getDisplayContent().mWaitingForConfig) {
+                // Don't remove this window until rotation has completed and is not waiting for the
+                // complete configuration.
                 continue;
             }
             win.reportResized();
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 37b5a7c..937c9d9 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -187,7 +187,7 @@
             Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets,
             Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
             DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
-            Surface outSurface, InsetsState outInsetsState) {
+            SurfaceControl outSurfaceControl, InsetsState outInsetsState) {
         if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
                 + Binder.getCallingPid());
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
@@ -195,7 +195,7 @@
                 requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
                 outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
                 outStableInsets, outsets, outBackdropFrame, cutout,
-                mergedConfiguration, outSurface, outInsetsState);
+                mergedConfiguration, outSurfaceControl, outInsetsState);
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
                 + Binder.getCallingPid());
@@ -430,6 +430,18 @@
         }
     }
 
+    @Override
+    public void insetsModified(IWindow window, InsetsState state) {
+        synchronized (mService.mWindowMap) {
+            final WindowState windowState = mService.windowForClientLocked(this, window,
+                    false /* throwOnError */);
+            if (windowState != null) {
+                windowState.getDisplayContent().getInsetsStateController().onInsetsModified(
+                        windowState, state);
+            }
+        }
+    }
+
     void windowAddedLocked(String packageName) {
         mPackageName = packageName;
         mRelayoutTag = "relayoutWindow: " + mPackageName;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b10fd31..d334bd2 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -44,6 +44,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.os.IBinder;
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -328,6 +329,23 @@
         return boundsChange;
     }
 
+    @Override
+    public boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
+            ConfigurationContainer requestingContainer) {
+        if (super.onDescendantOrientationChanged(freezeDisplayToken, requestingContainer)) {
+            return true;
+        }
+
+        // No one in higher hierarchy handles this request, let's adjust our bounds to fulfill
+        // it if possible.
+        // TODO: Move to TaskRecord after unification is done.
+        if (mTaskRecord != null) {
+            mTaskRecord.onConfigurationChanged(mTaskRecord.getParent().getConfiguration());
+            return true;
+        }
+        return false;
+    }
+
     void resize(boolean relayout, boolean forced) {
         if (setBounds(getRequestedOverrideBounds(), forced) != BOUNDS_CHANGE_NONE && relayout) {
             getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 1fb7979..6acd864 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -51,6 +51,7 @@
 import android.view.Gravity;
 import android.view.View;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.wm.LaunchParamsController.LaunchParams;
 import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
 
@@ -102,19 +103,27 @@
         mSupervisor = supervisor;
     }
 
+    @VisibleForTesting
+    int onCalculate(TaskRecord task, ActivityInfo.WindowLayout layout, ActivityRecord activity,
+            ActivityRecord source, ActivityOptions options, LaunchParams currentParams,
+            LaunchParams outParams) {
+        return onCalculate(task, layout, activity, source, options, PHASE_BOUNDS, currentParams,
+                outParams);
+    }
+
     @Override
     public int onCalculate(TaskRecord task, ActivityInfo.WindowLayout layout,
                            ActivityRecord activity, ActivityRecord source, ActivityOptions options,
-                           LaunchParams currentParams, LaunchParams outParams) {
+                           int phase, LaunchParams currentParams, LaunchParams outParams) {
         initLogBuilder(task, activity);
-        final int result = calculate(task, layout, activity, source, options, currentParams,
+        final int result = calculate(task, layout, activity, source, options, phase, currentParams,
                 outParams);
         outputLog();
         return result;
     }
 
     private int calculate(TaskRecord task, ActivityInfo.WindowLayout layout,
-            ActivityRecord activity, ActivityRecord source, ActivityOptions options,
+            ActivityRecord activity, ActivityRecord source, ActivityOptions options, int phase,
             LaunchParams currentParams, LaunchParams outParams) {
         final ActivityRecord root;
         if (task != null) {
@@ -145,6 +154,10 @@
                     + display.getWindowingMode());
         }
 
+        if (phase == PHASE_DISPLAY) {
+            return RESULT_CONTINUE;
+        }
+
         // STEP 2: Resolve launch windowing mode.
         // STEP 2.1: Determine if any parameter has specified initial bounds. That might be the
         // launch bounds from activity options, or size/gravity passed in layout. It also treats the
@@ -206,10 +219,16 @@
                 }
             }
 
-            if (launchMode == WINDOWING_MODE_FREEFORM && !currentParams.mBounds.isEmpty()) {
+            if (!currentParams.mBounds.isEmpty()) {
+                // Carry over bounds from callers regardless of launch mode because bounds is still
+                // used to restore last non-fullscreen bounds when launch mode is not freeform.
+                // Therefore it's not a resolution step for non-freeform launch mode and only
+                // consider it fully resolved only when launch mode is freeform.
                 outParams.mBounds.set(currentParams.mBounds);
-                fullyResolvedCurrentParam = true;
-                if (DEBUG) appendLog("inherit-bounds=" + outParams.mBounds);
+                if (launchMode == WINDOWING_MODE_FREEFORM) {
+                    fullyResolvedCurrentParam = true;
+                    if (DEBUG) appendLog("inherit-bounds=" + outParams.mBounds);
+                }
             }
         }
 
@@ -247,6 +266,10 @@
         outParams.mWindowingMode = launchMode == display.getWindowingMode()
                 ? WINDOWING_MODE_UNDEFINED : launchMode;
 
+        if (phase == PHASE_WINDOWING_MODE) {
+            return RESULT_CONTINUE;
+        }
+
         // STEP 3: Determine final launch bounds based on resolved windowing mode and activity
         // requested orientation. We set bounds to empty for fullscreen mode and keep bounds as is
         // for all other windowing modes that's not freeform mode. One can read comments in
@@ -288,10 +311,11 @@
             displayId = optionLaunchId;
         }
 
-        if (displayId == INVALID_DISPLAY && source != null) {
-            final int sourceDisplayId = source.getDisplayId();
-            if (DEBUG) appendLog("display-from-source=" + sourceDisplayId);
-            displayId = sourceDisplayId;
+        // If the source activity is a no-display activity, pass on the launch display id from
+        // source activity as currently preferred.
+        if (displayId == INVALID_DISPLAY && source != null && source.noDisplay) {
+            displayId = source.mHandoverLaunchDisplayId;
+            if (DEBUG) appendLog("display-from-no-display-source=" + displayId);
         }
 
         ActivityStack stack =
@@ -301,6 +325,12 @@
             displayId = stack.mDisplayId;
         }
 
+        if (displayId == INVALID_DISPLAY && source != null) {
+            final int sourceDisplayId = source.getDisplayId();
+            if (DEBUG) appendLog("display-from-source=" + sourceDisplayId);
+            displayId = sourceDisplayId;
+        }
+
         if (displayId != INVALID_DISPLAY
                 && mSupervisor.mRootActivityContainer.getActivityDisplay(displayId) == null) {
             displayId = currentParams.mPreferredDisplayId;
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 5bb6440..1d56f04 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -44,6 +44,9 @@
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -481,6 +484,32 @@
         mTask = task;
     }
 
+    void cleanUpResourcesForDestroy() {
+        if (!mActivities.isEmpty()) {
+            return;
+        }
+
+        // This task is going away, so save the last state if necessary.
+        saveLaunchingStateIfNeeded();
+
+        // TODO: VI what about activity?
+        final boolean isVoiceSession = voiceSession != null;
+        if (isVoiceSession) {
+            try {
+                voiceSession.taskFinished(intent, taskId);
+            } catch (RemoteException e) {
+            }
+        }
+        if (autoRemoveFromRecents() || isVoiceSession) {
+            // Task creator asked to remove this when done, or this task was a voice
+            // interaction, so it should not remain on the recent tasks list.
+            mService.mStackSupervisor.mRecentTasks.remove(this);
+        }
+
+        removeWindowContainer();
+    }
+
+    @VisibleForTesting
     void removeWindowContainer() {
         mService.getLockTaskController().clearLockedTask(this);
         if (mTask == null) {
@@ -885,7 +914,6 @@
                     info.packageName, info.targetActivity);
             if (_intent != null) {
                 Intent targetIntent = new Intent(_intent);
-                targetIntent.setComponent(targetComponent);
                 targetIntent.setSelector(null);
                 targetIntent.setSourceBounds(null);
                 if (DEBUG_TASKS) Slog.v(TAG_TASKS,
@@ -1234,10 +1262,6 @@
         setFrontOfTask();
     }
 
-    void addActivityAtBottom(ActivityRecord r) {
-        addActivityAtIndex(0, r);
-    }
-
     void addActivityToTop(ActivityRecord r) {
         addActivityAtIndex(mActivities.size(), r);
     }
@@ -1253,6 +1277,34 @@
     }
 
     /**
+     * Checks if the root activity requires a particular orientation (either by override or
+     * activityInfo) and returns that. Otherwise, this returns ORIENTATION_UNDEFINED.
+     */
+    private int getRootActivityRequestedOrientation() {
+        ActivityRecord root = getRootActivity();
+        if (getRequestedOverrideConfiguration().orientation != ORIENTATION_UNDEFINED
+                || root == null) {
+            return getRequestedOverrideConfiguration().orientation;
+        }
+        int rootScreenOrientation = root.getOrientation();
+        if (rootScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
+            // NOSENSOR means the display's "natural" orientation, so return that.
+            ActivityDisplay display = mStack != null ? mStack.getDisplay() : null;
+            if (display != null && display.mDisplayContent != null) {
+                return mStack.getDisplay().mDisplayContent.getNaturalOrientation();
+            }
+        } else if (rootScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
+            // LOCKED means the activity's orientation remains unchanged, so return existing value.
+            return root.getConfiguration().orientation;
+        } else if (ActivityInfo.isFixedOrientationLandscape(rootScreenOrientation)) {
+            return ORIENTATION_LANDSCAPE;
+        } else if (ActivityInfo.isFixedOrientationPortrait(rootScreenOrientation)) {
+            return ORIENTATION_PORTRAIT;
+        }
+        return ORIENTATION_UNDEFINED;
+    }
+
+    /**
      * Adds an activity {@param r} at the given {@param index}. The activity {@param r} must either
      * be in the current task or unparented to any task.
      */
@@ -1716,7 +1768,7 @@
         updateTaskDescription();
     }
 
-    private void adjustForMinimalTaskDimensions(Rect bounds) {
+    void adjustForMinimalTaskDimensions(Rect bounds, Rect previousBounds) {
         if (bounds == null) {
             return;
         }
@@ -1747,9 +1799,8 @@
             return;
         }
 
-        final Rect configBounds = getRequestedOverrideBounds();
         if (adjustWidth) {
-            if (!configBounds.isEmpty() && bounds.right == configBounds.right) {
+            if (!previousBounds.isEmpty() && bounds.right == previousBounds.right) {
                 bounds.left = bounds.right - minWidth;
             } else {
                 // Either left bounds match, or neither match, or the previous bounds were
@@ -1758,7 +1809,7 @@
             }
         }
         if (adjustHeight) {
-            if (!configBounds.isEmpty() && bounds.bottom == configBounds.bottom) {
+            if (!previousBounds.isEmpty() && bounds.bottom == previousBounds.bottom) {
                 bounds.top = bounds.bottom - minHeight;
             } else {
                 // Either top bounds match, or neither match, or the previous bounds were
@@ -1829,11 +1880,27 @@
 
     @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
+        // Check if the new configuration supports persistent bounds (eg. is Freeform) and if so
+        // restore the last recorded non-fullscreen bounds.
+        final boolean prevPersistTaskBounds = getWindowConfiguration().persistTaskBounds();
+        final boolean nextPersistTaskBounds =
+                getRequestedOverrideConfiguration().windowConfiguration.persistTaskBounds()
+                || newParentConfig.windowConfiguration.persistTaskBounds();
+        if (!prevPersistTaskBounds && nextPersistTaskBounds
+                && mLastNonFullscreenBounds != null && !mLastNonFullscreenBounds.isEmpty()) {
+            // Bypass onRequestedOverrideConfigurationChanged here to avoid infinite loop.
+            getRequestedOverrideConfiguration().windowConfiguration
+                    .setBounds(mLastNonFullscreenBounds);
+        }
+
         final boolean wasInMultiWindowMode = inMultiWindowMode();
         super.onConfigurationChanged(newParentConfig);
         if (wasInMultiWindowMode != inMultiWindowMode()) {
             mService.mStackSupervisor.scheduleUpdateMultiWindowMode(this);
         }
+
+        // If the configuration supports persistent bounds (eg. Freeform), keep track of the
+        // current (non-fullscreen) bounds for persistence.
         if (getWindowConfiguration().persistTaskBounds()) {
             final Rect currentBounds = getRequestedOverrideBounds();
             if (!currentBounds.isEmpty()) {
@@ -2023,7 +2090,7 @@
      * configuring an "inherit-bounds" window which means that all configuration settings would
      * just be inherited from the parent configuration.
      **/
-    void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, @NonNull Rect bounds,
+    void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
             @NonNull Configuration parentConfig) {
         int windowingMode = inOutConfig.windowConfiguration.getWindowingMode();
         if (windowingMode == WINDOWING_MODE_UNDEFINED) {
@@ -2036,6 +2103,7 @@
         }
         density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
 
+        final Rect bounds = inOutConfig.windowConfiguration.getBounds();
         Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
         if (outAppBounds == null || outAppBounds.isEmpty()) {
             inOutConfig.windowConfiguration.setAppBounds(bounds);
@@ -2083,13 +2151,14 @@
                     // Iterating across all screen orientations, and return the minimum of the task
                     // width taking into account that the bounds might change because the snap
                     // algorithm snaps to a different value
-                    getSmallestScreenWidthDpForDockedBounds(bounds);
+                    inOutConfig.smallestScreenWidthDp =
+                            getSmallestScreenWidthDpForDockedBounds(bounds);
                 }
                 // otherwise, it will just inherit
             }
         }
 
-        if (inOutConfig.orientation == Configuration.ORIENTATION_UNDEFINED) {
+        if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
             inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
                     ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
         }
@@ -2110,32 +2179,56 @@
         }
     }
 
-    // TODO(b/113900640): remove this once ActivityRecord is changed to not need it anymore.
-    void computeResolvedOverrideConfiguration(Configuration inOutConfig, Configuration parentConfig,
-            Configuration overrideConfig) {
-        inOutConfig.setTo(overrideConfig);
-
-        Rect outOverrideBounds = inOutConfig.windowConfiguration.getBounds();
-        if (outOverrideBounds != null && !outOverrideBounds.isEmpty()) {
-            adjustForMinimalTaskDimensions(outOverrideBounds);
-
-            int windowingMode = overrideConfig.windowConfiguration.getWindowingMode();
-            if (windowingMode == WINDOWING_MODE_UNDEFINED) {
-                windowingMode = parentConfig.windowConfiguration.getWindowingMode();
-            }
-            if (windowingMode == WINDOWING_MODE_FREEFORM) {
-                // by policy, make sure the window remains within parent
-                fitWithinBounds(outOverrideBounds, parentConfig.windowConfiguration.getBounds());
-            }
-
-            computeConfigResourceOverrides(inOutConfig, outOverrideBounds, parentConfig);
-        }
-    }
-
     @Override
     void resolveOverrideConfiguration(Configuration newParentConfig) {
-        computeResolvedOverrideConfiguration(getResolvedOverrideConfiguration(), newParentConfig,
-                getRequestedOverrideConfiguration());
+        mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
+        super.resolveOverrideConfiguration(newParentConfig);
+        int windowingMode =
+                getRequestedOverrideConfiguration().windowConfiguration.getWindowingMode();
+        if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+            windowingMode = newParentConfig.windowConfiguration.getWindowingMode();
+        }
+        Rect outOverrideBounds =
+                getResolvedOverrideConfiguration().windowConfiguration.getBounds();
+
+        if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
+            // In FULLSCREEN mode, always start with empty bounds to indicate "fill parent"
+            outOverrideBounds.setEmpty();
+
+            // If the task or its root activity require a different orientation, make it fit the
+            // available bounds by scaling down its bounds.
+            int forcedOrientation = getRootActivityRequestedOrientation();
+            if (forcedOrientation != ORIENTATION_UNDEFINED
+                    && forcedOrientation != newParentConfig.orientation) {
+                final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
+                final int parentWidth = parentBounds.width();
+                final int parentHeight = parentBounds.height();
+                final float aspect = ((float) parentHeight) / parentWidth;
+                if (forcedOrientation == ORIENTATION_LANDSCAPE) {
+                    final int height = (int) (parentWidth / aspect);
+                    final int top = parentBounds.centerY() - height / 2;
+                    outOverrideBounds.set(
+                            parentBounds.left, top, parentBounds.right, top + height);
+                } else {
+                    final int width = (int) (parentHeight * aspect);
+                    final int left = parentBounds.centerX() - width / 2;
+                    outOverrideBounds.set(
+                            left, parentBounds.top, left + width, parentBounds.bottom);
+                }
+            }
+        }
+
+        if (outOverrideBounds.isEmpty()) {
+            // If the task fills the parent, just inherit all the other configs from parent.
+            return;
+        }
+
+        adjustForMinimalTaskDimensions(outOverrideBounds, mTmpBounds);
+        if (windowingMode == WINDOWING_MODE_FREEFORM) {
+            // by policy, make sure the window remains within parent somewhere
+            fitWithinBounds(outOverrideBounds, newParentConfig.windowConfiguration.getBounds());
+        }
+        computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
     }
 
     Rect updateOverrideConfigurationFromLaunchBounds() {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 9a56606..2d3e3ae 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -111,6 +111,7 @@
     private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s";
     private final Window mWindow;
     private final Surface mSurface;
+    private SurfaceControl mSurfaceControl;
     private SurfaceControl mChildSurfaceControl;
     private final IWindowSession mSession;
     private final WindowManagerService mService;
@@ -136,7 +137,7 @@
         final Window window = new Window();
         final IWindowSession session = WindowManagerGlobal.getWindowSession();
         window.setSession(session);
-        final Surface surface = new Surface();
+        final SurfaceControl surfaceControl = new SurfaceControl();
         final Rect tmpRect = new Rect();
         final DisplayCutout.ParcelableWrapper tmpCutout = new DisplayCutout.ParcelableWrapper();
         final Rect tmpFrame = new Rect();
@@ -213,14 +214,14 @@
             // Local call.
         }
         final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
-                surface, snapshot, layoutParams.getTitle(), backgroundColor, statusBarColor,
+                surfaceControl, snapshot, layoutParams.getTitle(), backgroundColor, statusBarColor,
                 navigationBarColor, sysUiVis, windowFlags, windowPrivateFlags, taskBounds,
                 currentOrientation);
         window.setOuter(snapshotSurface);
         try {
             session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
                     tmpFrame, tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
-                    tmpCutout, tmpMergedConfiguration, surface, mTmpInsetsState);
+                    tmpCutout, tmpMergedConfiguration, surfaceControl, mTmpInsetsState);
         } catch (RemoteException e) {
             // Local call.
         }
@@ -230,15 +231,16 @@
     }
 
     @VisibleForTesting
-    TaskSnapshotSurface(WindowManagerService service, Window window, Surface surface,
+    TaskSnapshotSurface(WindowManagerService service, Window window, SurfaceControl surfaceControl,
             TaskSnapshot snapshot, CharSequence title, int backgroundColor, int statusBarColor,
             int navigationBarColor, int sysUiVis, int windowFlags, int windowPrivateFlags,
             Rect taskBounds, int currentOrientation) {
         mService = service;
+        mSurface = new Surface();
         mHandler = new Handler(mService.mH.getLooper());
         mSession = WindowManagerGlobal.getWindowSession();
         mWindow = window;
-        mSurface = surface;
+        mSurfaceControl = surfaceControl;
         mSnapshot = snapshot;
         mTitle = title;
         mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
@@ -281,6 +283,8 @@
 
     private void drawSnapshot() {
         final GraphicBuffer buffer = mSnapshot.getSnapshot();
+        mSurface.copyFrom(mSurfaceControl);
+
         if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Drawing snapshot surface sizeMismatch="
                 + mSizeMismatch);
         if (mSizeMismatch) {
@@ -310,13 +314,14 @@
         if (!mSurface.isValid()) {
             throw new IllegalStateException("mSurface does not hold a valid surface.");
         }
-        final SurfaceSession session = new SurfaceSession(mSurface);
+        final SurfaceSession session = new SurfaceSession();
 
         // Keep a reference to it such that it doesn't get destroyed when finalized.
         mChildSurfaceControl = new SurfaceControl.Builder(session)
                 .setName(mTitle + " - task-snapshot-surface")
                 .setBufferSize(buffer.getWidth(), buffer.getHeight())
                 .setFormat(buffer.getFormat())
+                .setParent(mSurfaceControl)
                 .build();
         Surface surface = new Surface();
         surface.copyFrom(mChildSurfaceControl);
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 11d9ebb..ee74bdf 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -996,15 +996,20 @@
      * Adjusts the stack bounds if the IME is visible.
      *
      * @param imeWin The IME window.
+     * @param keepLastAmount Use {@code true} to keep the last adjusted amount from
+     *                       {@link DockedStackDividerController} for adjusting the stack bounds,
+     *                       Use {@code false} to reset adjusted amount as 0.
+     * @see #updateAdjustForIme(float, float, boolean)
      */
-    void setAdjustedForIme(WindowState imeWin, boolean forceUpdate) {
+    void setAdjustedForIme(WindowState imeWin, boolean keepLastAmount) {
         mImeWin = imeWin;
         mImeGoingAway = false;
-        if (!mAdjustedForIme || forceUpdate) {
+        if (!mAdjustedForIme || keepLastAmount) {
             mAdjustedForIme = true;
-            mAdjustImeAmount = 0f;
-            mAdjustDividerAmount = 0f;
-            updateAdjustForIme(0f, 0f, true /* force */);
+            DockedStackDividerController controller = getDisplayContent().mDividerControllerLocked;
+            final float adjustImeAmount = keepLastAmount ? controller.mLastAnimationProgress : 0f;
+            final float adjustDividerAmount = keepLastAmount ? controller.mLastDividerProgress : 0f;
+            updateAdjustForIme(adjustImeAmount, adjustDividerAmount, true /* force */);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index 53d2cb0..b219419 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -16,6 +16,12 @@
 
 package com.android.server.wm;
 
+import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
+import static android.view.PointerIcon.TYPE_NOT_SPECIFIED;
+import static android.view.PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW;
+import static android.view.PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW;
+import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
+
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.hardware.input.InputManager;
@@ -25,21 +31,18 @@
 
 import com.android.server.wm.WindowManagerService.H;
 
-import static android.view.PointerIcon.TYPE_NOT_SPECIFIED;
-import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
-import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
-import static android.view.PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW;
-import static android.view.PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW;
-
 public class TaskTapPointerEventListener implements PointerEventListener {
 
-    final private Region mTouchExcludeRegion = new Region();
+    private final Region mTouchExcludeRegion = new Region();
+    private final Region mTmpRegion = new Region();
     private final WindowManagerService mService;
     private final DisplayContent mDisplayContent;
     private final Handler mHandler;
     private final Runnable mMoveDisplayToTop;
     private final Rect mTmpRect = new Rect();
     private int mPointerIconType = TYPE_NOT_SPECIFIED;
+    private int mLastDownX;
+    private int mLastDownY;
 
     public TaskTapPointerEventListener(WindowManagerService service,
             DisplayContent displayContent) {
@@ -47,9 +50,27 @@
         mDisplayContent = displayContent;
         mHandler = new Handler(mService.mH.getLooper());
         mMoveDisplayToTop = () -> {
+            int x;
+            int y;
+            synchronized (this) {
+                x = mLastDownX;
+                y = mLastDownY;
+            }
             synchronized (mService.mGlobalLock) {
-                mDisplayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP,
-                        mDisplayContent, true /* includingParents */);
+                if (!mService.mPerDisplayFocusEnabled
+                        && mService.mRoot.getTopFocusedDisplayContent() != mDisplayContent
+                        && inputMethodWindowContains(x, y)) {
+                    // In a single focus system, if the input method window and the input method
+                    // target window are on the different displays, when the user is tapping on the
+                    // input method window, we don't move its display to top. Otherwise, the input
+                    // method target window will lose the focus.
+                    return;
+                }
+                WindowContainer parent = mDisplayContent.getParent();
+                if (parent != null) {
+                    parent.positionChildAt(WindowContainer.POSITION_TOP, mDisplayContent,
+                            true /* includingParents */);
+                }
             }
         };
     }
@@ -70,6 +91,8 @@
                         mService.mTaskPositioningController.handleTapOutsideTask(
                                 mDisplayContent, x, y);
                     }
+                    mLastDownX = x;
+                    mLastDownY = y;
                     mHandler.post(mMoveDisplayToTop);
                 }
             }
@@ -122,4 +145,13 @@
     private int getDisplayId() {
         return mDisplayContent.getDisplayId();
     }
+
+    private boolean inputMethodWindowContains(int x, int y) {
+        final WindowState inputMethodWindow = mDisplayContent.mInputMethodWindow;
+        if (inputMethodWindow == null || !inputMethodWindow.isVisibleLw()) {
+            return false;
+        }
+        inputMethodWindow.getTouchableRegion(mTmpRegion);
+        return mTmpRegion.contains(x, y);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 651089d..32c5a3b 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -31,10 +31,12 @@
 
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.app.WindowConfiguration;
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.IBinder;
 import android.util.Pools;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -698,8 +700,58 @@
         }
     }
 
+    /**
+     * Called when this container or one of its descendants changed its requested orientation, and
+     * wants this container to handle it or pass it to its parent.
+     *
+     * @param freezeDisplayToken freeze this app window token if display needs to freeze
+     * @param requestingContainer the container which orientation request has changed
+     * @return {@code true} if handled; {@code false} otherwise.
+     */
+    boolean onDescendantOrientationChanged(@Nullable IBinder freezeDisplayToken,
+            @Nullable ConfigurationContainer requestingContainer) {
+        final WindowContainer parent = getParent();
+        if (parent == null) {
+            return false;
+        }
+        return parent.onDescendantOrientationChanged(freezeDisplayToken,
+                requestingContainer);
+    }
+
+    /**
+     * Calls {@link #setOrientation(int, IBinder, ActivityRecord)} with {@code null} to the last 2
+     * parameters.
+     *
+     * @param orientation the specified orientation.
+     */
     void setOrientation(int orientation) {
+        setOrientation(orientation, null /* freezeDisplayToken */,
+                null /* ActivityRecord */);
+    }
+
+    /**
+     * Sets the specified orientation of this container. It percolates this change upward along the
+     * hierarchy to let each level of the hierarchy a chance to respond to it.
+     *
+     * @param orientation the specified orientation. Needs to be one of {@link
+     *      android.content.pm.ActivityInfo.ScreenOrientation}.
+     * @param freezeDisplayToken uses this token to freeze display if orientation change is not
+     *                           done. Display will not be frozen if this is {@code null}, which
+     *                           should only happen in tests.
+     * @param requestingContainer the container which orientation request has changed. Mostly used
+     *                            to ensure it gets correct configuration.
+     */
+    void setOrientation(int orientation, @Nullable IBinder freezeDisplayToken,
+            @Nullable ConfigurationContainer requestingContainer) {
+        final boolean changed = mOrientation != orientation;
         mOrientation = orientation;
+        if (!changed) {
+            return;
+        }
+        final WindowContainer parent = getParent();
+        if (parent != null) {
+            onDescendantOrientationChanged(freezeDisplayToken, requestingContainer);
+        }
     }
 
     int getOrientation() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 9f1a587..646fdd9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -334,6 +334,11 @@
     public abstract void registerAppTransitionListener(AppTransitionListener listener);
 
     /**
+     * Reports that the password for the given user has changed.
+     */
+    public abstract void reportPasswordChanged(int userId);
+
+    /**
      * Retrieves a height of input method window for given display.
      */
     public abstract int getInputMethodWindowVisibleHeight(int displayId);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 002d6d4..91aac7e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -22,6 +22,7 @@
 import static android.Manifest.permission.READ_FRAME_BUFFER;
 import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS;
 import static android.Manifest.permission.RESTRICTED_VR_ACCESS;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
 import static android.app.StatusBarManager.DISABLE_MASK;
@@ -72,7 +73,6 @@
 import static com.android.server.LockGuard.INDEX_WINDOW;
 import static com.android.server.LockGuard.installLock;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static com.android.server.wm.KeyguardDisableHandler.KEYGUARD_POLICY_CHANGED;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
@@ -181,7 +181,6 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.MergedConfiguration;
-import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -235,6 +234,7 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.LatencyTracker;
+import com.android.internal.util.Preconditions;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.internal.view.WindowManagerPolicyThread;
 import com.android.server.AnimationThread;
@@ -396,7 +396,7 @@
         public void onReceive(Context context, Intent intent) {
             switch (intent.getAction()) {
                 case ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED:
-                    mKeyguardDisableHandler.sendEmptyMessage(KEYGUARD_POLICY_CHANGED);
+                    mKeyguardDisableHandler.updateKeyguardEnabled(getSendingUserId());
                     break;
             }
         }
@@ -433,6 +433,12 @@
     final long mDrawLockTimeoutMillis;
     final boolean mAllowAnimationsInLowPowerMode;
 
+    /**
+     * Use very low resolution task snapshots. Replaces task snapshot starting windows with
+     * splashscreen starting windows. Used on low RAM devices to save memory.
+     */
+    final boolean mLowRamTaskSnapshots;
+
     final boolean mAllowBootMessages;
 
     final boolean mLimitedAlphaCompositing;
@@ -611,6 +617,9 @@
     boolean mClientFreezingScreen = false;
     int mAppsFreezingScreen = 0;
 
+    @VisibleForTesting
+    boolean mPerDisplayFocusEnabled;
+
     // State while inside of layoutAndPlaceSurfacesLocked().
     boolean mFocusMayChange;
 
@@ -944,6 +953,10 @@
                 com.android.internal.R.integer.config_maxUiWidth);
         mDisableTransitionAnimation = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_disableTransitionAnimation);
+        mPerDisplayFocusEnabled = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_perDisplayFocusEnabled);
+        mLowRamTaskSnapshots = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_lowRamTaskSnapshotsAndRecents);
         mInputManager = inputManager; // Must be before createDisplayContentLocked.
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
         mDisplayWindowSettings = new DisplayWindowSettings(this);
@@ -961,7 +974,7 @@
 
         mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
 
-        mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy);
+        mKeyguardDisableHandler = KeyguardDisableHandler.create(mContext, mPolicy, mH);
 
         mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
@@ -1040,7 +1053,7 @@
         IntentFilter filter = new IntentFilter();
         // Track changes to DevicePolicyManager state so we can enable/disable keyguard.
         filter.addAction(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
-        mContext.registerReceiver(mBroadcastReceiver, filter);
+        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
 
         mLatencyTracker = LatencyTracker.getInstance(context);
 
@@ -1861,7 +1874,7 @@
             long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
             Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
             DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
-            Surface outSurface, InsetsState outInsetsState) {
+            SurfaceControl outSurfaceControl, InsetsState outInsetsState) {
         int result = 0;
         boolean configChanged;
         final boolean hasStatusBarPermission =
@@ -2034,7 +2047,7 @@
                 result = win.relayoutVisibleWindow(result, attrChanges, oldVisibility);
 
                 try {
-                    result = createSurfaceControl(outSurface, result, win, winAnimator);
+                    result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
                 } catch (Exception e) {
                     displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
 
@@ -2065,7 +2078,7 @@
                     // handled yet, or it might want to draw a last frame. If we already have a
                     // surface, let the client use that, but don't create new surface at this point.
                     Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: getSurface");
-                    winAnimator.mSurfaceController.getSurface(outSurface);
+                    winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl);
                     Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                 } else {
                     if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win);
@@ -2073,7 +2086,7 @@
                     try {
                         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmReleaseOutSurface_"
                                 + win.mAttrs.getTitle());
-                        outSurface.release();
+                        outSurfaceControl.release();
                     } finally {
                         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                     }
@@ -2169,7 +2182,7 @@
                 + ", requestedHeight=" + requestedHeight
                 + ", viewVisibility=" + viewVisibility
                 + "\nRelayout returning frame=" + outFrame
-                + ", surface=" + outSurface);
+                + ", surface=" + outSurfaceControl);
 
             if (localLOGV || DEBUG_FOCUS) Slog.v(
                 TAG_WM, "Relayout of " + win + ": focusMayChange=" + focusMayChange);
@@ -2239,7 +2252,7 @@
         return focusMayChange;
     }
 
-    private int createSurfaceControl(Surface outSurface, int result, WindowState win,
+    private int createSurfaceControl(SurfaceControl outSurfaceControl, int result, WindowState win,
             WindowStateAnimator winAnimator) {
         if (!win.mHasSurface) {
             result |= RELAYOUT_RES_SURFACE_CHANGED;
@@ -2253,13 +2266,13 @@
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
         if (surfaceController != null) {
-            surfaceController.getSurface(outSurface);
-            if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, "  OUT SURFACE " + outSurface + ": copied");
+            surfaceController.getSurfaceControl(outSurfaceControl);
+            if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, "  OUT SURFACE " + outSurfaceControl + ": copied");
         } else {
             // For some reason there isn't a surface.  Clear the
             // caller's object so they see the same state.
             Slog.w(TAG_WM, "Failed to create surface control for " + win);
-            outSurface.release();
+            outSurfaceControl.release();
         }
 
         return result;
@@ -2378,85 +2391,6 @@
         }
     }
 
-    @Override
-    public Configuration updateOrientationFromAppTokens(Configuration currentConfig,
-            IBinder freezeThisOneIfNeeded, int displayId) {
-        return updateOrientationFromAppTokens(currentConfig, freezeThisOneIfNeeded, displayId,
-                false /* forceUpdate */);
-    }
-
-    public Configuration updateOrientationFromAppTokens(Configuration currentConfig,
-            IBinder freezeThisOneIfNeeded, int displayId, boolean forceUpdate) {
-        if (!checkCallingPermission(MANAGE_APP_TOKENS, "updateOrientationFromAppTokens()")) {
-            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
-        }
-
-        final Configuration config;
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                config = updateOrientationFromAppTokensLocked(currentConfig, freezeThisOneIfNeeded,
-                        displayId, forceUpdate);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-
-        return config;
-    }
-
-    /**
-     * Update orientation of the target display, returning a non-null new Configuration if it has
-     * changed from the current orientation. If a non-null configuration is returned, someone must
-     * call {@link #setNewDisplayOverrideConfiguration(Configuration, int)} to tell the window
-     * manager it can unfreeze the screen. This will typically be done by calling
-     * {@link #sendNewConfiguration(int)}.
-     *
-     * @see android.view.IWindowManager#updateOrientationFromAppTokens(Configuration, IBinder, int)
-     */
-    private Configuration updateOrientationFromAppTokensLocked(Configuration currentConfig,
-            IBinder freezeThisOneIfNeeded, int displayId, boolean forceUpdate) {
-        if (!mDisplayReady) {
-            return null;
-        }
-        Configuration config = null;
-
-        final DisplayContent dc = mRoot.getDisplayContent(displayId);
-        if (dc != null && dc.updateOrientationFromAppTokens(forceUpdate)) {
-            // If we changed the orientation but mOrientationChangeComplete is already true,
-            // we used seamless rotation, and we don't need to freeze the screen.
-            if (freezeThisOneIfNeeded != null && !mRoot.mOrientationChangeComplete) {
-                final AppWindowToken atoken = mRoot.getAppWindowToken(freezeThisOneIfNeeded);
-                if (atoken != null) {
-                    atoken.startFreezingScreen();
-                }
-            }
-            config = computeNewConfigurationLocked(displayId);
-
-        } else if (currentConfig != null) {
-            // No obvious action we need to take, but if our current state mismatches the activity
-            // manager's, update it, disregarding font scale, which should remain set to the value
-            // of the previous configuration.
-            // Here we're calling Configuration#unset() instead of setToDefaults() because we need
-            // to keep override configs clear of non-empty values (e.g. fontSize).
-            mTempConfiguration.unset();
-            mTempConfiguration.updateFrom(currentConfig);
-            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
-            displayContent.computeScreenConfiguration(mTempConfiguration);
-            if (currentConfig.diff(mTempConfiguration) != 0) {
-                displayContent.mWaitingForConfig = true;
-                displayContent.setLayoutNeeded();
-                int anim[] = new int[2];
-                displayContent.getDisplayPolicy().selectRotationAnimationLw(anim);
-
-                startFreezingDisplayLocked(anim[0], anim[1], displayContent);
-                config = new Configuration(mTempConfiguration);
-            }
-        }
-
-        return config;
-    }
-
     void setNewDisplayOverrideConfiguration(Configuration overrideConfig,
             @NonNull DisplayContent dc) {
         if (dc.mWaitingForConfig) {
@@ -2817,45 +2751,38 @@
     }
 
     @Override
-    public void disableKeyguard(IBinder token, String tag) {
+    public void disableKeyguard(IBinder token, String tag, int userId) {
+        userId = mAmInternal.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+                userId, false /* allowAll */, ALLOW_FULL_ONLY, "disableKeyguard", null);
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
             != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires DISABLE_KEYGUARD permission");
         }
-        // If this isn't coming from the system then don't allow disabling the lockscreen
-        // to bypass security.
-        if (Binder.getCallingUid() != SYSTEM_UID && isKeyguardSecure()) {
-            Log.d(TAG_WM, "current mode is SecurityMode, ignore disableKeyguard");
-            return;
+        final int callingUid = Binder.getCallingUid();
+        final long origIdentity = Binder.clearCallingIdentity();
+        try {
+            mKeyguardDisableHandler.disableKeyguard(token, tag, callingUid, userId);
+        } finally {
+            Binder.restoreCallingIdentity(origIdentity);
         }
-
-        // If this isn't coming from the current profiles, ignore it.
-        if (!isCurrentProfileLocked(UserHandle.getCallingUserId())) {
-            Log.d(TAG_WM, "non-current profiles, ignore disableKeyguard");
-            return;
-        }
-
-        if (token == null) {
-            throw new IllegalArgumentException("token == null");
-        }
-
-        mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage(
-                KeyguardDisableHandler.KEYGUARD_DISABLE, new Pair<IBinder, String>(token, tag)));
     }
 
     @Override
-    public void reenableKeyguard(IBinder token) {
+    public void reenableKeyguard(IBinder token, int userId) {
+        userId = mAmInternal.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+                userId, false /* allowAll */, ALLOW_FULL_ONLY, "reenableKeyguard", null);
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
             != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires DISABLE_KEYGUARD permission");
         }
-
-        if (token == null) {
-            throw new IllegalArgumentException("token == null");
+        Preconditions.checkNotNull(token, "token is null");
+        final int callingUid = Binder.getCallingUid();
+        final long origIdentity = Binder.clearCallingIdentity();
+        try {
+            mKeyguardDisableHandler.reenableKeyguard(token, callingUid, userId);
+        } finally {
+            Binder.restoreCallingIdentity(origIdentity);
         }
-
-        mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage(
-                KeyguardDisableHandler.KEYGUARD_REENABLE, token));
     }
 
     /**
@@ -3139,11 +3066,7 @@
             mCurrentUserId = newUserId;
             mCurrentProfileIds = currentProfileIds;
             mPolicy.setCurrentUserLw(newUserId);
-
-            // If keyguard was disabled, re-enable it
-            // TODO: Keep track of keyguardEnabled state per user and use here...
-            // e.g. enabled = mKeyguardDisableHandler.getEnabledStateForUser(newUserId);
-            mPolicy.enableKeyguard(true);
+            mKeyguardDisableHandler.setCurrentUser(newUserId);
 
             // Hide windows that should not be seen by the new user.
             mRoot.switchUser();
@@ -4484,7 +4407,6 @@
 
                     AccessibilityController accessibilityController = null;
 
-                    final boolean topFocusedDisplayChanged = msg.arg1 != 0;
                     synchronized (mGlobalLock) {
                         // TODO(multidisplay): Accessibility supported only of default desiplay.
                         if (mAccessibilityController != null && displayContent.isDefaultDisplay) {
@@ -4495,19 +4417,7 @@
                         newFocus = displayContent.mCurrentFocus;
                     }
                     if (lastFocus == newFocus) {
-                        // Report focus to ViewRootImpl when top focused display changes.
-                        // Or, nothing to do for no window focus change.
-                        if (topFocusedDisplayChanged && newFocus != null) {
-                            if (DEBUG_FOCUS_LIGHT) {
-                                Slog.d(TAG, "Reporting focus: " + newFocus
-                                        + " due to top focused display change.");
-                            }
-                            // See {@link IWindow#windowFocusChanged} to know why set
-                            // reportToClient as false.
-                            newFocus.reportFocusChangedSerialized(true, mInTouchMode,
-                                    false /* reportToClient */);
-                            notifyFocusChanged();
-                        }
+                        // Focus is not changing, so nothing to do.
                         return;
                     }
                     synchronized (mGlobalLock) {
@@ -4529,15 +4439,13 @@
 
                     if (newFocus != null) {
                         if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Gaining focus: " + newFocus);
-                        newFocus.reportFocusChangedSerialized(true, mInTouchMode,
-                                true /* reportToClient */);
+                        newFocus.reportFocusChangedSerialized(true, mInTouchMode);
                         notifyFocusChanged();
                     }
 
                     if (lastFocus != null) {
                         if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Losing focus: " + lastFocus);
-                        lastFocus.reportFocusChangedSerialized(false, mInTouchMode,
-                                true /* reportToClient */);
+                        lastFocus.reportFocusChangedSerialized(false, mInTouchMode);
                     }
                 } break;
 
@@ -4554,8 +4462,7 @@
                     for (int i = 0; i < N; i++) {
                         if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Losing delayed focus: " +
                                 losers.get(i));
-                        losers.get(i).reportFocusChangedSerialized(false, mInTouchMode,
-                                true /* reportToClient */);
+                        losers.get(i).reportFocusChangedSerialized(false, mInTouchMode);
                     }
                 } break;
 
@@ -6259,7 +6166,7 @@
                     dumpWindowsLocked(pw, true, null);
                 }
                 return;
-            } else if ("all".equals(cmd) || "a".equals(cmd)) {
+            } else if ("all".equals(cmd)) {
                 synchronized (mGlobalLock) {
                     dumpWindowsLocked(pw, true, null);
                 }
@@ -7124,6 +7031,11 @@
         }
 
         @Override
+        public void reportPasswordChanged(int userId) {
+            mKeyguardDisableHandler.updateKeyguardEnabled(userId);
+        }
+
+        @Override
         public int getInputMethodWindowVisibleHeight(int displayId) {
             synchronized (mGlobalLock) {
                 final DisplayContent dc = mRoot.getDisplayContent(displayId);
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index f7f7528..c38a974 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -141,6 +141,9 @@
     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 whitelisted to start activities even if
+    // it's not in the foreground
+    private volatile boolean mAllowBackgroundActivityStarts;
 
     // Thread currently set for VR scheduling
     int mVrThreadTid;
@@ -343,6 +346,14 @@
         return mUsingWrapper;
     }
 
+    public void setAllowBackgroundActivityStarts(boolean allowBackgroundActivityStarts) {
+        mAllowBackgroundActivityStarts = allowBackgroundActivityStarts;
+    }
+
+    public boolean areBackgroundActivityStartsAllowed() {
+        return mAllowBackgroundActivityStarts;
+    }
+
     public void setInstrumenting(boolean instrumenting) {
         mInstrumenting = instrumenting;
     }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d2dfa76..cd29b3c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1373,12 +1373,15 @@
 
     @Override
     boolean isVisible() {
-        return wouldBeVisibleIfPolicyIgnored() && mPolicyVisibility;
+        return wouldBeVisibleIfPolicyIgnored() && mPolicyVisibility
+                // If we don't have a provider, this window isn't used as a window generating
+                // insets, so nobody can hide it over the inset APIs.
+                && (mInsetProvider == null || mInsetProvider.isClientVisible());
     }
 
     /**
-     * @return True if the window would be visible if we'd ignore policy visibility, false
-     *         otherwise.
+     * @return {@code true} if the window would be visible if we'd ignore policy visibility,
+     *         {@code false} otherwise.
      */
     boolean wouldBeVisibleIfPolicyIgnored() {
         return mHasSurface && !isParentWindowHidden()
@@ -2168,11 +2171,13 @@
                 mTmpRect.inset(-delta, -delta);
             }
             region.set(mTmpRect);
-            region.translate(-mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top);
+            cropRegionToStackBoundsIfNeeded(region);
         } else {
             // Not modal or full screen modal
-            getTouchableRegion(region, true /* forSurface */);
+            getTouchableRegion(region);
         }
+        // Translate to surface based coordinates.
+        region.translate(-mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top);
 
         return flags;
     }
@@ -2809,16 +2814,6 @@
 
     /** Get the touchable region in global coordinates. */
     void getTouchableRegion(Region outRegion) {
-        getTouchableRegion(outRegion, false /* forSurface */);
-    }
-
-    /** If {@param forSuface} is {@code true}, the region will be translated to surface based. */
-    private void getTouchableRegion(Region outRegion, boolean forSurface) {
-        if (inPinnedWindowingMode() && !isFocused()) {
-            outRegion.setEmpty();
-            return;
-        }
-
         final Rect frame = mWindowFrames.mFrame;
         switch (mTouchableInsets) {
             default:
@@ -2833,18 +2828,11 @@
                 break;
             case TOUCHABLE_INSETS_REGION: {
                 outRegion.set(mGivenTouchableRegion);
+                outRegion.translate(frame.left, frame.top);
                 break;
             }
         }
         cropRegionToStackBoundsIfNeeded(outRegion);
-
-        if (forSurface) {
-            if (mTouchableInsets != TOUCHABLE_INSETS_REGION) {
-                outRegion.translate(-frame.left, -frame.top);
-            }
-            outRegion.getBounds(mTmpRect);
-            applyInsets(outRegion, mTmpRect, mAttrs.surfaceInsets);
-        }
     }
 
     private void cropRegionToStackBoundsIfNeeded(Region region) {
@@ -2866,13 +2854,12 @@
      * Report a focus change.  Must be called with no locks held, and consistently
      * from the same serialized thread (such as dispatched from a handler).
      */
-    void reportFocusChangedSerialized(boolean focused, boolean inTouchMode,
-            boolean reportToClient) {
+    void reportFocusChangedSerialized(boolean focused, boolean inTouchMode) {
         try {
-            mClient.windowFocusChanged(focused, inTouchMode, reportToClient);
+            mClient.windowFocusChanged(focused, inTouchMode);
         } catch (RemoteException e) {
         }
-        if (mFocusCallbacks != null && reportToClient) {
+        if (mFocusCallbacks != null) {
             final int N = mFocusCallbacks.beginBroadcast();
             for (int i=0; i<N; i++) {
                 IWindowFocusObserver obs = mFocusCallbacks.getBroadcastItem(i);
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index ce627e2..c2a8e7e 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -478,8 +478,8 @@
         return mSurfaceControl.getHandle();
     }
 
-    void getSurface(Surface outSurface) {
-        outSurface.copyFrom(mSurfaceControl);
+    void getSurfaceControl(SurfaceControl outSurfaceControl) {
+        outSurfaceControl.copyFrom(mSurfaceControl);
     }
 
     int getLayer() {
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 6d72191..b4fe837 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -111,6 +111,7 @@
         "android.hardware.light@2.0",
         "android.hardware.power@1.0",
         "android.hardware.power@1.1",
+        "android.hardware.power.stats@1.0",
         "android.hardware.tetheroffload.config@1.0",
         "android.hardware.thermal@1.0",
         "android.hardware.tv.cec@1.0",
diff --git a/services/core/jni/BroadcastRadio/NativeCallbackThread.h b/services/core/jni/BroadcastRadio/NativeCallbackThread.h
index 53990be..0f62de9 100644
--- a/services/core/jni/BroadcastRadio/NativeCallbackThread.h
+++ b/services/core/jni/BroadcastRadio/NativeCallbackThread.h
@@ -41,7 +41,7 @@
     DISALLOW_COPY_AND_ASSIGN(NativeCallbackThread);
 
 public:
-    NativeCallbackThread(JavaVM *vm);
+    explicit NativeCallbackThread(JavaVM *vm);
     virtual ~NativeCallbackThread();
 
     void enqueue(const Task &task);
diff --git a/services/core/jni/BroadcastRadio/Tuner.cpp b/services/core/jni/BroadcastRadio/Tuner.cpp
index 9c2e1e5..a2a7f7d 100644
--- a/services/core/jni/BroadcastRadio/Tuner.cpp
+++ b/services/core/jni/BroadcastRadio/Tuner.cpp
@@ -73,7 +73,8 @@
     wp<V1_1::ITunerCallback> mTunerCallback;
 
 public:
-    HalDeathRecipient(wp<V1_1::ITunerCallback> tunerCallback):mTunerCallback(tunerCallback) {}
+    explicit HalDeathRecipient(wp<V1_1::ITunerCallback> tunerCallback)
+        : mTunerCallback(tunerCallback) {}
 
     virtual void serviceDied(uint64_t cookie, const wp<hidl::base::V1_0::IBase>& who);
 };
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index 0ff60e4..024760d 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -27,9 +27,11 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <unordered_map>
 
 #include <android/hardware/power/1.0/IPower.h>
 #include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/stats/1.0/IPowerStats.h>
 #include <android/system/suspend/1.0/ISystemSuspend.h>
 #include <android/system/suspend/1.0/ISystemSuspendCallback.h>
 #include <android_runtime/AndroidRuntime.h>
@@ -74,6 +76,33 @@
 static jmethodID jputVoter = NULL;
 static jmethodID jputState = NULL;
 
+std::mutex gPowerHalMutex;
+std::unordered_map<uint32_t, std::string> gPowerStatsHalEntityNames = {};
+std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>>
+    gPowerStatsHalStateNames = {};
+std::vector<uint32_t> gPowerStatsHalPlatformIds = {};
+std::vector<uint32_t> gPowerStatsHalSubsystemIds = {};
+sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0 = nullptr;
+std::function<void(JNIEnv*, jobject)> gGetLowPowerStatsImpl = {};
+std::function<jint(JNIEnv*, jobject)> gGetPlatformLowPowerStatsImpl = {};
+std::function<jint(JNIEnv*, jobject)> gGetSubsystemLowPowerStatsImpl = {};
+
+// The caller must be holding gPowerHalMutex.
+static void deinitPowerStatsLocked() {
+    gPowerStatsHalV1_0 = nullptr;
+}
+
+struct PowerHalDeathRecipient : virtual public hardware::hidl_death_recipient {
+    virtual void serviceDied(uint64_t cookie,
+            const wp<android::hidl::base::V1_0::IBase>& who) override {
+        // The HAL just died. Reset all handles to HAL services.
+        std::lock_guard<std::mutex> lock(gPowerHalMutex);
+        deinitPowerStatsLocked();
+    }
+};
+
+sp<PowerHalDeathRecipient> gDeathRecipient = new PowerHalDeathRecipient();
+
 class WakeupCallback : public ISystemSuspendCallback {
 public:
     Return<void> notifyWakeup(bool success) override {
@@ -202,18 +231,291 @@
     return mergedreasonpos - mergedreason;
 }
 
-static void getLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject jrpmStats) {
-    if (jrpmStats == NULL) {
-        jniThrowException(env, "java/lang/NullPointerException",
-                "The rpmstats jni input jobject jrpmStats is null.");
-        return;
+// The caller must be holding gPowerHalMutex.
+static bool checkResultLocked(const Return<void> &ret, const char* function) {
+    if (!ret.isOk()) {
+        ALOGE("%s failed: requested HAL service not available. Description: %s",
+            function, ret.description().c_str());
+        if (ret.isDeadObject()) {
+            deinitPowerStatsLocked();
+        }
+        return false;
     }
-    if (jgetAndUpdatePlatformState == NULL || jgetSubsystem == NULL
-            || jputVoter == NULL || jputState == NULL) {
-        ALOGE("A rpmstats jni jmethodID is null.");
+    return true;
+}
+
+// The caller must be holding gPowerHalMutex.
+// gPowerStatsHalV1_0 must not be null
+static bool initializePowerStats() {
+    using android::hardware::power::stats::V1_0::Status;
+    using android::hardware::power::stats::V1_0::PowerEntityType;
+
+    // Clear out previous content if we are re-initializing
+    gPowerStatsHalEntityNames.clear();
+    gPowerStatsHalStateNames.clear();
+    gPowerStatsHalPlatformIds.clear();
+    gPowerStatsHalSubsystemIds.clear();
+
+    Return<void> ret;
+    ret = gPowerStatsHalV1_0->getPowerEntityInfo([](auto infos, auto status) {
+        if (status != Status::SUCCESS) {
+            ALOGE("Error getting power entity info");
+            return;
+        }
+
+        // construct lookup table of powerEntityId to power entity name
+        // also construct vector of platform and subsystem IDs
+        for (auto info : infos) {
+            gPowerStatsHalEntityNames.emplace(info.powerEntityId, info.powerEntityName);
+            if (info.type == PowerEntityType::POWER_DOMAIN) {
+                gPowerStatsHalPlatformIds.emplace_back(info.powerEntityId);
+            } else {
+                gPowerStatsHalSubsystemIds.emplace_back(info.powerEntityId);
+            }
+        }
+    });
+    if (!checkResultLocked(ret, __func__)) {
+        return false;
+    }
+
+    ret = gPowerStatsHalV1_0->getPowerEntityStateInfo({}, [](auto stateSpaces, auto status) {
+        if (status != Status::SUCCESS) {
+            ALOGE("Error getting state info");
+            return;
+        }
+
+        // construct lookup table of powerEntityId, powerEntityStateId to power entity state name
+        for (auto stateSpace : stateSpaces) {
+            std::unordered_map<uint32_t, std::string> stateNames = {};
+            for (auto state : stateSpace.states) {
+                stateNames.emplace(state.powerEntityStateId,
+                    state.powerEntityStateName);
+            }
+            gPowerStatsHalStateNames.emplace(stateSpace.powerEntityId, stateNames);
+        }
+    });
+    if (!checkResultLocked(ret, __func__)) {
+        return false;
+    }
+
+    return (!gPowerStatsHalEntityNames.empty()) && (!gPowerStatsHalStateNames.empty());
+}
+
+// The caller must be holding gPowerHalMutex.
+static bool getPowerStatsHalLocked() {
+    if (gPowerStatsHalV1_0 == nullptr) {
+        gPowerStatsHalV1_0 = android::hardware::power::stats::V1_0::IPowerStats::getService();
+        if (gPowerStatsHalV1_0 == nullptr) {
+            ALOGE("Unable to get power.stats HAL service.");
+            return false;
+        }
+
+        // Link death recipient to power.stats service handle
+        hardware::Return<bool> linked = gPowerStatsHalV1_0->linkToDeath(gDeathRecipient, 0);
+        if (!linked.isOk()) {
+            ALOGE("Transaction error in linking to power.stats HAL death: %s",
+                    linked.description().c_str());
+            deinitPowerStatsLocked();
+            return false;
+        } else if (!linked) {
+            ALOGW("Unable to link to power.stats HAL death notifications");
+            // We should still continue even though linking failed
+        }
+        return initializePowerStats();
+    }
+    return true;
+}
+
+// The caller must be holding powerHalMutex.
+static void getPowerStatsHalLowPowerData(JNIEnv* env, jobject jrpmStats) {
+    using android::hardware::power::stats::V1_0::Status;
+
+    if (!getPowerStatsHalLocked()) {
+        ALOGE("failed to get low power stats");
         return;
     }
 
+    // Get power entity state residency data
+    bool success = false;
+    Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData({},
+        [&env, &jrpmStats, &success](auto results, auto status) {
+            if (status == Status::NOT_SUPPORTED) {
+                ALOGW("getPowerEntityStateResidencyData is not supported");
+                success = false;
+                return;
+            }
+
+            for (auto result : results) {
+                jobject jsubsystem = env->CallObjectMethod(jrpmStats, jgetSubsystem,
+                    env->NewStringUTF(gPowerStatsHalEntityNames.at(result.powerEntityId).c_str()));
+                if (jsubsystem == NULL) {
+                    ALOGE("The rpmstats jni jobject jsubsystem is null.");
+                    return;
+                }
+                for (auto stateResidency : result.stateResidencyData) {
+
+                    env->CallVoidMethod(jsubsystem, jputState,
+                        env->NewStringUTF(gPowerStatsHalStateNames.at(result.powerEntityId)
+                        .at(stateResidency.powerEntityStateId).c_str()),
+                        stateResidency.totalTimeInStateMs,
+                        stateResidency.totalStateEntryCount);
+                }
+            }
+            success = true;
+        });
+    checkResultLocked(ret, __func__);
+    if (!success) {
+        ALOGE("getPowerEntityStateResidencyData failed");
+    }
+}
+
+static jint getPowerStatsHalPlatformData(JNIEnv* env, jobject outBuf) {
+    using android::hardware::power::stats::V1_0::Status;
+    using hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
+    using hardware::power::stats::V1_0::PowerEntityStateResidencyData;
+
+    if (!getPowerStatsHalLocked()) {
+        ALOGE("failed to get low power stats");
+        return -1;
+    }
+
+    char *output = (char*)env->GetDirectBufferAddress(outBuf);
+    char *offset = output;
+    int remaining = (int)env->GetDirectBufferCapacity(outBuf);
+    int total_added = -1;
+
+    // Get power entity state residency data
+    Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData(
+        gPowerStatsHalPlatformIds,
+        [&offset, &remaining, &total_added](auto results, auto status) {
+            if (status == Status::NOT_SUPPORTED) {
+                ALOGW("getPowerEntityStateResidencyData is not supported");
+                return;
+            }
+
+            for (size_t i = 0; i < results.size(); i++) {
+                const PowerEntityStateResidencyResult& result = results[i];
+
+                for (size_t j = 0; j < result.stateResidencyData.size(); j++) {
+                    const PowerEntityStateResidencyData& stateResidency =
+                        result.stateResidencyData[j];
+                    int added = snprintf(offset, remaining,
+                        "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " ",
+                        j + 1, gPowerStatsHalStateNames.at(result.powerEntityId)
+                           .at(stateResidency.powerEntityStateId).c_str(),
+                        stateResidency.totalTimeInStateMs,
+                        stateResidency.totalStateEntryCount);
+                    if (added < 0) {
+                        break;
+                    }
+                    if (added > remaining) {
+                        added = remaining;
+                    }
+                    offset += added;
+                    remaining -= added;
+                    total_added += added;
+                }
+                if (remaining <= 0) {
+                    /* rewrite NULL character*/
+                    offset--;
+                    total_added--;
+                    ALOGE("power.stats Hal: buffer not enough");
+                    break;
+                }
+            }
+        });
+    if (!checkResultLocked(ret, __func__)) {
+        return -1;
+    }
+
+    total_added += 1;
+    return total_added;
+}
+
+static jint getPowerStatsHalSubsystemData(JNIEnv* env, jobject outBuf) {
+    using android::hardware::power::stats::V1_0::Status;
+    using hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
+    using hardware::power::stats::V1_0::PowerEntityStateResidencyData;
+
+    if (!getPowerStatsHalLocked()) {
+        ALOGE("failed to get low power stats");
+        return -1;
+    }
+
+    char *output = (char*)env->GetDirectBufferAddress(outBuf);
+    char *offset = output;
+    int remaining = (int)env->GetDirectBufferCapacity(outBuf);
+    int total_added = -1;
+
+    // Get power entity state residency data
+    Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData(
+        gPowerStatsHalSubsystemIds,
+        [&offset, &remaining, &total_added](auto results, auto status) {
+            if (status == Status::NOT_SUPPORTED) {
+                ALOGW("getPowerEntityStateResidencyData is not supported");
+                return;
+            }
+
+            int added = snprintf(offset, remaining, "SubsystemPowerState ");
+            offset += added;
+            remaining -= added;
+            total_added += added;
+
+            for (size_t i = 0; i < results.size(); i++) {
+                const PowerEntityStateResidencyResult& result = results[i];
+                added = snprintf(offset, remaining, "subsystem_%zu name=%s ",
+                        i + 1, gPowerStatsHalEntityNames.at(result.powerEntityId).c_str());
+                if (added < 0) {
+                    break;
+                }
+
+                if (added > remaining) {
+                    added = remaining;
+                }
+
+                offset += added;
+                remaining -= added;
+                total_added += added;
+
+                for (size_t j = 0; j < result.stateResidencyData.size(); j++) {
+                    const PowerEntityStateResidencyData& stateResidency =
+                        result.stateResidencyData[j];
+                    added = snprintf(offset, remaining,
+                        "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " last entry=%"
+                        PRIu64 " ", j + 1, gPowerStatsHalStateNames.at(result.powerEntityId)
+                           .at(stateResidency.powerEntityStateId).c_str(),
+                        stateResidency.totalTimeInStateMs,
+                        stateResidency.totalStateEntryCount,
+                        stateResidency.lastEntryTimestampMs);
+                    if (added < 0) {
+                        break;
+                    }
+                    if (added > remaining) {
+                        added = remaining;
+                    }
+                    offset += added;
+                    remaining -= added;
+                    total_added += added;
+                }
+                if (remaining <= 0) {
+                    /* rewrite NULL character*/
+                    offset--;
+                    total_added--;
+                    ALOGE("power.stats Hal: buffer not enough");
+                    break;
+                }
+            }
+        });
+    if (!checkResultLocked(ret, __func__)) {
+        return -1;
+    }
+
+    total_added += 1;
+    return total_added;
+}
+
+// The caller must be holding powerHalMutex.
+static void getPowerHalLowPowerData(JNIEnv* env, jobject jrpmStats) {
     sp<IPowerV1_0> powerHalV1_0 = getPowerHalV1_0();
     if (powerHalV1_0 == nullptr) {
         ALOGE("Power Hal not loaded");
@@ -286,17 +588,12 @@
     processPowerHalReturn(ret, "getSubsystemLowPowerStats");
 }
 
-static jint getPlatformLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
+static jint getPowerHalPlatformData(JNIEnv* env, jobject outBuf) {
     char *output = (char*)env->GetDirectBufferAddress(outBuf);
     char *offset = output;
     int remaining = (int)env->GetDirectBufferCapacity(outBuf);
     int total_added = -1;
 
-    if (outBuf == NULL) {
-        jniThrowException(env, "java/lang/NullPointerException", "null argument");
-        return -1;
-    }
-
     {
         sp<IPowerV1_0> powerHalV1_0 = getPowerHalV1_0();
         if (powerHalV1_0 == nullptr) {
@@ -365,7 +662,7 @@
     return total_added;
 }
 
-static jint getSubsystemLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
+static jint getPowerHalSubsystemData(JNIEnv* env, jobject outBuf) {
     char *output = (char*)env->GetDirectBufferAddress(outBuf);
     char *offset = output;
     int remaining = (int)env->GetDirectBufferCapacity(outBuf);
@@ -374,11 +671,6 @@
     // This is a IPower 1.1 API
     sp<IPowerV1_1> powerHal_1_1 = nullptr;
 
-    if (outBuf == NULL) {
-        jniThrowException(env, "java/lang/NullPointerException", "null argument");
-        return -1;
-    }
-
     {
         // Trying to get 1.1, this will succeed only for devices supporting 1.1
         powerHal_1_1 = getPowerHalV1_1();
@@ -458,6 +750,88 @@
     return total_added;
 }
 
+static void setUpPowerStatsLocked() {
+    // First see if power.stats HAL is available. Fall back to power HAL if
+    // power.stats HAL is unavailable.
+    if (android::hardware::power::stats::V1_0::IPowerStats::getService() != nullptr) {
+        ALOGI("Using power.stats HAL");
+        gGetLowPowerStatsImpl = getPowerStatsHalLowPowerData;
+        gGetPlatformLowPowerStatsImpl = getPowerStatsHalPlatformData;
+        gGetSubsystemLowPowerStatsImpl = getPowerStatsHalSubsystemData;
+    } else if (android::hardware::power::V1_0::IPower::getService() != nullptr) {
+        ALOGI("Using power HAL");
+        gGetLowPowerStatsImpl = getPowerHalLowPowerData;
+        gGetPlatformLowPowerStatsImpl = getPowerHalPlatformData;
+        gGetSubsystemLowPowerStatsImpl = getPowerHalSubsystemData;
+    }
+}
+
+static void getLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject jrpmStats) {
+    if (jrpmStats == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException",
+                "The rpmstats jni input jobject jrpmStats is null.");
+        return;
+    }
+    if (jgetAndUpdatePlatformState == NULL || jgetSubsystem == NULL
+            || jputVoter == NULL || jputState == NULL) {
+        ALOGE("A rpmstats jni jmethodID is null.");
+        return;
+    }
+
+    std::lock_guard<std::mutex> lock(gPowerHalMutex);
+
+    if (!gGetLowPowerStatsImpl) {
+        setUpPowerStatsLocked();
+    }
+
+    if (gGetLowPowerStatsImpl) {
+        return gGetLowPowerStatsImpl(env, jrpmStats);
+    }
+
+    ALOGE("Unable to load Power Hal or power.stats HAL");
+    return;
+}
+
+static jint getPlatformLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
+    if (outBuf == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", "null argument");
+        return -1;
+    }
+
+    std::lock_guard<std::mutex> lock(gPowerHalMutex);
+
+    if (!gGetPlatformLowPowerStatsImpl) {
+        setUpPowerStatsLocked();
+    }
+
+    if (gGetPlatformLowPowerStatsImpl) {
+        return gGetPlatformLowPowerStatsImpl(env, outBuf);
+    }
+
+    ALOGE("Unable to load Power Hal or power.stats HAL");
+    return -1;
+}
+
+static jint getSubsystemLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
+    if (outBuf == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", "null argument");
+        return -1;
+    }
+
+    std::lock_guard<std::mutex> lock(gPowerHalMutex);
+
+    if (!gGetSubsystemLowPowerStatsImpl) {
+        setUpPowerStatsLocked();
+    }
+
+    if (gGetSubsystemLowPowerStatsImpl) {
+        return gGetSubsystemLowPowerStatsImpl(env, outBuf);
+    }
+
+    ALOGE("Unable to load Power Hal or power.stats HAL");
+    return -1;
+}
+
 static const JNINativeMethod method_table[] = {
     { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup },
     { "getLowPowerStats", "(Lcom/android/internal/os/RpmStats;)V", (void*)getLowPowerStats },
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
index c22109c..b08d13f 100644
--- a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
+++ b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
@@ -89,7 +89,7 @@
 private:
     class HdmiCecCallback : public IHdmiCecCallback {
     public:
-        HdmiCecCallback(HdmiCecController* controller) : mController(controller) {};
+        explicit HdmiCecCallback(HdmiCecController* controller) : mController(controller) {};
         Return<void> onCecMessage(const CecMessage& event)  override;
         Return<void> onHotplugEvent(const HotplugEvent& event)  override;
     private:
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index bf83ca9..43d2dcf 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -107,7 +107,6 @@
     jmethodID getLongPressTimeout;
     jmethodID getPointerLayer;
     jmethodID getPointerIcon;
-    jmethodID getPointerDisplayId;
     jmethodID getKeyboardLayoutOverlay;
     jmethodID getDeviceAlias;
     jmethodID getTouchCalibrationForInputDevice;
@@ -175,6 +174,15 @@
     loadSystemIconAsSpriteWithPointerIcon(env, contextObj, style, &pointerIcon, outSpriteIcon);
 }
 
+static void updatePointerControllerFromViewport(
+        sp<PointerController> controller, const DisplayViewport* const viewport) {
+    if (controller != nullptr && viewport != nullptr) {
+        const int32_t width = viewport->logicalRight - viewport->logicalLeft;
+        const int32_t height = viewport->logicalBottom - viewport->logicalTop;
+        controller->setDisplayViewport(width, height, viewport->orientation);
+    }
+}
+
 enum {
     WM_ACTION_PASS_TO_USER = 1,
 };
@@ -234,7 +242,6 @@
             jfloatArray matrixArr);
     virtual TouchAffineTransformation getTouchAffineTransformation(
             const std::string& inputDeviceDescriptor, int32_t surfaceRotation);
-    virtual void updatePointerDisplay();
 
     /* --- InputDispatcherPolicyInterface implementation --- */
 
@@ -307,11 +314,10 @@
 
     std::atomic<bool> mInteractive;
 
-    void updateInactivityTimeoutLocked();
+    void updateInactivityTimeoutLocked(const sp<PointerController>& controller);
     void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
     void ensureSpriteControllerLocked();
-    const DisplayViewport* findDisplayViewportLocked(int32_t displayId);
-    int32_t getPointerDisplayId();
+
     static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
 
     static inline JNIEnv* jniEnv() {
@@ -385,10 +391,9 @@
     return false;
 }
 
-const DisplayViewport* NativeInputManager::findDisplayViewportLocked(int32_t displayId)
-        REQUIRES(mLock) {
-    for (const DisplayViewport& v : mLocked.viewports) {
-        if (v.displayId == displayId) {
+static const DisplayViewport* findInternalViewport(const std::vector<DisplayViewport>& viewports) {
+    for (const DisplayViewport& v : viewports) {
+        if (v.type == ViewportType::VIEWPORT_INTERNAL) {
             return &v;
         }
     }
@@ -415,10 +420,20 @@
         }
     }
 
-    { // acquire lock
+    const DisplayViewport* newInternalViewport = findInternalViewport(viewports);
+    {
         AutoMutex _l(mLock);
+        const DisplayViewport* oldInternalViewport = findInternalViewport(mLocked.viewports);
+        // Internal viewport has changed if there wasn't one earlier, and there is one now, or,
+        // if they are different.
+        const bool internalViewportChanged = (newInternalViewport != nullptr) &&
+                (oldInternalViewport == nullptr || (*oldInternalViewport != *newInternalViewport));
+        if (internalViewportChanged) {
+            sp<PointerController> controller = mLocked.pointerController.promote();
+            updatePointerControllerFromViewport(controller, newInternalViewport);
+        }
         mLocked.viewports = viewports;
-    } // release lock
+    }
 
     mInputManager->getReader()->requestRefreshConfiguration(
             InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -541,43 +556,15 @@
 
         controller = new PointerController(this, mLooper, mLocked.spriteController);
         mLocked.pointerController = controller;
-        updateInactivityTimeoutLocked();
-    }
 
+        const DisplayViewport* internalViewport = findInternalViewport(mLocked.viewports);
+        updatePointerControllerFromViewport(controller, internalViewport);
+
+        updateInactivityTimeoutLocked(controller);
+    }
     return controller;
 }
 
-int32_t NativeInputManager::getPointerDisplayId() {
-    JNIEnv* env = jniEnv();
-    jint pointerDisplayId = env->CallIntMethod(mServiceObj,
-            gServiceClassInfo.getPointerDisplayId);
-    if (checkAndClearExceptionFromCallback(env, "getPointerDisplayId")) {
-        pointerDisplayId = ADISPLAY_ID_DEFAULT;
-    }
-
-    return pointerDisplayId;
-}
-
-void NativeInputManager::updatePointerDisplay() {
-    ATRACE_CALL();
-
-    jint pointerDisplayId = getPointerDisplayId();
-
-    AutoMutex _l(mLock);
-    sp<PointerController> controller = mLocked.pointerController.promote();
-    if (controller != nullptr) {
-        const DisplayViewport* viewport = findDisplayViewportLocked(pointerDisplayId);
-        if (viewport == nullptr) {
-            ALOGW("Can't find pointer display viewport, fallback to default display.");
-            viewport = findDisplayViewportLocked(ADISPLAY_ID_DEFAULT);
-        }
-
-        if (viewport != nullptr) {
-            controller->setDisplayViewport(*viewport);
-        }
-    }
-}
-
 void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) {
     if (mLocked.spriteController == nullptr) {
         JNIEnv* env = jniEnv();
@@ -834,16 +821,16 @@
 
     if (mLocked.systemUiVisibility != visibility) {
         mLocked.systemUiVisibility = visibility;
-        updateInactivityTimeoutLocked();
+
+        sp<PointerController> controller = mLocked.pointerController.promote();
+        if (controller != nullptr) {
+            updateInactivityTimeoutLocked(controller);
+        }
     }
 }
 
-void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) {
-    sp<PointerController> controller = mLocked.pointerController.promote();
-    if (controller == nullptr) {
-        return;
-    }
-
+void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller)
+        REQUIRES(mLock) {
     bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
     controller->setInactivityTimeout(lightsOut
             ? PointerController::INACTIVITY_TIMEOUT_SHORT
@@ -1837,9 +1824,6 @@
     GET_METHOD_ID(gServiceClassInfo.getPointerIcon, clazz,
             "getPointerIcon", "()Landroid/view/PointerIcon;");
 
-    GET_METHOD_ID(gServiceClassInfo.getPointerDisplayId, clazz,
-            "getPointerDisplayId", "()I");
-
     GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz,
             "getKeyboardLayoutOverlay",
             "(Landroid/hardware/input/InputDeviceIdentifier;)[Ljava/lang/String;");
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 0e349b7..6d8fc1c 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -25,6 +25,7 @@
 #include <android/hardware/gnss/1.0/IGnssMeasurement.h>
 #include <android/hardware/gnss/1.1/IGnssMeasurement.h>
 #include <android/hardware/gnss/2.0/IGnssMeasurement.h>
+#include <android/hardware/gnss/measurement_corrections/1.0/IMeasurementCorrections.h>
 #include <nativehelper/JNIHelp.h>
 #include "jni.h"
 #include "hardware_legacy/power.h"
@@ -68,6 +69,25 @@
 static jmethodID method_reportNavigationMessages;
 static jmethodID method_reportLocationBatch;
 static jmethodID method_reportGnssServiceDied;
+static jmethodID method_correctionsGetLatitudeDegrees;
+static jmethodID method_correctionsGetLongitudeDegrees;
+static jmethodID method_correctionsGetAltitudeMeters;
+static jmethodID method_correctionsGetToaGpsNanosecondsOfWeek;
+static jmethodID method_correctionsGetSingleSatCorrectionList;
+static jmethodID method_listSize;
+static jmethodID method_correctionListGet;
+static jmethodID method_correctionSatFlags;
+static jmethodID method_correctionSatConstType;
+static jmethodID method_correctionSatId;
+static jmethodID method_correctionSatCarrierFreq;
+static jmethodID method_correctionSatIsLos;
+static jmethodID method_correctionSatEpl;
+static jmethodID method_correctionSatEplUnc;
+static jmethodID method_correctionSatRefPlane;
+static jmethodID method_correctionPlaneLatDeg;
+static jmethodID method_correctionPlaneLngDeg;
+static jmethodID method_correctionPlaneAltDeg;
+static jmethodID method_correctionPlaneAzimDeg;
 
 /*
  * Save a pointer to JavaVm to attach/detach threads executing
@@ -84,15 +104,12 @@
 using android::hardware::Return;
 using android::hardware::Void;
 using android::hardware::hidl_vec;
+using android::hardware::hidl_string;
 using android::hardware::hidl_death_recipient;
 using android::hardware::gnss::V1_0::GnssConstellationType;
 using android::hardware::gnss::V1_0::GnssLocation;
 using android::hardware::gnss::V1_0::GnssLocationFlags;
 
-using android::hardware::gnss::V1_0::IAGnss;
-using android::hardware::gnss::V1_0::IAGnssCallback;
-using android::hardware::gnss::V1_0::IAGnssCallback;
-using android::hardware::gnss::V1_0::IAGnssRil;
 using android::hardware::gnss::V1_0::IAGnssRilCallback;
 using android::hardware::gnss::V1_0::IGnssBatching;
 using android::hardware::gnss::V1_0::IGnssBatchingCallback;
@@ -106,7 +123,10 @@
 using android::hardware::gnss::V1_0::IGnssXtra;
 using android::hardware::gnss::V1_0::IGnssXtraCallback;
 
-using android::hardware::gnss::V1_1::IGnssCallback;
+using android::hardware::gnss::V2_0::IGnssCallback;
+using android::hardware::gnss::measurement_corrections::V1_0::MeasurementCorrections;
+using android::hardware::gnss::measurement_corrections::V1_0::SingleSatCorrection;
+using android::hardware::gnss::measurement_corrections::V1_0::ReflectingPlane;
 
 using android::hidl::base::V1_0::IBase;
 
@@ -121,6 +141,15 @@
 using IGnssMeasurementCallback_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurementCallback;
 using IGnssMeasurementCallback_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurementCallback;
 using IGnssMeasurementCallback_V2_0 = android::hardware::gnss::V2_0::IGnssMeasurementCallback;
+using IAGnssRil_V1_0 = android::hardware::gnss::V1_0::IAGnssRil;
+using IAGnssRil_V2_0 = android::hardware::gnss::V2_0::IAGnssRil;
+using IAGnss_V1_0 = android::hardware::gnss::V1_0::IAGnss;
+using IAGnss_V2_0 = android::hardware::gnss::V2_0::IAGnss;
+using IAGnssCallback_V1_0 = android::hardware::gnss::V1_0::IAGnssCallback;
+using IAGnssCallback_V2_0 = android::hardware::gnss::V2_0::IAGnssCallback;
+
+using IMeasurementCorrections =
+    android::hardware::gnss::measurement_corrections::V1_0::IMeasurementCorrections;
 
 struct GnssDeathRecipient : virtual public hidl_death_recipient
 {
@@ -141,9 +170,11 @@
 sp<IGnss_V1_1> gnssHal_V1_1 = nullptr;
 sp<IGnss_V2_0> gnssHal_V2_0 = nullptr;
 sp<IGnssXtra> gnssXtraIface = nullptr;
-sp<IAGnssRil> agnssRilIface = nullptr;
+sp<IAGnssRil_V1_0> agnssRilIface = nullptr;
+sp<IAGnssRil_V2_0> agnssRilIface_V2_0 = nullptr;
 sp<IGnssGeofencing> gnssGeofencingIface = nullptr;
-sp<IAGnss> agnssIface = nullptr;
+sp<IAGnss_V1_0> agnssIface = nullptr;
+sp<IAGnss_V2_0> agnssIface_V2_0 = nullptr;
 sp<IGnssBatching> gnssBatchingIface = nullptr;
 sp<IGnssDebug> gnssDebugIface = nullptr;
 sp<IGnssConfiguration_V1_0> gnssConfigurationIface = nullptr;
@@ -153,6 +184,11 @@
 sp<IGnssMeasurement_V1_1> gnssMeasurementIface_V1_1 = nullptr;
 sp<IGnssMeasurement_V2_0> gnssMeasurementIface_V2_0 = nullptr;
 sp<IGnssNavigationMessage> gnssNavigationMessageIface = nullptr;
+sp<IMeasurementCorrections> gnssCorrectionsIface = nullptr;
+// This boolean is needed to ensure that Gnsss Measurement Corrections related method are only
+// initalized when needed which will be few devices initially
+bool firstGnssMeasurementCorrectionInjected = false;
+
 
 #define WAKE_LOCK_NAME  "GPS"
 
@@ -277,6 +313,34 @@
     }
 }
 
+struct ScopedJniString {
+    ScopedJniString(JNIEnv* env, jstring javaString) : mEnv(env), mJavaString(javaString) {
+        mNativeString = mEnv->GetStringUTFChars(mJavaString, nullptr);
+    }
+
+    ~ScopedJniString() {
+        if (mNativeString != nullptr) {
+            mEnv->ReleaseStringUTFChars(mJavaString, mNativeString);
+        }
+    }
+
+    const char* c_str() const {
+        return mNativeString;
+    }
+
+    operator hidl_string() const {
+        return hidl_string(mNativeString);
+    }
+
+private:
+    ScopedJniString(const ScopedJniString&) = delete;
+    ScopedJniString& operator=(const ScopedJniString&) = delete;
+
+    JNIEnv* mEnv;
+    jstring mJavaString;
+    const char* mNativeString;
+};
+
 class ScopedJniThreadAttach {
 public:
     ScopedJniThreadAttach() {
@@ -413,6 +477,8 @@
     // New in 1.1
     Return<void> gnssNameCb(const android::hardware::hidl_string& name) override;
 
+    Return<void> gnssSetCapabilitiesCb_2_0(uint32_t capabilities) override;
+
     // TODO(b/73306084): Reconsider allocation cost vs threadsafety on these statics
     static const char* sNmeaString;
     static size_t sNmeaStringLength;
@@ -535,6 +601,10 @@
     return Void();
 }
 
+Return<void> GnssCallback::gnssSetCapabilitiesCb_2_0(uint32_t capabilities) {
+    return GnssCallback::gnssSetCapabilitesCb(capabilities);
+}
+
 Return<void> GnssCallback::gnssAcquireWakelockCb() {
     acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME);
     return Void();
@@ -999,21 +1069,21 @@
 }
 
 /*
- * AGnssCallback implements callback methods required by the IAGnss interface.
+ * AGnssCallback_V1_0 implements callback methods required by the IAGnssCallback 1.0 interface.
  */
-struct AGnssCallback : public IAGnssCallback {
+struct AGnssCallback_V1_0 : public IAGnssCallback_V1_0 {
     // Methods from ::android::hardware::gps::V1_0::IAGnssCallback follow.
     Return<void> agnssStatusIpV6Cb(
-      const IAGnssCallback::AGnssStatusIpV6& agps_status) override;
+      const IAGnssCallback_V1_0::AGnssStatusIpV6& agps_status) override;
 
     Return<void> agnssStatusIpV4Cb(
-      const IAGnssCallback::AGnssStatusIpV4& agps_status) override;
+      const IAGnssCallback_V1_0::AGnssStatusIpV4& agps_status) override;
  private:
     jbyteArray convertToIpV4(uint32_t ip);
 };
 
-Return<void> AGnssCallback::agnssStatusIpV6Cb(
-        const IAGnssCallback::AGnssStatusIpV6& agps_status) {
+Return<void> AGnssCallback_V1_0::agnssStatusIpV6Cb(
+        const IAGnssCallback_V1_0::AGnssStatusIpV6& agps_status) {
     JNIEnv* env = getJniEnv();
     jbyteArray byteArray = nullptr;
 
@@ -1046,8 +1116,8 @@
     return Void();
 }
 
-Return<void> AGnssCallback::agnssStatusIpV4Cb(
-        const IAGnssCallback::AGnssStatusIpV4& agps_status) {
+Return<void> AGnssCallback_V1_0::agnssStatusIpV4Cb(
+        const IAGnssCallback_V1_0::AGnssStatusIpV4& agps_status) {
     JNIEnv* env = getJniEnv();
     jbyteArray byteArray = nullptr;
 
@@ -1078,7 +1148,7 @@
     return Void();
 }
 
-jbyteArray AGnssCallback::convertToIpV4(uint32_t ip) {
+jbyteArray AGnssCallback_V1_0::convertToIpV4(uint32_t ip) {
     if (INADDR_NONE == ip) {
         return nullptr;
     }
@@ -1098,6 +1168,23 @@
 }
 
 /*
+ * AGnssCallback_V2_0 implements callback methods required by the IAGnssCallback 2.0 interface.
+ */
+struct AGnssCallback_V2_0 : public IAGnssCallback_V2_0 {
+    // Methods from ::android::hardware::gps::V2_0::IAGnssCallback follow.
+    Return<void> agnssStatusCb(IAGnssCallback_V2_0::AGnssType type,
+        IAGnssCallback_V2_0::AGnssStatusValue status) override;
+};
+
+Return<void> AGnssCallback_V2_0::agnssStatusCb(IAGnssCallback_V2_0::AGnssType type,
+        IAGnssCallback_V2_0::AGnssStatusValue status) {
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_reportAGpsStatus, type, status, nullptr);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Void();
+}
+
+/*
  * AGnssRilCallback implements the callback methods required by the AGnssRil
  * interface.
  */
@@ -1247,18 +1334,37 @@
         gnssXtraIface = gnssXtra;
     }
 
-    auto gnssRil = gnssHal->getExtensionAGnssRil();
-    if (!gnssRil.isOk()) {
-        ALOGD("Unable to get a handle to AGnssRil");
+    if (gnssHal_V2_0 != nullptr) {
+        auto agnssRil_V2_0 = gnssHal_V2_0->getExtensionAGnssRil_2_0();
+        if (!agnssRil_V2_0.isOk()) {
+            ALOGD("Unable to get a handle to AGnssRil_V2_0");
+        } else {
+            agnssRilIface_V2_0 = agnssRil_V2_0;
+            agnssRilIface = agnssRilIface_V2_0;
+        }
     } else {
-        agnssRilIface = gnssRil;
+        auto agnssRil_V1_0 = gnssHal->getExtensionAGnssRil();
+        if (!agnssRil_V1_0.isOk()) {
+            ALOGD("Unable to get a handle to AGnssRil");
+        } else {
+            agnssRilIface = agnssRil_V1_0;
+        }
     }
 
-    auto gnssAgnss = gnssHal->getExtensionAGnss();
-    if (!gnssAgnss.isOk()) {
-        ALOGD("Unable to get a handle to AGnss");
+    if (gnssHal_V2_0 != nullptr) {
+        auto agnss_V2_0 = gnssHal_V2_0->getExtensionAGnss_2_0();
+        if (!agnss_V2_0.isOk()) {
+            ALOGD("Unable to get a handle to AGnss_V2_0");
+        } else {
+            agnssIface_V2_0 = agnss_V2_0;
+        }
     } else {
-        agnssIface = gnssAgnss;
+        auto agnss_V1_0 = gnssHal->getExtensionAGnss();
+        if (!agnss_V1_0.isOk()) {
+            ALOGD("Unable to get a handle to AGnss");
+        } else {
+            agnssIface = agnss_V1_0;
+        }
     }
 
     auto gnssNavigationMessage = gnssHal->getExtensionGnssNavigationMessage();
@@ -1271,6 +1377,12 @@
     if (gnssHal_V2_0 != nullptr) {
         // TODO(b/119638366): getExtensionGnssMeasurement_1_1 from gnssHal_V2_0
         auto gnssMeasurement = gnssHal_V2_0->getExtensionGnssMeasurement_2_0();
+        auto gnssCorrections = gnssHal_V2_0->getExtensionMeasurementCorrections();
+        if (!gnssCorrections.isOk()) {
+            ALOGD("Unable to get a handle to GnssMeasurementCorrections interface");
+        } else {
+            gnssCorrectionsIface = gnssCorrections;
+        }
         if (!gnssMeasurement.isOk()) {
             ALOGD("Unable to get a handle to GnssMeasurement_V2_0");
         } else {
@@ -1374,11 +1486,14 @@
     sp<IGnssCallback> gnssCbIface = new GnssCallback();
 
     Return<bool> result = false;
-    if (gnssHal_V1_1 != nullptr) {
+    if (gnssHal_V2_0 != nullptr) {
+        result = gnssHal_V2_0->setCallback_2_0(gnssCbIface);
+    } else if (gnssHal_V1_1 != nullptr) {
         result = gnssHal_V1_1->setCallback_1_1(gnssCbIface);
     } else {
         result = gnssHal->setCallback(gnssCbIface);
     }
+
     if (!result.isOk() || !result) {
         ALOGE("SetCallback for Gnss Interface fails\n");
         return JNI_FALSE;
@@ -1395,11 +1510,14 @@
         }
     }
 
-    sp<IAGnssCallback> aGnssCbIface = new AGnssCallback();
-    if (agnssIface != nullptr) {
+    if (agnssIface_V2_0 != nullptr) {
+        sp<IAGnssCallback_V2_0> aGnssCbIface = new AGnssCallback_V2_0();
+        agnssIface_V2_0->setCallback(aGnssCbIface);
+    } else if (agnssIface != nullptr) {
+        sp<IAGnssCallback_V1_0> aGnssCbIface = new AGnssCallback_V1_0();
         agnssIface->setCallback(aGnssCbIface);
     } else {
-        ALOGI("Unable to Initialize AGnss interface\n");
+        ALOGI("Unable to initialize AGnss interface\n");
     }
 
     sp<IGnssGeofenceCallback> gnssGeofencingCbIface = new GnssGeofenceCallback();
@@ -1496,17 +1614,17 @@
 
 static void android_location_GnssLocationProvider_agps_set_reference_location_cellid(
         JNIEnv* /* env */, jobject /* obj */, jint type, jint mcc, jint mnc, jint lac, jint cid) {
-    IAGnssRil::AGnssRefLocation location;
+    IAGnssRil_V1_0::AGnssRefLocation location;
 
     if (agnssRilIface == nullptr) {
         ALOGE("No AGPS RIL interface in agps_set_reference_location_cellid");
         return;
     }
 
-    switch (static_cast<IAGnssRil::AGnssRefLocationType>(type)) {
-        case IAGnssRil::AGnssRefLocationType::GSM_CELLID:
-        case IAGnssRil::AGnssRefLocationType::UMTS_CELLID:
-          location.type = static_cast<IAGnssRil::AGnssRefLocationType>(type);
+    switch (static_cast<IAGnssRil_V1_0::AGnssRefLocationType>(type)) {
+        case IAGnssRil_V1_0::AGnssRefLocationType::GSM_CELLID:
+        case IAGnssRil_V1_0::AGnssRefLocationType::UMTS_CELLID:
+          location.type = static_cast<IAGnssRil_V1_0::AGnssRefLocationType>(type);
           location.cellID.mcc = mcc;
           location.cellID.mnc = mnc;
           location.cellID.lac = lac;
@@ -1528,9 +1646,8 @@
         return;
     }
 
-    const char *setid = env->GetStringUTFChars(setid_string, nullptr);
-    agnssRilIface->setSetId((IAGnssRil::SetIDType)type, setid);
-    env->ReleaseStringUTFChars(setid_string, setid);
+    ScopedJniString jniSetId{env, setid_string};
+    agnssRilIface->setSetId((IAGnssRil_V1_0::SetIDType)type, jniSetId);
 }
 
 static jint android_location_GnssLocationProvider_read_nmea(JNIEnv* env, jobject /* obj */,
@@ -1618,68 +1735,126 @@
     env->ReleasePrimitiveArrayCritical(data, bytes, JNI_ABORT);
 }
 
-static void android_location_GnssNetworkConnectivityHandler_agps_data_conn_open(
-        JNIEnv* env, jobject /* obj */, jstring apn, jint apnIpType) {
-    if (agnssIface == nullptr) {
-        ALOGE("no AGPS interface in agps_data_conn_open");
-        return;
-    }
-    if (apn == nullptr) {
-        jniThrowException(env, "java/lang/IllegalArgumentException", nullptr);
-        return;
-    }
+struct AGnssDispatcher {
+    static void dataConnOpen(sp<IAGnss_V1_0> agnssIface, JNIEnv* env, jstring apn, jint apnIpType);
+    static void dataConnOpen(sp<IAGnss_V2_0> agnssIface_V2_0, JNIEnv* env, jlong networkHandle,
+            jstring apn, jint apnIpType);
 
-    const char *apnStr = env->GetStringUTFChars(apn, nullptr);
+    template <class T>
+    static void dataConnClosed(sp<T> agnssIface);
 
-    auto result = agnssIface->dataConnOpen(apnStr, static_cast<IAGnss::ApnIpType>(apnIpType));
+    template <class T>
+    static void dataConnFailed(sp<T> agnssIface);
+
+    template <class T, class U>
+    static void setServer(sp<T> agnssIface, JNIEnv* env, jint type, jstring hostname, jint port);
+
+private:
+    AGnssDispatcher() = delete;
+    AGnssDispatcher(const AGnssDispatcher&) = delete;
+    AGnssDispatcher& operator=(const AGnssDispatcher&) = delete;
+};
+
+void AGnssDispatcher::dataConnOpen(sp<IAGnss_V1_0> agnssIface, JNIEnv* env, jstring apn,
+        jint apnIpType) {
+    ScopedJniString jniApn{env, apn};
+    auto result = agnssIface->dataConnOpen(jniApn,
+            static_cast<IAGnss_V1_0::ApnIpType>(apnIpType));
     if (!result.isOk() || !result){
         ALOGE("%s: Failed to set APN and its IP type", __func__);
     }
-    env->ReleaseStringUTFChars(apn, apnStr);
 }
 
-static void android_location_GnssNetworkConnectivityHandler_agps_data_conn_closed(JNIEnv* /* env */,
-                                                                       jobject /* obj */) {
-    if (agnssIface == nullptr) {
-        ALOGE("%s: AGPS interface not supported", __func__);
-        return;
+void AGnssDispatcher::dataConnOpen(sp<IAGnss_V2_0> agnssIface_V2_0, JNIEnv* env,
+        jlong networkHandle, jstring apn, jint apnIpType) {
+    ScopedJniString jniApn{env, apn};
+    auto result = agnssIface_V2_0->dataConnOpen(static_cast<uint64_t>(networkHandle), jniApn,
+            static_cast<IAGnss_V2_0::ApnIpType>(apnIpType));
+    if (!result.isOk() || !result){
+        ALOGE("%s: Failed to set APN and its IP type", __func__);
     }
+}
 
+template<class T>
+void AGnssDispatcher::dataConnClosed(sp<T> agnssIface) {
     auto result = agnssIface->dataConnClosed();
     if (!result.isOk() || !result) {
         ALOGE("%s: Failed to close AGnss data connection", __func__);
     }
 }
 
-static void android_location_GnssNetworkConnectivityHandler_agps_data_conn_failed(JNIEnv* /* env */,
-                                                                       jobject /* obj */) {
-    if (agnssIface == nullptr) {
-        ALOGE("%s: AGPS interface not supported", __func__);
-        return;
-    }
-
+template<class T>
+void AGnssDispatcher::dataConnFailed(sp<T> agnssIface) {
     auto result = agnssIface->dataConnFailed();
     if (!result.isOk() || !result) {
         ALOGE("%s: Failed to notify unavailability of AGnss data connection", __func__);
     }
 }
 
-static void android_location_GnssLocationProvider_set_agps_server(JNIEnv* env, jobject /* obj */,
-        jint type, jstring hostname, jint port) {
-    if (agnssIface == nullptr) {
-        ALOGE("no AGPS interface in set_agps_server");
-        return;
-    }
-
-    const char *c_hostname = env->GetStringUTFChars(hostname, nullptr);
-    auto result = agnssIface->setServer(static_cast<IAGnssCallback::AGnssType>(type),
-                                       c_hostname,
-                                       port);
+template <class T, class U>
+void AGnssDispatcher::setServer(sp<T> agnssIface, JNIEnv* env, jint type, jstring hostname,
+        jint port) {
+    ScopedJniString jniHostName{env, hostname};
+    auto result = agnssIface->setServer(static_cast<typename U::AGnssType>(type),
+            jniHostName, port);
     if (!result.isOk() || !result) {
         ALOGE("%s: Failed to set AGnss host name and port", __func__);
     }
+}
 
-    env->ReleaseStringUTFChars(hostname, c_hostname);
+static void android_location_GnssNetworkConnectivityHandler_agps_data_conn_open(
+        JNIEnv* env, jobject /* obj */, jlong networkHandle, jstring apn, jint apnIpType) {
+    if (apn == nullptr) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", nullptr);
+        return;
+    }
+
+    if (agnssIface_V2_0 != nullptr) {
+        AGnssDispatcher::dataConnOpen(agnssIface_V2_0, env, networkHandle, apn, apnIpType);
+    } else if (agnssIface != nullptr) {
+        AGnssDispatcher::dataConnOpen(agnssIface, env, apn, apnIpType);
+    } else {
+        ALOGE("%s: AGPS interface not supported", __func__);
+        return;
+    }
+}
+
+static void android_location_GnssNetworkConnectivityHandler_agps_data_conn_closed(JNIEnv* /* env */,
+                                                                       jobject /* obj */) {
+    if (agnssIface_V2_0 != nullptr) {
+        AGnssDispatcher::dataConnClosed(agnssIface_V2_0);
+    } else if (agnssIface != nullptr) {
+        AGnssDispatcher::dataConnClosed(agnssIface);
+    } else {
+        ALOGE("%s: AGPS interface not supported", __func__);
+        return;
+    }
+}
+
+static void android_location_GnssNetworkConnectivityHandler_agps_data_conn_failed(JNIEnv* /* env */,
+                                                                       jobject /* obj */) {
+    if (agnssIface_V2_0 != nullptr) {
+        AGnssDispatcher::dataConnFailed(agnssIface_V2_0);
+    } else if (agnssIface != nullptr) {
+        AGnssDispatcher::dataConnFailed(agnssIface);
+    } else {
+        ALOGE("%s: AGPS interface not supported", __func__);
+        return;
+    }
+}
+
+static void android_location_GnssLocationProvider_set_agps_server(JNIEnv* env, jobject /* obj */,
+        jint type, jstring hostname, jint port) {
+    if (agnssIface_V2_0 != nullptr) {
+        AGnssDispatcher::setServer<IAGnss_V2_0, IAGnssCallback_V2_0>(agnssIface_V2_0, env, type,
+                hostname, port);
+    } else if (agnssIface != nullptr) {
+        AGnssDispatcher::setServer<IAGnss_V1_0, IAGnssCallback_V1_0>(agnssIface, env, type,
+                hostname, port);
+    } else {
+        ALOGE("%s: AGPS interface not supported", __func__);
+        return;
+    }
 }
 
 static void android_location_GnssLocationProvider_send_ni_response(JNIEnv* /* env */,
@@ -1771,23 +1946,37 @@
                                                                        jint type,
                                                                        jboolean roaming,
                                                                        jboolean available,
-                                                                       jstring extraInfo,
-                                                                       jstring apn) {
-    if (agnssRilIface != nullptr) {
+                                                                       jstring apn,
+                                                                       jlong networkHandle,
+                                                                       jshort capabilities) {
+    if (agnssRilIface_V2_0 != nullptr) {
+        ScopedJniString jniApn{env, apn};
+        IAGnssRil_V2_0::NetworkAttributes networkAttributes = {
+            .networkHandle = static_cast<uint64_t>(networkHandle),
+            .isConnected = static_cast<bool>(connected),
+            .capabilities = static_cast<uint16_t>(capabilities),
+            .apn = jniApn
+        };
+
+        auto result = agnssRilIface_V2_0->updateNetworkState_2_0(networkAttributes);
+        if (!result.isOk() || !result) {
+            ALOGE("updateNetworkState_2_0 failed");
+        }
+    } else if (agnssRilIface != nullptr) {
+        ScopedJniString jniApn{env, apn};
+        hidl_string hidlApn{jniApn};
         auto result = agnssRilIface->updateNetworkState(connected,
-                                                       static_cast<IAGnssRil::NetworkType>(type),
-                                                       roaming);
+                static_cast<IAGnssRil_V1_0::NetworkType>(type), roaming);
         if (!result.isOk() || !result) {
             ALOGE("updateNetworkState failed");
         }
 
-        const char *c_apn = env->GetStringUTFChars(apn, nullptr);
-        result = agnssRilIface->updateNetworkAvailability(available, c_apn);
-        if (!result.isOk() || !result) {
-            ALOGE("updateNetworkAvailability failed");
+        if (!hidlApn.empty()) {
+            result = agnssRilIface->updateNetworkAvailability(available, hidlApn);
+            if (!result.isOk() || !result) {
+                ALOGE("updateNetworkAvailability failed");
+            }
         }
-
-        env->ReleaseStringUTFChars(apn, c_apn);
     } else {
         ALOGE("AGnssRilInterface does not exist");
     }
@@ -1903,6 +2092,150 @@
     return boolToJbool(result.isOk());
 }
 
+static jboolean android_location_GnssMeasurementsProvider_inject_gnss_measurement_corrections(
+        JNIEnv* env,
+        jobject obj /* clazz*/,
+        jobject correctionsObj) {
+
+    if (gnssCorrectionsIface == nullptr) {
+        ALOGW("Trying to inject GNSS corrections on a chipset that does not support them.");
+        return JNI_FALSE;
+    }
+    if (firstGnssMeasurementCorrectionInjected == false) {
+        jclass measCorrClass = env->GetObjectClass(correctionsObj);
+        method_correctionsGetLatitudeDegrees = env->GetMethodID(
+            measCorrClass,"getLatitudeDegrees", "()D");
+
+        method_correctionsGetLongitudeDegrees = env->GetMethodID(
+            measCorrClass, "getLongitudeDegrees", "()D");
+
+        method_correctionsGetAltitudeMeters = env->GetMethodID(
+            measCorrClass, "getAltitudeMeters", "()D");
+
+        method_correctionsGetToaGpsNanosecondsOfWeek = env->GetMethodID(
+            measCorrClass, "getToaGpsNanosecondsOfWeek", "()J");
+
+        method_correctionsGetSingleSatCorrectionList = env->GetMethodID(
+            measCorrClass, "getSingleSatCorrectionList", "()Ljava.util.List;");
+    }
+
+    jdouble latitudeDegreesCorr = env->CallDoubleMethod(
+        correctionsObj, method_correctionsGetLatitudeDegrees);
+    jdouble longitudeDegreesCorr = env->CallDoubleMethod(
+        correctionsObj, method_correctionsGetLongitudeDegrees);
+    jdouble altitudeDegreesCorr = env->CallDoubleMethod(
+        correctionsObj, method_correctionsGetAltitudeMeters);
+    jlong toaGpsNanosOfWeek = env->CallLongMethod(
+        correctionsObj, method_correctionsGetToaGpsNanosecondsOfWeek);
+    jobject singleSatCorrectionList = env->CallObjectMethod(correctionsObj,
+        method_correctionsGetSingleSatCorrectionList);
+
+    if (firstGnssMeasurementCorrectionInjected == false) {
+        jclass corrListClass = env->GetObjectClass(singleSatCorrectionList);
+        method_listSize = env->GetMethodID(corrListClass, "size", "()I");
+        method_correctionListGet = env->GetMethodID(
+            corrListClass, "get", "(I)Landroid/location/GnssSingleSatCorrection;");
+    }
+
+    auto len = (singleSatCorrectionList == nullptr)
+        ? 0
+        : env->CallIntMethod(singleSatCorrectionList, method_listSize);
+    hidl_vec<SingleSatCorrection> list(len);
+
+    for (uint16_t i = 0; i < len; ++i) {
+        jobject singleSatCorrectionObj = env->CallObjectMethod(
+        singleSatCorrectionList, method_correctionListGet, i);
+
+        if (firstGnssMeasurementCorrectionInjected == false) {
+            jclass singleSatCorrClass = env->GetObjectClass(singleSatCorrectionObj);
+            method_correctionSatFlags = env->GetMethodID(
+                singleSatCorrClass, "getSingleSatCorrectionFlags", "()I");
+            method_correctionSatConstType = env->GetMethodID(
+                singleSatCorrClass, "getConstellationType", "()I");
+            method_correctionSatId= env->GetMethodID(
+                singleSatCorrClass, "getSatId", "()I");
+            method_correctionSatCarrierFreq = env->GetMethodID(
+                singleSatCorrClass, "getCarrierFrequencyHz", "()F");
+            method_correctionSatIsLos = env->GetMethodID(
+                singleSatCorrClass,"getSatIsLos", "()Z");
+            method_correctionSatEpl = env->GetMethodID(
+                singleSatCorrClass, "getExcessPathLengthMeters", "()F");
+            method_correctionSatEplUnc = env->GetMethodID(
+                singleSatCorrClass, "getExcessPathLengthUncertaintyMeters", "()F");
+            method_correctionSatRefPlane = env->GetMethodID(
+                singleSatCorrClass, "getReflectingPlane",
+                "()Landroid/location/GnssReflectingPlane;");
+        }
+
+        jint correctionFlags =
+            env->CallIntMethod(singleSatCorrectionObj, method_correctionSatFlags);
+        jint constType = env->CallIntMethod(singleSatCorrectionObj,
+            method_correctionSatConstType);
+        jint satId =
+            env->CallIntMethod(singleSatCorrectionObj, method_correctionSatId);
+        jfloat carrierFreqHz = env->CallFloatMethod(
+            singleSatCorrectionObj, method_correctionSatCarrierFreq);
+        jboolean satIsLos = env->CallBooleanMethod(singleSatCorrectionObj,
+            method_correctionSatIsLos);
+        jfloat eplMeters =
+            env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEpl);
+        jfloat eplUncMeters = env->CallFloatMethod(singleSatCorrectionObj,
+            method_correctionSatEplUnc);
+        jobject reflectingPlaneObj = env->CallObjectMethod(
+            singleSatCorrectionObj, method_correctionSatRefPlane);
+
+        if (firstGnssMeasurementCorrectionInjected == false) {
+            jclass refPlaneClass = env->GetObjectClass(reflectingPlaneObj);
+            method_correctionPlaneLatDeg = env->GetMethodID(
+                refPlaneClass, "getLatitudeDegrees", "()D");
+            method_correctionPlaneLngDeg = env->GetMethodID(
+                refPlaneClass, "getLongitudeDegrees", "()D");
+            method_correctionPlaneAltDeg = env->GetMethodID(
+                refPlaneClass, "getAltitudeMeters", "()D");
+            method_correctionPlaneAzimDeg = env->GetMethodID(
+                refPlaneClass, "getAzimuthDegrees", "()D");
+        }
+
+        jdouble latitudeDegreesRefPlane = env->CallDoubleMethod(
+            reflectingPlaneObj, method_correctionPlaneLatDeg);
+        jdouble longitudeDegreesRefPlane = env->CallDoubleMethod(
+            reflectingPlaneObj, method_correctionPlaneLngDeg);
+        jdouble altitudeDegreesRefPlane = env->CallDoubleMethod(
+            reflectingPlaneObj, method_correctionPlaneAltDeg);
+        jdouble azimuthDegreeRefPlane = env->CallDoubleMethod(
+            reflectingPlaneObj, method_correctionPlaneAzimDeg);
+        ReflectingPlane reflectingPlane = {
+            .latitudeDegrees = latitudeDegreesRefPlane,
+            .longitudeDegrees = longitudeDegreesRefPlane,
+            .altitudeMeters = altitudeDegreesRefPlane,
+            .azimuthDegrees = azimuthDegreeRefPlane,
+        };
+
+        SingleSatCorrection singleSatCorrection = {
+            .singleSatCorrectionFlags = static_cast<uint16_t>(correctionFlags),
+            .constellation = static_cast<GnssConstellationType>(constType),
+            .svid = static_cast<uint16_t>(satId),
+            .carrierFrequencyHz = carrierFreqHz,
+            .satIsLos = static_cast<bool>(satIsLos),
+            .excessPathLengthMeters = eplMeters,
+            .excessPathLengthUncertaintyMeters = eplUncMeters,
+            .reflectingPlane = reflectingPlane,
+        };
+        list[i] = singleSatCorrection;
+    }
+    MeasurementCorrections measurementCorrections = {
+        .latitudeDegrees = latitudeDegreesCorr,
+        .longitudeDegrees = longitudeDegreesCorr,
+        .altitudeMeters = altitudeDegreesCorr,
+        .toaGpsNanosecondsOfWeek = static_cast<uint64_t>(toaGpsNanosOfWeek),
+        .satCorrections = list,
+    };
+
+    gnssCorrectionsIface->setCorrections(measurementCorrections);
+    firstGnssMeasurementCorrectionInjected = true;
+    return JNI_TRUE;
+}
+
 static jboolean android_location_GnssNavigationMessageProvider_is_navigation_message_supported(
         JNIEnv* env,
         jclass clazz) {
@@ -2098,7 +2431,6 @@
     }
 }
 
-
 static jint android_location_GnssBatchingProvider_get_batch_size(JNIEnv*, jclass) {
     if (gnssBatchingIface == nullptr) {
         return 0; // batching not supported, size = 0
@@ -2281,19 +2613,20 @@
 };
 
 static const JNINativeMethod sMeasurementMethods[] = {
-     /* name, signature, funcPtr */
-    {"native_is_measurement_supported",
-            "()Z",
-            reinterpret_cast<void *>(
-                    android_location_GnssMeasurementsProvider_is_measurement_supported)},
-    {"native_start_measurement_collection",
-             "(Z)Z",
-            reinterpret_cast<void *>(
-                    android_location_GnssMeasurementsProvider_start_measurement_collection)},
-    {"native_stop_measurement_collection",
-            "()Z",
-            reinterpret_cast<void *>(
-                    android_location_GnssMeasurementsProvider_stop_measurement_collection)},
+    /* name, signature, funcPtr */
+    {"native_is_measurement_supported", "()Z",
+     reinterpret_cast<void*>(
+         android_location_GnssMeasurementsProvider_is_measurement_supported)},
+    {"native_start_measurement_collection", "(Z)Z",
+     reinterpret_cast<void*>(
+         android_location_GnssMeasurementsProvider_start_measurement_collection)},
+    {"native_stop_measurement_collection", "()Z",
+     reinterpret_cast<void*>(
+         android_location_GnssMeasurementsProvider_stop_measurement_collection)},
+    {"native_inject_gnss_measurement_corrections",
+     "(Landroid/location/GnssMeasurementCorrections;)Z",
+     reinterpret_cast<void*>(
+         android_location_GnssMeasurementsProvider_inject_gnss_measurement_corrections)},
 };
 
 static const JNINativeMethod sNavigationMessageMethods[] = {
@@ -2317,10 +2650,10 @@
     {"native_is_agps_ril_supported", "()Z",
             reinterpret_cast<void *>(android_location_GnssNetworkConnectivityHandler_is_agps_ril_supported)},
     {"native_update_network_state",
-            "(ZIZZLjava/lang/String;Ljava/lang/String;)V",
+            "(ZIZZLjava/lang/String;JS)V",
             reinterpret_cast<void *>(android_location_GnssNetworkConnectivityHandler_update_network_state)},
     {"native_agps_data_conn_open",
-            "(Ljava/lang/String;I)V",
+            "(JLjava/lang/String;I)V",
             reinterpret_cast<void *>(android_location_GnssNetworkConnectivityHandler_agps_data_conn_open)},
     {"native_agps_data_conn_closed",
             "()V",
diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp
index ec94e3c9..3c87e42 100644
--- a/services/core/jni/com_android_server_security_VerityUtils.cpp
+++ b/services/core/jni/com_android_server_security_VerityUtils.cpp
@@ -20,13 +20,22 @@
 #include "jni.h"
 #include <utils/Log.h>
 
+#include <errno.h>
+#include <fcntl.h>
 #include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <android-base/unique_fd.h>
 
 // TODO(112037636): Always include once fsverity.h is upstreamed and backported.
 #define HAS_FSVERITY 0
 
 #if HAS_FSVERITY
 #include <linux/fsverity.h>
+
+const int kSha256Bytes = 32;
 #endif
 
 namespace android {
@@ -66,9 +75,26 @@
     jbyte* mElements;
 };
 
+int measureFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) {
+#if HAS_FSVERITY
+    auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_digest) + kSha256Bytes);
+    fsverity_digest* data = reinterpret_cast<fsverity_digest*>(raii->getRaw());
+    data->digest_size = kSha256Bytes;  // the only input/output parameter
+
+    const char* path = env->GetStringUTFChars(filePath, nullptr);
+    ::android::base::unique_fd rfd(open(path, O_RDONLY));
+    if (ioctl(rfd.get(), FS_IOC_MEASURE_VERITY, data) < 0) {
+      return errno;
+    }
+    return 0;
+#else
+    LOG_ALWAYS_FATAL("fs-verity is used while not enabled");
+    return -1;
+#endif  // HAS_FSVERITY
+}
+
 jbyteArray constructFsveritySignedData(JNIEnv* env, jobject /* clazz */, jbyteArray digest) {
 #if HAS_FSVERITY
-    const int kSha256Bytes = 32;
     auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_digest_disk) + kSha256Bytes);
     fsverity_digest_disk* data = reinterpret_cast<fsverity_digest_disk*>(raii->getRaw());
 
@@ -146,6 +172,7 @@
 }
 
 const JNINativeMethod sMethods[] = {
+    { "measureFsverityNative", "(Ljava/lang/String;)I", (void *)measureFsverity },
     { "constructFsveritySignedDataNative", "([B)[B", (void *)constructFsveritySignedData },
     { "constructFsverityDescriptorNative", "(J)[B", (void *)constructFsverityDescriptor },
     { "constructFsverityExtensionNative", "(SI)[B", (void *)constructFsverityExtension },
diff --git a/services/core/jni/com_android_server_storage_AppFuseBridge.cpp b/services/core/jni/com_android_server_storage_AppFuseBridge.cpp
index c8f842d..e519633 100644
--- a/services/core/jni/com_android_server_storage_AppFuseBridge.cpp
+++ b/services/core/jni/com_android_server_storage_AppFuseBridge.cpp
@@ -74,7 +74,7 @@
         }
     }
 
-    operator bool() {
+    explicit operator bool() {
         return mLocked;
     }
 
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 6c2a894..098b2ef 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -292,7 +292,7 @@
 
     class TvInputCallback : public ITvInputCallback {
     public:
-        TvInputCallback(JTvInputHal* hal);
+        explicit TvInputCallback(JTvInputHal* hal);
         Return<void> notify(const TvInputEvent& event) override;
     private:
         JTvInputHal* mHal;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
index 05912a5..de5dd17 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
@@ -66,6 +66,9 @@
         map.put(
                 DOWNLOAD_STATE_INITIALIZATION_ERROR,
                 InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION);
+        map.put(
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_TIMESTAMP_ERROR,
+                InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION);
 
         // Error constants corresponding to errors related to bad update file.
         map.put(
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7186cdf..7f6895a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -164,7 +164,6 @@
 import android.net.ProxyInfo;
 import android.net.Uri;
 import android.net.metrics.IpConnectivityLog;
-import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.os.Binder;
 import android.os.Build;
@@ -478,7 +477,8 @@
 
     /**
      * Strings logged with {@link
-     * com.android.internal.logging.nano.MetricsProto.MetricsEvent#PROVISIONING_ENTRY_POINT_ADB}.
+     * com.android.internal.logging.nano.MetricsProto.MetricsEvent#PROVISIONING_ENTRY_POINT_ADB}
+     * and {@link DevicePolicyEnums#PROVISIONING_ENTRY_POINT_ADB}.
      */
     private static final String LOG_TAG_PROFILE_OWNER = "profile-owner";
     private static final String LOG_TAG_DEVICE_OWNER = "device-owner";
@@ -2105,6 +2105,7 @@
             return Settings.Global.getInt(mContext.getContentResolver(), name, def);
         }
 
+        @Nullable
         String settingsGlobalGetString(String name) {
             return Settings.Global.getString(mContext.getContentResolver(), name);
         }
@@ -4328,6 +4329,11 @@
             }
         }
 
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.ADD_CROSS_PROFILE_WIDGET_PROVIDER)
+                .setAdmin(admin)
+                .write();
+
         if (changedProviders != null) {
             mLocalService.notifyCrossProfileProvidersChanged(userId, changedProviders);
             return true;
@@ -4355,6 +4361,11 @@
             }
         }
 
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.REMOVE_CROSS_PROFILE_WIDGET_PROVIDER)
+                .setAdmin(admin)
+                .write();
+
         if (changedProviders != null) {
             mLocalService.notifyCrossProfileProvidersChanged(userId, changedProviders);
             return true;
@@ -4979,26 +4990,22 @@
     private boolean resetPasswordInternal(String password, long tokenHandle, byte[] token,
             int flags, int callingUid, int userHandle) {
         int quality;
-        final int realQuality;
         synchronized (getLockObject()) {
             quality = getPasswordQuality(null, userHandle, /* parent */ false);
             if (quality == DevicePolicyManager.PASSWORD_QUALITY_MANAGED) {
                 quality = PASSWORD_QUALITY_UNSPECIFIED;
             }
             final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password);
-            realQuality = metrics.quality;
-            if (quality != PASSWORD_QUALITY_UNSPECIFIED) {
-
-                if (realQuality < quality
-                        && quality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
-                    Slog.w(LOG_TAG, "resetPassword: password quality 0x"
-                            + Integer.toHexString(realQuality)
-                            + " does not meet required quality 0x"
-                            + Integer.toHexString(quality));
-                    return false;
-                }
-                quality = Math.max(realQuality, quality);
+            final int realQuality = metrics.quality;
+            if (realQuality < quality
+                    && quality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
+                Slog.w(LOG_TAG, "resetPassword: password quality 0x"
+                        + Integer.toHexString(realQuality)
+                        + " does not meet required quality 0x"
+                        + Integer.toHexString(quality));
+                return false;
             }
+            quality = Math.max(realQuality, quality);
             int length = getPasswordMinimumLength(null, userHandle, /* parent */ false);
             if (password.length() < length) {
                 Slog.w(LOG_TAG, "resetPassword: password length " + password.length()
@@ -5075,7 +5082,7 @@
         try {
             if (token == null) {
                 if (!TextUtils.isEmpty(password)) {
-                    mLockPatternUtils.saveLockPassword(password, null, realQuality, userHandle);
+                    mLockPatternUtils.saveLockPassword(password, null, quality, userHandle);
                 } else {
                     mLockPatternUtils.clearLock(null, userHandle);
                 }
@@ -5084,7 +5091,7 @@
                 result = mLockPatternUtils.setLockCredentialWithToken(password,
                         TextUtils.isEmpty(password) ? LockPatternUtils.CREDENTIAL_TYPE_NONE
                                 : LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
-                        realQuality, tokenHandle, token, userHandle);
+                        quality, tokenHandle, token, userHandle);
             }
             boolean requireEntry = (flags & DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0;
             if (requireEntry) {
@@ -5491,6 +5498,12 @@
         final long id = mInjector.binderClearCallingIdentity();
         try {
             mCertificateMonitor.uninstallCaCerts(UserHandle.of(userId), aliases);
+            final boolean isDelegate = (admin == null);
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.UNINSTALL_CA_CERTS)
+                    .setAdmin(callerPackage)
+                    .setBoolean(isDelegate)
+                    .write();
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
         }
@@ -5556,7 +5569,14 @@
             final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
             try {
                 IKeyChainService keyChain = keyChainConnection.getService();
-                return keyChain.removeKeyPair(alias);
+                final boolean result = keyChain.removeKeyPair(alias);
+                final boolean isDelegate = (who == null);
+                DevicePolicyEventLogger
+                        .createEvent(DevicePolicyEnums.REMOVE_KEY_PAIR)
+                        .setAdmin(callerPackage)
+                        .setBoolean(isDelegate)
+                        .write();
+                return result;
             } catch (RemoteException e) {
                 Log.e(LOG_TAG, "Removing keypair", e);
             } finally {
@@ -5740,6 +5760,14 @@
                         return false;
                     }
                 }
+                final boolean isDelegate = (who == null);
+                DevicePolicyEventLogger
+                        .createEvent(DevicePolicyEnums.GENERATE_KEY_PAIR)
+                        .setAdmin(callerPackage)
+                        .setBoolean(isDelegate)
+                        .setInt(idAttestationFlags)
+                        .setStrings(algorithm)
+                        .write();
                 return true;
             }
         } catch (RemoteException e) {
@@ -5768,6 +5796,12 @@
                 return false;
             }
             keyChain.setUserSelectable(alias, isUserSelectable);
+            final boolean isDelegate = (who == null);
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.SET_KEY_PAIR_CERTIFICATE)
+                    .setAdmin(callerPackage)
+                    .setBoolean(isDelegate)
+                    .write();
             return true;
         } catch (InterruptedException e) {
             Log.w(LOG_TAG, "Interrupted while setting keypair certificate", e);
@@ -5830,6 +5864,12 @@
                     sendPrivateKeyAliasResponse(chosenAlias, response);
                 }
             }, null, Activity.RESULT_OK, null, null);
+            final String adminPackageName =
+                    (aliasChooser != null ? aliasChooser.getPackageName() : null);
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.CHOOSE_PRIVATE_KEY_ALIAS)
+                    .setAdmin(adminPackageName)
+                    .write();
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
         }
@@ -5925,9 +5965,9 @@
 
             // If set, remove exclusive scopes from all other delegates
             if (exclusiveScopes != null && !exclusiveScopes.isEmpty()) {
-                for (Map.Entry<String, List<String>> entry : policy.mDelegationMap.entrySet()) {
-                    final String currentPackage = entry.getKey();
-                    final List<String> currentScopes = entry.getValue();
+                for (int i = policy.mDelegationMap.size() - 1; i >= 0; --i) {
+                    final String currentPackage = policy.mDelegationMap.keyAt(i);
+                    final List<String> currentScopes = policy.mDelegationMap.valueAt(i);
 
                     if (!currentPackage.equals(delegatePackage)) {
                         // Iterate through all other delegates
@@ -5935,7 +5975,7 @@
                             // And if this delegate had some exclusive scopes which are now moved
                             // to the new delegate, notify about its delegation changes.
                             if (currentScopes.isEmpty()) {
-                                policy.mDelegationMap.remove(currentPackage);
+                                policy.mDelegationMap.removeAt(i);
                             }
                             sendDelegationChangedBroadcast(currentPackage,
                                     new ArrayList<>(currentScopes), userId);
@@ -6219,6 +6259,11 @@
     public void setCertInstallerPackage(ComponentName who, String installerPackage)
             throws SecurityException {
         setDelegatedScopePreO(who, installerPackage, DELEGATION_CERT_INSTALL);
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_CERT_INSTALLER_PACKAGE)
+                .setAdmin(who)
+                .setStrings(installerPackage)
+                .write();
     }
 
     @Override
@@ -6250,6 +6295,13 @@
             if (!connectivityManager.setAlwaysOnVpnPackageForUser(userId, vpnPackage, lockdown)) {
                 throw new UnsupportedOperationException();
             }
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.SET_ALWAYS_ON_VPN_PACKAGE)
+                    .setAdmin(admin)
+                    .setStrings(vpnPackage)
+                    .setBoolean(lockdown)
+                    .setInt(/* number of vpn packages */ 0)
+                    .write();
         } finally {
             mInjector.binderRestoreCallingIdentity(token);
         }
@@ -6965,6 +7017,11 @@
                 updateScreenCaptureDisabled(userHandle, disabled);
             }
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_SCREEN_CAPTURE_DISABLED)
+                .setAdmin(who)
+                .setBoolean(disabled)
+                .write();
     }
 
     /**
@@ -7036,6 +7093,11 @@
                 mInjector.binderRestoreCallingIdentity(ident);
             }
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_AUTO_TIME_REQUIRED)
+                .setAdmin(who)
+                .setBoolean(required)
+                .write();
     }
 
     /**
@@ -7167,6 +7229,10 @@
                             DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED), UserHandle.ALL);
             mHandler.postDelayed(mRemoteBugreportTimeoutRunnable,
                     RemoteBugreportUtils.REMOTE_BUGREPORT_TIMEOUT_MILLIS);
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.REQUEST_BUGREPORT)
+                    .setAdmin(who)
+                    .write();
             return true;
         } catch (RemoteException re) {
             // should never happen
@@ -7377,6 +7443,11 @@
         }
         // Tell the user manager that the restrictions have changed.
         pushUserRestrictions(userHandle);
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_CAMERA_DISABLED)
+                .setAdmin(who)
+                .setBoolean(disabled)
+                .write();
     }
 
     /**
@@ -7528,6 +7599,13 @@
             // Notify package manager.
             mInjector.getPackageManagerInternal().setKeepUninstalledPackages(packageList);
         }
+        final boolean isDelegate = (who == null);
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_KEEP_UNINSTALLED_PACKAGES)
+                .setAdmin(callerPackage)
+                .setBoolean(isDelegate)
+                .setStrings(packageList.toArray(new String[0]))
+                .write();
     }
 
     @Override
@@ -7585,6 +7663,11 @@
             if (isAdb()) {
                 // Log device owner provisioning was started using adb.
                 MetricsLogger.action(mContext, PROVISIONING_ENTRY_POINT_ADB, LOG_TAG_DEVICE_OWNER);
+                DevicePolicyEventLogger
+                        .createEvent(DevicePolicyEnums.PROVISIONING_ENTRY_POINT_ADB)
+                        .setAdmin(admin)
+                        .setStrings(LOG_TAG_DEVICE_OWNER)
+                        .write();
             }
 
             mOwners.setDeviceOwner(admin, ownerName, userId);
@@ -7854,6 +7937,11 @@
             if (isAdb()) {
                 // Log profile owner provisioning was started using adb.
                 MetricsLogger.action(mContext, PROVISIONING_ENTRY_POINT_ADB, LOG_TAG_PROFILE_OWNER);
+                DevicePolicyEventLogger
+                        .createEvent(DevicePolicyEnums.PROVISIONING_ENTRY_POINT_ADB)
+                        .setAdmin(who)
+                        .setStrings(LOG_TAG_PROFILE_OWNER)
+                        .write();
             }
 
             mOwners.setProfileOwner(who, ownerName, userHandle);
@@ -7941,6 +8029,10 @@
                 mInjector.binderRestoreCallingIdentity(token);
             }
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_DEVICE_OWNER_LOCK_SCREEN_INFO)
+                .setAdmin(who)
+                .write();
     }
 
     @Override
@@ -8127,6 +8219,10 @@
         final long id = mInjector.binderClearCallingIdentity();
         try {
             mUserManager.setUserName(userId, profileName);
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.SET_PROFILE_NAME)
+                    .setAdmin(who)
+                    .write();
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
         }
@@ -8601,6 +8697,13 @@
                 mInjector.binderRestoreCallingIdentity(id);
             }
         }
+        final String activityPackage =
+                (activity != null ? activity.getPackageName() : null);
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.ADD_PERSISTENT_PREFERRED_ACTIVITY)
+                .setAdmin(who)
+                .setStrings(activityPackage, getIntentFilterActions(filter))
+                .write();
     }
 
     @Override
@@ -8664,6 +8767,13 @@
         final long id = mInjector.binderClearCallingIdentity();
         try {
             mUserManager.setApplicationRestrictions(packageName, settings, userHandle);
+            final boolean isDelegate = (who == null);
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.SET_APPLICATION_RESTRICTIONS)
+                    .setAdmin(callerPackage)
+                    .setBoolean(isDelegate)
+                    .setStrings(packageName)
+                    .write();
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
         }
@@ -8795,6 +8905,24 @@
                 mInjector.binderRestoreCallingIdentity(id);
             }
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.ADD_CROSS_PROFILE_INTENT_FILTER)
+                .setAdmin(who)
+                .setStrings(getIntentFilterActions(filter))
+                .setInt(flags)
+                .write();
+    }
+
+    private static String[] getIntentFilterActions(IntentFilter filter) {
+        if (filter == null) {
+            return null;
+        }
+        final int actionsCount = filter.countActions();
+        final String[] actions = new String[actionsCount];
+        for (int i = 0; i < actionsCount; i++) {
+            actions[i] = filter.getAction(i);
+        }
+        return actions;
     }
 
     @Override
@@ -8914,6 +9042,13 @@
             admin.permittedAccessiblityServices = packageList;
             saveSettingsLocked(UserHandle.getCallingUserId());
         }
+        final String[] packageArray =
+                packageList != null ? ((List<String>) packageList).toArray(new String[0]) : null;
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_PERMITTED_ACCESSIBILITY_SERVICES)
+                .setAdmin(who)
+                .setStrings(packageArray)
+                .write();
         return true;
     }
 
@@ -9088,6 +9223,13 @@
             admin.permittedInputMethods = packageList;
             saveSettingsLocked(callingUserId);
         }
+        final String[] packageArray =
+                packageList != null ? ((List<String>) packageList).toArray(new String[0]) : null;
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_PERMITTED_INPUT_METHODS)
+                .setAdmin(who)
+                .setStrings(packageArray)
+                .write();
         return true;
     }
 
@@ -9147,19 +9289,14 @@
                 InputMethodManager inputMethodManager =
                         mContext.getSystemService(InputMethodManager.class);
                 List<InputMethodInfo> imes = inputMethodManager.getInputMethodList();
-                long id = mInjector.binderClearCallingIdentity();
-                try {
-                    if (imes != null) {
-                        for (InputMethodInfo ime : imes) {
-                            ServiceInfo serviceInfo = ime.getServiceInfo();
-                            ApplicationInfo applicationInfo = serviceInfo.applicationInfo;
-                            if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-                                result.add(serviceInfo.packageName);
-                            }
+                if (imes != null) {
+                    for (InputMethodInfo ime : imes) {
+                        ServiceInfo serviceInfo = ime.getServiceInfo();
+                        ApplicationInfo applicationInfo = serviceInfo.applicationInfo;
+                        if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                            result.add(serviceInfo.packageName);
                         }
                     }
-                } finally {
-                    mInjector.binderRestoreCallingIdentity(id);
                 }
             }
             return result;
@@ -9628,6 +9765,7 @@
     public String[] setPackagesSuspended(ComponentName who, String callerPackage,
             String[] packageNames, boolean suspended) {
         int callingUserId = UserHandle.getCallingUserId();
+        String[] result = null;
         synchronized (getLockObject()) {
             // Ensure the caller is a DO/PO or a package access delegate.
             enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
@@ -9635,7 +9773,8 @@
 
             long id = mInjector.binderClearCallingIdentity();
             try {
-                return mIPackageManager.setPackagesSuspendedAsUser(packageNames, suspended,
+                result = mIPackageManager
+                        .setPackagesSuspendedAsUser(packageNames, suspended,
                         null, null, null, PLATFORM_PACKAGE_NAME, callingUserId);
             } catch (RemoteException re) {
                 // Shouldn't happen.
@@ -9643,8 +9782,18 @@
             } finally {
                 mInjector.binderRestoreCallingIdentity(id);
             }
-            return packageNames;
         }
+        final boolean isDelegate = (who == null);
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_PACKAGES_SUSPENDED)
+                .setAdmin(callerPackage)
+                .setBoolean(isDelegate)
+                .setStrings(packageNames)
+                .write();
+        if (result != null) {
+            return result;
+        }
+        return packageNames;
     }
 
     @Override
@@ -9781,6 +9930,7 @@
     public boolean setApplicationHidden(ComponentName who, String callerPackage, String packageName,
             boolean hidden) {
         int callingUserId = UserHandle.getCallingUserId();
+        boolean result = false;
         synchronized (getLockObject()) {
             // Ensure the caller is a DO/PO or a package access delegate.
             enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
@@ -9788,16 +9938,23 @@
 
             long id = mInjector.binderClearCallingIdentity();
             try {
-                return mIPackageManager.setApplicationHiddenSettingAsUser(
-                        packageName, hidden, callingUserId);
+                result = mIPackageManager
+                        .setApplicationHiddenSettingAsUser(packageName, hidden, callingUserId);
             } catch (RemoteException re) {
                 // shouldn't happen
                 Slog.e(LOG_TAG, "Failed to setApplicationHiddenSetting", re);
             } finally {
                 mInjector.binderRestoreCallingIdentity(id);
             }
-            return false;
         }
+        final boolean isDelegate = (who == null);
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_APPLICATION_HIDDEN)
+                .setAdmin(callerPackage)
+                .setBoolean(isDelegate)
+                .setStrings(packageName, hidden ? "hidden" : "not_hidden")
+                .write();
+        return result;
     }
 
     @Override
@@ -9862,10 +10019,18 @@
                 mInjector.binderRestoreCallingIdentity(id);
             }
         }
+        final boolean isDelegate = (who == null);
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.ENABLE_SYSTEM_APP)
+                .setAdmin(callerPackage)
+                .setBoolean(isDelegate)
+                .setStrings(packageName)
+                .write();
     }
 
     @Override
     public int enableSystemAppWithIntent(ComponentName who, String callerPackage, Intent intent) {
+        int numberOfAppsInstalled = 0;
         synchronized (getLockObject()) {
             // Ensure the caller is a DO/PO or an enable system app delegate.
             enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
@@ -9887,7 +10052,6 @@
                 if (VERBOSE_LOG) {
                     Slog.d(LOG_TAG, "Enabling system activities: " + activitiesToEnable);
                 }
-                int numberOfAppsInstalled = 0;
                 if (activitiesToEnable != null) {
                     for (ResolveInfo info : activitiesToEnable) {
                         if (info.activityInfo != null) {
@@ -9903,7 +10067,6 @@
                         }
                     }
                 }
-                return numberOfAppsInstalled;
             } catch (RemoteException e) {
                 // shouldn't happen
                 Slog.wtf(LOG_TAG, "Failed to resolve intent for: " + intent);
@@ -9912,6 +10075,14 @@
                 mInjector.binderRestoreCallingIdentity(id);
             }
         }
+        final boolean isDelegate = (who == null);
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.ENABLE_SYSTEM_APP_WITH_INTENT)
+                .setAdmin(callerPackage)
+                .setBoolean(isDelegate)
+                .setStrings(intent.getAction())
+                .write();
+        return numberOfAppsInstalled;
     }
 
     private boolean isSystemApp(IPackageManager pm, String packageName, int userId)
@@ -9928,6 +10099,7 @@
     @Override
     public boolean installExistingPackage(ComponentName who, String callerPackage,
             String packageName) {
+        boolean result;
         synchronized (getLockObject()) {
             // Ensure the caller is a PO or an install existing package delegate
             enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
@@ -9946,7 +10118,8 @@
                 }
 
                 // Install the package.
-                return mIPackageManager.installExistingPackageAsUser(packageName, callingUserId,
+                result = mIPackageManager
+                        .installExistingPackageAsUser(packageName, callingUserId,
                         0 /*installFlags*/, PackageManager.INSTALL_REASON_POLICY)
                         == PackageManager.INSTALL_SUCCEEDED;
             } catch (RemoteException re) {
@@ -9956,6 +10129,16 @@
                 mInjector.binderRestoreCallingIdentity(id);
             }
         }
+        if (result) {
+            final boolean isDelegate = (who == null);
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.INSTALL_EXISTING_PACKAGE)
+                    .setAdmin(callerPackage)
+                    .setBoolean(isDelegate)
+                    .setStrings(packageName)
+                    .write();
+        }
+        return result;
     }
 
     @Override
@@ -10019,6 +10202,13 @@
                 mInjector.binderRestoreCallingIdentity(id);
             }
         }
+        final boolean isDelegate = (who == null);
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_UNINSTALL_BLOCKED)
+                .setAdmin(callerPackage)
+                .setBoolean(isDelegate)
+                .setStrings(packageName)
+                .write();
     }
 
     @Override
@@ -10060,6 +10250,11 @@
                 saveSettingsLocked(mInjector.userHandleGetCallingUserId());
             }
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_CROSS_PROFILE_CALLER_ID_DISABLED)
+                .setAdmin(who)
+                .setBoolean(disabled)
+                .write();
     }
 
     @Override
@@ -10098,6 +10293,11 @@
                 saveSettingsLocked(mInjector.userHandleGetCallingUserId());
             }
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_CROSS_PROFILE_CONTACTS_SEARCH_DISABLED)
+                .setAdmin(who)
+                .setBoolean(disabled)
+                .write();
     }
 
     @Override
@@ -10197,6 +10397,11 @@
                 saveSettingsLocked(UserHandle.getCallingUserId());
             }
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_BLUETOOTH_CONTACT_SHARING_DISABLED)
+                .setAdmin(who)
+                .setBoolean(disabled)
+                .write();
     }
 
     @Override
@@ -10356,6 +10561,12 @@
                     } else {
                         sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_LOCK_TASK_EXITING);
                     }
+                    DevicePolicyEventLogger
+                            .createEvent(DevicePolicyEnums.SET_LOCKTASK_MODE_ENABLED)
+                            .setAdmin(admin.info.getPackageName())
+                            .setBoolean(isEnabled)
+                            .setStrings(pkg)
+                            .write();
                 }
             }
         }
@@ -10524,6 +10735,11 @@
         synchronized (getLockObject()) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             setUserRestriction(who, UserManager.DISALLOW_UNMUTE_DEVICE, on);
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.SET_MASTER_VOLUME_MUTED)
+                    .setAdmin(who)
+                    .setBoolean(on)
+                    .write();
         }
     }
 
@@ -10553,6 +10769,10 @@
                 mInjector.binderRestoreCallingIdentity(id);
             }
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_USER_ICON)
+                .setAdmin(who)
+                .write();
     }
 
     @Override
@@ -10578,6 +10798,11 @@
             }
             mLockPatternUtils.setLockScreenDisabled(disabled, userId);
             mInjector.getIWindowManager().dismissKeyguard(null /* callback */, null /* message */);
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.SET_KEYGUARD_DISABLED)
+                    .setAdmin(who)
+                    .setBoolean(disabled)
+                    .write();
         } catch (RemoteException e) {
             // Same process, does not happen.
         } finally {
@@ -10616,6 +10841,11 @@
                 saveSettingsLocked(userId);
             }
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_STATUS_BAR_DISABLED)
+                .setAdmin(who)
+                .setBoolean(disabled)
+                .write();
         return true;
     }
 
@@ -11036,6 +11266,11 @@
         mContext.sendBroadcastAsUser(
                 new Intent(DevicePolicyManager.ACTION_SYSTEM_UPDATE_POLICY_CHANGED),
                 UserHandle.SYSTEM);
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_SYSTEM_UPDATE_POLICY)
+                .setAdmin(who)
+                .setInt(policy != null ? policy.getPolicyType() : 0)
+                .write();
     }
 
     @Override
@@ -11584,11 +11819,15 @@
 
         final long ident = mInjector.binderClearCallingIdentity();
         try {
-            final WifiInfo wifiInfo = mInjector.getWifiManager().getConnectionInfo();
-            if (wifiInfo == null) {
+            String[] macAddresses = mInjector.getWifiManager().getFactoryMacAddresses();
+            if (macAddresses == null) {
                 return null;
             }
-            return wifiInfo.hasRealMacAddress() ? wifiInfo.getMacAddress() : null;
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.GET_WIFI_MAC_ADDRESS)
+                    .setAdmin(admin)
+                    .write();
+            return macAddresses.length > 0 ? macAddresses[0] : null;
         } finally {
             mInjector.binderRestoreCallingIdentity(ident);
         }
@@ -11634,6 +11873,10 @@
             if (mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
                 throw new IllegalStateException("Cannot be called with ongoing call on the device");
             }
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.REBOOT)
+                    .setAdmin(admin)
+                    .write();
             mInjector.powerManagerReboot(PowerManager.REBOOT_REQUESTED_BY_DEVICE_OWNER);
         } finally {
             mInjector.binderRestoreCallingIdentity(ident);
@@ -11655,6 +11898,10 @@
                 saveSettingsLocked(userHandle);
             }
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_SHORT_SUPPORT_MESSAGE)
+                .setAdmin(who)
+                .write();
     }
 
     @Override
@@ -11685,6 +11932,10 @@
                 saveSettingsLocked(userHandle);
             }
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_LONG_SUPPORT_MESSAGE)
+                .setAdmin(who)
+                .write();
     }
 
     @Override
@@ -11750,6 +12001,10 @@
             admin.organizationColor = color;
             saveSettingsLocked(userHandle);
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_ORGANIZATION_COLOR)
+                .setAdmin(who)
+                .write();
     }
 
     @Override
@@ -13153,6 +13408,7 @@
         }
 
         final long id = mInjector.binderClearCallingIdentity();
+        String ownerType = null;
         try {
             synchronized (getLockObject()) {
                 /*
@@ -13173,6 +13429,7 @@
                     bundle = new PersistableBundle();
                 }
                 if (isProfileOwner(admin, callingUserId)) {
+                    ownerType = ADMIN_TYPE_PROFILE_OWNER;
                     prepareTransfer(admin, target, bundle, callingUserId,
                             ADMIN_TYPE_PROFILE_OWNER);
                     transferProfileOwnershipLocked(admin, target, callingUserId);
@@ -13183,6 +13440,7 @@
                         notifyAffiliatedProfileTransferOwnershipComplete(callingUserId);
                     }
                 } else if (isDeviceOwner(admin, callingUserId)) {
+                    ownerType = ADMIN_TYPE_DEVICE_OWNER;
                     prepareTransfer(admin, target, bundle, callingUserId,
                             ADMIN_TYPE_DEVICE_OWNER);
                     transferDeviceOwnershipLocked(admin, target, callingUserId);
@@ -13194,6 +13452,11 @@
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.TRANSFER_OWNERSHIP)
+                .setAdmin(admin)
+                .setStrings(target.getPackageName(), ownerType)
+                .write();
     }
 
     private void prepareTransfer(ComponentName admin, ComponentName target,
@@ -13630,7 +13893,11 @@
 
         Preconditions.checkNotNull(who, "ComponentName is null");
         enforceDeviceOwner(who);
-        switch (mInjector.settingsGlobalGetString(PRIVATE_DNS_MODE)) {
+        String currentMode = mInjector.settingsGlobalGetString(PRIVATE_DNS_MODE);
+        if (currentMode == null) {
+            currentMode = ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
+        }
+        switch (currentMode) {
             case ConnectivityManager.PRIVATE_DNS_MODE_OFF:
                 return PRIVATE_DNS_MODE_OFF;
             case ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC:
@@ -13669,6 +13936,11 @@
                         mContext, updateFileDescriptor, callback, mInjector, mConstants);
             }
             updateInstaller.startInstallUpdate();
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.INSTALL_SYSTEM_UPDATE)
+                    .setAdmin(admin)
+                    .setBoolean(isDeviceAB())
+                    .write();
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
         }
@@ -13694,6 +13966,11 @@
                 saveSettingsLocked(mInjector.userHandleGetCallingUserId());
             }
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.ADD_CROSS_PROFILE_CALENDAR_PACKAGE)
+                .setAdmin(who)
+                .setStrings(packageName)
+                .write();
     }
 
     @Override
@@ -13713,6 +13990,13 @@
                 saveSettingsLocked(mInjector.userHandleGetCallingUserId());
             }
         }
+        if (isRemoved) {
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.REMOVE_CROSS_PROFILE_CALENDAR_PACKAGE)
+                    .setAdmin(who)
+                    .setStrings(packageName)
+                    .write();
+        }
         return isRemoved;
     }
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e1b83fc..4326c39 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -63,6 +63,7 @@
 import android.util.Slog;
 import android.util.TimingsTraceLog;
 import android.view.WindowManager;
+import android.view.inputmethod.InputMethodSystemProperty;
 
 import com.android.internal.R;
 import com.android.internal.logging.MetricsLogger;
@@ -122,6 +123,7 @@
 import com.android.server.power.ThermalManagerService;
 import com.android.server.restrictions.RestrictionsManagerService;
 import com.android.server.role.RoleManagerService;
+import com.android.server.rollback.RollbackManagerService;
 import com.android.server.security.KeyAttestationApplicationIdProviderService;
 import com.android.server.security.KeyChainSystemService;
 import com.android.server.signedconfig.SignedConfigService;
@@ -143,6 +145,7 @@
 import com.android.server.wm.ActivityTaskManagerService;
 import com.android.server.wm.WindowManagerGlobalLock;
 import com.android.server.wm.WindowManagerService;
+import com.google.android.startop.iorap.IorapForwardingService;
 
 import dalvik.system.VMRuntime;
 
@@ -724,6 +727,10 @@
         mSystemServiceManager.startService(new OverlayManagerService(mSystemContext, installer));
         traceEnd();
 
+        traceBeginAndSlog("StartSensorPrivacyService");
+        mSystemServiceManager.startService(new SensorPrivacyService(mSystemContext));
+        traceEnd();
+
         // The sensor service needs access to package manager service, app ops
         // service, and permissions service, therefore we start it after them.
         // Start sensor service in a separate thread. Completion should be checked
@@ -774,6 +781,11 @@
         traceBeginAndSlog("StartLooperStatsService");
         mSystemServiceManager.startService(LooperStatsService.Lifecycle.class);
         traceEnd();
+
+        // Manages apk rollbacks.
+        traceBeginAndSlog("StartRollbackManagerService");
+        mSystemServiceManager.startService(RollbackManagerService.class);
+        traceEnd();
     }
 
     /**
@@ -996,10 +1008,13 @@
             mSystemServiceManager.startService(PinnerService.class);
             traceEnd();
 
+            traceBeginAndSlog("IorapForwardingService");
+            mSystemServiceManager.startService(IorapForwardingService.class);
+            traceEnd();
+
             traceBeginAndSlog("SignedConfigService");
             SignedConfigService.registerUpdateReceiver(mSystemContext);
             traceEnd();
-
         } catch (RuntimeException e) {
             Slog.e("System", "******************************************");
             Slog.e("System", "************ Failure starting core service", e);
@@ -1015,7 +1030,7 @@
         // Bring up services needed for UI.
         if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
             traceBeginAndSlog("StartInputMethodManagerLifecycle");
-            if (MultiClientInputMethodManagerService.isConfiguredToUse()) {
+            if (InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED) {
                 mSystemServiceManager.startService(
                         MultiClientInputMethodManagerService.Lifecycle.class);
             } else {
@@ -1259,6 +1274,16 @@
             }
             traceEnd();
 
+            traceBeginAndSlog("StartNetworkStack");
+            try {
+                final android.net.NetworkStack networkStack =
+                        context.getSystemService(android.net.NetworkStack.class);
+                networkStack.start(context);
+            } catch (Throwable e) {
+                reportWtf("starting Network Stack", e);
+            }
+            traceEnd();
+
             traceBeginAndSlog("StartNsdService");
             try {
                 serviceDiscovery = NsdService.create(context);
@@ -2112,30 +2137,33 @@
 
     private void startContentCaptureService(@NonNull Context context) {
 
-        // First check if it was explicitly enabled by Settings
-        boolean explicitlySupported = false;
+        // Check if it was explicitly enabled by Settings
         final String settings = Settings.Global.getString(context.getContentResolver(),
                 Settings.Global.CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED);
-        if (settings != null) {
-            explicitlySupported = Boolean.parseBoolean(settings);
-            if (explicitlySupported) {
+        if (settings == null) {
+            // Better be safe than sorry...
+            Slog.d(TAG, "ContentCaptureService disabled because its not set by OEM");
+            return;
+        }
+        switch (settings) {
+            case "always":
+                // Should be used only during development
                 Slog.d(TAG, "ContentCaptureService explicitly enabled by Settings");
-            } else {
-                Slog.d(TAG, "ContentCaptureService explicitly disabled by Settings");
+                break;
+            case "default":
+                // Default case: check if OEM overlaid the resource that defines the service.
+                final String serviceName = context.getString(
+                        com.android.internal.R.string.config_defaultContentCaptureService);
+                if (TextUtils.isEmpty(serviceName)) {
+                    Slog.d(TAG, "ContentCaptureService disabled because resource is not overlaid");
+                    return;
+                }
+                break;
+            default:
+                // Kill switch for OEMs
+                Slog.d(TAG, "ContentCaptureService disabled because its set to: " + settings);
                 return;
-            }
         }
-
-        // Then check if OEM overlaid the resource that defines the service.
-        if (!explicitlySupported) {
-            final String serviceName = context
-                    .getString(com.android.internal.R.string.config_defaultContentCaptureService);
-            if (TextUtils.isEmpty(serviceName)) {
-                Slog.d(TAG, "ContentCaptureService disabled because resource is not overlaid");
-                return;
-            }
-        }
-
         traceBeginAndSlog("StartContentCaptureService");
         mSystemServiceManager.startService(CONTENT_CAPTURE_MANAGER_SERVICE_CLASS);
         traceEnd();
diff --git a/services/net/java/android/net/dhcp/DhcpLease.java b/services/net/java/android/net/dhcp/DhcpLease.java
index 6cdd2aa..6849cfa 100644
--- a/services/net/java/android/net/dhcp/DhcpLease.java
+++ b/services/net/java/android/net/dhcp/DhcpLease.java
@@ -58,6 +58,11 @@
         mHostname = hostname;
     }
 
+    /**
+     * Get the clientId associated with this lease, if any.
+     *
+     * <p>If the lease is not associated to a clientId, this returns null.
+     */
     @Nullable
     public byte[] getClientId() {
         if (mClientId == null) {
@@ -97,6 +102,11 @@
                 (hostname == null ? mHostname : hostname));
     }
 
+    /**
+     * Determine whether this lease matches a client with the specified parameters.
+     * @param clientId clientId of the client if any, or null otherwise.
+     * @param hwAddr Hardware address of the client.
+     */
     public boolean matchesClient(@Nullable byte[] clientId, @NonNull MacAddress hwAddr) {
         if (mClientId != null) {
             return Arrays.equals(mClientId, clientId);
@@ -110,7 +120,7 @@
         if (!(obj instanceof DhcpLease)) {
             return false;
         }
-        final DhcpLease other = (DhcpLease)obj;
+        final DhcpLease other = (DhcpLease) obj;
         return Arrays.equals(mClientId, other.mClientId)
                 && mHwAddr.equals(other.mHwAddr)
                 && mNetAddr.equals(other.mNetAddr)
diff --git a/services/net/java/android/net/dhcp/DhcpLeaseRepository.java b/services/net/java/android/net/dhcp/DhcpLeaseRepository.java
index 2dda421..b3d0512 100644
--- a/services/net/java/android/net/dhcp/DhcpLeaseRepository.java
+++ b/services/net/java/android/net/dhcp/DhcpLeaseRepository.java
@@ -29,8 +29,8 @@
 import android.annotation.Nullable;
 import android.net.IpPrefix;
 import android.net.MacAddress;
-import android.net.util.SharedLog;
 import android.net.dhcp.DhcpServer.Clock;
+import android.net.util.SharedLog;
 import android.util.ArrayMap;
 
 import java.net.Inet4Address;
@@ -117,7 +117,7 @@
      */
     private final LinkedHashMap<Inet4Address, Long> mDeclinedAddrs = new LinkedHashMap<>();
 
-    public DhcpLeaseRepository(@NonNull IpPrefix prefix, @NonNull Set<Inet4Address> reservedAddrs,
+    DhcpLeaseRepository(@NonNull IpPrefix prefix, @NonNull Set<Inet4Address> reservedAddrs,
             long leaseTimeMs, @NonNull SharedLog log, @NonNull Clock clock) {
         updateParams(prefix, reservedAddrs, leaseTimeMs);
         mLog = log;
@@ -250,8 +250,8 @@
                 // reqAddr null (RENEWING/REBINDING): client renewing its own lease for clientAddr.
                 // reqAddr set with sid not set (INIT-REBOOT): client verifying configuration.
                 // In both cases, throw if clientAddr or reqAddr does not match the known lease.
-                throw new InvalidAddressException("Incorrect address for client in " +
-                        (reqAddr != null ? "INIT-REBOOT" : "RENEWING/REBINDING"));
+                throw new InvalidAddressException("Incorrect address for client in "
+                        + (reqAddr != null ? "INIT-REBOOT" : "RENEWING/REBINDING"));
             }
         }
 
diff --git a/services/net/java/android/net/dhcp/DhcpPacketListener.java b/services/net/java/android/net/dhcp/DhcpPacketListener.java
index 6f620c5..dce8b61 100644
--- a/services/net/java/android/net/dhcp/DhcpPacketListener.java
+++ b/services/net/java/android/net/dhcp/DhcpPacketListener.java
@@ -32,32 +32,32 @@
  */
 abstract class DhcpPacketListener extends FdEventsReader<DhcpPacketListener.Payload> {
     static final class Payload {
-        final byte[] bytes = new byte[DhcpPacket.MAX_LENGTH];
-        Inet4Address srcAddr;
-        int srcPort;
+        protected final byte[] mBytes = new byte[DhcpPacket.MAX_LENGTH];
+        protected Inet4Address mSrcAddr;
+        protected int mSrcPort;
     }
 
-    public DhcpPacketListener(@NonNull Handler handler) {
+    DhcpPacketListener(@NonNull Handler handler) {
         super(handler, new Payload());
     }
 
     @Override
     protected int recvBufSize(@NonNull Payload buffer) {
-        return buffer.bytes.length;
+        return buffer.mBytes.length;
     }
 
     @Override
     protected final void handlePacket(@NonNull Payload recvbuf, int length) {
-        if (recvbuf.srcAddr == null) {
+        if (recvbuf.mSrcAddr == null) {
             return;
         }
 
         try {
-            final DhcpPacket packet = DhcpPacket.decodeFullPacket(recvbuf.bytes, length,
+            final DhcpPacket packet = DhcpPacket.decodeFullPacket(recvbuf.mBytes, length,
                     DhcpPacket.ENCAP_BOOTP);
-            onReceive(packet, recvbuf.srcAddr, recvbuf.srcPort);
+            onReceive(packet, recvbuf.mSrcAddr, recvbuf.mSrcPort);
         } catch (DhcpPacket.ParseException e) {
-            logParseError(recvbuf.bytes, length, e);
+            logParseError(recvbuf.mBytes, length, e);
         }
     }
 
@@ -66,11 +66,11 @@
             throws Exception {
         final InetSocketAddress addr = new InetSocketAddress();
         final int read = Os.recvfrom(
-                fd, packetBuffer.bytes, 0, packetBuffer.bytes.length, 0 /* flags */, addr);
+                fd, packetBuffer.mBytes, 0, packetBuffer.mBytes.length, 0 /* flags */, addr);
 
         // Buffers with null srcAddr will be dropped in handlePacket()
-        packetBuffer.srcAddr = inet4AddrOrNull(addr);
-        packetBuffer.srcPort = addr.getPort();
+        packetBuffer.mSrcAddr = inet4AddrOrNull(addr);
+        packetBuffer.mSrcPort = addr.getPort();
         return read;
     }
 
diff --git a/services/net/java/android/net/dhcp/DhcpServer.java b/services/net/java/android/net/dhcp/DhcpServer.java
index cee6fa9..641bba2 100644
--- a/services/net/java/android/net/dhcp/DhcpServer.java
+++ b/services/net/java/android/net/dhcp/DhcpServer.java
@@ -39,7 +39,6 @@
 import android.net.MacAddress;
 import android.net.NetworkUtils;
 import android.net.TrafficStats;
-import android.net.util.InterfaceParams;
 import android.net.util.SharedLog;
 import android.os.Handler;
 import android.os.Looper;
@@ -85,7 +84,7 @@
     @NonNull
     private final ServerHandler mHandler;
     @NonNull
-    private final InterfaceParams mIface;
+    private final String mIfName;
     @NonNull
     private final DhcpLeaseRepository mLeaseRepo;
     @NonNull
@@ -102,6 +101,13 @@
     @NonNull
     private DhcpServingParams mServingParams;
 
+    /**
+     * Clock to be used by DhcpServer to track time for lease expiration.
+     *
+     * <p>The clock should track time as may be measured by clients obtaining a lease. It does not
+     * need to be monotonous across restarts of the server as long as leases are cleared when the
+     * server is stopped.
+     */
     public static class Clock {
         /**
          * @see SystemClock#elapsedRealtime()
@@ -111,13 +117,43 @@
         }
     }
 
+    /**
+     * Dependencies for the DhcpServer. Useful to be mocked in tests.
+     */
     public interface Dependencies {
+        /**
+         * Send a packet to the specified datagram socket.
+         *
+         * @param fd File descriptor of the socket.
+         * @param buffer Data to be sent.
+         * @param dst Destination address of the packet.
+         */
         void sendPacket(@NonNull FileDescriptor fd, @NonNull ByteBuffer buffer,
                 @NonNull InetAddress dst) throws ErrnoException, IOException;
+
+        /**
+         * Create a DhcpLeaseRepository for the server.
+         * @param servingParams Parameters used to serve DHCP requests.
+         * @param log Log to be used by the repository.
+         * @param clock Clock that the repository must use to track time.
+         */
         DhcpLeaseRepository makeLeaseRepository(@NonNull DhcpServingParams servingParams,
                 @NonNull SharedLog log, @NonNull Clock clock);
+
+        /**
+         * Create a packet listener that will send packets to be processed.
+         */
         DhcpPacketListener makePacketListener();
+
+        /**
+         * Create a clock that the server will use to track time.
+         */
         Clock makeClock();
+
+        /**
+         * Add an entry to the ARP cache table.
+         * @param fd Datagram socket file descriptor that must use the new entry.
+         */
         void addArpEntry(@NonNull Inet4Address ipv4Addr, @NonNull MacAddress ethAddr,
                 @NonNull String ifname, @NonNull FileDescriptor fd) throws IOException;
     }
@@ -135,7 +171,7 @@
             return new DhcpLeaseRepository(
                     DhcpServingParams.makeIpPrefix(servingParams.serverAddr),
                     servingParams.excludedAddrs,
-                    servingParams.dhcpLeaseTimeSecs*1000, log.forSubComponent(REPO_TAG), clock);
+                    servingParams.dhcpLeaseTimeSecs * 1000, log.forSubComponent(REPO_TAG), clock);
         }
 
         @Override
@@ -161,20 +197,20 @@
         }
     }
 
-    public DhcpServer(@NonNull Looper looper, @NonNull InterfaceParams iface,
+    public DhcpServer(@NonNull Looper looper, @NonNull String ifName,
             @NonNull DhcpServingParams params, @NonNull SharedLog log) {
-        this(looper, iface, params, log, null);
+        this(looper, ifName, params, log, null);
     }
 
     @VisibleForTesting
-    DhcpServer(@NonNull Looper looper, @NonNull InterfaceParams iface,
+    DhcpServer(@NonNull Looper looper, @NonNull String ifName,
             @NonNull DhcpServingParams params, @NonNull SharedLog log,
             @Nullable Dependencies deps) {
         if (deps == null) {
             deps = new DependenciesImpl();
         }
         mHandler = new ServerHandler(looper);
-        mIface = iface;
+        mIfName = ifName;
         mServingParams = params;
         mLog = log;
         mDeps = deps;
@@ -213,7 +249,7 @@
     }
 
     private class ServerHandler extends Handler {
-        public ServerHandler(@NonNull Looper looper) {
+        ServerHandler(@NonNull Looper looper) {
             super(looper);
         }
 
@@ -444,7 +480,7 @@
 
     private boolean addArpEntry(@NonNull MacAddress macAddr, @NonNull Inet4Address inetAddr) {
         try {
-            mDeps.addArpEntry(inetAddr, macAddr, mIface.name, mSocket);
+            mDeps.addArpEntry(inetAddr, macAddr, mIfName, mSocket);
             return true;
         } catch (IOException e) {
             mLog.e("Error adding client to ARP table", e);
@@ -497,22 +533,24 @@
     }
 
     private class PacketListener extends DhcpPacketListener {
-        public PacketListener() {
+        PacketListener() {
             super(mHandler);
         }
 
         @Override
-        protected void onReceive(DhcpPacket packet, Inet4Address srcAddr, int srcPort) {
+        protected void onReceive(@NonNull DhcpPacket packet, @NonNull Inet4Address srcAddr,
+                int srcPort) {
             processPacket(packet, srcPort);
         }
 
         @Override
-        protected void logError(String msg, Exception e) {
+        protected void logError(@NonNull String msg, Exception e) {
             mLog.e("Error receiving packet: " + msg, e);
         }
 
         @Override
-        protected void logParseError(byte[] packet, int length, DhcpPacket.ParseException e) {
+        protected void logParseError(@NonNull byte[] packet, int length,
+                @NonNull DhcpPacket.ParseException e) {
             mLog.e("Error parsing packet", e);
         }
 
@@ -526,7 +564,7 @@
                 // SO_BINDTODEVICE actually takes a string. This works because the first member
                 // of struct ifreq is a NULL-terminated interface name.
                 // TODO: add a setsockoptString()
-                Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mIface.name);
+                Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mIfName);
                 Os.setsockoptInt(mSocket, SOL_SOCKET, SO_BROADCAST, 1);
                 Os.bind(mSocket, Inet4Address.ANY, DHCP_SERVER);
                 NetworkUtils.protectFromVpn(mSocket);
diff --git a/services/net/java/android/net/dhcp/DhcpServingParams.java b/services/net/java/android/net/dhcp/DhcpServingParams.java
index df15ba1..2780814a 100644
--- a/services/net/java/android/net/dhcp/DhcpServingParams.java
+++ b/services/net/java/android/net/dhcp/DhcpServingParams.java
@@ -17,6 +17,7 @@
 package android.net.dhcp;
 
 import static android.net.NetworkUtils.getPrefixMaskAsInet4Address;
+import static android.net.NetworkUtils.intToInet4AddressHTH;
 import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
 import static android.net.util.NetworkConstants.IPV4_MAX_MTU;
 import static android.net.util.NetworkConstants.IPV4_MIN_MTU;
@@ -24,6 +25,7 @@
 import static java.lang.Integer.toUnsignedLong;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.NetworkUtils;
@@ -103,6 +105,37 @@
         this.metered = metered;
     }
 
+    /**
+     * Create parameters from a stable AIDL-compatible parcel.
+     */
+    public static DhcpServingParams fromParcelableObject(@NonNull DhcpServingParamsParcel parcel)
+            throws InvalidParameterException {
+        final LinkAddress serverAddr = new LinkAddress(
+                intToInet4AddressHTH(parcel.serverAddr),
+                parcel.serverAddrPrefixLength);
+        return new Builder()
+                .setServerAddr(serverAddr)
+                .setDefaultRouters(toInet4AddressSet(parcel.defaultRouters))
+                .setDnsServers(toInet4AddressSet(parcel.dnsServers))
+                .setExcludedAddrs(toInet4AddressSet(parcel.excludedAddrs))
+                .setDhcpLeaseTimeSecs(parcel.dhcpLeaseTimeSecs)
+                .setLinkMtu(parcel.linkMtu)
+                .setMetered(parcel.metered)
+                .build();
+    }
+
+    private static Set<Inet4Address> toInet4AddressSet(@Nullable int[] addrs) {
+        if (addrs == null) {
+            return new HashSet<>(0);
+        }
+
+        final HashSet<Inet4Address> res = new HashSet<>();
+        for (int addr : addrs) {
+            res.add(intToInet4AddressHTH(addr));
+        }
+        return res;
+    }
+
     @NonNull
     public Inet4Address getServerInet4Addr() {
         return (Inet4Address) serverAddr.getAddress();
@@ -134,13 +167,13 @@
      * of the parameters.
      */
     public static class Builder {
-        private LinkAddress serverAddr;
-        private Set<Inet4Address> defaultRouters;
-        private Set<Inet4Address> dnsServers;
-        private Set<Inet4Address> excludedAddrs;
-        private long dhcpLeaseTimeSecs;
-        private int linkMtu = MTU_UNSET;
-        private boolean metered;
+        private LinkAddress mServerAddr;
+        private Set<Inet4Address> mDefaultRouters;
+        private Set<Inet4Address> mDnsServers;
+        private Set<Inet4Address> mExcludedAddrs;
+        private long mDhcpLeaseTimeSecs;
+        private int mLinkMtu = MTU_UNSET;
+        private boolean mMetered;
 
         /**
          * Set the server address and served prefix for the DHCP server.
@@ -148,7 +181,7 @@
          * <p>This parameter is required.
          */
         public Builder setServerAddr(@NonNull LinkAddress serverAddr) {
-            this.serverAddr = serverAddr;
+            this.mServerAddr = serverAddr;
             return this;
         }
 
@@ -159,7 +192,7 @@
          * always be set explicitly before building the {@link DhcpServingParams}.
          */
         public Builder setDefaultRouters(@NonNull Set<Inet4Address> defaultRouters) {
-            this.defaultRouters = defaultRouters;
+            this.mDefaultRouters = defaultRouters;
             return this;
         }
 
@@ -189,7 +222,7 @@
          * {@link DhcpServingParams}.
          */
         public Builder setDnsServers(@NonNull Set<Inet4Address> dnsServers) {
-            this.dnsServers = dnsServers;
+            this.mDnsServers = dnsServers;
             return this;
         }
 
@@ -219,7 +252,7 @@
          * and do not need to be set here.
          */
         public Builder setExcludedAddrs(@NonNull Set<Inet4Address> excludedAddrs) {
-            this.excludedAddrs = excludedAddrs;
+            this.mExcludedAddrs = excludedAddrs;
             return this;
         }
 
@@ -239,7 +272,7 @@
          * <p>This parameter is required.
          */
         public Builder setDhcpLeaseTimeSecs(long dhcpLeaseTimeSecs) {
-            this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs;
+            this.mDhcpLeaseTimeSecs = dhcpLeaseTimeSecs;
             return this;
         }
 
@@ -250,7 +283,7 @@
          * is optional and defaults to {@link #MTU_UNSET}.
          */
         public Builder setLinkMtu(int linkMtu) {
-            this.linkMtu = linkMtu;
+            this.mLinkMtu = linkMtu;
             return this;
         }
 
@@ -260,7 +293,7 @@
          * <p>If not set, the default value is false.
          */
         public Builder setMetered(boolean metered) {
-            this.metered = metered;
+            this.mMetered = metered;
             return this;
         }
 
@@ -274,54 +307,57 @@
          */
         @NonNull
         public DhcpServingParams build() throws InvalidParameterException {
-            if (serverAddr == null) {
+            if (mServerAddr == null) {
                 throw new InvalidParameterException("Missing serverAddr");
             }
-            if (defaultRouters == null) {
+            if (mDefaultRouters == null) {
                 throw new InvalidParameterException("Missing defaultRouters");
             }
-            if (dnsServers == null) {
+            if (mDnsServers == null) {
                 // Empty set is OK, but enforce explicitly setting it
                 throw new InvalidParameterException("Missing dnsServers");
             }
-            if (dhcpLeaseTimeSecs <= 0 || dhcpLeaseTimeSecs > toUnsignedLong(INFINITE_LEASE)) {
-                throw new InvalidParameterException("Invalid lease time: " + dhcpLeaseTimeSecs);
+            if (mDhcpLeaseTimeSecs <= 0 || mDhcpLeaseTimeSecs > toUnsignedLong(INFINITE_LEASE)) {
+                throw new InvalidParameterException("Invalid lease time: " + mDhcpLeaseTimeSecs);
             }
-            if (linkMtu != MTU_UNSET && (linkMtu < IPV4_MIN_MTU || linkMtu > IPV4_MAX_MTU)) {
-                throw new InvalidParameterException("Invalid link MTU: " + linkMtu);
+            if (mLinkMtu != MTU_UNSET && (mLinkMtu < IPV4_MIN_MTU || mLinkMtu > IPV4_MAX_MTU)) {
+                throw new InvalidParameterException("Invalid link MTU: " + mLinkMtu);
             }
-            if (!serverAddr.isIPv4()) {
+            if (!mServerAddr.isIPv4()) {
                 throw new InvalidParameterException("serverAddr must be IPv4");
             }
-            if (serverAddr.getPrefixLength() < MIN_PREFIX_LENGTH
-                    || serverAddr.getPrefixLength() > MAX_PREFIX_LENGTH) {
+            if (mServerAddr.getPrefixLength() < MIN_PREFIX_LENGTH
+                    || mServerAddr.getPrefixLength() > MAX_PREFIX_LENGTH) {
                 throw new InvalidParameterException("Prefix length is not in supported range");
             }
 
-            final IpPrefix prefix = makeIpPrefix(serverAddr);
-            for (Inet4Address addr : defaultRouters) {
+            final IpPrefix prefix = makeIpPrefix(mServerAddr);
+            for (Inet4Address addr : mDefaultRouters) {
                 if (!prefix.contains(addr)) {
                     throw new InvalidParameterException(String.format(
-                            "Default router %s is not in server prefix %s", addr, serverAddr));
+                            "Default router %s is not in server prefix %s", addr, mServerAddr));
                 }
             }
 
             final Set<Inet4Address> excl = new HashSet<>();
-            if (excludedAddrs != null) {
-                excl.addAll(excludedAddrs);
+            if (mExcludedAddrs != null) {
+                excl.addAll(mExcludedAddrs);
             }
-            excl.add((Inet4Address) serverAddr.getAddress());
-            excl.addAll(defaultRouters);
-            excl.addAll(dnsServers);
+            excl.add((Inet4Address) mServerAddr.getAddress());
+            excl.addAll(mDefaultRouters);
+            excl.addAll(mDnsServers);
 
-            return new DhcpServingParams(serverAddr,
-                    Collections.unmodifiableSet(new HashSet<>(defaultRouters)),
-                    Collections.unmodifiableSet(new HashSet<>(dnsServers)),
+            return new DhcpServingParams(mServerAddr,
+                    Collections.unmodifiableSet(new HashSet<>(mDefaultRouters)),
+                    Collections.unmodifiableSet(new HashSet<>(mDnsServers)),
                     Collections.unmodifiableSet(excl),
-                    dhcpLeaseTimeSecs, linkMtu, metered);
+                    mDhcpLeaseTimeSecs, mLinkMtu, mMetered);
         }
     }
 
+    /**
+     * Utility method to create an IpPrefix with the address and prefix length of a LinkAddress.
+     */
     @NonNull
     static IpPrefix makeIpPrefix(@NonNull LinkAddress addr) {
         return new IpPrefix(addr.getAddress(), addr.getPrefixLength());
diff --git a/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java b/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java
new file mode 100644
index 0000000..f068c3a
--- /dev/null
+++ b/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.dhcp;
+
+import static android.net.NetworkUtils.inet4AddressToIntHTH;
+
+import android.annotation.NonNull;
+import android.net.LinkAddress;
+
+import com.google.android.collect.Sets;
+
+import java.net.Inet4Address;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * Subclass of {@link DhcpServingParamsParcel} with additional utility methods for building.
+ *
+ * <p>This utility class does not check for validity of the parameters: invalid parameters are
+ * reported by the receiving module when unparceling the parcel.
+ *
+ * @see DhcpServingParams
+ * @hide
+ */
+public class DhcpServingParamsParcelExt extends DhcpServingParamsParcel {
+    public static final int MTU_UNSET = 0;
+
+    /**
+     * Set the server address and served prefix for the DHCP server.
+     *
+     * <p>This parameter is required.
+     */
+    public DhcpServingParamsParcelExt setServerAddr(@NonNull LinkAddress serverAddr) {
+        this.serverAddr = inet4AddressToIntHTH((Inet4Address) serverAddr.getAddress());
+        this.serverAddrPrefixLength = serverAddr.getPrefixLength();
+        return this;
+    }
+
+    /**
+     * Set the default routers to be advertised to DHCP clients.
+     *
+     * <p>Each router must be inside the served prefix. This may be an empty set, but it must
+     * always be set explicitly.
+     */
+    public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Set<Inet4Address> defaultRouters) {
+        this.defaultRouters = toIntArray(defaultRouters);
+        return this;
+    }
+
+    /**
+     * Set the default routers to be advertised to DHCP clients.
+     *
+     * <p>Each router must be inside the served prefix. This may be an empty list of routers,
+     * but it must always be set explicitly.
+     */
+    public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Inet4Address... defaultRouters) {
+        return setDefaultRouters(Sets.newArraySet(defaultRouters));
+    }
+
+    /**
+     * Convenience method to build the parameters with no default router.
+     *
+     * <p>Equivalent to calling {@link #setDefaultRouters(Inet4Address...)} with no address.
+     */
+    public DhcpServingParamsParcelExt setNoDefaultRouter() {
+        return setDefaultRouters();
+    }
+
+    /**
+     * Set the DNS servers to be advertised to DHCP clients.
+     *
+     * <p>This may be an empty set, but it must always be set explicitly.
+     */
+    public DhcpServingParamsParcelExt setDnsServers(@NonNull Set<Inet4Address> dnsServers) {
+        this.dnsServers = toIntArray(dnsServers);
+        return this;
+    }
+
+    /**
+     * Set the DNS servers to be advertised to DHCP clients.
+     *
+     * <p>This may be an empty list of servers, but it must always be set explicitly.
+     */
+    public DhcpServingParamsParcelExt setDnsServers(@NonNull Inet4Address... dnsServers) {
+        return setDnsServers(Sets.newArraySet(dnsServers));
+    }
+
+    /**
+     * Convenience method to build the parameters with no DNS server.
+     *
+     * <p>Equivalent to calling {@link #setDnsServers(Inet4Address...)} with no address.
+     */
+    public DhcpServingParamsParcelExt setNoDnsServer() {
+        return setDnsServers();
+    }
+
+    /**
+     * Set excluded addresses that the DHCP server is not allowed to assign to clients.
+     *
+     * <p>This parameter is optional. DNS servers and default routers are always excluded
+     * and do not need to be set here.
+     */
+    public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Set<Inet4Address> excludedAddrs) {
+        this.excludedAddrs = toIntArray(excludedAddrs);
+        return this;
+    }
+
+    /**
+     * Set excluded addresses that the DHCP server is not allowed to assign to clients.
+     *
+     * <p>This parameter is optional. DNS servers and default routers are always excluded
+     * and do not need to be set here.
+     */
+    public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Inet4Address... excludedAddrs) {
+        return setExcludedAddrs(Sets.newArraySet(excludedAddrs));
+    }
+
+    /**
+     * Set the lease time for leases assigned by the DHCP server.
+     *
+     * <p>This parameter is required.
+     */
+    public DhcpServingParamsParcelExt setDhcpLeaseTimeSecs(long dhcpLeaseTimeSecs) {
+        this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs;
+        return this;
+    }
+
+    /**
+     * Set the link MTU to be advertised to DHCP clients.
+     *
+     * <p>If set to {@link #MTU_UNSET}, no MTU will be advertised to clients. This parameter
+     * is optional and defaults to {@link #MTU_UNSET}.
+     */
+    public DhcpServingParamsParcelExt setLinkMtu(int linkMtu) {
+        this.linkMtu = linkMtu;
+        return this;
+    }
+
+    /**
+     * Set whether the DHCP server should send the ANDROID_METERED vendor-specific option.
+     *
+     * <p>If not set, the default value is false.
+     */
+    public DhcpServingParamsParcelExt setMetered(boolean metered) {
+        this.metered = metered;
+        return this;
+    }
+
+    private static int[] toIntArray(@NonNull Collection<Inet4Address> addrs) {
+        int[] res = new int[addrs.size()];
+        int i = 0;
+        for (Inet4Address addr : addrs) {
+            res[i] = inet4AddressToIntHTH(addr);
+            i++;
+        }
+        return res;
+    }
+}
diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java
index 823c0a1..493350d 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -138,9 +138,9 @@
             return NetdService.getInstance();
         }
 
-        public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface,
+        public DhcpServer makeDhcpServer(Looper looper, String ifName,
                 DhcpServingParams params, SharedLog log) {
-            return new DhcpServer(looper, iface, params, log);
+            return new DhcpServer(looper, ifName, params, log);
         }
     }
 
@@ -256,12 +256,6 @@
         if (mUsingLegacyDhcp) {
             return true;
         }
-
-        final InterfaceParams ifaceParams = mDeps.getInterfaceParams(mIfaceName);
-        if (ifaceParams == null) {
-            Log.e(TAG, "Failed to find interface params for DHCPv4");
-            return false;
-        }
         final DhcpServingParams params;
         try {
             params = new DhcpServingParams.Builder()
@@ -277,7 +271,7 @@
             return false;
         }
 
-        mDhcpServer = mDeps.makeDhcpServer(getHandler().getLooper(), ifaceParams, params,
+        mDhcpServer = mDeps.makeDhcpServer(getHandler().getLooper(), mIfaceName, params,
                 mLog.forSubComponent("DHCP"));
         mDhcpServer.start();
         return true;
diff --git a/services/net/java/android/net/util/SharedLog.java b/services/net/java/android/net/util/SharedLog.java
index 5a73a4e..74bc147 100644
--- a/services/net/java/android/net/util/SharedLog.java
+++ b/services/net/java/android/net/util/SharedLog.java
@@ -35,8 +35,8 @@
  * @hide
  */
 public class SharedLog {
-    private final static int DEFAULT_MAX_RECORDS = 500;
-    private final static String COMPONENT_DELIMITER = ".";
+    private static final int DEFAULT_MAX_RECORDS = 500;
+    private static final String COMPONENT_DELIMITER = ".";
 
     private enum Category {
         NONE,
@@ -69,6 +69,9 @@
         mComponent = component;
     }
 
+    /**
+     * Create a SharedLog based on this log with an additional component prefix on each logged line.
+     */
     public SharedLog forSubComponent(String component) {
         if (!isRootLogInstance()) {
             component = mComponent + COMPONENT_DELIMITER + component;
@@ -76,6 +79,11 @@
         return new SharedLog(mLocalLog, mTag, component);
     }
 
+    /**
+     * Dump the contents of this log.
+     *
+     * <p>This method may be called on any thread.
+     */
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         mLocalLog.readOnlyLocalLog().dump(fd, writer, args);
     }
@@ -84,10 +92,21 @@
     // Methods that both log an entry and emit it to the system log.
     //////
 
+    /**
+     * Log an error due to an exception. This does not include the exception stacktrace.
+     *
+     * <p>The log entry will be also added to the system log.
+     * @see #e(String, Throwable)
+     */
     public void e(Exception e) {
         Log.e(mTag, record(Category.ERROR, e.toString()));
     }
 
+    /**
+     * Log an error message.
+     *
+     * <p>The log entry will be also added to the system log.
+     */
     public void e(String msg) {
         Log.e(mTag, record(Category.ERROR, msg));
     }
@@ -96,7 +115,7 @@
      * Log an error due to an exception, with the exception stacktrace if provided.
      *
      * <p>The error and exception message appear in the shared log, but the stacktrace is only
-     * logged in general log output (logcat).
+     * logged in general log output (logcat). The log entry will be also added to the system log.
      */
     public void e(@NonNull String msg, @Nullable Throwable exception) {
         if (exception == null) {
@@ -106,10 +125,20 @@
         Log.e(mTag, record(Category.ERROR, msg + ": " + exception.getMessage()), exception);
     }
 
+    /**
+     * Log an informational message.
+     *
+     * <p>The log entry will be also added to the system log.
+     */
     public void i(String msg) {
         Log.i(mTag, record(Category.NONE, msg));
     }
 
+    /**
+     * Log a warning message.
+     *
+     * <p>The log entry will be also added to the system log.
+     */
     public void w(String msg) {
         Log.w(mTag, record(Category.WARN, msg));
     }
@@ -118,14 +147,30 @@
     // Methods that only log an entry (and do NOT emit to the system log).
     //////
 
+    /**
+     * Log a general message to be only included in the in-memory log.
+     *
+     * <p>The log entry will *not* be added to the system log.
+     */
     public void log(String msg) {
         record(Category.NONE, msg);
     }
 
+    /**
+     * Log a general, formatted message to be only included in the in-memory log.
+     *
+     * <p>The log entry will *not* be added to the system log.
+     * @see String#format(String, Object...)
+     */
     public void logf(String fmt, Object... args) {
         log(String.format(fmt, args));
     }
 
+    /**
+     * Log a message with MARK level.
+     *
+     * <p>The log entry will *not* be added to the system log.
+     */
     public void mark(String msg) {
         record(Category.MARK, msg);
     }
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 6f10ed5..9159f0d 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -27,6 +27,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     bmgrlib \
+    bu \
     services.backup \
     services.core \
     services.net
@@ -40,7 +41,8 @@
 
 LOCAL_MODULE := FrameworksServicesRoboTests
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    $(call all-java-files-under, backup/src)
 
 LOCAL_RESOURCE_DIR := \
     $(LOCAL_PATH)/res
@@ -80,4 +82,13 @@
 
 LOCAL_TEST_PACKAGE := FrameworksServicesLib
 
-include external/robolectric-shadows/run_robotests.mk
\ No newline at end of file
+LOCAL_ROBOTEST_FILES := $(call find-files-in-subdirs,$(LOCAL_PATH)/src,*Test.java,.) \
+    $(call find-files-in-subdirs,$(LOCAL_PATH)/backup/src,*Test.java,.)
+
+include external/robolectric-shadows/run_robotests.mk
+
+###################################################################
+# include subdir Android.mk files
+###################################################################
+include $(CLEAR_VARS)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/services/robotests/backup/Android.mk b/services/robotests/backup/Android.mk
new file mode 100644
index 0000000..cc59b0c
--- /dev/null
+++ b/services/robotests/backup/Android.mk
@@ -0,0 +1,84 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+###################################################################
+# BackupFrameworksServicesLib app just for Robolectric test target      #
+###################################################################
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := BackupFrameworksServicesLib
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    bmgrlib \
+    bu \
+    services.backup \
+    services.core \
+    services.net
+
+include $(BUILD_PACKAGE)
+
+###################################################################
+# BackupFrameworksServicesLib Robolectric test target.                  #
+###################################################################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := BackupFrameworksServicesRoboTests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    $(call all-java-files-under, ../src/com/android/server/testing/shadows)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_JAVA_RESOURCE_DIRS := config
+
+# Include the testing libraries
+LOCAL_JAVA_LIBRARIES := \
+    platform-test-annotations \
+    robolectric_android-all-stub \
+    Robolectric_all-target \
+    mockito-robolectric-prebuilt \
+    truth-prebuilt \
+    testng
+
+LOCAL_INSTRUMENTATION_FOR := BackupFrameworksServicesLib
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+###################################################################
+# BackupFrameworksServicesLib runner target to run the previous target. #
+###################################################################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := RunBackupFrameworksServicesRoboTests
+
+LOCAL_JAVA_LIBRARIES := \
+    BackupFrameworksServicesRoboTests \
+    platform-test-annotations \
+    robolectric_android-all-stub \
+    Robolectric_all-target \
+    mockito-robolectric-prebuilt \
+    truth-prebuilt \
+    testng
+
+LOCAL_TEST_PACKAGE := BackupFrameworksServicesLib
+
+include external/robolectric-shadows/run_robotests.mk
diff --git a/services/robotests/backup/config/robolectric.properties b/services/robotests/backup/config/robolectric.properties
new file mode 100644
index 0000000..850557a
--- /dev/null
+++ b/services/robotests/backup/config/robolectric.properties
@@ -0,0 +1 @@
+sdk=NEWEST_SDK
\ No newline at end of file
diff --git a/services/robotests/src/android/app/backup/BackupUtilsTest.java b/services/robotests/backup/src/android/app/backup/BackupUtilsTest.java
similarity index 100%
rename from services/robotests/src/android/app/backup/BackupUtilsTest.java
rename to services/robotests/backup/src/android/app/backup/BackupUtilsTest.java
diff --git a/services/robotests/src/android/app/backup/ForwardingBackupAgent.java b/services/robotests/backup/src/android/app/backup/ForwardingBackupAgent.java
similarity index 100%
rename from services/robotests/src/android/app/backup/ForwardingBackupAgent.java
rename to services/robotests/backup/src/android/app/backup/ForwardingBackupAgent.java
diff --git a/services/robotests/src/com/android/commands/bmgr/BmgrTest.java b/services/robotests/backup/src/com/android/commands/bmgr/BmgrTest.java
similarity index 100%
rename from services/robotests/src/com/android/commands/bmgr/BmgrTest.java
rename to services/robotests/backup/src/com/android/commands/bmgr/BmgrTest.java
diff --git a/services/robotests/backup/src/com/android/commands/bu/AdbBackupTest.java b/services/robotests/backup/src/com/android/commands/bu/AdbBackupTest.java
new file mode 100644
index 0000000..6869f56
--- /dev/null
+++ b/services/robotests/backup/src/com/android/commands/bu/AdbBackupTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.commands.bu;
+
+import static org.mockito.Mockito.verify;
+
+import android.app.backup.IBackupManager;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowParcelFileDescriptor;
+
+/** Unit tests for {@link com.android.commands.bu.Backup}. */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowParcelFileDescriptor.class})
+@Presubmit
+public class AdbBackupTest {
+    @Mock private IBackupManager mBackupManager;
+    private Backup mBackup;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mBackup = new Backup(mBackupManager);
+    }
+
+    @Test
+    public void testRun_whenUserNotSpecified_callsAdbBackupAsSystemUser() throws Exception {
+        mBackup.run(new String[] {"backup", "-all"});
+
+        verify(mBackupManager).isBackupServiceActive(UserHandle.USER_SYSTEM);
+    }
+
+    @Test
+    public void testRun_whenUserSpecified_callsBackupManagerAsSpecifiedUser() throws Exception {
+        mBackup.run(new String[] {"backup", "-user", "10", "-all"});
+
+        verify(mBackupManager).isBackupServiceActive(10);
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java b/services/robotests/backup/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java
rename to services/robotests/backup/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerConstantsTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java
rename to services/robotests/backup/src/com/android/server/backup/BackupManagerConstantsTest.java
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
new file mode 100644
index 0000000..769a9d4
--- /dev/null
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -0,0 +1,1577 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+import static android.Manifest.permission.BACKUP;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startBackupThread;
+import static com.android.server.backup.testing.TransportData.backupTransport;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.robolectric.Shadows.shadowOf;
+import static org.testng.Assert.expectThrows;
+
+import android.annotation.UserIdInt;
+import android.app.Application;
+import android.app.backup.IBackupManagerMonitor;
+import android.app.backup.IBackupObserver;
+import android.app.backup.IFullBackupRestoreObserver;
+import android.app.backup.ISelectBackupTransportCallback;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import android.util.SparseArray;
+
+import com.android.server.backup.testing.TransportData;
+import com.android.server.testing.shadows.ShadowBinder;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowContextWrapper;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBinder.class})
+@Presubmit
+public class BackupManagerServiceTest {
+    private static final String TEST_PACKAGE = "package";
+    private static final String TEST_TRANSPORT = "transport";
+    private static final String[] ADB_TEST_PACKAGES = {TEST_PACKAGE};
+
+    private ShadowContextWrapper mShadowContext;
+    private Context mContext;
+    @UserIdInt private int mUserOneId;
+    @UserIdInt private int mUserTwoId;
+    @Mock private UserBackupManagerService mUserOneService;
+    @Mock private UserBackupManagerService mUserTwoService;
+
+    /** Initialize {@link BackupManagerService}. */
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        Application application = RuntimeEnvironment.application;
+        mContext = application;
+        mShadowContext = shadowOf(application);
+
+        mUserOneId = UserHandle.USER_SYSTEM + 1;
+        mUserTwoId = mUserOneId + 1;
+    }
+
+    /**
+     * Clean up and reset state that was created for testing {@link BackupManagerService}
+     * operations.
+     */
+    @After
+    public void tearDown() throws Exception {
+        ShadowBinder.reset();
+    }
+
+    /**
+     * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}. This is
+     * specifically to prevent overloading the logs in production.
+     */
+    @Test
+    public void testMoreDebug_isFalse() throws Exception {
+        boolean moreDebug = BackupManagerService.MORE_DEBUG;
+
+        assertThat(moreDebug).isFalse();
+    }
+
+    /** Test that the constructor does not create {@link UserBackupManagerService} instances. */
+    @Test
+    public void testConstructor_doesNotRegisterUsers() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        assertThat(backupManagerService.getServiceUsers().size()).isEqualTo(0);
+    }
+
+    /** Test that the constructor handles {@code null} parameters. */
+    @Test
+    public void testConstructor_withNullContext_throws() throws Exception {
+        expectThrows(
+                NullPointerException.class,
+                () ->
+                        new BackupManagerService(
+                                /* context */ null,
+                                new Trampoline(mContext),
+                                startBackupThread(null)));
+    }
+
+    /** Test that the constructor handles {@code null} parameters. */
+    @Test
+    public void testConstructor_withNullTrampoline_throws() throws Exception {
+        expectThrows(
+                NullPointerException.class,
+                () ->
+                        new BackupManagerService(
+                                mContext, /* trampoline */ null, startBackupThread(null)));
+    }
+
+    /** Test that the constructor handles {@code null} parameters. */
+    @Test
+    public void testConstructor_withNullBackupThread_throws() throws Exception {
+        expectThrows(
+                NullPointerException.class,
+                () ->
+                        new BackupManagerService(
+                                mContext, new Trampoline(mContext), /* backupThread */ null));
+    }
+
+    /** Test that the service registers users. */
+    @Test
+    public void testStartServiceForUser_registersUser() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.startServiceForUser(mUserOneId);
+
+        SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers();
+        assertThat(serviceUsers.size()).isEqualTo(1);
+        assertThat(serviceUsers.get(mUserOneId)).isNotNull();
+    }
+
+    /** Test that the service registers users. */
+    @Test
+    public void testStartServiceForUser_withServiceInstance_registersUser() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.startServiceForUser(mUserOneId, mUserOneService);
+
+        SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers();
+        assertThat(serviceUsers.size()).isEqualTo(1);
+        assertThat(serviceUsers.get(mUserOneId)).isEqualTo(mUserOneService);
+    }
+
+    /** Test that the service unregisters users when stopped. */
+    @Test
+    public void testStopServiceForUser_forRegisteredUser_unregistersCorrectUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+
+        backupManagerService.stopServiceForUser(mUserOneId);
+
+        SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers();
+        assertThat(serviceUsers.size()).isEqualTo(1);
+        assertThat(serviceUsers.get(mUserOneId)).isNull();
+        assertThat(serviceUsers.get(mUserTwoId)).isEqualTo(mUserTwoService);
+    }
+
+    /** Test that the service unregisters users when stopped. */
+    @Test
+    public void testStopServiceForUser_forRegisteredUser_tearsDownCorrectUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+
+        backupManagerService.stopServiceForUser(mUserOneId);
+
+        verify(mUserOneService).tearDownService();
+        verify(mUserTwoService, never()).tearDownService();
+    }
+
+    /** Test that the service unregisters users when stopped. */
+    @Test
+    public void testStopServiceForUser_forUnknownUser_doesNothing() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.stopServiceForUser(mUserOneId);
+
+        SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers();
+        assertThat(serviceUsers.size()).isEqualTo(0);
+    }
+
+    /**
+     * Test that the backup services throws a {@link SecurityException} if the caller does not have
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+     */
+    @Test
+    public void testGetServiceForUser_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        expectThrows(
+                SecurityException.class,
+                () ->
+                        backupManagerService.getServiceForUserIfCallerHasPermission(
+                                mUserOneId, "test"));
+    }
+
+    /**
+     * Test that the backup services does not throw a {@link SecurityException} if the caller has
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+     */
+    @Test
+    public void testGetServiceForUserIfCallerHasPermission_withPermission_worksForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ true);
+
+        assertEquals(
+                mUserOneService,
+                backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test"));
+    }
+
+    /**
+     * Test that the backup services does not throw a {@link SecurityException} if the caller does
+     * not have INTERACT_ACROSS_USERS_FULL permission and passes in the calling user id.
+     */
+    @Test
+    public void testGetServiceForUserIfCallerHasPermission_withoutPermission_worksForCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        assertEquals(
+                mUserOneService,
+                backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test"));
+    }
+
+    // ---------------------------------------------
+    // Backup agent tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testDataChanged_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.dataChanged(mUserOneId, TEST_PACKAGE);
+
+        verify(mUserOneService).dataChanged(TEST_PACKAGE);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testDataChanged_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.dataChanged(mUserTwoId, TEST_PACKAGE);
+
+        verify(mUserOneService, never()).dataChanged(TEST_PACKAGE);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testAgentConnected_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+        IBinder agentBinder = mock(IBinder.class);
+
+        backupManagerService.agentConnected(mUserOneId, TEST_PACKAGE, agentBinder);
+
+        verify(mUserOneService).agentConnected(TEST_PACKAGE, agentBinder);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testAgentConnected_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+        IBinder agentBinder = mock(IBinder.class);
+
+        backupManagerService.agentConnected(mUserTwoId, TEST_PACKAGE, agentBinder);
+
+        verify(mUserOneService, never()).agentConnected(TEST_PACKAGE, agentBinder);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testAgentDisconnected_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.agentDisconnected(mUserOneId, TEST_PACKAGE);
+
+        verify(mUserOneService).agentDisconnected(TEST_PACKAGE);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testAgentDisconnected_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.agentDisconnected(mUserTwoId, TEST_PACKAGE);
+
+        verify(mUserOneService, never()).agentDisconnected(TEST_PACKAGE);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testOpComplete_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.opComplete(mUserOneId, /* token */ 0, /* result */ 0L);
+
+        verify(mUserOneService).opComplete(/* token */ 0, /* result */ 0L);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testOpComplete_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.opComplete(mUserTwoId, /* token */ 0, /* result */ 0L);
+
+        verify(mUserOneService, never()).opComplete(/* token */ 0, /* result */ 0L);
+    }
+
+    // ---------------------------------------------
+    // Transport tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testInitializeTransports_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+        String[] transports = {TEST_TRANSPORT};
+
+        backupManagerService.initializeTransports(mUserOneId, transports, /* observer */ null);
+
+        verify(mUserOneService).initializeTransports(transports, /* observer */ null);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testInitializeTransports_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+        String[] transports = {TEST_TRANSPORT};
+
+        backupManagerService.initializeTransports(mUserTwoId, transports, /* observer */ null);
+
+        verify(mUserOneService, never()).initializeTransports(transports, /* observer */ null);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testClearBackupData_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.clearBackupData(mUserOneId, TEST_TRANSPORT, TEST_PACKAGE);
+
+        verify(mUserOneService).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testClearBackupData_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.clearBackupData(mUserTwoId, TEST_TRANSPORT, TEST_PACKAGE);
+
+        verify(mUserOneService, never()).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testGetCurrentTransport_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.getCurrentTransport(mUserOneId);
+
+        verify(mUserOneService).getCurrentTransport();
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testGetCurrentTransport_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.getCurrentTransport(mUserTwoId);
+
+        verify(mUserOneService, never()).getCurrentTransport();
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testGetCurrentTransportComponent_onRegisteredUser_callsMethodForUser()
+            throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.getCurrentTransportComponent(mUserOneId);
+
+        verify(mUserOneService).getCurrentTransportComponent();
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testGetCurrentTransportComponent_onUnknownUser_doesNotPropagateCall()
+            throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.getCurrentTransportComponent(mUserTwoId);
+
+        verify(mUserOneService, never()).getCurrentTransportComponent();
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testListAllTransports_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.listAllTransports(mUserOneId);
+
+        verify(mUserOneService).listAllTransports();
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testListAllTransports_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.listAllTransports(mUserTwoId);
+
+        verify(mUserOneService, never()).listAllTransports();
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testListAllTransportComponents_onRegisteredUser_callsMethodForUser()
+            throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.listAllTransportComponents(mUserOneId);
+
+        verify(mUserOneService).listAllTransportComponents();
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testListAllTransportComponents_onUnknownUser_doesNotPropagateCall()
+            throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.listAllTransportComponents(mUserTwoId);
+
+        verify(mUserOneService, never()).listAllTransportComponents();
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testUpdateTransportAttributes_onRegisteredUser_callsMethodForUser()
+            throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+        TransportData transport = backupTransport();
+        Intent configurationIntent = new Intent();
+        Intent dataManagementIntent = new Intent();
+
+        backupManagerService.updateTransportAttributes(
+                mUserOneId,
+                transport.getTransportComponent(),
+                transport.transportName,
+                configurationIntent,
+                "currentDestinationString",
+                dataManagementIntent,
+                "dataManagementLabel");
+
+        verify(mUserOneService)
+                .updateTransportAttributes(
+                        transport.getTransportComponent(),
+                        transport.transportName,
+                        configurationIntent,
+                        "currentDestinationString",
+                        dataManagementIntent,
+                        "dataManagementLabel");
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testUpdateTransportAttributes_onUnknownUser_doesNotPropagateCall()
+            throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+        TransportData transport = backupTransport();
+        Intent configurationIntent = new Intent();
+        Intent dataManagementIntent = new Intent();
+
+        backupManagerService.updateTransportAttributes(
+                mUserTwoId,
+                transport.getTransportComponent(),
+                transport.transportName,
+                configurationIntent,
+                "currentDestinationString",
+                dataManagementIntent,
+                "dataManagementLabel");
+
+        verify(mUserOneService, never())
+                .updateTransportAttributes(
+                        transport.getTransportComponent(),
+                        transport.transportName,
+                        configurationIntent,
+                        "currentDestinationString",
+                        dataManagementIntent,
+                        "dataManagementLabel");
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testSelectBackupTransport_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.selectBackupTransport(mUserOneId, TEST_TRANSPORT);
+
+        verify(mUserOneService).selectBackupTransport(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testSelectBackupTransport_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.selectBackupTransport(mUserTwoId, TEST_TRANSPORT);
+
+        verify(mUserOneService, never()).selectBackupTransport(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testSelectTransportAsync_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+        TransportData transport = backupTransport();
+        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+        backupManagerService.selectBackupTransportAsync(
+                mUserOneId, transport.getTransportComponent(), callback);
+
+        verify(mUserOneService)
+                .selectBackupTransportAsync(transport.getTransportComponent(), callback);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testSelectBackupTransportAsync_onUnknownUser_doesNotPropagateCall()
+            throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+        TransportData transport = backupTransport();
+        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+        backupManagerService.selectBackupTransportAsync(
+                mUserTwoId, transport.getTransportComponent(), callback);
+
+        verify(mUserOneService, never())
+                .selectBackupTransportAsync(transport.getTransportComponent(), callback);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testGetConfigurationIntent_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.getConfigurationIntent(mUserOneId, TEST_TRANSPORT);
+
+        verify(mUserOneService).getConfigurationIntent(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testGetConfigurationIntent_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.getConfigurationIntent(mUserTwoId, TEST_TRANSPORT);
+
+        verify(mUserOneService, never()).getConfigurationIntent(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testGetDestinationString_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.getDestinationString(mUserOneId, TEST_TRANSPORT);
+
+        verify(mUserOneService).getDestinationString(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testGetDestinationString_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.getDestinationString(mUserTwoId, TEST_TRANSPORT);
+
+        verify(mUserOneService, never()).getDestinationString(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testGetDataManagementIntent_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.getDataManagementIntent(mUserOneId, TEST_TRANSPORT);
+
+        verify(mUserOneService).getDataManagementIntent(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testGetDataManagementIntent_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.getDataManagementIntent(mUserTwoId, TEST_TRANSPORT);
+
+        verify(mUserOneService, never()).getDataManagementIntent(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testGetDataManagementLabel_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.getDataManagementLabel(mUserOneId, TEST_TRANSPORT);
+
+        verify(mUserOneService).getDataManagementLabel(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testGetDataManagementLabel_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.getDataManagementLabel(mUserTwoId, TEST_TRANSPORT);
+
+        verify(mUserOneService, never()).getDataManagementLabel(TEST_TRANSPORT);
+    }
+
+    // ---------------------------------------------
+    // Settings tests
+    // ---------------------------------------------
+    /**
+     * Test that the backup services throws a {@link SecurityException} if the caller does not have
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+     */
+    @Test
+    public void testSetBackupEnabled_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        expectThrows(
+                SecurityException.class,
+                () -> backupManagerService.setBackupEnabled(mUserTwoId, true));
+    }
+
+    /**
+     * Test that the backup service does not throw a {@link SecurityException} if the caller has
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+     */
+    @Test
+    public void testSetBackupEnabled_withPermission_propagatesForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+        backupManagerService.setBackupEnabled(mUserTwoId, true);
+
+        verify(mUserTwoService).setBackupEnabled(true);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testSetBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.setBackupEnabled(mUserOneId, true);
+
+        verify(mUserOneService).setBackupEnabled(true);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testSetBackupEnabled_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.setBackupEnabled(mUserTwoId, true);
+
+        verify(mUserOneService, never()).setBackupEnabled(true);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testSetAutoRestore_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.setAutoRestore(mUserOneId, true);
+
+        verify(mUserOneService).setAutoRestore(true);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testSetAutoRestore_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.setAutoRestore(mUserTwoId, true);
+
+        verify(mUserOneService, never()).setAutoRestore(true);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testIsBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.isBackupEnabled(mUserOneId);
+
+        verify(mUserOneService).isBackupEnabled();
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testIsBackupEnabled_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.isBackupEnabled(mUserTwoId);
+
+        verify(mUserOneService, never()).isBackupEnabled();
+    }
+
+    // ---------------------------------------------
+    // Backup tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testIsAppEligibleForBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.isAppEligibleForBackup(mUserOneId, TEST_PACKAGE);
+
+        verify(mUserOneService).isAppEligibleForBackup(TEST_PACKAGE);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testIsAppEligibleForBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.isAppEligibleForBackup(mUserTwoId, TEST_PACKAGE);
+
+        verify(mUserOneService, never()).isAppEligibleForBackup(TEST_PACKAGE);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testFilterAppsEligibleForBackup_onRegisteredUser_callsMethodForUser()
+            throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+        String[] packages = {TEST_PACKAGE};
+
+        backupManagerService.filterAppsEligibleForBackup(mUserOneId, packages);
+
+        verify(mUserOneService).filterAppsEligibleForBackup(packages);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testFilterAppsEligibleForBackup_onUnknownUser_doesNotPropagateCall()
+            throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+        String[] packages = {TEST_PACKAGE};
+
+        backupManagerService.filterAppsEligibleForBackup(mUserTwoId, packages);
+
+        verify(mUserOneService, never()).filterAppsEligibleForBackup(packages);
+    }
+
+    /**
+     * Test verifying that {@link BackupManagerService#backupNow(int)} throws a {@link
+     * SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+     */
+    @Test
+    public void testBackupNow_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        expectThrows(SecurityException.class, () -> backupManagerService.backupNow(mUserTwoId));
+    }
+
+    /**
+     * Test that the backup service does not throw a {@link SecurityException} if the caller has
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+     */
+    @Test
+    public void testBackupNow_withPermission_propagatesForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+        backupManagerService.backupNow(mUserTwoId);
+
+        verify(mUserTwoService).backupNow();
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testBackupNow_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.backupNow(mUserOneId);
+
+        verify(mUserOneService).backupNow();
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testBackupNow_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.backupNow(mUserTwoId);
+
+        verify(mUserOneService, never()).backupNow();
+    }
+
+    /**
+     * Test that the backup services throws a {@link SecurityException} if the caller does not have
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+     */
+    @Test
+    public void testRequestBackup_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+        String[] packages = {TEST_PACKAGE};
+        IBackupObserver observer = mock(IBackupObserver.class);
+        IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+
+        expectThrows(
+                SecurityException.class,
+                () ->
+                        backupManagerService.requestBackup(
+                                mUserTwoId, packages, observer, monitor, 0));
+    }
+
+    /**
+     * Test that the backup service does not throw a {@link SecurityException} if the caller has
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+     */
+    @Test
+    public void testRequestBackup_withPermission_propagatesForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+        String[] packages = {TEST_PACKAGE};
+        IBackupObserver observer = mock(IBackupObserver.class);
+        IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+        backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
+
+        verify(mUserTwoService).requestBackup(packages, observer, monitor, /* flags */ 0);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testRequestBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        String[] packages = {TEST_PACKAGE};
+        IBackupObserver observer = mock(IBackupObserver.class);
+        IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.requestBackup(mUserOneId, packages, observer, monitor, /* flags */ 0);
+
+        verify(mUserOneService).requestBackup(packages, observer, monitor, /* flags */ 0);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testRequestBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        String[] packages = {TEST_PACKAGE};
+        IBackupObserver observer = mock(IBackupObserver.class);
+        IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
+
+        verify(mUserOneService, never()).requestBackup(packages, observer, monitor, /* flags */ 0);
+    }
+
+    /**
+     * Test verifying that {@link BackupManagerService#cancelBackups(int)} throws a {@link
+     * SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+     */
+    @Test
+    public void testCancelBackups_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        expectThrows(SecurityException.class, () -> backupManagerService.cancelBackups(mUserTwoId));
+    }
+
+    /**
+     * Test that the backup service does not throw a {@link SecurityException} if the caller has
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+     */
+    @Test
+    public void testCancelBackups_withPermission_propagatesForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+        backupManagerService.cancelBackups(mUserTwoId);
+
+        verify(mUserTwoService).cancelBackups();
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testCancelBackups_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.cancelBackups(mUserOneId);
+
+        verify(mUserOneService).cancelBackups();
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testCancelBackups_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.cancelBackups(mUserTwoId);
+
+        verify(mUserOneService, never()).cancelBackups();
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testBeginFullBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
+        FullBackupJob job = new FullBackupJob();
+
+        backupManagerService.beginFullBackup(job);
+
+        verify(mUserOneService).beginFullBackup(job);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testBeginFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+        FullBackupJob job = new FullBackupJob();
+
+        backupManagerService.beginFullBackup(job);
+
+        verify(mUserOneService, never()).beginFullBackup(job);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testEndFullBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
+
+        backupManagerService.endFullBackup();
+
+        verify(mUserOneService).endFullBackup();
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testEndFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.endFullBackup();
+
+        verify(mUserOneService, never()).endFullBackup();
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testFullTransportBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+        String[] packages = {TEST_PACKAGE};
+
+        backupManagerService.fullTransportBackup(mUserOneId, packages);
+
+        verify(mUserOneService).fullTransportBackup(packages);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testFullTransportBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+        String[] packages = {TEST_PACKAGE};
+
+        backupManagerService.fullTransportBackup(mUserTwoId, packages);
+
+        verify(mUserOneService, never()).fullTransportBackup(packages);
+    }
+
+    // ---------------------------------------------
+    // Restore tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testRestoreAtInstall_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.restoreAtInstall(mUserOneId, TEST_PACKAGE, /* token */ 0);
+
+        verify(mUserOneService).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testRestoreAtInstall_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.restoreAtInstall(mUserTwoId, TEST_PACKAGE, /* token */ 0);
+
+        verify(mUserOneService, never()).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testBeginRestoreSession_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT);
+
+        verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testBeginRestoreSession_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT);
+
+        verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testGetAvailableRestoreToken_onRegisteredUser_callsMethodForUser()
+            throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.getAvailableRestoreToken(mUserOneId, TEST_PACKAGE);
+
+        verify(mUserOneService).getAvailableRestoreToken(TEST_PACKAGE);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testGetAvailableRestoreToken_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.getAvailableRestoreToken(mUserTwoId, TEST_PACKAGE);
+
+        verify(mUserOneService, never()).getAvailableRestoreToken(TEST_PACKAGE);
+    }
+
+    // ---------------------------------------------
+    // Adb backup/restore tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testSetBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
+
+        backupManagerService.setBackupPassword("currentPassword", "newPassword");
+
+        verify(mUserOneService).setBackupPassword("currentPassword", "newPassword");
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testSetBackupPassword_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.setBackupPassword("currentPassword", "newPassword");
+
+        verify(mUserOneService, never()).setBackupPassword("currentPassword", "newPassword");
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testHasBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
+
+        backupManagerService.hasBackupPassword();
+
+        verify(mUserOneService).hasBackupPassword();
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testHasBackupPassword_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.hasBackupPassword();
+
+        verify(mUserOneService, never()).hasBackupPassword();
+    }
+
+    /**
+     * Test that the backup services throws a {@link SecurityException} if the caller does not have
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+     */
+    @Test
+    public void testAdbBackup_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        expectThrows(
+                SecurityException.class,
+                () ->
+                        backupManagerService.adbBackup(
+                                mUserTwoId,
+                                /* parcelFileDescriptor*/ null,
+                                /* includeApks */ true,
+                                /* includeObbs */ true,
+                                /* includeShared */ true,
+                                /* doWidgets */ true,
+                                /* doAllApps */ true,
+                                /* includeSystem */ true,
+                                /* doCompress */ true,
+                                /* doKeyValue */ true,
+                                null));
+    }
+
+    /**
+     * Test that the backup service does not throw a {@link SecurityException} if the caller has
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+     */
+    @Test
+    public void testAdbBackup_withPermission_propagatesForNonCallingUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+        ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+        backupManagerService.adbBackup(
+                mUserTwoId,
+                parcelFileDescriptor,
+                /* includeApks */ true,
+                /* includeObbs */ true,
+                /* includeShared */ true,
+                /* doWidgets */ true,
+                /* doAllApps */ true,
+                /* includeSystem */ true,
+                /* doCompress */ true,
+                /* doKeyValue */ true,
+                ADB_TEST_PACKAGES);
+
+        verify(mUserTwoService)
+                .adbBackup(
+                        parcelFileDescriptor,
+                        /* includeApks */ true,
+                        /* includeObbs */ true,
+                        /* includeShared */ true,
+                        /* doWidgets */ true,
+                        /* doAllApps */ true,
+                        /* includeSystem */ true,
+                        /* doCompress */ true,
+                        /* doKeyValue */ true,
+                        ADB_TEST_PACKAGES);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testAdbBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.adbBackup(
+                mUserOneId,
+                parcelFileDescriptor,
+                /* includeApks */ true,
+                /* includeObbs */ true,
+                /* includeShared */ true,
+                /* doWidgets */ true,
+                /* doAllApps */ true,
+                /* includeSystem */ true,
+                /* doCompress */ true,
+                /* doKeyValue */ true,
+                ADB_TEST_PACKAGES);
+
+        verify(mUserOneService)
+                .adbBackup(
+                        parcelFileDescriptor,
+                        /* includeApks */ true,
+                        /* includeObbs */ true,
+                        /* includeShared */ true,
+                        /* doWidgets */ true,
+                        /* doAllApps */ true,
+                        /* includeSystem */ true,
+                        /* doCompress */ true,
+                        /* doKeyValue */ true,
+                        ADB_TEST_PACKAGES);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testAdbBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.adbBackup(
+                mUserTwoId,
+                parcelFileDescriptor,
+                /* includeApks */ true,
+                /* includeObbs */ true,
+                /* includeShared */ true,
+                /* doWidgets */ true,
+                /* doAllApps */ true,
+                /* includeSystem */ true,
+                /* doCompress */ true,
+                /* doKeyValue */ true,
+                ADB_TEST_PACKAGES);
+
+        verify(mUserOneService, never())
+                .adbBackup(
+                        parcelFileDescriptor,
+                        /* includeApks */ true,
+                        /* includeObbs */ true,
+                        /* includeShared */ true,
+                        /* doWidgets */ true,
+                        /* doAllApps */ true,
+                        /* includeSystem */ true,
+                        /* doCompress */ true,
+                        /* doKeyValue */ true,
+                        ADB_TEST_PACKAGES);
+    }
+
+    /**
+     * Test that the backup services throws a {@link SecurityException} if the caller does not have
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+     */
+    @Test
+    public void testAdbRestore_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        expectThrows(
+                SecurityException.class, () -> backupManagerService.adbRestore(mUserTwoId, null));
+    }
+
+    /**
+     * Test that the backup service does not throw a {@link SecurityException} if the caller has
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+     */
+    @Test
+    public void testAdbRestore_withPermission_propagatesForNonCallingUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+        ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+        backupManagerService.adbRestore(mUserTwoId, parcelFileDescriptor);
+
+        verify(mUserTwoService).adbRestore(parcelFileDescriptor);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testAdbRestore_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.adbRestore(mUserOneId, parcelFileDescriptor);
+
+        verify(mUserOneService).adbRestore(parcelFileDescriptor);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testAdbRestore_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.adbRestore(mUserTwoId, parcelFileDescriptor);
+
+        verify(mUserOneService, never()).adbRestore(parcelFileDescriptor);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testAcknowledgeAdbBackupOrRestore_onRegisteredUser_callsMethodForUser()
+            throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+        IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
+
+        backupManagerService.acknowledgeAdbBackupOrRestore(
+                mUserOneId,
+                /* token */ 0,
+                /* allow */ true,
+                "currentPassword",
+                "encryptionPassword",
+                observer);
+
+        verify(mUserOneService)
+                .acknowledgeAdbBackupOrRestore(
+                        /* token */ 0,
+                        /* allow */ true,
+                        "currentPassword",
+                        "encryptionPassword",
+                        observer);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testAcknowledgeAdbBackupOrRestore_onUnknownUser_doesNotPropagateCall()
+            throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+        IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
+
+        backupManagerService.acknowledgeAdbBackupOrRestore(
+                mUserTwoId,
+                /* token */ 0,
+                /* allow */ true,
+                "currentPassword",
+                "encryptionPassword",
+                observer);
+
+        verify(mUserOneService, never())
+                .acknowledgeAdbBackupOrRestore(
+                        /* token */ 0,
+                        /* allow */ true,
+                        "currentPassword",
+                        "encryptionPassword",
+                        observer);
+    }
+
+    // ---------------------------------------------
+    //  Service tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testDump_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
+        File testFile = new File(mContext.getFilesDir(), "test");
+        testFile.createNewFile();
+        FileDescriptor fileDescriptor = new FileDescriptor();
+        PrintWriter printWriter = new PrintWriter(testFile);
+        String[] args = {"1", "2"};
+
+        backupManagerService.dump(fileDescriptor, printWriter, args);
+
+        verify(mUserOneService).dump(fileDescriptor, printWriter, args);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testDump_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+        File testFile = new File(mContext.getFilesDir(), "test");
+        testFile.createNewFile();
+        FileDescriptor fileDescriptor = new FileDescriptor();
+        PrintWriter printWriter = new PrintWriter(testFile);
+        String[] args = {"1", "2"};
+
+        backupManagerService.dump(fileDescriptor, printWriter, args);
+
+        verify(mUserOneService, never()).dump(fileDescriptor, printWriter, args);
+    }
+
+    private BackupManagerService createService() {
+        mShadowContext.grantPermissions(BACKUP);
+        return new BackupManagerService(
+                mContext, new Trampoline(mContext), startBackupThread(null));
+    }
+
+    private BackupManagerService createServiceAndRegisterUser(
+            int userId, UserBackupManagerService userBackupManagerService) {
+        BackupManagerService backupManagerService = createService();
+        backupManagerService.startServiceForUser(userId, userBackupManagerService);
+        return backupManagerService;
+    }
+
+    /**
+     * Sets the calling user to {@code userId} and grants the permission INTERACT_ACROSS_USERS_FULL
+     * to the caller if {@code shouldGrantPermission} is {@code true}, else it denies the
+     * permission.
+     */
+    private void setCallerAndGrantInteractUserPermission(
+            @UserIdInt int userId, boolean shouldGrantPermission) {
+        ShadowBinder.setCallingUserHandle(UserHandle.of(userId));
+        if (shouldGrantPermission) {
+            mShadowContext.grantPermissions(INTERACT_ACROSS_USERS_FULL);
+        } else {
+            mShadowContext.denyPermissions(INTERACT_ACROSS_USERS_FULL);
+        }
+    }
+
+    private ParcelFileDescriptor getFileDescriptorForAdbTest() throws Exception {
+        File testFile = new File(mContext.getFilesDir(), "test");
+        testFile.createNewFile();
+        return ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/KeyValueBackupJobTest.java b/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/KeyValueBackupJobTest.java
rename to services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/TransportManagerTest.java
rename to services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
diff --git a/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
similarity index 96%
rename from services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java
rename to services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index efbcb96..8d5c301 100644
--- a/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -71,7 +71,6 @@
 import org.robolectric.shadows.ShadowContextWrapper;
 import org.robolectric.shadows.ShadowLooper;
 import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.shadows.ShadowSettings;
 
 import java.io.File;
 import java.util.List;
@@ -87,6 +86,7 @@
     private static final String TAG = "BMSTest";
     private static final String PACKAGE_1 = "some.package.1";
     private static final String PACKAGE_2 = "some.package.2";
+    private static final int USER_ID = 10;
 
     @Mock private TransportManager mTransportManager;
     private HandlerThread mBackupThread;
@@ -432,7 +432,7 @@
     }
 
     private String getSettingsTransport() {
-        return ShadowSettings.ShadowSecure.getString(
+        return Settings.Secure.getString(
                 mContext.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
     }
 
@@ -829,7 +829,7 @@
     public void testRequestBackup_whenNotProvisioned() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
-        backupManagerService.setProvisioned(false);
+        backupManagerService.setSetupComplete(false);
 
         int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
 
@@ -848,7 +848,7 @@
         setUpCurrentTransport(mTransportManager, mTransport.unregistered());
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
         backupManagerService.setEnabled(true);
-        backupManagerService.setProvisioned(true);
+        backupManagerService.setSetupComplete(true);
 
         int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
 
@@ -868,7 +868,7 @@
         setUpCurrentTransport(mTransportManager, mTransport);
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
         backupManagerService.setEnabled(true);
-        backupManagerService.setProvisioned(true);
+        backupManagerService.setSetupComplete(true);
         // Haven't set PACKAGE_1 as eligible
 
         int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
@@ -965,20 +965,20 @@
     private UserBackupManagerService createBackupManagerServiceForRequestBackup() {
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
         backupManagerService.setEnabled(true);
-        backupManagerService.setProvisioned(true);
+        backupManagerService.setSetupComplete(true);
         return backupManagerService;
     }
 
     /**
-     * Test verifying that {@link UserBackupManagerService#createAndInitializeService(Context,
-     * Trampoline, HandlerThread, File, File, TransportManager)} posts a transport registration task
-     * to the backup thread.
+     * Test verifying that creating a new instance posts a transport registration task to the backup
+     * thread.
      */
     @Test
     public void testCreateAndInitializeService_postRegisterTransports() {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
 
         UserBackupManagerService.createAndInitializeService(
+                USER_ID,
                 mContext,
                 new Trampoline(mContext),
                 mBackupThread,
@@ -991,15 +991,15 @@
     }
 
     /**
-     * Test verifying that {@link UserBackupManagerService#createAndInitializeService(Context,
-     * Trampoline, HandlerThread, File, File, TransportManager)} does not directly register
-     * transports on the main thread.
+     * Test verifying that creating a new instance does not directly register transports on the main
+     * thread.
      */
     @Test
     public void testCreateAndInitializeService_doesNotRegisterTransportsSynchronously() {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
 
         UserBackupManagerService.createAndInitializeService(
+                USER_ID,
                 mContext,
                 new Trampoline(mContext),
                 mBackupThread,
@@ -1011,17 +1011,14 @@
         verify(mTransportManager, never()).registerTransports();
     }
 
-    /**
-     * Test checking non-null argument on {@link
-     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
-     * File, TransportManager)}.
-     */
+    /** Test checking non-null argument on instance creation. */
     @Test
     public void testCreateAndInitializeService_withNullContext_throws() {
         expectThrows(
                 NullPointerException.class,
                 () ->
                         UserBackupManagerService.createAndInitializeService(
+                                USER_ID,
                                 /* context */ null,
                                 new Trampoline(mContext),
                                 mBackupThread,
@@ -1030,17 +1027,14 @@
                                 mTransportManager));
     }
 
-    /**
-     * Test checking non-null argument on {@link
-     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
-     * File, TransportManager)}.
-     */
+    /** Test checking non-null argument on instance creation. */
     @Test
     public void testCreateAndInitializeService_withNullTrampoline_throws() {
         expectThrows(
                 NullPointerException.class,
                 () ->
                         UserBackupManagerService.createAndInitializeService(
+                                USER_ID,
                                 mContext,
                                 /* trampoline */ null,
                                 mBackupThread,
@@ -1049,17 +1043,14 @@
                                 mTransportManager));
     }
 
-    /**
-     * Test checking non-null argument on {@link
-     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
-     * File, TransportManager)}.
-     */
+    /** Test checking non-null argument on instance creation. */
     @Test
     public void testCreateAndInitializeService_withNullBackupThread_throws() {
         expectThrows(
                 NullPointerException.class,
                 () ->
                         UserBackupManagerService.createAndInitializeService(
+                                USER_ID,
                                 mContext,
                                 new Trampoline(mContext),
                                 /* backupThread */ null,
@@ -1068,17 +1059,14 @@
                                 mTransportManager));
     }
 
-    /**
-     * Test checking non-null argument on {@link
-     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
-     * File, TransportManager)}.
-     */
+    /** Test checking non-null argument on instance creation. */
     @Test
     public void testCreateAndInitializeService_withNullStateDir_throws() {
         expectThrows(
                 NullPointerException.class,
                 () ->
                         UserBackupManagerService.createAndInitializeService(
+                                USER_ID,
                                 mContext,
                                 new Trampoline(mContext),
                                 mBackupThread,
@@ -1089,8 +1077,8 @@
 
     /**
      * Test checking non-null argument on {@link
-     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
-     * File, TransportManager)}.
+     * UserBackupManagerService#createAndInitializeService(int, Context, Trampoline, HandlerThread,
+     * File, File, TransportManager)}.
      */
     @Test
     public void testCreateAndInitializeService_withNullDataDir_throws() {
@@ -1098,6 +1086,7 @@
                 NullPointerException.class,
                 () ->
                         UserBackupManagerService.createAndInitializeService(
+                                USER_ID,
                                 mContext,
                                 new Trampoline(mContext),
                                 mBackupThread,
@@ -1108,8 +1097,8 @@
 
     /**
      * Test checking non-null argument on {@link
-     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
-     * File, TransportManager)}.
+     * UserBackupManagerService#createAndInitializeService(int, Context, Trampoline, HandlerThread,
+     * File, File, TransportManager)}.
      */
     @Test
     public void testCreateAndInitializeService_withNullTransportManager_throws() {
@@ -1117,6 +1106,7 @@
                 NullPointerException.class,
                 () ->
                         UserBackupManagerService.createAndInitializeService(
+                                USER_ID,
                                 mContext,
                                 new Trampoline(mContext),
                                 mBackupThread,
@@ -1127,7 +1117,7 @@
 
     private UserBackupManagerService createUserBackupManagerServiceAndRunTasks() {
         return BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks(
-                mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
+                USER_ID, mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
     }
 
     private void setUpPowerManager(UserBackupManagerService backupManagerService) {
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunk/ChunkTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java
diff --git a/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
rename to services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
diff --git a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java b/services/robotests/backup/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
rename to services/robotests/backup/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
diff --git a/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java b/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java
new file mode 100644
index 0000000..b754356
--- /dev/null
+++ b/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+
+import com.android.server.backup.KeyValueBackupJob;
+import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
+import com.android.server.backup.testing.BackupManagerServiceTestUtils;
+import com.android.server.backup.testing.TestUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.io.File;
+
+/**
+ * Tests verifying the interaction between {@link SetupObserver} and {@link
+ * UserBackupManagerService}.
+ */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class SetupObserverTest {
+    private static final String TAG = "SetupObserverTest";
+    private static final int USER_ID = 10;
+
+    @Mock private TransportManager mTransportManager;
+
+    private Context mContext;
+    private UserBackupManagerService mUserBackupManagerService;
+    private HandlerThread mHandlerThread;
+
+    /** Setup state. */
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mHandlerThread = BackupManagerServiceTestUtils.startSilentBackupThread(TAG);
+        mUserBackupManagerService =
+                BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks(
+                        USER_ID,
+                        mContext,
+                        mHandlerThread,
+                        new File(mContext.getDataDir(), "test1"),
+                        new File(mContext.getDataDir(), "test2"),
+                        mTransportManager);
+    }
+
+    /** Test observer handles changes from not setup -> setup correctly. */
+    @Test
+    public void testOnChange_whenNewlySetup_updatesState() throws Exception {
+        SetupObserver setupObserver = new SetupObserver(mUserBackupManagerService, new Handler());
+        mUserBackupManagerService.setSetupComplete(false);
+        changeSetupCompleteSettingForUser(true, USER_ID);
+
+        setupObserver.onChange(true);
+
+        assertThat(mUserBackupManagerService.isSetupComplete()).isTrue();
+    }
+
+    /** Test observer handles changes from setup -> not setup correctly. */
+    @Test
+    public void testOnChange_whenPreviouslySetup_doesNotUpdateState() throws Exception {
+        SetupObserver setupObserver = new SetupObserver(mUserBackupManagerService, new Handler());
+        mUserBackupManagerService.setSetupComplete(true);
+        changeSetupCompleteSettingForUser(false, USER_ID);
+
+        setupObserver.onChange(true);
+
+        assertThat(mUserBackupManagerService.isSetupComplete()).isTrue();
+    }
+
+    /** Test observer handles changes from not setup -> not setup correctly. */
+    @Test
+    public void testOnChange_whenNotPreviouslySetup_doesNotUpdateStateIfNoChange()
+            throws Exception {
+        SetupObserver setupObserver = new SetupObserver(mUserBackupManagerService, new Handler());
+        mUserBackupManagerService.setSetupComplete(false);
+        changeSetupCompleteSettingForUser(false, USER_ID);
+
+        setupObserver.onChange(true);
+
+        assertThat(mUserBackupManagerService.isSetupComplete()).isFalse();
+    }
+
+    /** Test observer handles changes from not setup -> setup correctly. */
+    @Test
+    public void testOnChange_whenNewlySetup_schedulesBackup() throws Exception {
+        SetupObserver setupObserver = new SetupObserver(mUserBackupManagerService, new Handler());
+        mUserBackupManagerService.setSetupComplete(false);
+        changeSetupCompleteSettingForUser(true, USER_ID);
+        // Setup conditions for a full backup job to be scheduled.
+        mUserBackupManagerService.setEnabled(true);
+        mUserBackupManagerService.enqueueFullBackup("testPackage", /* lastBackedUp */ 0);
+        // Clear the handler of all pending tasks. This is to prevent the below assertion on the
+        // handler from encountering false positives due to other tasks being scheduled as part of
+        // setup work.
+        TestUtils.runToEndOfTasks(mHandlerThread.getLooper());
+
+        setupObserver.onChange(true);
+
+        assertThat(KeyValueBackupJob.isScheduled()).isTrue();
+        // Verifies that the full backup job is scheduled. The job is scheduled via a posted message
+        // on the backup handler so we verify that a message exists.
+        assertThat(mUserBackupManagerService.getBackupHandler().hasMessagesOrCallbacks()).isTrue();
+    }
+
+    private void changeSetupCompleteSettingForUser(boolean value, int userId) {
+        Settings.Secure.putIntForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.USER_SETUP_COMPLETE,
+                value ? 1 : 0,
+                userId);
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/AgentExceptionTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java
rename to services/robotests/backup/src/com/android/server/backup/keyvalue/AgentExceptionTest.java
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/BackupExceptionTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java
rename to services/robotests/backup/src/com/android/server/backup/keyvalue/BackupExceptionTest.java
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
rename to services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
similarity index 99%
rename from services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
rename to services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 099127c..5e84ab0 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -134,7 +134,6 @@
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowApplication;
 import org.robolectric.shadows.ShadowLooper;
 import org.robolectric.shadows.ShadowPackageManager;
 import org.robolectric.shadows.ShadowQueuedWork;
@@ -169,6 +168,7 @@
     private static final PackageData PACKAGE_2 = keyValuePackage(2);
     private static final String BACKUP_AGENT_SHARED_PREFS_SYNCHRONIZER_CLASS =
             "android.app.backup.BackupAgent$SharedPrefsSynchronizer";
+    private static final int USER_ID = 10;
 
     @Mock private TransportManager mTransportManager;
     @Mock private DataChangedJournal mOldJournal;
@@ -187,7 +187,6 @@
     private File mBaseStateDir;
     private File mDataDir;
     private Application mApplication;
-    private ShadowApplication mShadowApplication;
     private Looper mMainLooper;
     private FrameworkShadowLooper mShadowMainLooper;
     private Context mContext;
@@ -199,7 +198,6 @@
         mTransport = backupTransport();
 
         mApplication = RuntimeEnvironment.application;
-        mShadowApplication = shadowOf(mApplication);
         mContext = mApplication;
 
         mMainLooper = Looper.getMainLooper();
@@ -224,7 +222,7 @@
         setUpBinderCallerAndApplicationAsSystem(mApplication);
         mBackupManagerService =
                 spy(createUserBackupManagerServiceAndRunTasks(
-                        mContext, mBaseStateDir, mDataDir, mTransportManager));
+                        USER_ID, mContext, mBaseStateDir, mDataDir, mTransportManager));
         setUpBackupManagerServiceBasics(
                 mBackupManagerService,
                 mApplication,
@@ -2438,7 +2436,7 @@
                     packageData.packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
             PackageInfo packageInfo = getPackageInfo(packageData);
             mShadowPackageManager.addPackage(packageInfo);
-            mShadowApplication.sendBroadcast(getPackageAddedIntent(packageData));
+            mContext.sendBroadcast(getPackageAddedIntent(packageData));
             // Run the backup looper because on the receiver we post MSG_SCHEDULE_BACKUP_PACKAGE
             mShadowBackupLooper.runToEndOfTasks();
             BackupAgent backupAgent = spy(BackupAgent.class);
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/TaskExceptionTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java
rename to services/robotests/backup/src/com/android/server/backup/keyvalue/TaskExceptionTest.java
diff --git a/services/robotests/src/com/android/server/backup/remote/FutureBackupCallbackTest.java b/services/robotests/backup/src/com/android/server/backup/remote/FutureBackupCallbackTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/remote/FutureBackupCallbackTest.java
rename to services/robotests/backup/src/com/android/server/backup/remote/FutureBackupCallbackTest.java
diff --git a/services/robotests/src/com/android/server/backup/remote/RemoteCallTest.java b/services/robotests/backup/src/com/android/server/backup/remote/RemoteCallTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/remote/RemoteCallTest.java
rename to services/robotests/backup/src/com/android/server/backup/remote/RemoteCallTest.java
diff --git a/services/robotests/src/com/android/server/backup/remote/RemoteResultTest.java b/services/robotests/backup/src/com/android/server/backup/remote/RemoteResultTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/remote/RemoteResultTest.java
rename to services/robotests/backup/src/com/android/server/backup/remote/RemoteResultTest.java
diff --git a/services/robotests/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java b/services/robotests/backup/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java
rename to services/robotests/backup/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java
diff --git a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
similarity index 97%
rename from services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
rename to services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 859392d..4009876 100644
--- a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -479,8 +479,11 @@
 
     @Test
     public void testRestorePackage_whenCallerIsNotPackageAndPermissionGranted() throws Exception {
-        mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP);
-        ShadowBinder.setCallingUid(1);
+        final int pid = 1211;
+        final int uid = 1;
+        mShadowApplication.grantPermissions(pid, uid, android.Manifest.permission.BACKUP);
+        ShadowBinder.setCallingPid(pid);
+        ShadowBinder.setCallingUid(uid);
         setUpPackage(PACKAGE_1, /* uid */ 2);
         when(mBackupManagerService.getAvailableRestoreToken(PACKAGE_1)).thenReturn(TOKEN_1);
         setUpTransport(mTransport);
@@ -494,8 +497,11 @@
 
     @Test
     public void testRestorePackage_whenCallerIsNotPackageAndPermissionDenied() throws Exception {
-        mShadowApplication.denyPermissions(android.Manifest.permission.BACKUP);
-        ShadowBinder.setCallingUid(1);
+        final int pid = 1211;
+        final int uid = 1;
+        mShadowApplication.denyPermissions(pid, uid, android.Manifest.permission.BACKUP);
+        ShadowBinder.setCallingPid(pid);
+        ShadowBinder.setCallingUid(uid);
         setUpPackage(PACKAGE_1, /* uid */ 2);
         when(mBackupManagerService.getAvailableRestoreToken(PACKAGE_1)).thenReturn(TOKEN_1);
         setUpTransport(mTransport);
@@ -508,7 +514,11 @@
 
     @Test
     public void testRestorePackage_whenPackageNotFound() throws Exception {
-        mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP);
+        final int pid = 1211;
+        final int uid = 1;
+        mShadowApplication.grantPermissions(pid, uid, android.Manifest.permission.BACKUP);
+        ShadowBinder.setCallingPid(pid);
+        ShadowBinder.setCallingUid(uid);
         setUpPackage(PACKAGE_1, /* uid */ 1);
         setUpTransport(mTransport);
         IRestoreSession restoreSession = createActiveRestoreSession(null, mTransport);
diff --git a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
similarity index 92%
rename from services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
rename to services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index 06f6d21..47abcc5 100644
--- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -58,13 +58,17 @@
      * <p>If the class-under-test is going to execute methods as the system, it's a good idea to
      * also call {@link #setUpBinderCallerAndApplicationAsSystem(Application)} before this method.
      *
-     * @see #createUserBackupManagerServiceAndRunTasks(Context, HandlerThread, File, File,
+     * @see #createUserBackupManagerServiceAndRunTasks(int, Context, HandlerThread, File, File,
      *     TransportManager)
      */
     public static UserBackupManagerService createUserBackupManagerServiceAndRunTasks(
-            Context context, File baseStateDir, File dataDir, TransportManager transportManager) {
+            int userId,
+            Context context,
+            File baseStateDir,
+            File dataDir,
+            TransportManager transportManager) {
         return createUserBackupManagerServiceAndRunTasks(
-                context, startBackupThread(null), baseStateDir, dataDir, transportManager);
+                userId, context, startBackupThread(null), baseStateDir, dataDir, transportManager);
     }
 
     /**
@@ -75,6 +79,7 @@
      * also call {@link #setUpBinderCallerAndApplicationAsSystem(Application)} before this method.
      */
     public static UserBackupManagerService createUserBackupManagerServiceAndRunTasks(
+            int userId,
             Context context,
             HandlerThread backupThread,
             File baseStateDir,
@@ -82,6 +87,7 @@
             TransportManager transportManager) {
         UserBackupManagerService backupManagerService =
                 UserBackupManagerService.createAndInitializeService(
+                        userId,
                         context,
                         new Trampoline(context),
                         backupThread,
@@ -130,11 +136,13 @@
     }
 
     public static void setUpBinderCallerAndApplicationAsSystem(Application application) {
-        ShadowBinder.setCallingUid(Process.SYSTEM_UID);
-        ShadowBinder.setCallingPid(1211);
+        final int uid = Process.SYSTEM_UID;
+        final int pid = 1211;
+        ShadowBinder.setCallingUid(uid);
+        ShadowBinder.setCallingPid(pid);
         ShadowApplication shadowApplication = shadowOf(application);
-        shadowApplication.grantPermissions("android.permission.BACKUP");
-        shadowApplication.grantPermissions("android.permission.CONFIRM_FULL_BACKUP");
+        shadowApplication.grantPermissions(pid, uid, "android.permission.BACKUP");
+        shadowApplication.grantPermissions(pid, uid, "android.permission.CONFIRM_FULL_BACKUP");
     }
 
     /**
diff --git a/services/robotests/src/com/android/server/backup/testing/CryptoTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/CryptoTestUtils.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/testing/CryptoTestUtils.java
rename to services/robotests/backup/src/com/android/server/backup/testing/CryptoTestUtils.java
diff --git a/services/robotests/src/com/android/server/backup/testing/PackageData.java b/services/robotests/backup/src/com/android/server/backup/testing/PackageData.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/testing/PackageData.java
rename to services/robotests/backup/src/com/android/server/backup/testing/PackageData.java
diff --git a/services/robotests/src/com/android/server/backup/testing/TestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/testing/TestUtils.java
rename to services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java
diff --git a/services/robotests/src/com/android/server/backup/testing/TransportData.java b/services/robotests/backup/src/com/android/server/backup/testing/TransportData.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/testing/TransportData.java
rename to services/robotests/backup/src/com/android/server/backup/testing/TransportData.java
diff --git a/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/TransportTestUtils.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java
rename to services/robotests/backup/src/com/android/server/backup/testing/TransportTestUtils.java
diff --git a/services/robotests/src/com/android/server/backup/testing/Utils.java b/services/robotests/backup/src/com/android/server/backup/testing/Utils.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/testing/Utils.java
rename to services/robotests/backup/src/com/android/server/backup/testing/Utils.java
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportClientManagerTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java
rename to services/robotests/backup/src/com/android/server/backup/transport/TransportClientManagerTest.java
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportClientTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
rename to services/robotests/backup/src/com/android/server/backup/transport/TransportClientTest.java
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportStatsTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportStatsTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/transport/TransportStatsTest.java
rename to services/robotests/backup/src/com/android/server/backup/transport/TransportStatsTest.java
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
deleted file mode 100644
index 96ef0ce..0000000
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ /dev/null
@@ -1,641 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.backup;
-
-import static com.android.server.backup.testing.TransportData.backupTransport;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.robolectric.Shadows.shadowOf;
-import static org.testng.Assert.expectThrows;
-
-import android.annotation.UserIdInt;
-import android.app.Application;
-import android.app.backup.IBackupManagerMonitor;
-import android.app.backup.IBackupObserver;
-import android.app.backup.IFullBackupRestoreObserver;
-import android.app.backup.ISelectBackupTransportCallback;
-import android.content.Context;
-import android.content.Intent;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.UserHandle;
-import android.platform.test.annotations.Presubmit;
-
-import com.android.server.backup.testing.BackupManagerServiceTestUtils;
-import com.android.server.backup.testing.TransportData;
-import com.android.server.testing.shadows.ShadowBinder;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowContextWrapper;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowBinder.class})
-@Presubmit
-public class BackupManagerServiceTest {
-    private static final String TEST_PACKAGE = "package";
-    private static final String TEST_TRANSPORT = "transport";
-
-    private static final int NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 1;
-
-    private ShadowContextWrapper mShadowContext;
-    @Mock private UserBackupManagerService mUserBackupManagerService;
-    private BackupManagerService mBackupManagerService;
-    private Context mContext;
-    @UserIdInt private int mUserId;
-
-    /** Initialize {@link BackupManagerService}. */
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        Application application = RuntimeEnvironment.application;
-        mContext = application;
-        mShadowContext = shadowOf(application);
-        mUserId = NON_USER_SYSTEM;
-        mBackupManagerService =
-                new BackupManagerService(
-                        application,
-                        new Trampoline(application),
-                        BackupManagerServiceTestUtils.startBackupThread(null));
-        mBackupManagerService.setUserBackupManagerService(mUserBackupManagerService);
-    }
-
-    /**
-     * Clean up and reset state that was created for testing {@link BackupManagerService}
-     * operations.
-     */
-    @After
-    public void tearDown() throws Exception {
-        ShadowBinder.reset();
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}.
-     * This is specifically to prevent overloading the logs in production.
-     */
-    @Test
-    public void testMoreDebug_isFalse() throws Exception {
-        boolean moreDebug = BackupManagerService.MORE_DEBUG;
-
-        assertThat(moreDebug).isFalse();
-    }
-
-    // TODO(b/118520567): Change the following tests to use the per-user instance of
-    // UserBackupManagerService once it's implemented. Currently these tests only test the straight
-    // forward redirection.
-
-    // ---------------------------------------------
-    // Backup agent tests
-    // ---------------------------------------------
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testDataChanged_callsDataChangedForUser() throws Exception {
-        mBackupManagerService.dataChanged(TEST_PACKAGE);
-
-        verify(mUserBackupManagerService).dataChanged(TEST_PACKAGE);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testAgentConnected_callsAgentConnectedForUser() throws Exception {
-        IBinder agentBinder = mock(IBinder.class);
-
-        mBackupManagerService.agentConnected(TEST_PACKAGE, agentBinder);
-
-        verify(mUserBackupManagerService).agentConnected(TEST_PACKAGE, agentBinder);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testAgentDisconnected_callsAgentDisconnectedForUser() throws Exception {
-        mBackupManagerService.agentDisconnected(TEST_PACKAGE);
-
-        verify(mUserBackupManagerService).agentDisconnected(TEST_PACKAGE);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testOpComplete_callsOpCompleteForUser() throws Exception {
-        mBackupManagerService.opComplete(/* token */ 0, /* result */ 0L);
-
-        verify(mUserBackupManagerService).opComplete(/* token */ 0, /* result */ 0L);
-    }
-
-    // ---------------------------------------------
-    // Transport tests
-    // ---------------------------------------------
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testInitializeTransports_callsInitializeTransportsForUser() throws Exception {
-        String[] transports = {TEST_TRANSPORT};
-
-        mBackupManagerService.initializeTransports(transports, /* observer */ null);
-
-        verify(mUserBackupManagerService).initializeTransports(transports, /* observer */ null);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testClearBackupData_callsClearBackupDataForUser() throws Exception {
-        mBackupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
-
-        verify(mUserBackupManagerService).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testGetCurrentTransport_callsGetCurrentTransportForUser() throws Exception {
-        mBackupManagerService.getCurrentTransport();
-
-        verify(mUserBackupManagerService).getCurrentTransport();
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testGetCurrentTransportComponent_callsGetCurrentTransportComponentForUser()
-            throws Exception {
-        mBackupManagerService.getCurrentTransportComponent();
-
-        verify(mUserBackupManagerService).getCurrentTransportComponent();
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testListAllTransports_callsListAllTransportsForUser() throws Exception {
-        mBackupManagerService.listAllTransports();
-
-        verify(mUserBackupManagerService).listAllTransports();
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testListAllTransportComponents_callsListAllTransportComponentsForUser()
-            throws Exception {
-        mBackupManagerService.listAllTransportComponents();
-
-        verify(mUserBackupManagerService).listAllTransportComponents();
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testGetTransportWhitelist_callsGetTransportWhitelistForUser() throws Exception {
-        mBackupManagerService.getTransportWhitelist();
-
-        verify(mUserBackupManagerService).getTransportWhitelist();
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testUpdateTransportAttributes_callsUpdateTransportAttributesForUser()
-            throws Exception {
-        TransportData transport = backupTransport();
-        Intent configurationIntent = new Intent();
-        Intent dataManagementIntent = new Intent();
-
-        mBackupManagerService.updateTransportAttributes(
-                transport.getTransportComponent(),
-                transport.transportName,
-                configurationIntent,
-                "currentDestinationString",
-                dataManagementIntent,
-                "dataManagementLabel");
-
-        verify(mUserBackupManagerService)
-                .updateTransportAttributes(
-                        transport.getTransportComponent(),
-                        transport.transportName,
-                        configurationIntent,
-                        "currentDestinationString",
-                        dataManagementIntent,
-                        "dataManagementLabel");
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testSelectBackupTransport_callsSelectBackupTransportForUser() throws Exception {
-        mBackupManagerService.selectBackupTransport(TEST_TRANSPORT);
-
-        verify(mUserBackupManagerService).selectBackupTransport(TEST_TRANSPORT);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testSelectTransportAsync_callsSelectTransportAsyncForUser() throws Exception {
-        TransportData transport = backupTransport();
-        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
-        mBackupManagerService.selectBackupTransportAsync(
-                transport.getTransportComponent(), callback);
-
-        verify(mUserBackupManagerService)
-                .selectBackupTransportAsync(transport.getTransportComponent(), callback);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testGetConfigurationIntent_callsGetConfigurationIntentForUser() throws Exception {
-        mBackupManagerService.getConfigurationIntent(TEST_TRANSPORT);
-
-        verify(mUserBackupManagerService).getConfigurationIntent(TEST_TRANSPORT);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testGetDestinationString_callsGetDestinationStringForUser() throws Exception {
-        mBackupManagerService.getDestinationString(TEST_TRANSPORT);
-
-        verify(mUserBackupManagerService).getDestinationString(TEST_TRANSPORT);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testGetDataManagementIntent_callsGetDataManagementIntentForUser() throws Exception {
-        mBackupManagerService.getDataManagementIntent(TEST_TRANSPORT);
-
-        verify(mUserBackupManagerService).getDataManagementIntent(TEST_TRANSPORT);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testGetDataManagementLabel_callsGetDataManagementLabelForUser() throws Exception {
-        mBackupManagerService.getDataManagementLabel(TEST_TRANSPORT);
-
-        verify(mUserBackupManagerService).getDataManagementLabel(TEST_TRANSPORT);
-    }
-
-    // ---------------------------------------------
-    // Settings tests
-    // ---------------------------------------------
-    /**
-     * Test verifying that {@link BackupManagerService#setBackupEnabled(int, boolean)} throws a
-     * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
-     */
-    @Test
-    public void setBackupEnabled_withoutPermission_throwsSecurityException() {
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
-        expectThrows(
-                SecurityException.class,
-                () -> mBackupManagerService.setBackupEnabled(mUserId, true));
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#setBackupEnabled(int, boolean)} does not
-     * require the caller to have INTERACT_ACROSS_USERS_FULL permission when the calling user id is
-     * the same as the target user id.
-     */
-    @Test
-    public void setBackupEnabled_whenCallingUserIsTargetUser_doesntNeedPermission() {
-        ShadowBinder.setCallingUserHandle(UserHandle.of(mUserId));
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
-        mBackupManagerService.setBackupEnabled(mUserId, true);
-
-        verify(mUserBackupManagerService).setBackupEnabled(true);
-    }
-
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void setBackupEnabled_callsSetBackupEnabledForUser() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
-        mBackupManagerService.setBackupEnabled(mUserId, true);
-
-        verify(mUserBackupManagerService).setBackupEnabled(true);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void setAutoRestore_callsSetAutoRestoreForUser() throws Exception {
-        mBackupManagerService.setAutoRestore(true);
-
-        verify(mUserBackupManagerService).setAutoRestore(true);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testSetBackupProvisioned_callsSetBackupProvisionedForUser() throws Exception {
-        mBackupManagerService.setBackupProvisioned(true);
-
-        verify(mUserBackupManagerService).setBackupProvisioned(true);
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#isBackupEnabled(int)} throws a
-     * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
-     */
-    @Test
-    public void testIsBackupEnabled_withoutPermission_throwsSecurityException() {
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
-        expectThrows(
-                SecurityException.class,
-                () -> mBackupManagerService.isBackupEnabled(mUserId));
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testIsBackupEnabled_callsIsBackupEnabledForUser() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
-        mBackupManagerService.isBackupEnabled(mUserId);
-
-        verify(mUserBackupManagerService).isBackupEnabled();
-    }
-
-    // ---------------------------------------------
-    // Backup tests
-    // ---------------------------------------------
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testIsAppEligibleForBackup_callsIsAppEligibleForBackupForUser() throws Exception {
-        mBackupManagerService.isAppEligibleForBackup(TEST_PACKAGE);
-
-        verify(mUserBackupManagerService).isAppEligibleForBackup(TEST_PACKAGE);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testFilterAppsEligibleForBackup_callsFilterAppsEligibleForBackupForUser()
-            throws Exception {
-        String[] packages = {TEST_PACKAGE};
-
-        mBackupManagerService.filterAppsEligibleForBackup(packages);
-
-        verify(mUserBackupManagerService).filterAppsEligibleForBackup(packages);
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#backupNow(int)} throws a
-     * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
-     */
-    @Test
-    public void testBackupNow_withoutPermission_throwsSecurityException() {
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
-        expectThrows(
-                SecurityException.class,
-                () -> mBackupManagerService.backupNow(mUserId));
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testBackupNow_callsBackupNowForUser() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
-        mBackupManagerService.backupNow(mUserId);
-
-        verify(mUserBackupManagerService).backupNow();
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#requestBackup(int, String[], IBackupObserver,
-     * IBackupManagerMonitor, int)} throws a {@link SecurityException} if the caller does not have
-     * INTERACT_ACROSS_USERS_FULL permission.
-     */
-    @Test
-    public void testRequestBackup_withoutPermission_throwsSecurityException() {
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-        String[] packages = {TEST_PACKAGE};
-        IBackupObserver observer = mock(IBackupObserver.class);
-        IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
-
-        expectThrows(
-                SecurityException.class,
-                () -> mBackupManagerService.requestBackup(mUserId, packages, observer, monitor, 0));
-    }
-
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testRequestBackup_callsRequestBackupForUser() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-        String[] packages = {TEST_PACKAGE};
-        IBackupObserver observer = mock(IBackupObserver.class);
-        IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
-
-        mBackupManagerService.requestBackup(mUserId, packages, observer, monitor,
-                /* flags */ 0);
-
-        verify(mUserBackupManagerService).requestBackup(packages, observer, monitor, /* flags */ 0);
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#cancelBackups(int)} throws a
-     * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
-     */
-    @Test
-    public void testCancelBackups_withoutPermission_throwsSecurityException() {
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
-        expectThrows(
-                SecurityException.class,
-                () -> mBackupManagerService.cancelBackups(mUserId));
-    }
-
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testCancelBackups_callsCancelBackupsForUser() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
-        mBackupManagerService.cancelBackups(mUserId);
-
-        verify(mUserBackupManagerService).cancelBackups();
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testBeginFullBackup_callsBeginFullBackupForUser() throws Exception {
-        FullBackupJob job = new FullBackupJob();
-
-        mBackupManagerService.beginFullBackup(job);
-
-        verify(mUserBackupManagerService).beginFullBackup(job);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testEndFullBackup_callsEndFullBackupForUser() throws Exception {
-        mBackupManagerService.endFullBackup();
-
-        verify(mUserBackupManagerService).endFullBackup();
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testFullTransportBackup_callsFullTransportBackupForUser() throws Exception {
-        String[] packages = {TEST_PACKAGE};
-
-        mBackupManagerService.fullTransportBackup(packages);
-
-        verify(mUserBackupManagerService).fullTransportBackup(packages);
-    }
-
-    // ---------------------------------------------
-    // Restore tests
-    // ---------------------------------------------
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testRestoreAtInstall_callsRestoreAtInstallForUser() throws Exception {
-        mBackupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0);
-
-        verify(mUserBackupManagerService).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testBeginRestoreSession_callsBeginRestoreSessionForUser() throws Exception {
-        mBackupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
-
-        verify(mUserBackupManagerService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testGetAvailableRestoreToken_callsGetAvailableRestoreTokenForUser()
-            throws Exception {
-        mBackupManagerService.getAvailableRestoreToken(TEST_PACKAGE);
-
-        verify(mUserBackupManagerService).getAvailableRestoreToken(TEST_PACKAGE);
-    }
-
-    // ---------------------------------------------
-    // Adb backup/restore tests
-    // ---------------------------------------------
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testSetBackupPassword_callsSetBackupPasswordForUser() throws Exception {
-        mBackupManagerService.setBackupPassword("currentPassword", "newPassword");
-
-        verify(mUserBackupManagerService).setBackupPassword("currentPassword", "newPassword");
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testHasBackupPassword_callsHasBackupPasswordForUser() throws Exception {
-        mBackupManagerService.hasBackupPassword();
-
-        verify(mUserBackupManagerService).hasBackupPassword();
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testAdbBackup_callsAdbBackupForUser() throws Exception {
-        File testFile = new File(mContext.getFilesDir(), "test");
-        testFile.createNewFile();
-        ParcelFileDescriptor parcelFileDescriptor =
-                ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
-        String[] packages = {TEST_PACKAGE};
-
-        mBackupManagerService.adbBackup(
-                parcelFileDescriptor,
-                /* includeApks */ true,
-                /* includeObbs */ true,
-                /* includeShared */ true,
-                /* doWidgets */ true,
-                /* doAllApps */ true,
-                /* includeSystem */ true,
-                /* doCompress */ true,
-                /* doKeyValue */ true,
-                packages);
-
-        verify(mUserBackupManagerService)
-                .adbBackup(
-                        parcelFileDescriptor,
-                        /* includeApks */ true,
-                        /* includeObbs */ true,
-                        /* includeShared */ true,
-                        /* doWidgets */ true,
-                        /* doAllApps */ true,
-                        /* includeSystem */ true,
-                        /* doCompress */ true,
-                        /* doKeyValue */ true,
-                        packages);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testAdbRestore_callsAdbRestoreForUser() throws Exception {
-        File testFile = new File(mContext.getFilesDir(), "test");
-        testFile.createNewFile();
-        ParcelFileDescriptor parcelFileDescriptor =
-                ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
-
-        mBackupManagerService.adbRestore(parcelFileDescriptor);
-
-        verify(mUserBackupManagerService).adbRestore(parcelFileDescriptor);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testAcknowledgeAdbBackupOrRestore_callsAcknowledgeAdbBackupOrRestoreForUser()
-            throws Exception {
-        IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
-
-        mBackupManagerService.acknowledgeAdbBackupOrRestore(
-                /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer);
-
-        verify(mUserBackupManagerService)
-                .acknowledgeAdbBackupOrRestore(
-                        /* token */ 0,
-                        /* allow */ true,
-                        "currentPassword",
-                        "encryptionPassword",
-                        observer);
-    }
-
-    // ---------------------------------------------
-    //  Service tests
-    // ---------------------------------------------
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testDump_callsDumpForUser() throws Exception {
-        File testFile = new File(mContext.getFilesDir(), "test");
-        testFile.createNewFile();
-        FileDescriptor fileDescriptor = new FileDescriptor();
-        PrintWriter printWriter = new PrintWriter(testFile);
-        String[] args = {"1", "2"};
-
-        mBackupManagerService.dump(fileDescriptor, printWriter, args);
-
-        verify(mUserBackupManagerService).dump(fileDescriptor, printWriter, args);
-    }
-}
diff --git a/core/java/android/service/contentcapture/InteractionContext.aidl b/services/startop/Android.bp
similarity index 65%
copy from core/java/android/service/contentcapture/InteractionContext.aidl
copy to services/startop/Android.bp
index 982e095..093b4ec 100644
--- a/core/java/android/service/contentcapture/InteractionContext.aidl
+++ b/services/startop/Android.bp
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
+/*
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -14,6 +14,11 @@
  * limitations under the License.
  */
 
-package android.service.contentcapture;
+java_library_static {
+    name: "services.startop",
 
-parcelable InteractionContext;
+    static_libs: [
+        // frameworks/base/startop/iorap
+        "services.startop.iorap",
+    ],
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index 3979a8e..148faad 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -25,7 +25,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
@@ -42,7 +41,6 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
@@ -66,7 +64,6 @@
 import android.util.Log;
 import android.util.SparseArray;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -96,6 +93,8 @@
     @Mock
     private ContentResolver mMockResolver;
     @Mock
+    private Context mMockContext;
+    @Mock
     private IActivityManager mIActivityManager;
     @Mock
     private UsageStatsManagerInternal mUsageStatsManagerInternal;
@@ -221,17 +220,16 @@
                 .thenReturn(STANDBY_BUCKET_ACTIVE);
         doReturn(Looper.getMainLooper()).when(Looper::myLooper);
 
-        final Context context = spy(InstrumentationRegistry.getTargetContext());
-        when(context.getContentResolver()).thenReturn(mMockResolver);
-        doNothing().when(mMockResolver).registerContentObserver(any(), anyBoolean(), any());
+        when(mMockContext.getContentResolver()).thenReturn(mMockResolver);
         doReturn("min_futurity=0").when(() ->
                 Settings.Global.getString(mMockResolver, Settings.Global.ALARM_MANAGER_CONSTANTS));
-        mInjector = new Injector(context);
-        mService = new AlarmManagerService(context, mInjector);
+        mInjector = new Injector(mMockContext);
+        mService = new AlarmManagerService(mMockContext, mInjector);
         spyOn(mService);
         doNothing().when(mService).publishBinderService(any(), any());
         mService.onStart();
         mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+        spyOn(mService.mHandler);
 
         assertEquals(0, mService.mConstants.MIN_FUTURITY);
         assertEquals(mService.mSystemUiUid, SYSTEM_UI_UID);
@@ -273,7 +271,7 @@
 
         final ArgumentCaptor<PendingIntent.OnFinished> onFinishedCaptor =
                 ArgumentCaptor.forClass(PendingIntent.OnFinished.class);
-        verify(alarmPi).send(any(Context.class), eq(0), any(Intent.class),
+        verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class),
                 onFinishedCaptor.capture(), any(Handler.class), isNull(), any());
         verify(mWakeLock).acquire();
         onFinishedCaptor.getValue().onSendFinished(alarmPi, null, 0, null, null);
@@ -423,11 +421,23 @@
         assertNotNull(restrictedAlarms.get(TEST_CALLING_UID));
 
         listenerArgumentCaptor.getValue().unblockAlarmsForUid(TEST_CALLING_UID);
-        verify(alarmPi).send(any(Context.class), eq(0), any(Intent.class), any(),
+        verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class), any(),
                 any(Handler.class), isNull(), any());
         assertNull(restrictedAlarms.get(TEST_CALLING_UID));
     }
 
+    @Test
+    public void sendsTimeTickOnInteractive() {
+        final ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        // Stubbing so the handler doesn't actually run the runnable.
+        doReturn(true).when(mService.mHandler).post(runnableCaptor.capture());
+        // change interactive state: false -> true
+        mService.interactiveStateChangedLocked(false);
+        mService.interactiveStateChangedLocked(true);
+        runnableCaptor.getValue().run();
+        verify(mMockContext).sendBroadcastAsUser(mService.mTimeTickIntent, UserHandle.ALL);
+    }
+
     @After
     public void tearDown() {
         if (mMockingSession != null) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 04a8408..1a16e56 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -46,9 +46,11 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 
 import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
@@ -56,6 +58,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.hardware.Sensor;
 import android.hardware.SensorManager;
 import android.location.LocationManager;
 import android.location.LocationProvider;
@@ -70,6 +73,7 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.deviceidle.ConstraintController;
 import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
@@ -77,6 +81,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.MockitoSession;
 import org.mockito.quality.Strictness;
@@ -89,6 +94,7 @@
     private DeviceIdleController mDeviceIdleController;
     private AnyMotionDetectorForTest mAnyMotionDetector;
     private AppStateTrackerForTest mAppStateTracker;
+    private DeviceIdleController.Constants mConstants;
     private InjectorForTest mInjector;
 
     private MockitoSession mMockingSession;
@@ -97,7 +103,7 @@
     @Mock
     private ConnectivityService mConnectivityService;
     @Mock
-    private DeviceIdleController.Constants mConstants;
+    private ContentResolver mContentResolver;
     @Mock
     private IActivityManager mIActivityManager;
     @Mock
@@ -108,10 +114,13 @@
     private PowerManager.WakeLock mWakeLock;
     @Mock
     private PowerManagerInternal mPowerManagerInternal;
+    @Mock
+    private SensorManager mSensorManager;
 
     class InjectorForTest extends DeviceIdleController.Injector {
         ConnectivityService connectivityService;
         LocationManager locationManager;
+        ConstraintController constraintController;
 
         InjectorForTest(Context ctx) {
             super(ctx);
@@ -139,37 +148,51 @@
         }
 
         @Override
-        DeviceIdleController.Constants getConstants(DeviceIdleController controller,
-                Handler handler,
-                ContentResolver resolver) {
-            return mConstants;
-        }
-
-        @Override
         LocationManager getLocationManager() {
             return locationManager;
         }
 
         @Override
         DeviceIdleController.MyHandler getHandler(DeviceIdleController controller) {
-            return mock(DeviceIdleController.MyHandler.class);
+            return mock(DeviceIdleController.MyHandler.class, Answers.RETURNS_DEEP_STUBS);
         }
 
         @Override
         PowerManager getPowerManager() {
             return mPowerManager;
         }
+
+        @Override
+        SensorManager getSensorManager() {
+            return mSensorManager;
+        }
+
+        @Override
+        ConstraintController getConstraintController(
+                Handler handler, DeviceIdleController.LocalService localService) {
+            return constraintController;
+        }
+
+        @Override
+        boolean useMotionSensor() {
+            return true;
+        }
     }
 
     private class AnyMotionDetectorForTest extends AnyMotionDetector {
         boolean isMonitoring = false;
 
         AnyMotionDetectorForTest() {
-            super(mPowerManager, mock(Handler.class), mock(SensorManager.class),
+            super(mPowerManager, mock(Handler.class), mSensorManager,
                     mock(DeviceIdleCallback.class), 0.5f);
         }
 
         @Override
+        public boolean hasSensor() {
+            return true;
+        }
+
+        @Override
         public void checkForAnyMotion() {
             isMonitoring = true;
         }
@@ -201,8 +224,10 @@
         mMockingSession = mockitoSession()
                 .initMocks(this)
                 .strictness(Strictness.LENIENT)
-                .mockStatic(LocalServices.class)
+                .spyStatic(LocalServices.class)
                 .startMocking();
+        spyOn(getContext());
+        doReturn(null).when(getContext()).registerReceiver(any(), any());
         doReturn(mock(ActivityManagerInternal.class))
                 .when(() -> LocalServices.getService(ActivityManagerInternal.class));
         doReturn(mock(ActivityTaskManagerInternal.class))
@@ -216,9 +241,13 @@
         when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock);
         doNothing().when(mWakeLock).acquire();
         doNothing().when(mAlarmManager).set(anyInt(), anyLong(), anyString(), any(), any());
+        doReturn(mock(Sensor.class)).when(mSensorManager)
+                .getDefaultSensor(eq(Sensor.TYPE_SIGNIFICANT_MOTION), eq(true));
+        doReturn(true).when(mSensorManager).registerListener(any(), any(), anyInt());
         mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper());
         mAnyMotionDetector = new AnyMotionDetectorForTest();
         mInjector = new InjectorForTest(getContext());
+        doNothing().when(mContentResolver).registerContentObserver(any(), anyBoolean(), any());
         mDeviceIdleController = new DeviceIdleController(getContext(), mInjector);
         spyOn(mDeviceIdleController);
         doNothing().when(mDeviceIdleController).publishBinderService(any(), any());
@@ -226,6 +255,10 @@
         mDeviceIdleController.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
         mDeviceIdleController.setDeepEnabledForTest(true);
         mDeviceIdleController.setLightEnabledForTest(true);
+
+        // Get the same Constants object that mDeviceIdleController got.
+        mConstants = mInjector.getConstants(mDeviceIdleController,
+                mInjector.getHandler(mDeviceIdleController), mContentResolver);
     }
 
     @After
@@ -233,9 +266,10 @@
         if (mMockingSession != null) {
             mMockingSession.finishMocking();
         }
-        // DeviceIdleController adds this to LocalServices in the constructor, so we have to remove
-        // it after each test, otherwise, subsequent tests will fail.
+        // DeviceIdleController adds these to LocalServices in the constructor, so we have to remove
+        // them after each test, otherwise, subsequent tests will fail.
         LocalServices.removeServiceForTest(AppStateTracker.class);
+        LocalServices.removeServiceForTest(DeviceIdleController.LocalService.class);
     }
 
     @Test
@@ -1454,8 +1488,8 @@
 
     private void setAlarmSoon(boolean isSoon) {
         if (isSoon) {
-            doReturn(SystemClock.elapsedRealtime() + mConstants.MIN_TIME_TO_ALARM / 2).when(
-                    mAlarmManager).getNextWakeFromIdleTime();
+            doReturn(SystemClock.elapsedRealtime() + mConstants.MIN_TIME_TO_ALARM / 2)
+                    .when(mAlarmManager).getNextWakeFromIdleTime();
         } else {
             doReturn(Long.MAX_VALUE).when(mAlarmManager).getNextWakeFromIdleTime();
         }
@@ -1479,27 +1513,36 @@
                 assertFalse(mDeviceIdleController.isScreenOn());
                 break;
             case STATE_IDLE_PENDING:
-                assertTrue(mDeviceIdleController.mMotionListener.isActive());
+                assertEquals(
+                        mDeviceIdleController.hasMotionSensor(),
+                        mDeviceIdleController.mMotionListener.isActive());
                 assertFalse(mAnyMotionDetector.isMonitoring);
                 assertFalse(mDeviceIdleController.isCharging());
                 assertFalse(mDeviceIdleController.isScreenOn());
                 break;
             case STATE_SENSING:
-                assertTrue(mDeviceIdleController.mMotionListener.isActive());
-                assertTrue(mAnyMotionDetector.isMonitoring);
+                assertEquals(
+                        mDeviceIdleController.hasMotionSensor(),
+                        mDeviceIdleController.mMotionListener.isActive());
+                assertEquals(
+                        mDeviceIdleController.hasMotionSensor(),
+                        mAnyMotionDetector.isMonitoring);
                 assertFalse(mDeviceIdleController.isCharging());
                 assertFalse(mDeviceIdleController.isScreenOn());
                 break;
             case STATE_LOCATING:
-                assertTrue(mDeviceIdleController.mMotionListener.isActive());
-                assertTrue(mAnyMotionDetector.isMonitoring);
+                assertEquals(
+                        mDeviceIdleController.hasMotionSensor(),
+                        mDeviceIdleController.mMotionListener.isActive());
                 assertFalse(mDeviceIdleController.isCharging());
                 assertFalse(mDeviceIdleController.isScreenOn());
                 break;
             case STATE_IDLE:
-                assertTrue(mDeviceIdleController.mMotionListener.isActive()
+                if (mDeviceIdleController.hasMotionSensor()) {
+                    assertTrue(mDeviceIdleController.mMotionListener.isActive()
                         // If quick doze is enabled, the motion listener should NOT be active.
                         || mDeviceIdleController.isQuickDozeEnabled());
+                }
                 assertFalse(mAnyMotionDetector.isMonitoring);
                 assertFalse(mDeviceIdleController.isCharging());
                 assertFalse(mDeviceIdleController.isScreenOn());
@@ -1507,9 +1550,11 @@
                 verifyLightStateConditions(LIGHT_STATE_OVERRIDE);
                 break;
             case STATE_IDLE_MAINTENANCE:
-                assertTrue(mDeviceIdleController.mMotionListener.isActive()
+                if (mDeviceIdleController.hasMotionSensor()) {
+                    assertTrue(mDeviceIdleController.mMotionListener.isActive()
                         // If quick doze is enabled, the motion listener should NOT be active.
                         || mDeviceIdleController.isQuickDozeEnabled());
+                }
                 assertFalse(mAnyMotionDetector.isMonitoring);
                 assertFalse(mDeviceIdleController.isCharging());
                 assertFalse(mDeviceIdleController.isScreenOn());
diff --git a/services/tests/mockingservicestests/src/com/android/server/deviceidle/BluetoothConstraintTest.java b/services/tests/mockingservicestests/src/com/android/server/deviceidle/BluetoothConstraintTest.java
new file mode 100644
index 0000000..f74ac1f
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/deviceidle/BluetoothConstraintTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.deviceidle;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.DeviceIdleController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.util.Arrays;
+
+/**
+ * Tests for {@link com.android.server.deviceidle.BluetoothConstraint}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class BluetoothConstraintTest {
+
+    private MockitoSession mMockingSession;
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Context mContext;
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Handler mHandler;
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private BluetoothManager mBluetoothManager;
+
+    @Mock
+    private DeviceIdleController.LocalService mDeviceIdleService;
+
+    private BluetoothConstraint mConstraint;
+
+    @Before
+    public void setUp() {
+        mMockingSession = mockitoSession()
+                .initMocks(this)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
+        doReturn(mBluetoothManager)
+                .when(mContext).getSystemService(BluetoothManager.class);
+        mConstraint = new BluetoothConstraint(mContext, mHandler, mDeviceIdleService);
+    }
+
+    @After
+    public void tearDown() {
+        if (mMockingSession != null) {
+            mMockingSession.finishMocking();
+        }
+    }
+
+    @Test
+    public void testIsConnected_noBluetoothAdapter() {
+        doReturn(null).when(mBluetoothManager).getAdapter();
+        assertFalse(BluetoothConstraint.isBluetoothConnected(mBluetoothManager));
+    }
+
+    @Test
+    public void testIsConnected_noConnectedDevice() {
+        enableBluetooth(true);
+        assertFalse(BluetoothConstraint.isBluetoothConnected(mBluetoothManager));
+    }
+
+    @Test
+    public void testIsConnected_twoConnectedDevices() {
+        enableBluetooth(true, mock(BluetoothDevice.class), mock(BluetoothDevice.class));
+        assertTrue(BluetoothConstraint.isBluetoothConnected(mBluetoothManager));
+    }
+
+    @Test
+    public void testStartMonitoring_updatesActiveAtCorrectTimes() {
+        // First setup -> no callbacks should fire.
+        BluetoothConstraint constraint = mConstraint;
+        verify(mDeviceIdleService, never()).onConstraintStateChanged(any(), anyBoolean());
+        verify(mContext, never()).registerReceiver(eq(constraint.mReceiver), any());
+
+        InOrder order = inOrder(mDeviceIdleService);
+
+        // No devices -> active=false should be triggered.
+        enableBluetooth(true);
+        constraint.startMonitoring();
+        order.verify(mDeviceIdleService, times(1)).onConstraintStateChanged(any(), eq(false));
+
+        // One device -> active=true should be triggered.
+        enableBluetooth(true, mock(BluetoothDevice.class));
+        constraint.mReceiver.onReceive(
+                mContext, new Intent(BluetoothDevice.ACTION_ACL_CONNECTED));
+        constraint.startMonitoring();
+        order.verify(mDeviceIdleService, times(1)).exitIdle(eq("bluetooth"));
+
+        // Stop monitoring -> broadcast receiver should be unregistered.
+        constraint.stopMonitoring();
+        verify(mContext, times(1)).unregisterReceiver(eq(constraint.mReceiver));
+        order.verifyNoMoreInteractions();
+
+    }
+
+    private void enableBluetooth(boolean enabled, BluetoothDevice... devices) {
+        when(mBluetoothManager.getAdapter().isEnabled()).thenReturn(enabled);
+        when(mBluetoothManager.getConnectedDevices(eq(BluetoothProfile.GATT)))
+                .thenReturn(Arrays.asList(devices));
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index effb5a7..f1cd0cd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -30,6 +30,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -62,6 +63,7 @@
 import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.JobSchedulerService.Constants;
+import com.android.server.job.controllers.QuotaController.ExecutionStats;
 import com.android.server.job.controllers.QuotaController.TimingSession;
 
 import org.junit.After;
@@ -131,13 +133,18 @@
         doReturn(mock(PackageManagerInternal.class))
                 .when(() -> LocalServices.getService(PackageManagerInternal.class));
 
-        // Freeze the clocks at this moment in time
+        // Freeze the clocks at 24 hours after this moment in time. Several tests create sessions
+        // in the past, and QuotaController sometimes floors values at 0, so if the test time
+        // causes sessions with negative timestamps, they will fail.
         JobSchedulerService.sSystemClock =
-                Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
-        JobSchedulerService.sUptimeMillisClock =
-                Clock.fixed(SystemClock.uptimeMillisClock().instant(), ZoneOffset.UTC);
-        JobSchedulerService.sElapsedRealtimeClock =
-                Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
+                getAdvancedClock(Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC),
+                        24 * HOUR_IN_MILLIS);
+        JobSchedulerService.sUptimeMillisClock = getAdvancedClock(
+                Clock.fixed(SystemClock.uptimeMillisClock().instant(), ZoneOffset.UTC),
+                24 * HOUR_IN_MILLIS);
+        JobSchedulerService.sElapsedRealtimeClock = getAdvancedClock(
+                Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC),
+                24 * HOUR_IN_MILLIS);
 
         // Initialize real objects.
         // Capture the listeners.
@@ -291,9 +298,17 @@
         mQuotaController.saveTimingSession(0, "com.android.test.stay", two);
         mQuotaController.saveTimingSession(0, "com.android.test.stay", one);
 
+        ExecutionStats expectedStats = new ExecutionStats();
+        expectedStats.invalidTimeElapsed = now + 24 * HOUR_IN_MILLIS;
+        expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
+
         mQuotaController.onAppRemovedLocked("com.android.test.remove", 10001);
         assertNull(mQuotaController.getTimingSessions(0, "com.android.test.remove"));
         assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test.stay"));
+        assertEquals(expectedStats,
+                mQuotaController.getExecutionStatsLocked(0, "com.android.test.remove", RARE_INDEX));
+        assertNotEquals(expectedStats,
+                mQuotaController.getExecutionStatsLocked(0, "com.android.test.stay", RARE_INDEX));
     }
 
     @Test
@@ -318,13 +333,21 @@
         mQuotaController.saveTimingSession(10, "com.android.test", two);
         mQuotaController.saveTimingSession(10, "com.android.test", one);
 
+        ExecutionStats expectedStats = new ExecutionStats();
+        expectedStats.invalidTimeElapsed = now + 24 * HOUR_IN_MILLIS;
+        expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
+
         mQuotaController.onUserRemovedLocked(0);
         assertNull(mQuotaController.getTimingSessions(0, "com.android.test"));
         assertEquals(expected, mQuotaController.getTimingSessions(10, "com.android.test"));
+        assertEquals(expectedStats,
+                mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX));
+        assertNotEquals(expectedStats,
+                mQuotaController.getExecutionStatsLocked(10, "com.android.test", RARE_INDEX));
     }
 
     @Test
-    public void testGetTrailingExecutionTimeLocked_NoTimer() {
+    public void testUpdateExecutionStatsLocked_NoTimer() {
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
         // Added in chronological order.
         mQuotaController.saveTimingSession(0, "com.android.test",
@@ -340,32 +363,288 @@
         mQuotaController.saveTimingSession(0, "com.android.test",
                 createTimingSession(now - 5 * MINUTE_IN_MILLIS, 4 * MINUTE_IN_MILLIS, 3));
 
-        assertEquals(0, mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
-                MINUTE_IN_MILLIS));
-        assertEquals(2 * MINUTE_IN_MILLIS,
-                mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
-                        3 * MINUTE_IN_MILLIS));
-        assertEquals(4 * MINUTE_IN_MILLIS,
-                mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
-                        5 * MINUTE_IN_MILLIS));
-        assertEquals(4 * MINUTE_IN_MILLIS,
-                mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
-                        49 * MINUTE_IN_MILLIS));
-        assertEquals(5 * MINUTE_IN_MILLIS,
-                mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
-                        50 * MINUTE_IN_MILLIS));
-        assertEquals(6 * MINUTE_IN_MILLIS,
-                mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
-                        HOUR_IN_MILLIS));
-        assertEquals(11 * MINUTE_IN_MILLIS,
-                mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
-                        2 * HOUR_IN_MILLIS));
-        assertEquals(12 * MINUTE_IN_MILLIS,
-                mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
-                        3 * HOUR_IN_MILLIS));
-        assertEquals(22 * MINUTE_IN_MILLIS,
-                mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
-                        6 * HOUR_IN_MILLIS));
+        // Test an app that hasn't had any activity.
+        ExecutionStats expectedStats = new ExecutionStats();
+        ExecutionStats inputStats = new ExecutionStats();
+
+        inputStats.windowSizeMs = expectedStats.windowSizeMs = 12 * HOUR_IN_MILLIS;
+        // Invalid time is now +24 hours since there are no sessions at all for the app.
+        expectedStats.invalidTimeElapsed = now + 24 * HOUR_IN_MILLIS;
+        mQuotaController.updateExecutionStatsLocked(0, "com.android.test.not.run", inputStats);
+        assertEquals(expectedStats, inputStats);
+
+        inputStats.windowSizeMs = expectedStats.windowSizeMs = MINUTE_IN_MILLIS;
+        // Invalid time is now +18 hours since there are no sessions in the window but the earliest
+        // session is 6 hours ago.
+        expectedStats.invalidTimeElapsed = now + 18 * HOUR_IN_MILLIS;
+        expectedStats.executionTimeInWindowMs = 0;
+        expectedStats.bgJobCountInWindow = 0;
+        expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 15;
+        mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+        assertEquals(expectedStats, inputStats);
+
+        inputStats.windowSizeMs = expectedStats.windowSizeMs = 3 * MINUTE_IN_MILLIS;
+        // Invalid time is now since the session straddles the window cutoff time.
+        expectedStats.invalidTimeElapsed = now;
+        expectedStats.executionTimeInWindowMs = 2 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 3;
+        expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 15;
+        mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+        assertEquals(expectedStats, inputStats);
+
+        inputStats.windowSizeMs = expectedStats.windowSizeMs = 5 * MINUTE_IN_MILLIS;
+        // Invalid time is now since the start of the session is at the very edge of the window
+        // cutoff time.
+        expectedStats.invalidTimeElapsed = now;
+        expectedStats.executionTimeInWindowMs = 4 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 3;
+        expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 15;
+        mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+        assertEquals(expectedStats, inputStats);
+
+        inputStats.windowSizeMs = expectedStats.windowSizeMs = 49 * MINUTE_IN_MILLIS;
+        // Invalid time is now +44 minutes since the earliest session in the window is now-5
+        // minutes.
+        expectedStats.invalidTimeElapsed = now + 44 * MINUTE_IN_MILLIS;
+        expectedStats.executionTimeInWindowMs = 4 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 3;
+        expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 15;
+        mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+        assertEquals(expectedStats, inputStats);
+
+        inputStats.windowSizeMs = expectedStats.windowSizeMs = 50 * MINUTE_IN_MILLIS;
+        // Invalid time is now since the session is at the very edge of the window cutoff time.
+        expectedStats.invalidTimeElapsed = now;
+        expectedStats.executionTimeInWindowMs = 5 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 4;
+        expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 15;
+        mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+        assertEquals(expectedStats, inputStats);
+
+        inputStats.windowSizeMs = expectedStats.windowSizeMs = HOUR_IN_MILLIS;
+        // Invalid time is now since the start of the session is at the very edge of the window
+        // cutoff time.
+        expectedStats.invalidTimeElapsed = now;
+        expectedStats.executionTimeInWindowMs = 6 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 5;
+        expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 15;
+        mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+        assertEquals(expectedStats, inputStats);
+
+        inputStats.windowSizeMs = expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
+        // Invalid time is now since the session straddles the window cutoff time.
+        expectedStats.invalidTimeElapsed = now;
+        expectedStats.executionTimeInWindowMs = 11 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 10;
+        expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 15;
+        expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
+                + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+        mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+        assertEquals(expectedStats, inputStats);
+
+        inputStats.windowSizeMs = expectedStats.windowSizeMs = 3 * HOUR_IN_MILLIS;
+        // Invalid time is now +59 minutes since the earliest session in the window is now-121
+        // minutes.
+        expectedStats.invalidTimeElapsed = now + 59 * MINUTE_IN_MILLIS;
+        expectedStats.executionTimeInWindowMs = 12 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 10;
+        expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 15;
+        expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
+                + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+        mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+        assertEquals(expectedStats, inputStats);
+
+        inputStats.windowSizeMs = expectedStats.windowSizeMs = 6 * HOUR_IN_MILLIS;
+        // Invalid time is now since the start of the session is at the very edge of the window
+        // cutoff time.
+        expectedStats.invalidTimeElapsed = now;
+        expectedStats.executionTimeInWindowMs = 22 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 15;
+        expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 15;
+        expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
+                + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+        mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+        assertEquals(expectedStats, inputStats);
+
+        // Make sure invalidTimeElapsed is set correctly when it's dependent on the max period.
+        mQuotaController.getTimingSessions(0, "com.android.test")
+                .add(0,
+                        createTimingSession(now - (23 * HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 3));
+        inputStats.windowSizeMs = expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
+        // Invalid time is now +1 hour since the earliest session in the max period is 1 hour
+        // before the end of the max period cutoff time.
+        expectedStats.invalidTimeElapsed = now + HOUR_IN_MILLIS;
+        expectedStats.executionTimeInWindowMs = 22 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 15;
+        expectedStats.executionTimeInMaxPeriodMs = 23 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 18;
+        expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
+                + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+        mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+        assertEquals(expectedStats, inputStats);
+
+        mQuotaController.getTimingSessions(0, "com.android.test")
+                .add(0,
+                        createTimingSession(now - (24 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS),
+                                2 * MINUTE_IN_MILLIS, 2));
+        inputStats.windowSizeMs = expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
+        // Invalid time is now since the earlist session straddles the max period cutoff time.
+        expectedStats.invalidTimeElapsed = now;
+        expectedStats.executionTimeInWindowMs = 22 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 15;
+        expectedStats.executionTimeInMaxPeriodMs = 24 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 20;
+        expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
+                + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+        mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+        assertEquals(expectedStats, inputStats);
+    }
+
+    /**
+     * Tests that getExecutionStatsLocked returns the correct stats.
+     */
+    @Test
+    public void testGetExecutionStatsLocked_Values() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
+
+        ExecutionStats expectedStats = new ExecutionStats();
+
+        // Active
+        expectedStats.windowSizeMs = 10 * MINUTE_IN_MILLIS;
+        expectedStats.invalidTimeElapsed = now + 4 * MINUTE_IN_MILLIS;
+        expectedStats.executionTimeInWindowMs = 3 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 5;
+        expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 20;
+        assertEquals(expectedStats,
+                mQuotaController.getExecutionStatsLocked(0, "com.android.test", ACTIVE_INDEX));
+
+        // Working
+        expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
+        expectedStats.invalidTimeElapsed = now;
+        expectedStats.executionTimeInWindowMs = 13 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 10;
+        expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 20;
+        expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS)
+                + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+        assertEquals(expectedStats,
+                mQuotaController.getExecutionStatsLocked(0, "com.android.test", WORKING_INDEX));
+
+        // Frequent
+        expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
+        expectedStats.invalidTimeElapsed = now + HOUR_IN_MILLIS;
+        expectedStats.executionTimeInWindowMs = 23 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 15;
+        expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 20;
+        expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS)
+                + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+        assertEquals(expectedStats,
+                mQuotaController.getExecutionStatsLocked(0, "com.android.test", FREQUENT_INDEX));
+
+        // Rare
+        expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
+        expectedStats.invalidTimeElapsed = now + HOUR_IN_MILLIS;
+        expectedStats.executionTimeInWindowMs = 33 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 20;
+        expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 20;
+        expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS)
+                + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+        assertEquals(expectedStats,
+                mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX));
+    }
+
+    /**
+     * Tests that getExecutionStatsLocked properly caches the stats and returns the cached object.
+     */
+    @Test
+    public void testGetExecutionStatsLocked_Caching() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
+        final ExecutionStats originalStatsActive = mQuotaController.getExecutionStatsLocked(0,
+                "com.android.test", ACTIVE_INDEX);
+        final ExecutionStats originalStatsWorking = mQuotaController.getExecutionStatsLocked(0,
+                "com.android.test", WORKING_INDEX);
+        final ExecutionStats originalStatsFrequent = mQuotaController.getExecutionStatsLocked(0,
+                "com.android.test", FREQUENT_INDEX);
+        final ExecutionStats originalStatsRare = mQuotaController.getExecutionStatsLocked(0,
+                "com.android.test", RARE_INDEX);
+
+        // Advance clock so that the working stats shouldn't be the same.
+        advanceElapsedClock(MINUTE_IN_MILLIS);
+        // Change frequent bucket size so that the stats need to be recalculated.
+        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = 6 * HOUR_IN_MILLIS;
+        mQuotaController.onConstantsUpdatedLocked();
+
+        ExecutionStats expectedStats = new ExecutionStats();
+        expectedStats.windowSizeMs = originalStatsActive.windowSizeMs;
+        expectedStats.invalidTimeElapsed = originalStatsActive.invalidTimeElapsed;
+        expectedStats.executionTimeInWindowMs = originalStatsActive.executionTimeInWindowMs;
+        expectedStats.bgJobCountInWindow = originalStatsActive.bgJobCountInWindow;
+        expectedStats.executionTimeInMaxPeriodMs = originalStatsActive.executionTimeInMaxPeriodMs;
+        expectedStats.bgJobCountInMaxPeriod = originalStatsActive.bgJobCountInMaxPeriod;
+        expectedStats.quotaCutoffTimeElapsed = originalStatsActive.quotaCutoffTimeElapsed;
+        final ExecutionStats newStatsActive = mQuotaController.getExecutionStatsLocked(0,
+                "com.android.test", ACTIVE_INDEX);
+        // Stats for the same bucket should use the same object.
+        assertTrue(originalStatsActive == newStatsActive);
+        assertEquals(expectedStats, newStatsActive);
+
+        expectedStats.windowSizeMs = originalStatsWorking.windowSizeMs;
+        expectedStats.invalidTimeElapsed = originalStatsWorking.invalidTimeElapsed;
+        expectedStats.executionTimeInWindowMs = originalStatsWorking.executionTimeInWindowMs;
+        expectedStats.bgJobCountInWindow = originalStatsWorking.bgJobCountInWindow;
+        expectedStats.quotaCutoffTimeElapsed = originalStatsWorking.quotaCutoffTimeElapsed;
+        final ExecutionStats newStatsWorking = mQuotaController.getExecutionStatsLocked(0,
+                "com.android.test", WORKING_INDEX);
+        assertTrue(originalStatsWorking == newStatsWorking);
+        assertNotEquals(expectedStats, newStatsWorking);
+
+        expectedStats.windowSizeMs = originalStatsFrequent.windowSizeMs;
+        expectedStats.invalidTimeElapsed = originalStatsFrequent.invalidTimeElapsed;
+        expectedStats.executionTimeInWindowMs = originalStatsFrequent.executionTimeInWindowMs;
+        expectedStats.bgJobCountInWindow = originalStatsFrequent.bgJobCountInWindow;
+        expectedStats.quotaCutoffTimeElapsed = originalStatsFrequent.quotaCutoffTimeElapsed;
+        final ExecutionStats newStatsFrequent = mQuotaController.getExecutionStatsLocked(0,
+                "com.android.test", FREQUENT_INDEX);
+        assertTrue(originalStatsFrequent == newStatsFrequent);
+        assertNotEquals(expectedStats, newStatsFrequent);
+
+        expectedStats.windowSizeMs = originalStatsRare.windowSizeMs;
+        expectedStats.invalidTimeElapsed = originalStatsRare.invalidTimeElapsed;
+        expectedStats.executionTimeInWindowMs = originalStatsRare.executionTimeInWindowMs;
+        expectedStats.bgJobCountInWindow = originalStatsRare.bgJobCountInWindow;
+        expectedStats.quotaCutoffTimeElapsed = originalStatsRare.quotaCutoffTimeElapsed;
+        final ExecutionStats newStatsRare = mQuotaController.getExecutionStatsLocked(0,
+                "com.android.test", RARE_INDEX);
+        assertTrue(originalStatsRare == newStatsRare);
+        assertEquals(expectedStats, newStatsRare);
     }
 
     @Test
@@ -394,6 +673,56 @@
     }
 
     @Test
+    public void testMaybeScheduleStartAlarmLocked_Active() {
+        // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
+        // because it schedules an alarm too. Prevent it from doing so.
+        spyOn(mQuotaController);
+        doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+
+        // Active window size is 10 minutes.
+        final int standbyBucket = ACTIVE_INDEX;
+
+        // No sessions saved yet.
+        mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
+                standbyBucket);
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        // Test with timing sessions out of window but still under max execution limit.
+        final long expectedAlarmTime =
+                (now - 18 * HOUR_IN_MILLIS) + 24 * HOUR_IN_MILLIS
+                        + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - 18 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1));
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - 12 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1));
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - 7 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1));
+        mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
+                standbyBucket);
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - 2 * HOUR_IN_MILLIS, 55 * MINUTE_IN_MILLIS, 1));
+        mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
+                standbyBucket);
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+        JobStatus jobStatus = createJobStatus("testMaybeScheduleStartAlarmLocked_Active", 1);
+        mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+        mQuotaController.prepareForExecutionLocked(jobStatus);
+        advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+        // Timer has only been going for 5 minutes in the past 10 minutes, which is under the window
+        // size limit, but the total execution time for the past 24 hours is 6 hours, so the job no
+        // longer has quota.
+        assertEquals(0, mQuotaController.getRemainingExecutionTimeLocked(jobStatus));
+        mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
+                standbyBucket);
+        verify(mAlarmManager, times(1)).set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK),
+                any(), any());
+    }
+
+    @Test
     public void testMaybeScheduleStartAlarmLocked_WorkingSet() {
         // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
         // because it schedules an alarm too. Prevent it from doing so.
@@ -579,8 +908,8 @@
 
         // Start in ACTIVE bucket.
         mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", ACTIVE_INDEX);
-        inOrder.verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(),
-                any());
+        inOrder.verify(mAlarmManager, never())
+                .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
         inOrder.verify(mAlarmManager, never()).cancel(any(AlarmManager.OnAlarmListener.class));
 
         // And down from there.
@@ -620,6 +949,124 @@
         inOrder.verify(mAlarmManager, times(1)).cancel(any(AlarmManager.OnAlarmListener.class));
     }
 
+
+    /**
+     * Tests that the start alarm is properly rescheduled if the earliest session that contributes
+     * to the app being out of quota contributes less than the quota buffer time.
+     */
+    @Test
+    public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_DefaultValues() {
+        // Use the default values
+        runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck();
+        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
+        runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck();
+    }
+
+    @Test
+    public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedBufferSize() {
+        // Make sure any new value is used correctly.
+        mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS *= 2;
+        mQuotaController.onConstantsUpdatedLocked();
+        runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck();
+        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
+        runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck();
+    }
+
+    @Test
+    public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedAllowedTime() {
+        // Make sure any new value is used correctly.
+        mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS /= 2;
+        mQuotaController.onConstantsUpdatedLocked();
+        runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck();
+        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
+        runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck();
+    }
+
+    @Test
+    public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedMaxTime() {
+        // Make sure any new value is used correctly.
+        mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS /= 2;
+        mQuotaController.onConstantsUpdatedLocked();
+        runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck();
+        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
+        runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck();
+    }
+
+    @Test
+    public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedEverything() {
+        // Make sure any new value is used correctly.
+        mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS *= 2;
+        mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS /= 2;
+        mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS /= 2;
+        mQuotaController.onConstantsUpdatedLocked();
+        runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck();
+        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
+        runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck();
+    }
+
+    private void runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck() {
+        // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
+        // because it schedules an alarm too. Prevent it from doing so.
+        spyOn(mQuotaController);
+        doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        // Working set window size is 2 hours.
+        final int standbyBucket = WORKING_INDEX;
+        final long contributionMs = mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS / 2;
+        final long remainingTimeMs =
+                mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS - contributionMs;
+
+        // Session straddles edge of bucket window. Only the contribution should be counted towards
+        // the quota.
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (2 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS),
+                        3 * MINUTE_IN_MILLIS + contributionMs, 3));
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - HOUR_IN_MILLIS, remainingTimeMs, 2));
+        // Expected alarm time should be when the app will have QUOTA_BUFFER_MS time of quota, which
+        // is 2 hours + (QUOTA_BUFFER_MS - contributionMs) after the start of the second session.
+        final long expectedAlarmTime = now - HOUR_IN_MILLIS + 2 * HOUR_IN_MILLIS
+                + (mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS - contributionMs);
+        mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
+                standbyBucket);
+        verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+    }
+
+
+    private void runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck() {
+        // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
+        // because it schedules an alarm too. Prevent it from doing so.
+        spyOn(mQuotaController);
+        doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        // Working set window size is 2 hours.
+        final int standbyBucket = WORKING_INDEX;
+        final long contributionMs = mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS / 2;
+        final long remainingTimeMs =
+                mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS - contributionMs;
+
+        // Session straddles edge of 24 hour window. Only the contribution should be counted towards
+        // the quota.
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (24 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS),
+                        3 * MINUTE_IN_MILLIS + contributionMs, 3));
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - 20 * HOUR_IN_MILLIS, remainingTimeMs, 300));
+        // Expected alarm time should be when the app will have QUOTA_BUFFER_MS time of quota, which
+        // is 24 hours + (QUOTA_BUFFER_MS - contributionMs) after the start of the second session.
+        final long expectedAlarmTime = now - 20 * HOUR_IN_MILLIS
+                //+ mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS
+                + 24 * HOUR_IN_MILLIS
+                + (mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS - contributionMs);
+        mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
+                standbyBucket);
+        verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+    }
+
     /** Tests that QuotaController doesn't throttle if throttling is turned off. */
     @Test
     public void testThrottleToggling() throws Exception {
@@ -652,6 +1099,7 @@
         mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = 30 * MINUTE_IN_MILLIS;
         mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = 45 * MINUTE_IN_MILLIS;
         mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = 60 * MINUTE_IN_MILLIS;
+        mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = 3 * HOUR_IN_MILLIS;
 
         mQuotaController.onConstantsUpdatedLocked();
 
@@ -662,6 +1110,7 @@
         assertEquals(45 * MINUTE_IN_MILLIS,
                 mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
         assertEquals(60 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
+        assertEquals(3 * HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
     }
 
     @Test
@@ -673,6 +1122,7 @@
         mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = -MINUTE_IN_MILLIS;
         mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = -MINUTE_IN_MILLIS;
         mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = -MINUTE_IN_MILLIS;
+        mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = -MINUTE_IN_MILLIS;
 
         mQuotaController.onConstantsUpdatedLocked();
 
@@ -682,6 +1132,7 @@
         assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
         assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
         assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
+        assertEquals(HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
 
         // Test larger than a day. Controller should cap at one day.
         mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = 25 * HOUR_IN_MILLIS;
@@ -690,6 +1141,7 @@
         mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = 25 * HOUR_IN_MILLIS;
         mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = 25 * HOUR_IN_MILLIS;
         mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = 25 * HOUR_IN_MILLIS;
+        mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = 25 * HOUR_IN_MILLIS;
 
         mQuotaController.onConstantsUpdatedLocked();
 
@@ -699,6 +1151,7 @@
         assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
         assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
         assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
+        assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
     }
 
     /** Tests that TimingSessions aren't saved when the device is charging. */
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
new file mode 100644
index 0000000..494677d
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
@@ -0,0 +1,886 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.job.controllers;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.AlarmManager;
+import android.app.job.JobInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManagerInternal;
+import android.os.SystemClock;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.job.JobSchedulerService;
+import com.android.server.job.JobSchedulerService.Constants;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.time.Clock;
+import java.time.Duration;
+import java.time.ZoneOffset;
+
+@RunWith(AndroidJUnit4.class)
+public class TimeControllerTest {
+    private static final long SECOND_IN_MILLIS = 1000L;
+    private static final long MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS;
+    private static final long HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS;
+    private static final String TAG_DEADLINE = "*job.deadline*";
+    private static final String TAG_DELAY = "*job.delay*";
+    private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
+    private static final int SOURCE_USER_ID = 0;
+
+    private Constants mConstants;
+    private TimeController mTimeController;
+
+    private MockitoSession mMockingSession;
+    @Mock
+    private AlarmManager mAlarmManager;
+    @Mock
+    private Context mContext;
+    @Mock
+    private JobSchedulerService mJobSchedulerService;
+
+    @Before
+    public void setUp() {
+        mMockingSession = mockitoSession()
+                .initMocks(this)
+                .strictness(Strictness.LENIENT)
+                .mockStatic(LocalServices.class)
+                .startMocking();
+        // Use default constants for now.
+        mConstants = new Constants();
+
+        // Called in StateController constructor.
+        when(mJobSchedulerService.getTestableContext()).thenReturn(mContext);
+        when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService);
+        when(mJobSchedulerService.getConstants()).thenReturn(mConstants);
+        // Called in TimeController constructor.
+        when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager);
+        // Used in JobStatus.
+        doReturn(mock(PackageManagerInternal.class))
+                .when(() -> LocalServices.getService(PackageManagerInternal.class));
+
+        // Freeze the clocks at this moment in time
+        JobSchedulerService.sSystemClock =
+                Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
+        JobSchedulerService.sUptimeMillisClock =
+                Clock.fixed(SystemClock.uptimeMillisClock().instant(), ZoneOffset.UTC);
+        JobSchedulerService.sElapsedRealtimeClock =
+                Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
+
+        // Initialize real objects.
+        mTimeController = new TimeController(mJobSchedulerService);
+        spyOn(mTimeController);
+    }
+
+    @After
+    public void tearDown() {
+        if (mMockingSession != null) {
+            mMockingSession.finishMocking();
+        }
+    }
+
+    private Clock getAdvancedClock(Clock clock, long incrementMs) {
+        return Clock.offset(clock, Duration.ofMillis(incrementMs));
+    }
+
+    private void advanceElapsedClock(long incrementMs) {
+        JobSchedulerService.sElapsedRealtimeClock = getAdvancedClock(
+                JobSchedulerService.sElapsedRealtimeClock, incrementMs);
+    }
+
+    private static JobInfo.Builder createJob() {
+        return new JobInfo.Builder(101, new ComponentName("foo", "bar"));
+    }
+
+    private JobStatus createJobStatus(String testTag, JobInfo.Builder job) {
+        JobInfo jobInfo = job.build();
+        return JobStatus.createFromJobInfo(
+                jobInfo, 1000, SOURCE_PACKAGE, SOURCE_USER_ID, testTag);
+    }
+
+    @Test
+    public void testMaybeStartTrackingJobLocked_AlreadySatisfied() {
+        JobStatus delaySatisfied = createJobStatus(
+                "testMaybeStartTrackingJobLocked_AlreadySatisfied",
+                createJob().setMinimumLatency(1));
+        JobStatus deadlineSatisfied = createJobStatus(
+                "testMaybeStartTrackingJobLocked_AlreadySatisfied",
+                createJob().setOverrideDeadline(1));
+
+        advanceElapsedClock(5);
+
+        mTimeController.maybeStartTrackingJobLocked(delaySatisfied, null);
+        mTimeController.maybeStartTrackingJobLocked(deadlineSatisfied, null);
+        verify(mAlarmManager, never())
+                .set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any());
+    }
+
+    @Test
+    public void testMaybeStartTrackingJobLocked_DelayInOrder_NoSkipping() {
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
+        mTimeController.onConstantsUpdatedLocked();
+
+        runTestMaybeStartTrackingJobLocked_DelayInOrder();
+    }
+
+    @Test
+    public void testMaybeStartTrackingJobLocked_DelayInOrder_WithSkipping_AllReady() {
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mTimeController.onConstantsUpdatedLocked();
+
+        doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());
+
+        runTestMaybeStartTrackingJobLocked_DelayInOrder();
+    }
+
+    private void runTestMaybeStartTrackingJobLocked_DelayInOrder() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+        JobStatus jobLatest = createJobStatus("testMaybeStartTrackingJobLocked_DelayInOrder",
+                createJob().setMinimumLatency(HOUR_IN_MILLIS));
+        JobStatus jobMiddle = createJobStatus("testMaybeStartTrackingJobLocked_DelayInOrder",
+                createJob().setMinimumLatency(30 * MINUTE_IN_MILLIS));
+        JobStatus jobEarliest = createJobStatus("testMaybeStartTrackingJobLocked_DelayInOrder",
+                createJob().setMinimumLatency(5 * MINUTE_IN_MILLIS));
+
+        InOrder inOrder = inOrder(mAlarmManager);
+
+        mTimeController.maybeStartTrackingJobLocked(jobEarliest, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DELAY),
+                        any(), any(), any());
+        mTimeController.maybeStartTrackingJobLocked(jobMiddle, null);
+        inOrder.verify(mAlarmManager, never())
+                .set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any());
+        mTimeController.maybeStartTrackingJobLocked(jobLatest, null);
+        inOrder.verify(mAlarmManager, never())
+                .set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any());
+    }
+
+    @Test
+    public void testMaybeStartTrackingJobLocked_DelayInOrder_WithSkipping_SomeNotReady() {
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mTimeController.onConstantsUpdatedLocked();
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+        JobStatus jobLatest = createJobStatus("testMaybeStartTrackingJobLocked_DelayInOrder",
+                createJob().setMinimumLatency(HOUR_IN_MILLIS));
+        JobStatus jobMiddle = createJobStatus("testMaybeStartTrackingJobLocked_DelayInOrder",
+                createJob().setMinimumLatency(30 * MINUTE_IN_MILLIS));
+        JobStatus jobEarliest = createJobStatus("testMaybeStartTrackingJobLocked_DelayInOrder",
+                createJob().setMinimumLatency(5 * MINUTE_IN_MILLIS));
+
+        doReturn(true).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobLatest), anyInt());
+        doReturn(false).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobMiddle), anyInt());
+        doReturn(true).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt());
+
+        InOrder inOrder = inOrder(mAlarmManager);
+
+        mTimeController.maybeStartTrackingJobLocked(jobEarliest, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DELAY),
+                        any(), any(), any());
+        mTimeController.maybeStartTrackingJobLocked(jobMiddle, null);
+        inOrder.verify(mAlarmManager, never())
+                .set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any());
+        mTimeController.maybeStartTrackingJobLocked(jobLatest, null);
+        inOrder.verify(mAlarmManager, never())
+                .set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any());
+    }
+
+    @Test
+    public void testMaybeStartTrackingJobLocked_DelayReverseOrder_NoSkipping() {
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
+        mTimeController.onConstantsUpdatedLocked();
+
+        runTestMaybeStartTrackingJobLocked_DelayReverseOrder();
+    }
+
+    @Test
+    public void testMaybeStartTrackingJobLocked_DelayReverseOrder_WithSkipping_AllReady() {
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mTimeController.onConstantsUpdatedLocked();
+
+        doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());
+
+        runTestMaybeStartTrackingJobLocked_DelayReverseOrder();
+    }
+
+    private void runTestMaybeStartTrackingJobLocked_DelayReverseOrder() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+        JobStatus jobLatest = createJobStatus("testMaybeStartTrackingJobLocked_DelayReverseOrder",
+                createJob().setMinimumLatency(HOUR_IN_MILLIS));
+        JobStatus jobMiddle = createJobStatus("testMaybeStartTrackingJobLocked_DelayReverseOrder",
+                createJob().setMinimumLatency(30 * MINUTE_IN_MILLIS));
+        JobStatus jobEarliest = createJobStatus("testMaybeStartTrackingJobLocked_DelayReverseOrder",
+                createJob().setMinimumLatency(5 * MINUTE_IN_MILLIS));
+
+        InOrder inOrder = inOrder(mAlarmManager);
+
+        mTimeController.maybeStartTrackingJobLocked(jobLatest, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + HOUR_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DELAY), any(),
+                        any(), any());
+        mTimeController.maybeStartTrackingJobLocked(jobMiddle, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 30 * MINUTE_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DELAY),
+                        any(), any(), any());
+        mTimeController.maybeStartTrackingJobLocked(jobEarliest, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DELAY),
+                        any(), any(), any());
+    }
+
+    @Test
+    public void testMaybeStartTrackingJobLocked_DelayReverseOrder_WithSkipping_SomeNotReady() {
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mTimeController.onConstantsUpdatedLocked();
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+        JobStatus jobLatest = createJobStatus("testMaybeStartTrackingJobLocked_DelayReverseOrder",
+                createJob().setMinimumLatency(HOUR_IN_MILLIS));
+        JobStatus jobMiddle = createJobStatus("testMaybeStartTrackingJobLocked_DelayReverseOrder",
+                createJob().setMinimumLatency(30 * MINUTE_IN_MILLIS));
+        JobStatus jobEarliest = createJobStatus("testMaybeStartTrackingJobLocked_DelayReverseOrder",
+                createJob().setMinimumLatency(5 * MINUTE_IN_MILLIS));
+
+        doReturn(true).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobLatest), anyInt());
+        doReturn(false).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobMiddle), anyInt());
+        doReturn(true).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt());
+
+        InOrder inOrder = inOrder(mAlarmManager);
+
+        mTimeController.maybeStartTrackingJobLocked(jobLatest, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + HOUR_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DELAY), any(),
+                        any(), any());
+        mTimeController.maybeStartTrackingJobLocked(jobMiddle, null);
+        // Middle alarm shouldn't be set since it won't be ready.
+        inOrder.verify(mAlarmManager, never())
+                .set(anyInt(), anyLong(), anyLong(), anyLong(), eq(TAG_DELAY), any(), any(), any());
+        mTimeController.maybeStartTrackingJobLocked(jobEarliest, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DELAY),
+                        any(), any(), any());
+    }
+
+    @Test
+    public void testMaybeStartTrackingJobLocked_DeadlineInOrder_NoSkipping() {
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
+        mTimeController.onConstantsUpdatedLocked();
+
+        runTestMaybeStartTrackingJobLocked_DeadlineInOrder();
+    }
+
+    @Test
+    public void testMaybeStartTrackingJobLocked_DeadlineInOrder_WithSkipping_AllReady() {
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mTimeController.onConstantsUpdatedLocked();
+
+        doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());
+
+        runTestMaybeStartTrackingJobLocked_DeadlineInOrder();
+    }
+
+    private void runTestMaybeStartTrackingJobLocked_DeadlineInOrder() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+        JobStatus jobLatest = createJobStatus("testMaybeStartTrackingJobLocked_DeadlineInOrder",
+                createJob().setOverrideDeadline(HOUR_IN_MILLIS));
+        JobStatus jobMiddle = createJobStatus("testMaybeStartTrackingJobLocked_DeadlineInOrder",
+                createJob().setOverrideDeadline(30 * MINUTE_IN_MILLIS));
+        JobStatus jobEarliest = createJobStatus("testMaybeStartTrackingJobLocked_DeadlineInOrder",
+                createJob().setOverrideDeadline(5 * MINUTE_IN_MILLIS));
+
+        InOrder inOrder = inOrder(mAlarmManager);
+
+        mTimeController.maybeStartTrackingJobLocked(jobEarliest, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(),
+                        eq(TAG_DEADLINE), any(), any(), any());
+        mTimeController.maybeStartTrackingJobLocked(jobMiddle, null);
+        inOrder.verify(mAlarmManager, never())
+                .set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any());
+        mTimeController.maybeStartTrackingJobLocked(jobLatest, null);
+        inOrder.verify(mAlarmManager, never())
+                .set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any());
+    }
+
+    @Test
+    public void testMaybeStartTrackingJobLocked_DeadlineInOrder_WithSkipping_SomeNotReady() {
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mTimeController.onConstantsUpdatedLocked();
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+        JobStatus jobLatest = createJobStatus("testMaybeStartTrackingJobLocked_DeadlineInOrder",
+                createJob().setOverrideDeadline(HOUR_IN_MILLIS));
+        JobStatus jobMiddle = createJobStatus("testMaybeStartTrackingJobLocked_DeadlineInOrder",
+                createJob().setOverrideDeadline(30 * MINUTE_IN_MILLIS));
+        JobStatus jobEarliest = createJobStatus("testMaybeStartTrackingJobLocked_DeadlineInOrder",
+                createJob().setOverrideDeadline(5 * MINUTE_IN_MILLIS));
+
+        doReturn(true).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobLatest), anyInt());
+        doReturn(false).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobMiddle), anyInt());
+        doReturn(true).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt());
+
+        InOrder inOrder = inOrder(mAlarmManager);
+
+        mTimeController.maybeStartTrackingJobLocked(jobEarliest, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(),
+                        eq(TAG_DEADLINE), any(), any(), any());
+        mTimeController.maybeStartTrackingJobLocked(jobMiddle, null);
+        inOrder.verify(mAlarmManager, never())
+                .set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any());
+        mTimeController.maybeStartTrackingJobLocked(jobLatest, null);
+        inOrder.verify(mAlarmManager, never())
+                .set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any());
+    }
+
+    @Test
+    public void testMaybeStartTrackingJobLocked_DeadlineReverseOrder_NoSkipping() {
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
+        mTimeController.onConstantsUpdatedLocked();
+
+        runTestMaybeStartTrackingJobLocked_DeadlineReverseOrder();
+    }
+
+    @Test
+    public void testMaybeStartTrackingJobLocked_DeadlineReverseOrder_WithSkipping_AllReady() {
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mTimeController.onConstantsUpdatedLocked();
+
+        doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());
+
+        runTestMaybeStartTrackingJobLocked_DeadlineReverseOrder();
+    }
+
+    private void runTestMaybeStartTrackingJobLocked_DeadlineReverseOrder() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+        JobStatus jobLatest = createJobStatus(
+                "testMaybeStartTrackingJobLocked_DeadlineReverseOrder",
+                createJob().setOverrideDeadline(HOUR_IN_MILLIS));
+        JobStatus jobMiddle = createJobStatus(
+                "testMaybeStartTrackingJobLocked_DeadlineReverseOrder",
+                createJob().setOverrideDeadline(30 * MINUTE_IN_MILLIS));
+        JobStatus jobEarliest = createJobStatus(
+                "testMaybeStartTrackingJobLocked_DeadlineReverseOrder",
+                createJob().setOverrideDeadline(5 * MINUTE_IN_MILLIS));
+
+        InOrder inOrder = inOrder(mAlarmManager);
+
+        mTimeController.maybeStartTrackingJobLocked(jobLatest, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + HOUR_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DEADLINE),
+                        any(), any(), any());
+        mTimeController.maybeStartTrackingJobLocked(jobMiddle, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 30 * MINUTE_IN_MILLIS), anyLong(), anyLong(),
+                        eq(TAG_DEADLINE), any(), any(), any());
+        mTimeController.maybeStartTrackingJobLocked(jobEarliest, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(),
+                        eq(TAG_DEADLINE), any(), any(), any());
+    }
+
+    @Test
+    public void testMaybeStartTrackingJobLocked_DeadlineReverseOrder_WithSkipping_SomeNotReady() {
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mTimeController.onConstantsUpdatedLocked();
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+        JobStatus jobLatest = createJobStatus(
+                "testMaybeStartTrackingJobLocked_DeadlineReverseOrder",
+                createJob().setOverrideDeadline(HOUR_IN_MILLIS));
+        JobStatus jobMiddle = createJobStatus(
+                "testMaybeStartTrackingJobLocked_DeadlineReverseOrder",
+                createJob().setOverrideDeadline(30 * MINUTE_IN_MILLIS));
+        JobStatus jobEarliest = createJobStatus(
+                "testMaybeStartTrackingJobLocked_DeadlineReverseOrder",
+                createJob().setOverrideDeadline(5 * MINUTE_IN_MILLIS));
+
+        doReturn(true).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobLatest), anyInt());
+        doReturn(false).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobMiddle), anyInt());
+        doReturn(true).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt());
+
+        InOrder inOrder = inOrder(mAlarmManager);
+
+        mTimeController.maybeStartTrackingJobLocked(jobLatest, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + HOUR_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DEADLINE),
+                        any(), any(), any());
+        mTimeController.maybeStartTrackingJobLocked(jobMiddle, null);
+        // Middle alarm should be skipped since the job wouldn't be ready.
+        inOrder.verify(mAlarmManager, never())
+                .set(anyInt(), anyLong(), anyLong(), anyLong(), eq(TAG_DEADLINE), any(), any(),
+                        any());
+        mTimeController.maybeStartTrackingJobLocked(jobEarliest, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(),
+                        eq(TAG_DEADLINE), any(), any(), any());
+    }
+
+    @Test
+    public void testJobSkipToggling() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+        JobStatus jobLatest = createJobStatus(
+                "testMaybeStartTrackingJobLocked_DeadlineReverseOrder",
+                createJob().setOverrideDeadline(HOUR_IN_MILLIS));
+        JobStatus jobEarliest = createJobStatus(
+                "testMaybeStartTrackingJobLocked_DeadlineReverseOrder",
+                createJob().setOverrideDeadline(5 * MINUTE_IN_MILLIS));
+
+        doReturn(true).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobLatest), anyInt());
+        doReturn(false).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt());
+
+        // Starting off with the skipping off, we should still set an alarm for the earlier job.
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
+        mTimeController.onConstantsUpdatedLocked();
+        InOrder inOrder = inOrder(mAlarmManager);
+
+        mTimeController.maybeStartTrackingJobLocked(jobEarliest, null);
+        mTimeController.maybeStartTrackingJobLocked(jobLatest, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(),
+                        eq(TAG_DEADLINE), any(), any(), any());
+
+        // Turn it on, use alarm for later job.
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mTimeController.onConstantsUpdatedLocked();
+
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + HOUR_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DEADLINE),
+                        any(), any(), any());
+
+        // Back off, use alarm for earlier job.
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
+        mTimeController.onConstantsUpdatedLocked();
+
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(),
+                        eq(TAG_DEADLINE), any(), any(), any());
+    }
+
+    @Test
+    public void testCheckExpiredDelaysAndResetAlarm_NoSkipping() {
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
+        mTimeController.onConstantsUpdatedLocked();
+
+        runTestCheckExpiredDelaysAndResetAlarm();
+    }
+
+    @Test
+    public void testCheckExpiredDelaysAndResetAlarm_WithSkipping_AllReady() {
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mTimeController.onConstantsUpdatedLocked();
+
+        doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());
+
+        runTestCheckExpiredDelaysAndResetAlarm();
+    }
+
+    private void runTestCheckExpiredDelaysAndResetAlarm() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+        JobStatus jobLatest = createJobStatus("testCheckExpiredDelaysAndResetAlarm",
+                createJob().setMinimumLatency(HOUR_IN_MILLIS));
+        JobStatus jobMiddle = createJobStatus("testCheckExpiredDelaysAndResetAlarm",
+                createJob().setMinimumLatency(30 * MINUTE_IN_MILLIS));
+        JobStatus jobEarliest = createJobStatus("testCheckExpiredDelaysAndResetAlarm",
+                createJob().setMinimumLatency(5 * MINUTE_IN_MILLIS));
+
+        InOrder inOrder = inOrder(mAlarmManager);
+
+        mTimeController.maybeStartTrackingJobLocked(jobLatest, null);
+        mTimeController.maybeStartTrackingJobLocked(jobMiddle, null);
+        mTimeController.maybeStartTrackingJobLocked(jobEarliest, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DELAY),
+                        any(), any(), any());
+
+        advanceElapsedClock(10 * MINUTE_IN_MILLIS);
+
+        mTimeController.checkExpiredDelaysAndResetAlarm();
+        assertTrue(jobEarliest.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY));
+        assertFalse(jobMiddle.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY));
+        assertFalse(jobLatest.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY));
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 30 * MINUTE_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DELAY),
+                        any(), any(), any());
+
+        advanceElapsedClock(30 * MINUTE_IN_MILLIS);
+
+        mTimeController.checkExpiredDelaysAndResetAlarm();
+        assertTrue(jobEarliest.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY));
+        assertTrue(jobMiddle.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY));
+        assertFalse(jobLatest.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY));
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + HOUR_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DELAY), any(),
+                        any(), any());
+
+        advanceElapsedClock(30 * MINUTE_IN_MILLIS);
+
+        mTimeController.checkExpiredDelaysAndResetAlarm();
+        assertTrue(jobEarliest.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY));
+        assertTrue(jobMiddle.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY));
+        assertTrue(jobLatest.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY));
+        inOrder.verify(mAlarmManager, never())
+                .set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any());
+    }
+
+    @Test
+    public void testCheckExpiredDelaysAndResetAlarm_WithSkipping_SomeNotReady() {
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mTimeController.onConstantsUpdatedLocked();
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+        JobStatus jobLatest = createJobStatus("testCheckExpiredDelaysAndResetAlarm",
+                createJob().setMinimumLatency(HOUR_IN_MILLIS));
+        JobStatus jobMiddle = createJobStatus("testCheckExpiredDelaysAndResetAlarm",
+                createJob().setMinimumLatency(30 * MINUTE_IN_MILLIS));
+        JobStatus jobEarliest = createJobStatus("testCheckExpiredDelaysAndResetAlarm",
+                createJob().setMinimumLatency(5 * MINUTE_IN_MILLIS));
+
+        doReturn(true).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobLatest), anyInt());
+        doReturn(false).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobMiddle), anyInt());
+        doReturn(true).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt());
+
+        InOrder inOrder = inOrder(mAlarmManager);
+
+        mTimeController.maybeStartTrackingJobLocked(jobLatest, null);
+        mTimeController.maybeStartTrackingJobLocked(jobMiddle, null);
+        mTimeController.maybeStartTrackingJobLocked(jobEarliest, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DELAY),
+                        any(), any(), any());
+
+        advanceElapsedClock(10 * MINUTE_IN_MILLIS);
+
+        mTimeController.checkExpiredDelaysAndResetAlarm();
+        assertTrue(jobEarliest.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY));
+        assertFalse(jobMiddle.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY));
+        assertFalse(jobLatest.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY));
+        // Middle job wouldn't be ready, so its alarm should be skipped.
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + HOUR_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DELAY), any(),
+                        any(), any());
+
+        advanceElapsedClock(55 * MINUTE_IN_MILLIS);
+
+        mTimeController.checkExpiredDelaysAndResetAlarm();
+        assertTrue(jobEarliest.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY));
+        assertTrue(jobMiddle.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY));
+        assertTrue(jobLatest.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY));
+        inOrder.verify(mAlarmManager, never())
+                .set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any());
+    }
+
+    @Test
+    public void testCheckExpiredDeadlinesAndResetAlarm_NoSkipping() {
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
+        mTimeController.onConstantsUpdatedLocked();
+
+        runTestCheckExpiredDeadlinesAndResetAlarm();
+    }
+
+    @Test
+    public void testCheckExpiredDeadlinesAndResetAlarm_WithSkipping_AllReady() {
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mTimeController.onConstantsUpdatedLocked();
+
+        doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());
+
+        runTestCheckExpiredDeadlinesAndResetAlarm();
+    }
+
+    private void runTestCheckExpiredDeadlinesAndResetAlarm() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+        JobStatus jobLatest = createJobStatus("testCheckExpiredDeadlinesAndResetAlarm",
+                createJob().setOverrideDeadline(HOUR_IN_MILLIS));
+        JobStatus jobMiddle = createJobStatus("testCheckExpiredDeadlinesAndResetAlarm",
+                createJob().setOverrideDeadline(30 * MINUTE_IN_MILLIS));
+        JobStatus jobEarliest = createJobStatus("testCheckExpiredDeadlinesAndResetAlarm",
+                createJob().setOverrideDeadline(5 * MINUTE_IN_MILLIS));
+
+        InOrder inOrder = inOrder(mAlarmManager);
+
+        mTimeController.maybeStartTrackingJobLocked(jobLatest, null);
+        mTimeController.maybeStartTrackingJobLocked(jobMiddle, null);
+        mTimeController.maybeStartTrackingJobLocked(jobEarliest, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(),
+                        eq(TAG_DEADLINE), any(), any(), any());
+
+        advanceElapsedClock(10 * MINUTE_IN_MILLIS);
+
+        mTimeController.checkExpiredDeadlinesAndResetAlarm();
+        assertTrue(jobEarliest.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE));
+        assertFalse(jobMiddle.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE));
+        assertFalse(jobLatest.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE));
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 30 * MINUTE_IN_MILLIS), anyLong(), anyLong(),
+                        eq(TAG_DEADLINE), any(), any(), any());
+
+        advanceElapsedClock(30 * MINUTE_IN_MILLIS);
+
+        mTimeController.checkExpiredDeadlinesAndResetAlarm();
+        assertTrue(jobEarliest.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE));
+        assertTrue(jobMiddle.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE));
+        assertFalse(jobLatest.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE));
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + HOUR_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DEADLINE),
+                        any(), any(), any());
+
+        advanceElapsedClock(30 * MINUTE_IN_MILLIS);
+
+        mTimeController.checkExpiredDeadlinesAndResetAlarm();
+        assertTrue(jobEarliest.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE));
+        assertTrue(jobMiddle.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE));
+        assertTrue(jobLatest.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE));
+        inOrder.verify(mAlarmManager, never())
+                .set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any());
+    }
+
+    @Test
+    public void testCheckExpiredDeadlinesAndResetAlarm_WithSkipping_SomeNotReady() {
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mTimeController.onConstantsUpdatedLocked();
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+        JobStatus jobLatest = createJobStatus("testCheckExpiredDeadlinesAndResetAlarm",
+                createJob().setOverrideDeadline(HOUR_IN_MILLIS));
+        JobStatus jobMiddle = createJobStatus("testCheckExpiredDeadlinesAndResetAlarm",
+                createJob().setOverrideDeadline(30 * MINUTE_IN_MILLIS));
+        JobStatus jobEarliest = createJobStatus("testCheckExpiredDeadlinesAndResetAlarm",
+                createJob().setOverrideDeadline(5 * MINUTE_IN_MILLIS));
+
+        doReturn(true).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobLatest), anyInt());
+        doReturn(false).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobMiddle), anyInt());
+        doReturn(true).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt());
+
+        InOrder inOrder = inOrder(mAlarmManager);
+
+        mTimeController.maybeStartTrackingJobLocked(jobLatest, null);
+        mTimeController.maybeStartTrackingJobLocked(jobMiddle, null);
+        mTimeController.maybeStartTrackingJobLocked(jobEarliest, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(),
+                        eq(TAG_DEADLINE), any(), any(), any());
+
+        advanceElapsedClock(10 * MINUTE_IN_MILLIS);
+
+        mTimeController.checkExpiredDeadlinesAndResetAlarm();
+        assertTrue(jobEarliest.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE));
+        assertFalse(jobMiddle.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE));
+        assertFalse(jobLatest.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE));
+        // Middle job wouldn't be ready, so its alarm should be skipped.
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + HOUR_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DEADLINE),
+                        any(), any(), any());
+
+        advanceElapsedClock(55 * MINUTE_IN_MILLIS);
+
+        mTimeController.checkExpiredDeadlinesAndResetAlarm();
+        assertTrue(jobEarliest.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE));
+        assertTrue(jobMiddle.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE));
+        assertTrue(jobLatest.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE));
+        inOrder.verify(mAlarmManager, never())
+                .set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any());
+    }
+
+    @Test
+    public void testEvaluateStateLocked_SkippingOff() {
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
+        mTimeController.onConstantsUpdatedLocked();
+        JobStatus job = createJobStatus("testEvaluateStateLocked_SkippingOff",
+                createJob().setOverrideDeadline(HOUR_IN_MILLIS));
+
+        mTimeController.evaluateStateLocked(job);
+        verify(mAlarmManager, never())
+                .set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any());
+    }
+
+    @Test
+    public void testEvaluateStateLocked_SkippingOn_Delay() {
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mTimeController.onConstantsUpdatedLocked();
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+        JobStatus jobLatest = createJobStatus("testEvaluateStateLocked_SkippingOn_Delay",
+                createJob().setMinimumLatency(HOUR_IN_MILLIS));
+        JobStatus jobMiddle = createJobStatus("testEvaluateStateLocked_SkippingOn_Delay",
+                createJob().setMinimumLatency(30 * MINUTE_IN_MILLIS));
+        JobStatus jobEarliest = createJobStatus("testEvaluateStateLocked_SkippingOn_Delay",
+                createJob().setMinimumLatency(5 * MINUTE_IN_MILLIS));
+
+        doReturn(false).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobLatest), anyInt());
+        doReturn(true).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobMiddle), anyInt());
+        doReturn(false).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt());
+
+        InOrder inOrder = inOrder(mAlarmManager);
+
+        mTimeController.maybeStartTrackingJobLocked(jobLatest, null);
+        mTimeController.maybeStartTrackingJobLocked(jobMiddle, null);
+        mTimeController.maybeStartTrackingJobLocked(jobEarliest, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 30 * MINUTE_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DELAY),
+                        any(), any(), any());
+
+        // Test evaluating something after the current deadline.
+        doReturn(true).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobLatest), anyInt());
+        mTimeController.evaluateStateLocked(jobLatest);
+        inOrder.verify(mAlarmManager, never())
+                .set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any());
+
+        // Test evaluating something before the current deadline.
+        doReturn(true).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt());
+        mTimeController.evaluateStateLocked(jobEarliest);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DELAY),
+                        any(), any(), any());
+        // Job goes back to not being ready. Middle is still true, so use that alarm.
+        doReturn(false).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt());
+        mTimeController.evaluateStateLocked(jobEarliest);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 30 * MINUTE_IN_MILLIS), anyLong(), anyLong(),
+                        eq(TAG_DELAY), any(), any(), any());
+        // Turn middle off. Latest is true, so use that alarm.
+        doReturn(false).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobMiddle), anyInt());
+        mTimeController.evaluateStateLocked(jobMiddle);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + HOUR_IN_MILLIS), anyLong(), anyLong(),
+                        eq(TAG_DELAY), any(), any(), any());
+    }
+
+    @Test
+    public void testEvaluateStateLocked_SkippingOn_Deadline() {
+        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mTimeController.onConstantsUpdatedLocked();
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+        JobStatus jobLatest = createJobStatus("testEvaluateStateLocked_SkippingOn_Deadline",
+                createJob().setOverrideDeadline(HOUR_IN_MILLIS));
+        JobStatus jobMiddle = createJobStatus("testEvaluateStateLocked_SkippingOn_Deadline",
+                createJob().setOverrideDeadline(30 * MINUTE_IN_MILLIS));
+        JobStatus jobEarliest = createJobStatus("testEvaluateStateLocked_SkippingOn_Deadline",
+                createJob().setOverrideDeadline(5 * MINUTE_IN_MILLIS));
+
+        doReturn(false).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobLatest), anyInt());
+        doReturn(true).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobMiddle), anyInt());
+        doReturn(false).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt());
+
+        InOrder inOrder = inOrder(mAlarmManager);
+
+        mTimeController.maybeStartTrackingJobLocked(jobLatest, null);
+        mTimeController.maybeStartTrackingJobLocked(jobMiddle, null);
+        mTimeController.maybeStartTrackingJobLocked(jobEarliest, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 30 * MINUTE_IN_MILLIS), anyLong(), anyLong(),
+                        eq(TAG_DEADLINE), any(), any(), any());
+
+        // Test evaluating something after the current deadline.
+        doReturn(true).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobLatest), anyInt());
+        mTimeController.evaluateStateLocked(jobLatest);
+        inOrder.verify(mAlarmManager, never())
+                .set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any());
+
+        // Test evaluating something before the current deadline.
+        doReturn(true).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt());
+        mTimeController.evaluateStateLocked(jobEarliest);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(),
+                        eq(TAG_DEADLINE), any(), any(), any());
+        // Job goes back to not being ready. Middle is still true, so use that alarm.
+        doReturn(false).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt());
+        mTimeController.evaluateStateLocked(jobEarliest);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 30 * MINUTE_IN_MILLIS), anyLong(), anyLong(),
+                        eq(TAG_DEADLINE), any(), any(), any());
+        // Turn middle off. Latest is true, so use that alarm.
+        doReturn(false).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(jobMiddle), anyInt());
+        mTimeController.evaluateStateLocked(jobMiddle);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + HOUR_IN_MILLIS), anyLong(), anyLong(),
+                        eq(TAG_DEADLINE), any(), any(), any());
+    }
+}
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index cf4d3a8..1b5ba26 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -25,7 +25,6 @@
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.BROADCAST_STICKY" />
-    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
     <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
     <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
diff --git a/services/tests/servicestests/res/values/strings.xml b/services/tests/servicestests/res/values/strings.xml
index 57da0af..50ccd1f 100644
--- a/services/tests/servicestests/res/values/strings.xml
+++ b/services/tests/servicestests/res/values/strings.xml
@@ -32,4 +32,6 @@
     <string name="config_batterySaverDeviceSpecificConfig_1"></string>
     <string name="config_batterySaverDeviceSpecificConfig_2">cpufreq-n=1:123/2:456</string>
     <string name="config_batterySaverDeviceSpecificConfig_3">cpufreq-n=2:222,cpufreq-i=3:333/4:444</string>
+    <string name="module_1_name" translatable="false">module_1_name</string>
+    <string name="module_2_name" translatable="false">module_2_name</string>
 </resources>
diff --git a/services/tests/servicestests/res/xml/unparseable_metadata1.xml b/services/tests/servicestests/res/xml/unparseable_metadata1.xml
new file mode 100644
index 0000000..73967f1
--- /dev/null
+++ b/services/tests/servicestests/res/xml/unparseable_metadata1.xml
@@ -0,0 +1,4 @@
+<not-module-metadata>
+  <module name="@string/module_1_name" packageName="com.android.module1" isHidden="false"/>
+  <module name="@string/module_2_name" packageName="com.android.module2" isHidden="true"/>
+</not-module-metadata>
diff --git a/services/tests/servicestests/res/xml/unparseable_metadata2.xml b/services/tests/servicestests/res/xml/unparseable_metadata2.xml
new file mode 100644
index 0000000..bb5a1b2
--- /dev/null
+++ b/services/tests/servicestests/res/xml/unparseable_metadata2.xml
@@ -0,0 +1,4 @@
+<module-metadata>
+  <module name="@string/module_1_name" packageName="com.android.module1" isHidden="false"/>
+  <not-module name="@string/module_2_name" packageName="com.android.module2" isHidden="true"/>
+</module-metadata>
diff --git a/services/tests/servicestests/res/xml/well_formed_metadata.xml b/services/tests/servicestests/res/xml/well_formed_metadata.xml
new file mode 100644
index 0000000..17cc369
--- /dev/null
+++ b/services/tests/servicestests/res/xml/well_formed_metadata.xml
@@ -0,0 +1,4 @@
+<module-metadata>
+  <module name="@string/module_1_name" packageName="com.android.module1" isHidden="false"/>
+  <module name="@string/module_2_name" packageName="com.android.module2" isHidden="true"/>
+</module-metadata>
diff --git a/services/tests/servicestests/res/xml/well_formed_metadata2.xml b/services/tests/servicestests/res/xml/well_formed_metadata2.xml
new file mode 100644
index 0000000..47279e6
--- /dev/null
+++ b/services/tests/servicestests/res/xml/well_formed_metadata2.xml
@@ -0,0 +1,5 @@
+<module-metadata>
+  <module name="@string/module_1_name" packageName="com.android.module1" isHidden="false"
+    attribute1="attribute1" attribute2="attribute2" />
+  <module name="@string/module_2_name" packageName="com.android.module2" isHidden="true"/>
+</module-metadata>
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 767eb60..6a10ff4 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -29,6 +29,8 @@
 import static android.app.ActivityManager.PROCESS_STATE_TOP;
 import static android.util.DebugUtils.valueToString;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static com.android.server.am.ActivityManagerInternalTest.CustomThread;
 import static com.android.server.am.ActivityManagerService.DISPATCH_UIDS_CHANGED_UI_MSG;
 import static com.android.server.am.ActivityManagerService.Injector;
@@ -69,6 +71,9 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.server.AppOpsService;
+import com.android.server.am.ProcessList.IsolatedUidRange;
+import com.android.server.am.ProcessList.IsolatedUidRangeAllocator;
+import com.android.server.wm.ActivityTaskManagerService;
 
 import org.junit.After;
 import org.junit.Before;
@@ -109,7 +114,7 @@
         UidRecord.CHANGE_ACTIVE
     };
 
-    @Mock private Context mContext;
+    private Context mContext = getInstrumentation().getTargetContext();
     @Mock private AppOpsService mAppOpsService;
     @Mock private PackageManager mPackageManager;
 
@@ -128,8 +133,8 @@
         mInjector = new TestInjector();
         mAms = new ActivityManagerService(mInjector);
         mAms.mWaitForNetworkTimeoutMs = 2000;
-
-        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        mAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
+        mAms.mActivityTaskManager.initialize(null, null, mHandler.getLooper());
     }
 
     @After
@@ -291,6 +296,113 @@
         }
     }
 
+    private void validateAppZygoteIsolatedUidRange(IsolatedUidRange uidRange) {
+        assertNotNull(uidRange);
+        assertTrue(uidRange.mFirstUid >= Process.FIRST_APP_ZYGOTE_ISOLATED_UID
+                && uidRange.mFirstUid <= Process.LAST_APP_ZYGOTE_ISOLATED_UID);
+        assertTrue(uidRange.mLastUid >= Process.FIRST_APP_ZYGOTE_ISOLATED_UID
+                && uidRange.mLastUid <= Process.LAST_APP_ZYGOTE_ISOLATED_UID);
+        assertTrue(uidRange.mLastUid > uidRange.mFirstUid
+                && ((uidRange.mLastUid - uidRange.mFirstUid + 1)
+                     == Process.NUM_UIDS_PER_APP_ZYGOTE));
+    }
+
+    private void verifyUidRangesNoOverlap(IsolatedUidRange uidRange1, IsolatedUidRange uidRange2) {
+        IsolatedUidRange lowRange = uidRange1.mFirstUid <= uidRange2.mFirstUid ? uidRange1 : uidRange2;
+        IsolatedUidRange highRange = lowRange == uidRange1  ? uidRange2 : uidRange1;
+
+        assertTrue(highRange.mFirstUid > lowRange.mLastUid);
+    }
+
+    @Test
+    public void testIsolatedUidRangeAllocator() {
+        final IsolatedUidRangeAllocator allocator = mAms.mProcessList.mAppIsolatedUidRangeAllocator;
+
+        // Create initial range
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.processName = "com.android.test.app";
+        appInfo.uid = 10000;
+        final IsolatedUidRange range = allocator.getOrCreateIsolatedUidRangeLocked(appInfo);
+        validateAppZygoteIsolatedUidRange(range);
+        verifyIsolatedUidAllocator(range);
+
+        // Create a second range
+        ApplicationInfo appInfo2 = new ApplicationInfo();
+        appInfo2.processName = "com.android.test.app2";
+        appInfo2.uid = 10001;
+        IsolatedUidRange range2 = allocator.getOrCreateIsolatedUidRangeLocked(appInfo2);
+        validateAppZygoteIsolatedUidRange(range2);
+        verifyIsolatedUidAllocator(range2);
+
+        // Verify ranges don't overlap
+        verifyUidRangesNoOverlap(range, range2);
+
+        // Free range, reallocate and verify
+        allocator.freeUidRangeLocked(appInfo2);
+        range2 = allocator.getOrCreateIsolatedUidRangeLocked(appInfo2);
+        validateAppZygoteIsolatedUidRange(range2);
+        verifyUidRangesNoOverlap(range, range2);
+        verifyIsolatedUidAllocator(range2);
+
+        // Free both, then try to allocate the maximum number of UID ranges
+        allocator.freeUidRangeLocked(appInfo);
+        allocator.freeUidRangeLocked(appInfo2);
+
+        int maxNumUidRanges = (Process.LAST_APP_ZYGOTE_ISOLATED_UID
+                - Process.FIRST_APP_ZYGOTE_ISOLATED_UID + 1) / Process.NUM_UIDS_PER_APP_ZYGOTE;
+        for (int i = 0; i < maxNumUidRanges; i++) {
+            appInfo = new ApplicationInfo();
+            appInfo.uid = 10000 + i;
+            appInfo.processName = "com.android.test.app" + Integer.toString(i);
+            IsolatedUidRange uidRange = allocator.getOrCreateIsolatedUidRangeLocked(appInfo);
+            validateAppZygoteIsolatedUidRange(uidRange);
+            verifyIsolatedUidAllocator(uidRange);
+        }
+
+        // Try to allocate another one and make sure it fails
+        appInfo = new ApplicationInfo();
+        appInfo.uid = 9000;
+        appInfo.processName = "com.android.test.app.failed";
+        IsolatedUidRange failedRange = allocator.getOrCreateIsolatedUidRangeLocked(appInfo);
+
+        assertNull(failedRange);
+    }
+
+    public void verifyIsolatedUid(ProcessList.IsolatedUidRange range, int uid) {
+        assertTrue(uid >= range.mFirstUid && uid <= range.mLastUid);
+    }
+
+    public void verifyIsolatedUidAllocator(ProcessList.IsolatedUidRange range) {
+        int uid = range.allocateIsolatedUidLocked(0);
+        verifyIsolatedUid(range, uid);
+
+        int uid2 = range.allocateIsolatedUidLocked(0);
+        verifyIsolatedUid(range, uid2);
+        assertTrue(uid2 != uid);
+
+        // Free both
+        range.freeIsolatedUidLocked(uid);
+        range.freeIsolatedUidLocked(uid2);
+
+        // Allocate the entire range
+        for (int i = 0; i < (range.mLastUid - range.mFirstUid + 1); ++i) {
+            uid = range.allocateIsolatedUidLocked(0);
+            verifyIsolatedUid(range, uid);
+        }
+
+        // Ensure the next one fails
+        uid = range.allocateIsolatedUidLocked(0);
+        assertEquals(uid, -1);
+    }
+
+    @Test
+    public void testGlobalIsolatedUidAllocator() {
+        final IsolatedUidRange globalUidRange = mAms.mProcessList.mGlobalIsolatedUids;
+        assertEquals(globalUidRange.mFirstUid, Process.FIRST_ISOLATED_UID);
+        assertEquals(globalUidRange.mLastUid, Process.LAST_ISOLATED_UID);
+        verifyIsolatedUidAllocator(globalUidRange);
+    }
+
     @Test
     public void testBlockStateForUid() {
         final UidRecord uidRec = new UidRecord(TEST_UID, null /* atmInternal */);
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 0889265..d4bb636 100644
--- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -193,6 +193,7 @@
                 false /* serialized */,
                 false /* sticky */,
                 false /* initialSticky */,
-                userId);
+                userId,
+                false /* allowBackgroundActivityStarts */);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
index 89c7b71..93cac08 100644
--- a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
@@ -20,6 +20,7 @@
 import static com.android.server.am.MemoryStatUtil.JIFFY_NANOS;
 import static com.android.server.am.MemoryStatUtil.MemoryStat;
 import static com.android.server.am.MemoryStatUtil.PAGE_SIZE;
+import static com.android.server.am.MemoryStatUtil.parseCmdlineFromProcfs;
 import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromMemcg;
 import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromProcfs;
 import static com.android.server.am.MemoryStatUtil.parseVmHWMFromProcfs;
@@ -31,6 +32,7 @@
 
 import org.junit.Test;
 
+import java.io.ByteArrayOutputStream;
 import java.util.Collections;
 
 /**
@@ -232,4 +234,41 @@
 
         assertEquals(0, parseVmHWMFromProcfs(null));
     }
+
+    @Test
+    public void testParseCmdlineFromProcfs_invalidValue() {
+        byte[] nothing = new byte[] {0x00, 0x74, 0x65, 0x73, 0x74}; // \0test
+
+        assertEquals("", parseCmdlineFromProcfs(bytesToString(nothing)));
+    }
+
+    @Test
+    public void testParseCmdlineFromProcfs_correctValue_noNullBytes() {
+        assertEquals("com.google.app", parseCmdlineFromProcfs("com.google.app"));
+    }
+
+    @Test
+    public void testParseCmdlineFromProcfs_correctValue_withNullBytes() {
+        byte[] trailing = new byte[] {0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00}; // test\0\0\0
+
+        assertEquals("test", parseCmdlineFromProcfs(bytesToString(trailing)));
+
+        // test\0\0test
+        byte[] inside = new byte[] {0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74};
+
+        assertEquals("test", parseCmdlineFromProcfs(bytesToString(trailing)));
+    }
+
+    @Test
+    public void testParseCmdlineFromProcfs_emptyContents() {
+        assertEquals("", parseCmdlineFromProcfs(""));
+
+        assertEquals("", parseCmdlineFromProcfs(null));
+    }
+
+    private static String bytesToString(byte[] bytes) {
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        output.write(bytes, 0, bytes.length);
+        return output.toString();
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java b/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
index d965f8a..0fd5921 100644
--- a/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
@@ -41,11 +41,14 @@
 
 /**
  * Tests for {@link SettingsToPropertiesMapper}
+ *
+ *  Build/Install/Run:
+ *  atest FrameworksServicesTests:SettingsToPropertiesMapperTest
  */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class SettingsToPropertiesMapperTest {
-    private static final String NAME_VALID_CHARACTERS_REGEX = "^[\\w\\.\\-@:]*$";
+    private static final String NAME_VALID_CHARACTERS_REGEX = "^[\\w\\-@:]*$";
     private static final String[] TEST_MAPPING = new String[] {
             Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS
     };
@@ -77,7 +80,28 @@
             }
             if (!globalSetting.matches(NAME_VALID_CHARACTERS_REGEX)) {
                 Assert.fail(globalSetting + " contains invalid characters. "
-                        + "Only alphanumeric characters, '.', '-', '@', ':' and '_' are valid.");
+                        + "Only alphanumeric characters, '-', '@', ':' and '_' are valid.");
+            }
+        }
+    }
+
+    @Test
+    public void validateRegisteredDeviceConfigScopes() {
+        HashSet<String> hashSet = new HashSet<>();
+        for (String deviceConfigScope : SettingsToPropertiesMapper.sDeviceConfigScopes) {
+            if (hashSet.contains(deviceConfigScope)) {
+                Assert.fail("deviceConfigScope "
+                        + deviceConfigScope
+                        + " is registered more than once in "
+                        + "SettingsToPropertiesMapper.sDeviceConfigScopes.");
+            }
+            hashSet.add(deviceConfigScope);
+            if (TextUtils.isEmpty(deviceConfigScope)) {
+                Assert.fail("empty deviceConfigScope registered.");
+            }
+            if (!deviceConfigScope.matches(NAME_VALID_CHARACTERS_REGEX)) {
+                Assert.fail(deviceConfigScope + " contains invalid characters. "
+                        + "Only alphanumeric characters, '-', '@', ':' and '_' are valid.");
             }
         }
     }
@@ -98,8 +122,7 @@
                 Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue2");
         mTestMapper.updatePropertyFromSetting(
                 Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
-                systemPropertyName,
-                true);
+                systemPropertyName);
         propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
         Assert.assertEquals("testValue2", propValue);
 
@@ -107,8 +130,7 @@
                 Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, null);
         mTestMapper.updatePropertyFromSetting(
                 Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
-                systemPropertyName,
-                true);
+                systemPropertyName);
         propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
         Assert.assertEquals("", propValue);
     }
diff --git a/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java b/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java
new file mode 100644
index 0000000..edd89f9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appops;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.OnOpNotedListener;
+import android.content.Context;
+import android.os.Process;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+
+
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+/**
+ * Tests watching noted ops.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AppOpsNotedWatcherTest {
+
+    private static final long NOTIFICATION_TIMEOUT_MILLIS = 5000;
+
+    public void testWatchNotedOpsRequiresPermission() {
+        // Create a mock listener
+        final OnOpNotedListener listener = mock(OnOpNotedListener.class);
+
+        // Try to start watching noted ops
+        final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
+        try {
+            appOpsManager.startWatchingNoted(new int[]{AppOpsManager.OP_FINE_LOCATION,
+                    AppOpsManager.OP_RECORD_AUDIO}, listener);
+            fail("Watching noted ops shoudl require " + Manifest.permission.WATCH_APPOPS);
+        } catch (SecurityException expected) {
+            /*ignored*/
+        }
+    }
+
+    @Test
+    public void testWatchNotedOps() {
+        // Create a mock listener
+        final OnOpNotedListener listener = mock(OnOpNotedListener.class);
+
+        // Start watching noted ops
+        final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
+        appOpsManager.startWatchingNoted(new int[]{AppOpsManager.OP_FINE_LOCATION,
+                AppOpsManager.OP_CAMERA}, listener);
+
+        // Note some ops
+        appOpsManager.noteOp(AppOpsManager.OP_FINE_LOCATION, Process.myUid(),
+                getContext().getPackageName());
+        appOpsManager.noteOp(AppOpsManager.OP_CAMERA, Process.myUid(),
+                getContext().getPackageName());
+
+        // Verify that we got called for the ops being noted
+        final InOrder inOrder = inOrder(listener);
+        inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
+                .times(1)).onOpNoted(eq(AppOpsManager.OP_FINE_LOCATION),
+                eq(Process.myUid()), eq(getContext().getPackageName()),
+                eq(AppOpsManager.MODE_ALLOWED));
+        inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
+                .times(1)).onOpNoted(eq(AppOpsManager.OP_CAMERA),
+                eq(Process.myUid()), eq(getContext().getPackageName()),
+                eq(AppOpsManager.MODE_ALLOWED));
+
+        // Stop watching
+        appOpsManager.stopWatchingNoted(listener);
+
+        // This should be the only two callbacks we got
+        verifyNoMoreInteractions(listener);
+    }
+
+    private static Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
index 751ed9b..db83505 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -23,6 +23,7 @@
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
@@ -132,45 +133,78 @@
     }
 
     @Test
-    public void startServiceForUser_whenMultiUserSettingDisabled_isIgnored() {
+    public void unlockUser_whenMultiUserSettingDisabled_callsBackupManagerServiceForSystemUser() {
         Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
+        mTrampoline.initializeService();
 
-        mTrampoline.startServiceForUser(10);
+        mTrampoline.unlockUser(UserHandle.USER_SYSTEM);
+
+        verify(mBackupManagerServiceMock).startServiceForUser(UserHandle.USER_SYSTEM);
+    }
+
+    @Test
+    public void unlockUser_whenMultiUserSettingDisabled_isIgnoredForNonSystemUser() {
+        Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
+        mTrampoline.initializeService();
+
+        mTrampoline.unlockUser(10);
 
         verify(mBackupManagerServiceMock, never()).startServiceForUser(10);
     }
 
     @Test
-    public void startServiceForUser_whenMultiUserSettingEnabled_callsBackupManagerService() {
+    public void unlockUser_whenMultiUserSettingEnabled_callsBackupManagerServiceForNonSystemUser() {
         Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1);
+        mTrampoline.initializeService();
 
-        mTrampoline.startServiceForUser(10);
+        mTrampoline.unlockUser(10);
 
         verify(mBackupManagerServiceMock).startServiceForUser(10);
     }
 
     @Test
-    public void initializeService_forUserSystem_successfullyInitialized() {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+    public void stopUser_whenMultiUserSettingDisabled_callsBackupManagerServiceForSystemUser() {
+        Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
+        mTrampoline.initializeService();
 
-        assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+        mTrampoline.stopUser(UserHandle.USER_SYSTEM);
+
+        verify(mBackupManagerServiceMock).stopServiceForUser(UserHandle.USER_SYSTEM);
     }
 
-    // The BackupManagerService can only be initialized by USER_SYSTEM, so we check that if any
-    // other user trying to initialize it leaves it non-active.
     @Test
-    public void initializeService_forNonUserSystem_nonInitialized() {
-        mTrampoline.initializeService(NON_USER_SYSTEM);
+    public void stopUser_whenMultiUserSettingDisabled_isIgnoredForNonSystemUser() {
+        Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
+        mTrampoline.initializeService();
 
-        assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
+        mTrampoline.stopUser(10);
+
+        verify(mBackupManagerServiceMock, never()).stopServiceForUser(10);
+    }
+
+    @Test
+    public void stopUser_whenMultiUserSettingEnabled_callsBackupManagerServiceForNonSystemUser() {
+        Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1);
+        mTrampoline.initializeService();
+
+        mTrampoline.stopUser(10);
+
+        verify(mBackupManagerServiceMock).stopServiceForUser(10);
+    }
+
+    @Test
+    public void initializeService_successfullyInitializesBackupService() {
+        mTrampoline.initializeService();
+
+        assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
     }
 
     @Test
     public void initializeService_globallyDisabled_nonInitialized() {
         TrampolineTestable.sBackupDisabled = true;
-
         TrampolineTestable trampoline = new TrampolineTestable(mContextMock);
-        trampoline.initializeService(UserHandle.USER_SYSTEM);
+
+        trampoline.initializeService();
 
         assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
     }
@@ -179,14 +213,21 @@
     @Test
     public void initializeService_suppressFileExists_nonInitialized() {
         when(mSuppressFileMock.exists()).thenReturn(true);
-
         TrampolineTestable trampoline = new TrampolineTestable(mContextMock);
-        trampoline.initializeService(UserHandle.USER_SYSTEM);
+
+        trampoline.initializeService();
 
         assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
     }
 
     @Test
+    public void initializeService_doesNotStartServiceForUsers() {
+        mTrampoline.initializeService();
+
+        verify(mBackupManagerServiceMock, never()).startServiceForUser(anyInt());
+    }
+
+    @Test
     public void isBackupServiceActive_calledBeforeInitialize_returnsFalse() {
         assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
     }
@@ -261,7 +302,7 @@
     @Test
     public void setBackupServiceActive_makeNonActive_serviceDeletedAndSuppressFileCreated()
             throws IOException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService();
         assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
 
         mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
@@ -275,7 +316,7 @@
     setBackupServiceActive_makeNonActive_serviceDeletedAndSuppressFileCreated_ioExceptionHandled()
             throws IOException {
         when(mSuppressFileMock.createNewFile()).thenThrow(new IOException());
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService();
         assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
 
         mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
@@ -300,10 +341,22 @@
     }
 
     @Test
+    public void dataChangedForUser_forwarded() throws RemoteException {
+        mTrampoline.initializeService();
+
+        mTrampoline.dataChangedForUser(mUserId, PACKAGE_NAME);
+
+        verify(mBackupManagerServiceMock).dataChanged(mUserId, PACKAGE_NAME);
+    }
+
+    @Test
     public void dataChanged_forwarded() throws RemoteException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        TrampolineTestable.sCallingUserId = mUserId;
+        mTrampoline.initializeService();
+
         mTrampoline.dataChanged(PACKAGE_NAME);
-        verify(mBackupManagerServiceMock).dataChanged(PACKAGE_NAME);
+
+        verify(mBackupManagerServiceMock).dataChanged(mUserId, PACKAGE_NAME);
     }
 
     @Test
@@ -313,10 +366,22 @@
     }
 
     @Test
+    public void clearBackupDataForUser_forwarded() throws RemoteException {
+        mTrampoline.initializeService();
+
+        mTrampoline.clearBackupDataForUser(mUserId, TRANSPORT_NAME, PACKAGE_NAME);
+
+        verify(mBackupManagerServiceMock).clearBackupData(mUserId, TRANSPORT_NAME, PACKAGE_NAME);
+    }
+
+    @Test
     public void clearBackupData_forwarded() throws RemoteException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        TrampolineTestable.sCallingUserId = mUserId;
+        mTrampoline.initializeService();
+
         mTrampoline.clearBackupData(TRANSPORT_NAME, PACKAGE_NAME);
-        verify(mBackupManagerServiceMock).clearBackupData(TRANSPORT_NAME, PACKAGE_NAME);
+
+        verify(mBackupManagerServiceMock).clearBackupData(mUserId, TRANSPORT_NAME, PACKAGE_NAME);
     }
 
     @Test
@@ -326,10 +391,22 @@
     }
 
     @Test
+    public void agentConnectedForUser_forwarded() throws RemoteException {
+        mTrampoline.initializeService();
+
+        mTrampoline.agentConnectedForUser(mUserId, PACKAGE_NAME, mAgentMock);
+
+        verify(mBackupManagerServiceMock).agentConnected(mUserId, PACKAGE_NAME, mAgentMock);
+    }
+
+    @Test
     public void agentConnected_forwarded() throws RemoteException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        TrampolineTestable.sCallingUserId = mUserId;
+        mTrampoline.initializeService();
+
         mTrampoline.agentConnected(PACKAGE_NAME, mAgentMock);
-        verify(mBackupManagerServiceMock).agentConnected(PACKAGE_NAME, mAgentMock);
+
+        verify(mBackupManagerServiceMock).agentConnected(mUserId, PACKAGE_NAME, mAgentMock);
     }
 
     @Test
@@ -339,10 +416,22 @@
     }
 
     @Test
+    public void agentDisconnectedForUser_forwarded() throws RemoteException {
+        mTrampoline.initializeService();
+
+        mTrampoline.agentDisconnectedForUser(mUserId, PACKAGE_NAME);
+
+        verify(mBackupManagerServiceMock).agentDisconnected(mUserId, PACKAGE_NAME);
+    }
+
+    @Test
     public void agentDisconnected_forwarded() throws RemoteException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        TrampolineTestable.sCallingUserId = mUserId;
+        mTrampoline.initializeService();
+
         mTrampoline.agentDisconnected(PACKAGE_NAME);
-        verify(mBackupManagerServiceMock).agentDisconnected(PACKAGE_NAME);
+
+        verify(mBackupManagerServiceMock).agentDisconnected(mUserId, PACKAGE_NAME);
     }
 
     @Test
@@ -352,10 +441,22 @@
     }
 
     @Test
+    public void restoreAtInstallForUser_forwarded() throws RemoteException {
+        mTrampoline.initializeService();
+
+        mTrampoline.restoreAtInstallForUser(mUserId, PACKAGE_NAME, 123);
+
+        verify(mBackupManagerServiceMock).restoreAtInstall(mUserId, PACKAGE_NAME, 123);
+    }
+
+    @Test
     public void restoreAtInstall_forwarded() throws RemoteException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        TrampolineTestable.sCallingUserId = mUserId;
+        mTrampoline.initializeService();
+
         mTrampoline.restoreAtInstall(PACKAGE_NAME, 123);
-        verify(mBackupManagerServiceMock).restoreAtInstall(PACKAGE_NAME, 123);
+
+        verify(mBackupManagerServiceMock).restoreAtInstall(mUserId, PACKAGE_NAME, 123);
     }
 
     @Test
@@ -366,7 +467,7 @@
 
     @Test
     public void setBackupEnabledForUser_forwarded() throws RemoteException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService();
 
         mTrampoline.setBackupEnabledForUser(mUserId, true);
 
@@ -376,7 +477,7 @@
     @Test
     public void setBackupEnabled_forwardedToCallingUserId() throws RemoteException {
         TrampolineTestable.sCallingUserId = mUserId;
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService();
 
         mTrampoline.setBackupEnabled(true);
 
@@ -390,23 +491,22 @@
     }
 
     @Test
+    public void setAutoRestoreForUser_forwarded() throws RemoteException {
+        mTrampoline.initializeService();
+
+        mTrampoline.setAutoRestoreForUser(mUserId, true);
+
+        verify(mBackupManagerServiceMock).setAutoRestore(mUserId, true);
+    }
+
+    @Test
     public void setAutoRestore_forwarded() throws RemoteException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        TrampolineTestable.sCallingUserId = mUserId;
+        mTrampoline.initializeService();
+
         mTrampoline.setAutoRestore(true);
-        verify(mBackupManagerServiceMock).setAutoRestore(true);
-    }
 
-    @Test
-    public void setBackupProvisioned_calledBeforeInitialize_ignored() throws RemoteException {
-        mTrampoline.setBackupProvisioned(true);
-        verifyNoMoreInteractions(mBackupManagerServiceMock);
-    }
-
-    @Test
-    public void setBackupProvisioned_forwarded() throws RemoteException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
-        mTrampoline.setBackupProvisioned(true);
-        verify(mBackupManagerServiceMock).setBackupProvisioned(true);
+        verify(mBackupManagerServiceMock).setAutoRestore(mUserId, true);
     }
 
     @Test
@@ -417,7 +517,7 @@
 
     @Test
     public void isBackupEnabledForUser_forwarded() throws RemoteException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService();
 
         mTrampoline.isBackupEnabledForUser(mUserId);
 
@@ -427,7 +527,7 @@
     @Test
     public void isBackupEnabled_forwardedToCallingUserId() throws RemoteException {
         TrampolineTestable.sCallingUserId = mUserId;
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService();
 
         mTrampoline.isBackupEnabled();
 
@@ -442,7 +542,7 @@
 
     @Test
     public void setBackupPassword_forwarded() throws RemoteException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService();
         mTrampoline.setBackupPassword(CURRENT_PASSWORD, NEW_PASSWORD);
         verify(mBackupManagerServiceMock).setBackupPassword(CURRENT_PASSWORD, NEW_PASSWORD);
     }
@@ -455,7 +555,7 @@
 
     @Test
     public void hasBackupPassword_forwarded() throws RemoteException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService();
         mTrampoline.hasBackupPassword();
         verify(mBackupManagerServiceMock).hasBackupPassword();
     }
@@ -468,7 +568,7 @@
 
     @Test
     public void backupNowForUser_forwarded() throws RemoteException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService();
 
         mTrampoline.backupNowForUser(mUserId);
 
@@ -478,7 +578,7 @@
     @Test
     public void backupNow_forwardedToCallingUserId() throws RemoteException {
         TrampolineTestable.sCallingUserId = mUserId;
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService();
 
         mTrampoline.backupNow();
 
@@ -487,47 +587,48 @@
 
     @Test
     public void adbBackup_calledBeforeInitialize_ignored() throws RemoteException {
-        mTrampoline.adbBackup(mParcelFileDescriptorMock, true, true, true, true, true, true, true,
-                true,
+        mTrampoline.adbBackup(mUserId, mParcelFileDescriptorMock, true, true,
+                true, true, true, true, true, true,
                 PACKAGE_NAMES);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
     public void adbBackup_forwarded() throws RemoteException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
-        mTrampoline.adbBackup(mParcelFileDescriptorMock, true, true, true, true, true, true, true,
-                true,
+        mTrampoline.initializeService();
+        mTrampoline.adbBackup(mUserId, mParcelFileDescriptorMock, true, true,
+                true, true, true, true, true, true,
                 PACKAGE_NAMES);
-        verify(mBackupManagerServiceMock).adbBackup(mParcelFileDescriptorMock, true, true, true,
-                true,
-                true, true, true, true, PACKAGE_NAMES);
+        verify(mBackupManagerServiceMock).adbBackup(mUserId, mParcelFileDescriptorMock, true,
+                true, true, true, true, true, true, true, PACKAGE_NAMES);
     }
 
     @Test
     public void fullTransportBackup_calledBeforeInitialize_ignored() throws RemoteException {
-        mTrampoline.fullTransportBackup(PACKAGE_NAMES);
+        mTrampoline.fullTransportBackupForUser(mUserId, PACKAGE_NAMES);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void fullTransportBackup_forwarded() throws RemoteException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
-        mTrampoline.fullTransportBackup(PACKAGE_NAMES);
-        verify(mBackupManagerServiceMock).fullTransportBackup(PACKAGE_NAMES);
+    public void fullTransportBackupForUser_forwarded() throws RemoteException {
+        mTrampoline.initializeService();
+
+        mTrampoline.fullTransportBackupForUser(mUserId, PACKAGE_NAMES);
+
+        verify(mBackupManagerServiceMock).fullTransportBackup(mUserId, PACKAGE_NAMES);
     }
 
     @Test
     public void adbRestore_calledBeforeInitialize_ignored() throws RemoteException {
-        mTrampoline.adbRestore(mParcelFileDescriptorMock);
+        mTrampoline.adbRestore(mUserId, mParcelFileDescriptorMock);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
     public void adbRestore_forwarded() throws RemoteException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
-        mTrampoline.adbRestore(mParcelFileDescriptorMock);
-        verify(mBackupManagerServiceMock).adbRestore(mParcelFileDescriptorMock);
+        mTrampoline.initializeService();
+        mTrampoline.adbRestore(mUserId, mParcelFileDescriptorMock);
+        verify(mBackupManagerServiceMock).adbRestore(mUserId, mParcelFileDescriptorMock);
     }
 
     @Test
@@ -539,12 +640,43 @@
     }
 
     @Test
+    public void acknowledgeFullBackupOrRestoreForUser_forwarded() throws RemoteException {
+        mTrampoline.initializeService();
+
+        mTrampoline.acknowledgeFullBackupOrRestoreForUser(
+                mUserId,
+                123,
+                true,
+                CURRENT_PASSWORD,
+                ENCRYPTION_PASSWORD,
+                mFullBackupRestoreObserverMock);
+
+        verify(mBackupManagerServiceMock)
+                .acknowledgeAdbBackupOrRestore(
+                        mUserId,
+                        123,
+                        true,
+                        CURRENT_PASSWORD,
+                        ENCRYPTION_PASSWORD,
+                        mFullBackupRestoreObserverMock);
+    }
+
+    @Test
     public void acknowledgeFullBackupOrRestore_forwarded() throws RemoteException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        TrampolineTestable.sCallingUserId = mUserId;
+        mTrampoline.initializeService();
+
         mTrampoline.acknowledgeFullBackupOrRestore(123, true, CURRENT_PASSWORD, ENCRYPTION_PASSWORD,
                 mFullBackupRestoreObserverMock);
-        verify(mBackupManagerServiceMock).acknowledgeAdbBackupOrRestore(123, true, CURRENT_PASSWORD,
-                ENCRYPTION_PASSWORD, mFullBackupRestoreObserverMock);
+
+        verify(mBackupManagerServiceMock)
+                .acknowledgeAdbBackupOrRestore(
+                        mUserId,
+                        123,
+                        true,
+                        CURRENT_PASSWORD,
+                        ENCRYPTION_PASSWORD,
+                        mFullBackupRestoreObserverMock);
     }
 
     @Test
@@ -554,13 +686,22 @@
     }
 
     @Test
-    public void getCurrentTransport_forwarded() throws RemoteException {
-        when(mBackupManagerServiceMock.getCurrentTransport()).thenReturn(TRANSPORT_NAME);
+    public void getCurrentTransportForUser_forwarded() throws RemoteException {
+        when(mBackupManagerServiceMock.getCurrentTransport(mUserId)).thenReturn(TRANSPORT_NAME);
+        mTrampoline.initializeService();
 
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        assertEquals(TRANSPORT_NAME, mTrampoline.getCurrentTransportForUser(mUserId));
+        verify(mBackupManagerServiceMock).getCurrentTransport(mUserId);
+    }
+
+    @Test
+    public void getCurrentTransport_forwarded() throws RemoteException {
+        TrampolineTestable.sCallingUserId = mUserId;
+        when(mBackupManagerServiceMock.getCurrentTransport(mUserId)).thenReturn(TRANSPORT_NAME);
+        mTrampoline.initializeService();
 
         assertEquals(TRANSPORT_NAME, mTrampoline.getCurrentTransport());
-        verify(mBackupManagerServiceMock).getCurrentTransport();
+        verify(mBackupManagerServiceMock).getCurrentTransport(mUserId);
     }
 
     @Test
@@ -570,28 +711,40 @@
     }
 
     @Test
-    public void listAllTransports_forwarded() throws RemoteException {
-        when(mBackupManagerServiceMock.listAllTransports()).thenReturn(TRANSPORTS);
+    public void listAllTransportsForUser_forwarded() throws RemoteException {
+        when(mBackupManagerServiceMock.listAllTransports(mUserId)).thenReturn(TRANSPORTS);
+        mTrampoline.initializeService();
 
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        assertEquals(TRANSPORTS, mTrampoline.listAllTransportsForUser(mUserId));
+        verify(mBackupManagerServiceMock).listAllTransports(mUserId);
+    }
+
+
+    @Test
+    public void listAllTransports_forwarded() throws RemoteException {
+        TrampolineTestable.sCallingUserId = mUserId;
+        when(mBackupManagerServiceMock.listAllTransports(mUserId)).thenReturn(TRANSPORTS);
+        mTrampoline.initializeService();
+
         assertEquals(TRANSPORTS, mTrampoline.listAllTransports());
-        verify(mBackupManagerServiceMock).listAllTransports();
+        verify(mBackupManagerServiceMock).listAllTransports(mUserId);
     }
 
     @Test
-    public void listAllTransportComponents_calledBeforeInitialize_ignored() throws RemoteException {
-        assertNull(mTrampoline.listAllTransportComponents());
+    public void listAllTransportComponentsForUser_calledBeforeInitialize_ignored()
+            throws RemoteException {
+        assertNull(mTrampoline.listAllTransportComponentsForUser(mUserId));
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void listAllTransportComponents_forwarded() throws RemoteException {
-        when(mBackupManagerServiceMock.listAllTransportComponents()).thenReturn(
+    public void listAllTransportComponentsForUser_forwarded() throws RemoteException {
+        when(mBackupManagerServiceMock.listAllTransportComponents(mUserId)).thenReturn(
                 TRANSPORT_COMPONENTS);
+        mTrampoline.initializeService();
 
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
-        assertEquals(TRANSPORT_COMPONENTS, mTrampoline.listAllTransportComponents());
-        verify(mBackupManagerServiceMock).listAllTransportComponents();
+        assertEquals(TRANSPORT_COMPONENTS, mTrampoline.listAllTransportComponentsForUser(mUserId));
+        verify(mBackupManagerServiceMock).listAllTransportComponents(mUserId);
     }
 
     @Test
@@ -603,28 +756,50 @@
     @Test
     public void getTransportWhitelist_forwarded() throws RemoteException {
         when(mBackupManagerServiceMock.getTransportWhitelist()).thenReturn(TRANSPORTS);
+        mTrampoline.initializeService();
 
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         assertEquals(TRANSPORTS, mTrampoline.getTransportWhitelist());
         verify(mBackupManagerServiceMock).getTransportWhitelist();
     }
 
     @Test
-    public void describeTransport_calledBeforeInitialize_ignored() throws RemoteException {
-        mTrampoline.updateTransportAttributes(TRANSPORT_COMPONENT_NAME, TRANSPORT_NAME, null,
-                "Transport Destination", null, "Data Management");
+    public void updateTransportAttributesForUser_calledBeforeInitialize_ignored()
+            throws RemoteException {
+        mTrampoline.updateTransportAttributesForUser(
+                mUserId,
+                TRANSPORT_COMPONENT_NAME,
+                TRANSPORT_NAME,
+                null,
+                "Transport Destination",
+                null,
+                "Data Management");
+
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void describeTransport_forwarded() throws RemoteException {
+    public void updateTransportAttributesForUser_forwarded() throws RemoteException {
         when(mBackupManagerServiceMock.getTransportWhitelist()).thenReturn(TRANSPORTS);
+        mTrampoline.initializeService();
 
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
-        mTrampoline.updateTransportAttributes(TRANSPORT_COMPONENT_NAME, TRANSPORT_NAME, null,
-                "Transport Destination", null, "Data Management");
-        verify(mBackupManagerServiceMock).updateTransportAttributes(TRANSPORT_COMPONENT_NAME,
-                TRANSPORT_NAME, null, "Transport Destination", null, "Data Management");
+        mTrampoline.updateTransportAttributesForUser(
+                mUserId,
+                TRANSPORT_COMPONENT_NAME,
+                TRANSPORT_NAME,
+                null,
+                "Transport Destination",
+                null,
+                "Data Management");
+
+        verify(mBackupManagerServiceMock)
+                .updateTransportAttributes(
+                        mUserId,
+                        TRANSPORT_COMPONENT_NAME,
+                        TRANSPORT_NAME,
+                        null,
+                        "Transport Destination",
+                        null,
+                        "Data Management");
     }
 
     @Test
@@ -634,16 +809,31 @@
     }
 
     @Test
-    public void selectBackupTransport_forwarded() throws RemoteException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
-        mTrampoline.selectBackupTransport(TRANSPORT_NAME);
-        verify(mBackupManagerServiceMock).selectBackupTransport(TRANSPORT_NAME);
+    public void selectBackupTransportForUser_forwarded() throws RemoteException {
+        mTrampoline.initializeService();
+
+        mTrampoline.selectBackupTransportForUser(mUserId, TRANSPORT_NAME);
+
+        verify(mBackupManagerServiceMock).selectBackupTransport(mUserId, TRANSPORT_NAME);
     }
 
     @Test
-    public void selectBackupTransportAsync_calledBeforeInitialize_ignored() throws Exception {
+    public void selectBackupTransport_forwarded() throws RemoteException {
+        TrampolineTestable.sCallingUserId = mUserId;
+        mTrampoline.initializeService();
+
+        mTrampoline.selectBackupTransport(TRANSPORT_NAME);
+
+        verify(mBackupManagerServiceMock).selectBackupTransport(mUserId, TRANSPORT_NAME);
+    }
+
+    @Test
+    public void selectBackupTransportAsyncForUser_calledBeforeInitialize_ignored()
+            throws Exception {
         LinkedBlockingQueue<Integer> q = new LinkedBlockingQueue();
-        mTrampoline.selectBackupTransportAsync(
+
+        mTrampoline.selectBackupTransportAsyncForUser(
+                mUserId,
                 TRANSPORT_COMPONENT_NAME,
                 new ISelectBackupTransportCallback() {
                     @Override
@@ -661,6 +851,7 @@
                         return null;
                     }
                 });
+
         verifyNoMoreInteractions(mBackupManagerServiceMock);
         Integer errorCode = q.poll(5, TimeUnit.SECONDS);
         assertNotNull(errorCode);
@@ -668,17 +859,19 @@
     }
 
     @Test
-    public void selectBackupTransportAsync_calledBeforeInitialize_ignored_nullListener()
+    public void selectBackupTransportAsyncForUser_calledBeforeInitialize_ignored_nullListener()
             throws Exception {
-        mTrampoline.selectBackupTransportAsync(TRANSPORT_COMPONENT_NAME, null);
+        mTrampoline.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, null);
+
         verifyNoMoreInteractions(mBackupManagerServiceMock);
         // No crash.
     }
 
     @Test
-    public void selectBackupTransportAsync_calledBeforeInitialize_ignored_listenerThrowException()
+    public void selectBackupTransportAsyncForUser_calledBeforeInitialize_ignored_listenerThrows()
             throws Exception {
-        mTrampoline.selectBackupTransportAsync(
+        mTrampoline.selectBackupTransportAsyncForUser(
+                mUserId,
                 TRANSPORT_COMPONENT_NAME,
                 new ISelectBackupTransportCallback() {
                     @Override
@@ -696,16 +889,19 @@
                         return null;
                     }
                 });
+
         verifyNoMoreInteractions(mBackupManagerServiceMock);
         // No crash.
     }
 
     @Test
-    public void selectBackupTransportAsync_forwarded() throws RemoteException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
-        mTrampoline.selectBackupTransportAsync(TRANSPORT_COMPONENT_NAME, null);
-        verify(mBackupManagerServiceMock).selectBackupTransportAsync(TRANSPORT_COMPONENT_NAME,
-                null);
+    public void selectBackupTransportAsyncForUser_forwarded() throws RemoteException {
+        mTrampoline.initializeService();
+
+        mTrampoline.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, null);
+
+        verify(mBackupManagerServiceMock)
+                .selectBackupTransportAsync(mUserId, TRANSPORT_COMPONENT_NAME, null);
     }
 
     @Test
@@ -715,14 +911,28 @@
     }
 
     @Test
-    public void getConfigurationIntent_forwarded() throws RemoteException {
+    public void getConfigurationIntentForUser_forwarded() throws RemoteException {
         Intent configurationIntentStub = new Intent();
-        when(mBackupManagerServiceMock.getConfigurationIntent(TRANSPORT_NAME)).thenReturn(
+        when(mBackupManagerServiceMock.getConfigurationIntent(mUserId, TRANSPORT_NAME)).thenReturn(
                 configurationIntentStub);
+        mTrampoline.initializeService();
 
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        assertEquals(
+                configurationIntentStub,
+                mTrampoline.getConfigurationIntentForUser(mUserId, TRANSPORT_NAME));
+        verify(mBackupManagerServiceMock).getConfigurationIntent(mUserId, TRANSPORT_NAME);
+    }
+
+    @Test
+    public void getConfigurationIntent_forwarded() throws RemoteException {
+        TrampolineTestable.sCallingUserId = mUserId;
+        Intent configurationIntentStub = new Intent();
+        when(mBackupManagerServiceMock.getConfigurationIntent(mUserId, TRANSPORT_NAME)).thenReturn(
+                configurationIntentStub);
+        mTrampoline.initializeService();
+
         assertEquals(configurationIntentStub, mTrampoline.getConfigurationIntent(TRANSPORT_NAME));
-        verify(mBackupManagerServiceMock).getConfigurationIntent(TRANSPORT_NAME);
+        verify(mBackupManagerServiceMock).getConfigurationIntent(mUserId, TRANSPORT_NAME);
     }
 
     @Test
@@ -732,13 +942,26 @@
     }
 
     @Test
+    public void getDestinationStringForUser_forwarded() throws RemoteException {
+        when(mBackupManagerServiceMock.getDestinationString(mUserId, TRANSPORT_NAME)).thenReturn(
+                DESTINATION_STRING);
+        mTrampoline.initializeService();
+
+        assertEquals(
+                DESTINATION_STRING,
+                mTrampoline.getDestinationStringForUser(mUserId, TRANSPORT_NAME));
+        verify(mBackupManagerServiceMock).getDestinationString(mUserId, TRANSPORT_NAME);
+    }
+
+    @Test
     public void getDestinationString_forwarded() throws RemoteException {
-        when(mBackupManagerServiceMock.getDestinationString(TRANSPORT_NAME)).thenReturn(
+        TrampolineTestable.sCallingUserId = mUserId;
+        when(mBackupManagerServiceMock.getDestinationString(mUserId, TRANSPORT_NAME)).thenReturn(
                 DESTINATION_STRING);
 
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService();
         assertEquals(DESTINATION_STRING, mTrampoline.getDestinationString(TRANSPORT_NAME));
-        verify(mBackupManagerServiceMock).getDestinationString(TRANSPORT_NAME);
+        verify(mBackupManagerServiceMock).getDestinationString(mUserId, TRANSPORT_NAME);
     }
 
     @Test
@@ -748,14 +971,28 @@
     }
 
     @Test
-    public void getDataManagementIntent_forwarded() throws RemoteException {
+    public void getDataManagementIntentForUser_forwarded() throws RemoteException {
         Intent dataManagementIntent = new Intent();
-        when(mBackupManagerServiceMock.getDataManagementIntent(TRANSPORT_NAME)).thenReturn(
+        when(mBackupManagerServiceMock.getDataManagementIntent(mUserId, TRANSPORT_NAME)).thenReturn(
                 dataManagementIntent);
+        mTrampoline.initializeService();
 
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        assertEquals(
+                dataManagementIntent,
+                mTrampoline.getDataManagementIntentForUser(mUserId, TRANSPORT_NAME));
+        verify(mBackupManagerServiceMock).getDataManagementIntent(mUserId, TRANSPORT_NAME);
+    }
+
+    @Test
+    public void getDataManagementIntent_forwarded() throws RemoteException {
+        TrampolineTestable.sCallingUserId = mUserId;
+        Intent dataManagementIntent = new Intent();
+        when(mBackupManagerServiceMock.getDataManagementIntent(mUserId, TRANSPORT_NAME)).thenReturn(
+                dataManagementIntent);
+        mTrampoline.initializeService();
+
         assertEquals(dataManagementIntent, mTrampoline.getDataManagementIntent(TRANSPORT_NAME));
-        verify(mBackupManagerServiceMock).getDataManagementIntent(TRANSPORT_NAME);
+        verify(mBackupManagerServiceMock).getDataManagementIntent(mUserId, TRANSPORT_NAME);
     }
 
     @Test
@@ -765,26 +1002,42 @@
     }
 
     @Test
-    public void getDataManagementLabel_forwarded() throws RemoteException {
-        when(mBackupManagerServiceMock.getDataManagementLabel(TRANSPORT_NAME)).thenReturn(
+    public void getDataManagementLabelForUser_forwarded() throws RemoteException {
+        when(mBackupManagerServiceMock.getDataManagementLabel(mUserId, TRANSPORT_NAME)).thenReturn(
                 DATA_MANAGEMENT_LABEL);
+        mTrampoline.initializeService();
 
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        assertEquals(
+                DATA_MANAGEMENT_LABEL,
+                mTrampoline.getDataManagementLabelForUser(mUserId, TRANSPORT_NAME));
+        verify(mBackupManagerServiceMock).getDataManagementLabel(mUserId, TRANSPORT_NAME);
+    }
+
+    @Test
+    public void getDataManagementLabel_forwarded() throws RemoteException {
+        TrampolineTestable.sCallingUserId = mUserId;
+        when(mBackupManagerServiceMock.getDataManagementLabel(mUserId, TRANSPORT_NAME)).thenReturn(
+                DATA_MANAGEMENT_LABEL);
+        mTrampoline.initializeService();
+
         assertEquals(DATA_MANAGEMENT_LABEL, mTrampoline.getDataManagementLabel(TRANSPORT_NAME));
-        verify(mBackupManagerServiceMock).getDataManagementLabel(TRANSPORT_NAME);
+        verify(mBackupManagerServiceMock).getDataManagementLabel(mUserId, TRANSPORT_NAME);
     }
 
     @Test
     public void beginRestoreSession_calledBeforeInitialize_ignored() throws RemoteException {
-        mTrampoline.beginRestoreSession(PACKAGE_NAME, TRANSPORT_NAME);
+        mTrampoline.beginRestoreSessionForUser(mUserId, PACKAGE_NAME, TRANSPORT_NAME);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void beginRestoreSession_forwarded() throws RemoteException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
-        mTrampoline.beginRestoreSession(PACKAGE_NAME, TRANSPORT_NAME);
-        verify(mBackupManagerServiceMock).beginRestoreSession(PACKAGE_NAME, TRANSPORT_NAME);
+    public void beginRestoreSessionForUser_forwarded() throws RemoteException {
+        mTrampoline.initializeService();
+
+        mTrampoline.beginRestoreSessionForUser(mUserId, PACKAGE_NAME, TRANSPORT_NAME);
+
+        verify(mBackupManagerServiceMock)
+                .beginRestoreSession(mUserId, PACKAGE_NAME, TRANSPORT_NAME);
     }
 
     @Test
@@ -795,39 +1048,46 @@
 
     @Test
     public void opComplete_forwarded() throws RemoteException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        TrampolineTestable.sCallingUserId = mUserId;
+        mTrampoline.initializeService();
+
         mTrampoline.opComplete(1, 2);
-        verify(mBackupManagerServiceMock).opComplete(1, 2);
+
+        verify(mBackupManagerServiceMock).opComplete(mUserId, 1, 2);
     }
 
     @Test
-    public void getAvailableRestoreToken_calledBeforeInitialize_ignored() throws RemoteException {
-        assertEquals(0, mTrampoline.getAvailableRestoreToken(PACKAGE_NAME));
+    public void getAvailableRestoreTokenForUser_calledBeforeInitialize_ignored()
+            throws RemoteException {
+        assertEquals(0, mTrampoline.getAvailableRestoreTokenForUser(mUserId, PACKAGE_NAME));
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void getAvailableRestoreToken_forwarded() throws RemoteException {
-        when(mBackupManagerServiceMock.getAvailableRestoreToken(PACKAGE_NAME)).thenReturn(123L);
+    public void getAvailableRestoreTokenForUser_forwarded() throws RemoteException {
+        when(mBackupManagerServiceMock.getAvailableRestoreToken(mUserId, PACKAGE_NAME))
+                .thenReturn(123L);
+        mTrampoline.initializeService();
 
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
-        assertEquals(123, mTrampoline.getAvailableRestoreToken(PACKAGE_NAME));
-        verify(mBackupManagerServiceMock).getAvailableRestoreToken(PACKAGE_NAME);
+        assertEquals(123, mTrampoline.getAvailableRestoreTokenForUser(mUserId, PACKAGE_NAME));
+        verify(mBackupManagerServiceMock).getAvailableRestoreToken(mUserId, PACKAGE_NAME);
     }
 
     @Test
-    public void isAppEligibleForBackup_calledBeforeInitialize_ignored() throws RemoteException {
-        assertFalse(mTrampoline.isAppEligibleForBackup(PACKAGE_NAME));
+    public void isAppEligibleForBackupForUser_calledBeforeInitialize_ignored()
+            throws RemoteException {
+        assertFalse(mTrampoline.isAppEligibleForBackupForUser(mUserId, PACKAGE_NAME));
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void isAppEligibleForBackup_forwarded() throws RemoteException {
-        when(mBackupManagerServiceMock.isAppEligibleForBackup(PACKAGE_NAME)).thenReturn(true);
+    public void isAppEligibleForBackupForUser_forwarded() throws RemoteException {
+        when(mBackupManagerServiceMock.isAppEligibleForBackup(mUserId, PACKAGE_NAME))
+                .thenReturn(true);
+        mTrampoline.initializeService();
 
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
-        assertTrue(mTrampoline.isAppEligibleForBackup(PACKAGE_NAME));
-        verify(mBackupManagerServiceMock).isAppEligibleForBackup(PACKAGE_NAME);
+        assertTrue(mTrampoline.isAppEligibleForBackupForUser(mUserId, PACKAGE_NAME));
+        verify(mBackupManagerServiceMock).isAppEligibleForBackup(mUserId, PACKAGE_NAME);
     }
 
     @Test
@@ -841,7 +1101,7 @@
     public void requestBackupForUser_forwarded() throws RemoteException {
         when(mBackupManagerServiceMock.requestBackup(mUserId, PACKAGE_NAMES,
                 mBackupObserverMock, mBackupManagerMonitorMock, 123)).thenReturn(456);
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService();
 
         assertEquals(456, mTrampoline.requestBackupForUser(mUserId, PACKAGE_NAMES,
                 mBackupObserverMock, mBackupManagerMonitorMock, 123));
@@ -854,7 +1114,7 @@
         TrampolineTestable.sCallingUserId = mUserId;
         when(mBackupManagerServiceMock.requestBackup(NON_USER_SYSTEM, PACKAGE_NAMES,
                 mBackupObserverMock, mBackupManagerMonitorMock, 123)).thenReturn(456);
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService();
 
         assertEquals(456, mTrampoline.requestBackup(PACKAGE_NAMES,
                 mBackupObserverMock, mBackupManagerMonitorMock, 123));
@@ -870,7 +1130,7 @@
 
     @Test
     public void cancelBackupsForUser_forwarded() throws RemoteException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService();
 
         mTrampoline.cancelBackupsForUser(mUserId);
 
@@ -880,7 +1140,7 @@
     @Test
     public void cancelBackups_forwardedToCallingUserId() throws RemoteException {
         TrampolineTestable.sCallingUserId = mUserId;
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService();
 
         mTrampoline.cancelBackups();
 
@@ -898,7 +1158,7 @@
         FullBackupJob fullBackupJob = new FullBackupJob();
         when(mBackupManagerServiceMock.beginFullBackup(fullBackupJob)).thenReturn(true);
 
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService();
         assertTrue(mTrampoline.beginFullBackup(fullBackupJob));
         verify(mBackupManagerServiceMock).beginFullBackup(fullBackupJob);
     }
@@ -911,7 +1171,7 @@
 
     @Test
     public void endFullBackup_forwarded() throws RemoteException {
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService();
         mTrampoline.endFullBackup();
         verify(mBackupManagerServiceMock).endFullBackup();
     }
@@ -921,8 +1181,7 @@
         when(mContextMock.checkCallingOrSelfPermission(
                 android.Manifest.permission.DUMP)).thenReturn(
                 PackageManager.PERMISSION_DENIED);
-
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService();
 
         mTrampoline.dump(mFileDescriptorStub, mPrintWriterMock, new String[0]);
 
@@ -945,8 +1204,7 @@
         when(mContextMock.checkCallingOrSelfPermission(
                 android.Manifest.permission.DUMP)).thenReturn(
                 PackageManager.PERMISSION_GRANTED);
-
-        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService();
 
         mTrampoline.dump(mFileDescriptorStub, mPrintWriterMock, null);
 
@@ -990,6 +1248,11 @@
             return sBackupManagerServiceMock;
         }
 
+        @Override
+        protected void postToHandler(Runnable runnable) {
+            runnable.run();
+        }
+
         int getCreateServiceCallsCount() {
             return mCreateServiceCallsCount;
         }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyEventLoggerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyEventLoggerTest.java
new file mode 100644
index 0000000..b24bca8
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyEventLoggerTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.devicepolicy;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.app.admin.DevicePolicyEventLogger;
+import android.content.ComponentName;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for {@link DevicePolicyEventLogger}.
+ * <p/>
+ * Run with <code>atest DevicePolicyEventLoggerTest</code>.
+ */
+@RunWith(AndroidJUnit4.class)
+public class DevicePolicyEventLoggerTest {
+    @Test
+    public void testAllFields() {
+        final DevicePolicyEventLogger eventLogger = DevicePolicyEventLogger
+                .createEvent(5)
+                .setBoolean(true)
+                .setStrings("string1", "string2", "string3")
+                .setAdmin(new ComponentName("com.test.package", ".TestAdmin"))
+                .setInt(4321)
+                .setTimePeriod(1234L);
+        assertThat(eventLogger.getEventId()).isEqualTo(5);
+        assertThat(eventLogger.getBoolean()).isTrue();
+        assertThat(eventLogger.getStringArray())
+                .isEqualTo(new String[] {"string1", "string2", "string3"});
+        assertThat(eventLogger.getAdminPackageName()).isEqualTo("com.test.package");
+        assertThat(eventLogger.getInt()).isEqualTo(4321);
+        assertThat(eventLogger.getTimePeriod()).isEqualTo(1234L);
+    }
+
+    @Test
+    public void testStrings() {
+        assertThat(DevicePolicyEventLogger
+                .createEvent(0)
+                .setStrings("string1", "string2", "string3").getStringArray())
+                .isEqualTo(new String[] {"string1", "string2", "string3"});
+
+        assertThat(DevicePolicyEventLogger
+                .createEvent(0)
+                .setStrings("string1", new String[] {"string2", "string3"}).getStringArray())
+                .isEqualTo(new String[] {"string1", "string2", "string3"});
+
+        assertThat(DevicePolicyEventLogger
+                .createEvent(0)
+                .setStrings("string1", "string2", new String[] {"string3"}).getStringArray())
+                .isEqualTo(new String[] {"string1", "string2", "string3"});
+
+        assertThat(DevicePolicyEventLogger
+                .createEvent(0)
+                .setStrings((String) null).getStringArray())
+                .isEqualTo(new String[] {null});
+
+        assertThat(DevicePolicyEventLogger
+                .createEvent(0)
+                .setStrings((String[]) null).getStringArray())
+                .isEqualTo(null);
+
+        assertThrows(NullPointerException.class, () -> DevicePolicyEventLogger
+                .createEvent(0)
+                .setStrings("string1", "string2", null));
+    }
+
+    @Test
+    public void testAdmins() {
+        assertThat(DevicePolicyEventLogger
+                .createEvent(0)
+                .setAdmin("com.package.name")
+                .getAdminPackageName())
+                .isEqualTo("com.package.name");
+
+        assertThat(DevicePolicyEventLogger
+                .createEvent(0)
+                .setAdmin(new ComponentName("com.package.name", ".TestAdmin"))
+                .getAdminPackageName())
+                .isEqualTo("com.package.name");
+    }
+
+    @Test
+    public void testDefaultValues() {
+        final DevicePolicyEventLogger eventLogger = DevicePolicyEventLogger
+                .createEvent(0);
+        assertThat(eventLogger.getEventId()).isEqualTo(0);
+        assertThat(eventLogger.getBoolean()).isFalse();
+        assertThat(eventLogger.getStringArray()).isEqualTo(null);
+        assertThat(eventLogger.getAdminPackageName()).isEqualTo(null);
+        assertThat(eventLogger.getInt()).isEqualTo(0);
+        assertThat(eventLogger.getTimePeriod()).isEqualTo(0L);
+    }
+}
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 729fac5..38e8ac2 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -72,7 +72,6 @@
 import android.content.pm.UserInfo;
 import android.graphics.Color;
 import android.net.Uri;
-import android.net.wifi.WifiInfo;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
 import android.os.Process;
@@ -1989,17 +1988,16 @@
         // Test 4, Caller is DO now.
         assertTrue(dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM));
 
-        // 4-1.  But no WifiInfo.
+        // 4-1.  But WifiManager is not ready.
         assertNull(dpm.getWifiMacAddress(admin1));
 
-        // 4-2.  Returns WifiInfo, but with the default MAC.
-        when(getServices().wifiManager.getConnectionInfo()).thenReturn(new WifiInfo());
+        // 4-2.  When WifiManager returns an empty array, dpm should also output null.
+        when(getServices().wifiManager.getFactoryMacAddresses()).thenReturn(new String[0]);
         assertNull(dpm.getWifiMacAddress(admin1));
 
         // 4-3. With a real MAC address.
-        final WifiInfo wi = new WifiInfo();
-        wi.setMacAddress("11:22:33:44:55:66");
-        when(getServices().wifiManager.getConnectionInfo()).thenReturn(wi);
+        final String[] macAddresses = new String[]{"11:22:33:44:55:66"};
+        when(getServices().wifiManager.getFactoryMacAddresses()).thenReturn(macAddresses);
         assertEquals("11:22:33:44:55:66", dpm.getWifiMacAddress(admin1));
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index e9bfa8f..4742a73 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -149,12 +149,11 @@
         verify(mMockInputManagerInternal).setDisplayViewports(viewportCaptor.capture());
         List<DisplayViewport> viewports = viewportCaptor.getValue();
 
-        // Expect to receive 3 viewports: internal, external, and virtual
-        assertEquals(3, viewports.size());
+        // Expect to receive 2 viewports: internal, and virtual
+        assertEquals(2, viewports.size());
 
         DisplayViewport virtualViewport = null;
         DisplayViewport internalViewport = null;
-        DisplayViewport externalViewport = null;
         for (int i = 0; i < viewports.size(); i++) {
             DisplayViewport v = viewports.get(i);
             switch (v.type) {
@@ -163,7 +162,7 @@
                     break;
                 }
                 case DisplayViewport.VIEWPORT_EXTERNAL: {
-                    externalViewport = v;
+                    fail("EXTERNAL viewport should not exist.");
                     break;
                 }
                 case DisplayViewport.VIEWPORT_VIRTUAL: {
@@ -172,14 +171,12 @@
                 }
             }
         }
-        // INTERNAL and EXTERNAL viewports get created upon access
+        // INTERNAL viewport gets created upon access.
         assertNotNull(internalViewport);
-        assertNotNull(externalViewport);
         assertNotNull(virtualViewport);
 
-        // INTERNAL and EXTERNAL
+        // INTERNAL
         assertTrue(internalViewport.valid);
-        assertTrue(externalViewport.valid);
 
         // VIRTUAL
         assertEquals(height, virtualViewport.deviceHeight);
@@ -216,39 +213,16 @@
         verify(mMockInputManagerInternal).setDisplayViewports(viewportCaptor.capture());
         List<DisplayViewport> viewports = viewportCaptor.getValue();
 
-        // Expect to receive 2 viewports: 1 internal, 1 external
-        assertEquals(2, viewports.size());
+        // Expect to receive actual viewports: 1 internal
+        assertEquals(1, viewports.size());
 
-        DisplayViewport internalViewport = null;
-        DisplayViewport externalViewport = null;
-        for (int i = 0; i < viewports.size(); i++) {
-            DisplayViewport v = viewports.get(i);
-            switch (v.type) {
-                case DisplayViewport.VIEWPORT_INTERNAL: {
-                    internalViewport = v;
-                    break;
-                }
-                case DisplayViewport.VIEWPORT_EXTERNAL: {
-                    externalViewport = v;
-                    break;
-                }
-                default: {
-                    fail("Unexpected viewport type: " + DisplayViewport.typeToString(v.type));
-                    break;
-                }
-            }
-        }
-        // INTERNAL and EXTERNAL viewports get created upon access
+        DisplayViewport internalViewport = viewports.get(0);
+
+        // INTERNAL is the only one actual display.
         assertNotNull(internalViewport);
-        assertNotNull(externalViewport);
+        assertEquals(DisplayViewport.VIEWPORT_INTERNAL, internalViewport.type);
         assertTrue(internalViewport.valid);
         assertEquals(displayId, internalViewport.displayId);
-
-        // To simplify comparison, override the type for external Viewport
-        // TODO (b/116850516) remove this
-        externalViewport.type = internalViewport.type;
-        assertEquals(internalViewport, externalViewport);
-        externalViewport.type = DisplayViewport.VIEWPORT_EXTERNAL; // undo the changes above
     }
 
     @Test
@@ -415,7 +389,7 @@
         if (attr == null) return; //sampling not supported on device, skip remainder of test.
 
         boolean enabled = displayManager.setDisplayedContentSamplingEnabledInternal(0, true, 0, 0);
-        assertTrue(!enabled);
+        assertTrue(enabled);
 
         displayManager.setDisplayedContentSamplingEnabledInternal(0, false, 0, 0);
         DisplayedContentSample sample = displayManager.getDisplayedContentSampleInternal(0, 0, 0);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 9e3a0ea..147a691 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -20,18 +20,17 @@
 import static com.android.server.hdmi.Constants.ADDR_TV;
 import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
 import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF;
-import static com.google.common.truth.Truth.assertThat;
 
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
 
 import android.media.AudioManager;
 import android.os.Looper;
 import android.os.SystemProperties;
 import android.os.test.TestLooper;
+
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
+
 import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
 
 import org.junit.Before;
@@ -123,6 +122,11 @@
 
                     @Override
                     void wakeUp() {}
+
+                    @Override
+                    boolean isControlEnabled() {
+                        return true;
+                    }
                 };
 
         mMyLooper = mTestLooper.getLooper();
@@ -144,7 +148,7 @@
     }
 
     @Test
-    public void handleGiveAudioStatus_volume_10_mute_true() {
+    public void handleGiveAudioStatus_volume_10_mute_true() throws Exception {
         mMusicVolume = 10;
         mMusicMute = true;
         mMusicMaxVolume = 20;
@@ -161,7 +165,7 @@
     }
 
     @Test
-    public void handleGiveSystemAudioModeStatus_originalOff() {
+    public void handleGiveSystemAudioModeStatus_originalOff() throws Exception {
         HdmiCecMessage expectedMessage =
                 HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
         HdmiCecMessage messageGive =
@@ -228,7 +232,7 @@
     }
 
     @Test
-    public void handleSetSystemAudioMode_setOn_orignalOff() {
+    public void handleSetSystemAudioMode_setOn_orignalOff() throws Exception {
         mMusicMute = true;
         HdmiCecMessage messageSet =
                 HdmiCecMessageBuilder.buildSetSystemAudioMode(ADDR_TV, ADDR_AUDIO_SYSTEM, true);
@@ -256,7 +260,7 @@
 
     @Ignore("b/80297700")
     @Test
-    public void handleSystemAudioModeRequest_turnOffByTv() {
+    public void handleSystemAudioModeRequest_turnOffByTv() throws Exception {
         assertThat(mMusicMute).isFalse();
         // Check if feature correctly turned off
         HdmiCecMessage messageGive =
@@ -282,7 +286,7 @@
 
     @Ignore("b/80297700")
     @Test
-    public void onStandbyAudioSystem_currentSystemAudioControlOn() {
+    public void onStandbyAudioSystem_currentSystemAudioControlOn() throws Exception {
         // Set system audio control on first
         mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
         // Check if standby correctly turns off the feature
@@ -296,7 +300,7 @@
     }
 
     @Test
-    public void systemAudioControlOnPowerOn_alwaysOn() {
+    public void systemAudioControlOnPowerOn_alwaysOn() throws Exception {
         mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
         mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
                 Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
@@ -307,7 +311,7 @@
     }
 
     @Test
-    public void systemAudioControlOnPowerOn_neverOn() {
+    public void systemAudioControlOnPowerOn_neverOn() throws Exception {
         mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
         mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
                 Constants.NEVER_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
@@ -318,7 +322,7 @@
     }
 
     @Test
-    public void systemAudioControlOnPowerOn_useLastState_off() {
+    public void systemAudioControlOnPowerOn_useLastState_off() throws Exception {
         mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
         mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
                 Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
@@ -329,7 +333,7 @@
     }
 
     @Test
-    public void systemAudioControlOnPowerOn_useLastState_on() {
+    public void systemAudioControlOnPowerOn_useLastState_on() throws Exception {
         mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
         mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
                 Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
@@ -340,7 +344,7 @@
     }
 
     @Test
-    public void handleActiveSource_updateActiveSource() {
+    public void handleActiveSource_updateActiveSource() throws Exception {
         HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
         ActiveSource expectedActiveSource = new ActiveSource(ADDR_TV, 0x0000);
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleActiveSource(message))
@@ -351,7 +355,7 @@
     }
 
     @Test
-    public void terminateSystemAudioMode_systemAudioModeOff() {
+    public void terminateSystemAudioMode_systemAudioModeOff() throws Exception {
         mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(false);
         assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isFalse();
         mMusicMute = false;
@@ -366,7 +370,7 @@
 
     @Ignore("b/80297700")
     @Test
-    public void terminateSystemAudioMode_systemAudioModeOn() {
+    public void terminateSystemAudioMode_systemAudioModeOn() throws Exception {
         mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
         assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isTrue();
         mMusicMute = false;
@@ -381,7 +385,7 @@
     }
 
     @Test
-    public void isPhysicalAddressMeOrBelow_isMe() {
+    public void isPhysicalAddressMeOrBelow_isMe() throws Exception {
         int targetPhysicalAddress = 0x1000;
         mNativeWrapper.setPhysicalAddress(0x1000);
         assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
@@ -389,7 +393,7 @@
     }
 
     @Test
-    public void isPhysicalAddressMeOrBelow_isBelow() {
+    public void isPhysicalAddressMeOrBelow_isBelow() throws Exception {
         int targetPhysicalAddress = 0x1100;
         mNativeWrapper.setPhysicalAddress(0x1000);
         assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
@@ -397,7 +401,7 @@
     }
 
     @Test
-    public void isPhysicalAddressMeOrBelow_neitherMeNorBelow() {
+    public void isPhysicalAddressMeOrBelow_neitherMeNorBelow() throws Exception {
         int targetPhysicalAddress = 0x3000;
         mNativeWrapper.setPhysicalAddress(0x2000);
         assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
@@ -420,7 +424,7 @@
     }
 
     @Test
-    public void handleRequestArcInitiate_isNotDirectConnectedToTv() {
+    public void handleRequestArcInitiate_isNotDirectConnectedToTv() throws Exception {
         HdmiCecMessage message = HdmiCecMessageBuilder
             .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
         HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
@@ -437,7 +441,7 @@
     }
 
     @Test
-    public void handleRequestArcInitiate_startArcInitiationActionFromAvr() {
+    public void handleRequestArcInitiate_startArcInitiationActionFromAvr() throws Exception {
         HdmiCecMessage message = HdmiCecMessageBuilder
             .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
         mNativeWrapper.setPhysicalAddress(0x1000);
@@ -452,7 +456,7 @@
     }
 
     @Test
-    public void handleRequestArcTerminate_arcIsOn_startTerminationActionFromAvr() {
+    public void handleRequestArcTerminate_arcIsOn_startTerminationActionFromAvr() throws Exception {
         mHdmiCecLocalDeviceAudioSystem.setArcStatus(true);
         assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isTrue();
 
@@ -469,7 +473,7 @@
     }
 
     @Test
-    public void handleRequestArcTerminate_arcIsNotOn() {
+    public void handleRequestArcTerminate_arcIsNotOn() throws Exception {
         assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
         HdmiCecMessage message = HdmiCecMessageBuilder
             .buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
@@ -486,7 +490,7 @@
     }
 
     @Test
-    public void handleRequestArcInit_arcIsNotSupported() {
+    public void handleRequestArcInit_arcIsNotSupported() throws Exception {
         HdmiCecMessage message = HdmiCecMessageBuilder
             .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
         HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
@@ -501,4 +505,15 @@
         mTestLooper.dispatchAll();
         assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
     }
+
+    @Test
+    public void onStandby_setAutoDeviceOff_true() throws Exception {
+        HdmiCecMessage expectedMessage =
+                HdmiCecMessageBuilder.buildStandby(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST);
+        mHdmiCecLocalDeviceAudioSystem.setAutoDeviceOff(true);
+        mHdmiCecLocalDeviceAudioSystem.onStandby(false, STANDBY_SCREEN_OFF);
+
+        mTestLooper.dispatchAll();
+        assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java b/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java
new file mode 100644
index 0000000..bd3d9ab
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import android.content.Context;
+import android.content.pm.ModuleInfo;
+import android.test.InstrumentationTestCase;
+
+import com.android.frameworks.servicestests.R;
+
+import java.util.Collections;
+import java.util.List;
+
+public class ModuleInfoProviderTest extends InstrumentationTestCase {
+    public void testSuccessfulParse() {
+        ModuleInfoProvider provider = getProvider(R.xml.well_formed_metadata);
+
+        List<ModuleInfo> mi = provider.getInstalledModules(0);
+        assertEquals(2, mi.size());
+
+        Collections.sort(mi, (ModuleInfo m1, ModuleInfo m2) ->
+                m1.getPackageName().compareTo(m1.getPackageName()));
+        assertEquals("com.android.module1", mi.get(0).getPackageName());
+        assertEquals("com.android.module2", mi.get(1).getPackageName());
+
+        ModuleInfo mi1 = provider.getModuleInfo("com.android.module1", 0);
+        assertEquals("com.android.module1", mi1.getPackageName());
+        assertEquals("module_1_name", mi1.getName());
+        assertEquals(false, mi1.isHidden());
+
+        ModuleInfo mi2 = provider.getModuleInfo("com.android.module2", 0);
+        assertEquals("com.android.module2", mi2.getPackageName());
+        assertEquals("module_2_name", mi2.getName());
+        assertEquals(true, mi2.isHidden());
+    }
+
+    public void testParseFailure_incorrectTopLevelElement() {
+        ModuleInfoProvider provider = getProvider(R.xml.unparseable_metadata1);
+        assertEquals(0, provider.getInstalledModules(0).size());
+    }
+
+    public void testParseFailure_incorrectModuleElement() {
+        ModuleInfoProvider provider = getProvider(R.xml.unparseable_metadata2);
+        assertEquals(0, provider.getInstalledModules(0).size());
+    }
+
+    public void testParse_unknownAttributesIgnored() {
+        ModuleInfoProvider provider = getProvider(R.xml.well_formed_metadata);
+
+        List<ModuleInfo> mi = provider.getInstalledModules(0);
+        assertEquals(2, mi.size());
+
+        ModuleInfo mi1 = provider.getModuleInfo("com.android.module1", 0);
+        assertEquals("com.android.module1", mi1.getPackageName());
+        assertEquals("module_1_name", mi1.getName());
+        assertEquals(false, mi1.isHidden());
+    }
+
+    /**
+     * Constructs an {@code ModuleInfoProvider} using the test package resources.
+     */
+    private ModuleInfoProvider getProvider(int resourceId) {
+        final Context ctx = getInstrumentation().getContext();
+        return new ModuleInfoProvider(ctx.getResources().getXml(resourceId), ctx.getResources());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
index 7755e94..5df4509 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
@@ -17,6 +17,7 @@
 package com.android.server.pm.dex;
 
 import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
+import static com.android.server.pm.dex.PackageDexUsage.MAX_SECONDARY_FILES_PER_OWNER;
 import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
 
 import static org.junit.Assert.assertEquals;
@@ -31,24 +32,28 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import dalvik.system.VMRuntime;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import dalvik.system.VMRuntime;
-
 import java.io.IOException;
 import java.io.StringReader;
 import java.io.StringWriter;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class PackageDexUsageTests {
+    private static final String ISA = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
+
     private PackageDexUsage mPackageDexUsage;
 
     private TestData mFooBaseUser0;
@@ -71,25 +76,23 @@
         String fooCodeDir = "/data/app/com.google.foo/";
         String fooDataDir = "/data/user/0/com.google.foo/";
 
-        String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
-
         mFooBaseUser0 = new TestData(fooPackageName,
-                fooCodeDir + "base.apk", 0, isa, false, true, fooPackageName);
+                fooCodeDir + "base.apk", 0, ISA, false, true, fooPackageName);
 
         mFooSplit1User0 = new TestData(fooPackageName,
-                fooCodeDir + "split-1.apk", 0, isa, false, true, fooPackageName);
+                fooCodeDir + "split-1.apk", 0, ISA, false, true, fooPackageName);
 
         mFooSplit2UsedByOtherApps0 = new TestData(fooPackageName,
-                fooCodeDir + "split-2.apk", 0, isa, true, true, "used.by.other.com");
+                fooCodeDir + "split-2.apk", 0, ISA, true, true, "used.by.other.com");
 
         mFooSecondary1User0 = new TestData(fooPackageName,
-                fooDataDir + "sec-1.dex", 0, isa, false, false, fooPackageName);
+                fooDataDir + "sec-1.dex", 0, ISA, false, false, fooPackageName);
 
         mFooSecondary1User1 = new TestData(fooPackageName,
-                fooDataDir + "sec-1.dex", 1, isa, false, false, fooPackageName);
+                fooDataDir + "sec-1.dex", 1, ISA, false, false, fooPackageName);
 
         mFooSecondary2UsedByOtherApps0 = new TestData(fooPackageName,
-                fooDataDir + "sec-2.dex", 0, isa, true, false, "used.by.other.com");
+                fooDataDir + "sec-2.dex", 0, ISA, true, false, "used.by.other.com");
 
         mInvalidIsa = new TestData(fooPackageName,
                 fooCodeDir + "base.apk", 0, "INVALID_ISA", false, true, "INALID_USER");
@@ -100,11 +103,11 @@
         String barDataDir1 = "/data/user/1/com.google.bar/";
 
         mBarBaseUser0 = new TestData(barPackageName,
-                barCodeDir + "base.apk", 0, isa, false, true, barPackageName);
+                barCodeDir + "base.apk", 0, ISA, false, true, barPackageName);
         mBarSecondary1User0 = new TestData(barPackageName,
-                barDataDir + "sec-1.dex", 0, isa, false, false, barPackageName);
+                barDataDir + "sec-1.dex", 0, ISA, false, false, barPackageName);
         mBarSecondary2User1 = new TestData(barPackageName,
-                barDataDir1 + "sec-2.dex", 1, isa, false, false, barPackageName);
+                barDataDir1 + "sec-2.dex", 1, ISA, false, false, barPackageName);
     }
 
     @Test
@@ -183,6 +186,25 @@
     }
 
     @Test
+    public void testRecordTooManySecondaries() {
+        int tooManyFiles = MAX_SECONDARY_FILES_PER_OWNER + 1;
+        List<TestData> expectedSecondaries = new ArrayList<>();
+        for (int i = 1; i <= tooManyFiles; i++) {
+            String fooPackageName = "com.google.foo";
+            TestData testData = new TestData(fooPackageName,
+                    "/data/user/0/" + fooPackageName + "/sec-" + i + "1.dex", 0, ISA, false, false,
+                    fooPackageName);
+            if (i < tooManyFiles) {
+                assertTrue("Adding " + testData.mDexFile, record(testData));
+                expectedSecondaries.add(testData);
+            } else {
+                assertFalse("Adding " + testData.mDexFile, record(testData));
+            }
+            assertPackageDexUsage(mPackageDexUsage, null, null, expectedSecondaries);
+        }
+    }
+
+    @Test
     public void testMultiplePackages() {
         assertTrue(record(mFooBaseUser0));
         assertTrue(record(mFooSecondary1User0));
@@ -540,7 +562,14 @@
 
     private void assertPackageDexUsage(PackageDexUsage packageDexUsage, Set<String> users,
             TestData primary, TestData... secondaries) {
-        String packageName = primary == null ? secondaries[0].mPackageName : primary.mPackageName;
+        assertPackageDexUsage(packageDexUsage, users, primary, Arrays.asList(secondaries));
+    }
+
+    private void assertPackageDexUsage(PackageDexUsage packageDexUsage, Set<String> users,
+            TestData primary, List<TestData> secondaries) {
+        String packageName = primary == null
+                ? secondaries.get(0).mPackageName
+                : primary.mPackageName;
         boolean primaryUsedByOtherApps = primary != null && primary.mUsedByOtherApps;
         PackageUseInfo pInfo = packageDexUsage.getPackageUseInfo(packageName);
 
@@ -554,7 +583,7 @@
         }
 
         Map<String, DexUseInfo> dexUseInfoMap = pInfo.getDexUseInfoMap();
-        assertEquals(secondaries.length, dexUseInfoMap.size());
+        assertEquals(secondaries.size(), dexUseInfoMap.size());
 
         // Check dex use info
         for (TestData testData : secondaries) {
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
index 8496a96..b348aee 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
@@ -699,10 +699,52 @@
         assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
     }
 
+    /** Verify the timeout message is delivered at the right time after past usage was reported */
+    @Test
+    public void testAppUsageObserver_PastUsage() throws Exception {
+        setTime(10_000L);
+        addAppUsageObserver(OBS_ID1, GROUP1, 6_000L);
+        setTime(20_000L);
+        startPastUsage(PKG_SOC1, 5_000);
+        setTime(21_000L);
+        assertTrue(mLimitReachedLatch.await(2_000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_SOC1);
+        // Verify that the observer was removed
+        assertFalse(hasAppUsageObserver(UID, OBS_ID1));
+    }
+
+    /**
+     * Verify the timeout message is delivered at the right time after past usage was reported
+     * that overlaps with already known usage
+     */
+    @Test
+    public void testAppUsageObserver_PastUsageOverlap() throws Exception {
+        setTime(0L);
+        addAppUsageObserver(OBS_ID1, GROUP1, 20_000L);
+        setTime(10_000L);
+        startUsage(PKG_SOC1);
+        setTime(20_000L);
+        stopUsage(PKG_SOC1);
+        setTime(25_000L);
+        startPastUsage(PKG_SOC1, 9_000);
+        setTime(26_000L);
+        // the 4 seconds of overlapped usage should not be counted
+        assertFalse(mLimitReachedLatch.await(2_000L, TimeUnit.MILLISECONDS));
+        setTime(30_000L);
+        assertTrue(mLimitReachedLatch.await(4_000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_SOC1);
+        // Verify that the observer was removed
+        assertFalse(hasAppUsageObserver(UID, OBS_ID1));
+    }
+
     private void startUsage(String packageName) {
         mController.noteUsageStart(packageName, USER_ID);
     }
 
+    private void startPastUsage(String packageName, int timeAgo) {
+        mController.noteUsageStart(packageName, USER_ID, timeAgo);
+    }
+
     private void stopUsage(String packageName) {
         mController.noteUsageStop(packageName, USER_ID);
     }
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 7a5eaa8..f4443fe 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -20,6 +20,7 @@
         "android-support-test",
         "mockito-target-inline-minus-junit4",
         "platform-test-annotations",
+        "hamcrest-library",
         "testables",
     ],
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 41d5a1c..20c5f5d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -27,6 +27,8 @@
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
@@ -58,6 +60,7 @@
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -112,7 +115,7 @@
     private static final Uri CUSTOM_SOUND = Settings.System.DEFAULT_ALARM_ALERT_URI;
     private static final AudioAttributes CUSTOM_ATTRIBUTES = new AudioAttributes.Builder()
             .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
-            .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
+            .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
             .build();
     private static final int CUSTOM_LIGHT_COLOR = Color.BLACK;
     private static final int CUSTOM_LIGHT_ON = 10000;
@@ -235,11 +238,11 @@
                 false /* noisy */, false /* buzzy*/, true /* lights */);
     }
 
-    private NotificationRecord getCustomLightsNotification() {
-        return getNotificationRecord(mId, false /* insistent */, true /* once */,
-                false /* noisy */, true /* buzzy*/, true /* lights */,
-                true /* defaultVibration */, true /* defaultSound */, false /* defaultLights */,
-                null, Notification.GROUP_ALERT_ALL, false);
+    private NotificationRecord getCallRecord(int id, boolean insistent) {
+        return getNotificationRecord(id, false, false /* once */, true /* noisy */,
+                false /* buzzy */, false /* lights */, false /* default vib */,
+                false /* default sound */, false /* default lights */, "",
+                Notification.GROUP_ALERT_ALL, false);
     }
 
     private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
@@ -349,11 +352,6 @@
                 eq(false), (AudioAttributes) anyObject());
     }
 
-    private void verifyCustomBeep() throws RemoteException {
-        verify(mRingtonePlayer, times(1)).playAsync(eq(CUSTOM_SOUND), (UserHandle) anyObject(),
-                eq(false), (AudioAttributes) anyObject());
-    }
-
     private void verifyNeverStopAudio() throws RemoteException {
         verify(mRingtonePlayer, never()).stopAsync();
     }
@@ -417,7 +415,7 @@
 
         verifyLights();
         assertTrue(r.isInterruptive());
-        assertFalse(r.getAudiblyAlerted());
+        assertEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -430,7 +428,7 @@
         verifyNeverVibrate();
         verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
         assertTrue(r.isInterruptive());
-        assertTrue(r.getAudiblyAlerted());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -441,7 +439,7 @@
 
         verifyBeep();
         assertTrue(r.isInterruptive());
-        assertTrue(r.getAudiblyAlerted());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -452,7 +450,7 @@
 
         verifyNeverBeep();
         assertFalse(r.isInterruptive());
-        assertFalse(r.getAudiblyAlerted());
+        assertEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -490,7 +488,7 @@
         verifyNeverBeep();
         verifyNeverVibrate();
         assertFalse(r.isInterruptive());
-        assertFalse(r.getAudiblyAlerted());
+        assertEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -503,7 +501,7 @@
         verifyNeverBeep();
         verifyNeverVibrate();
         assertFalse(r.isInterruptive());
-        assertFalse(r.getAudiblyAlerted());
+        assertEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -520,7 +518,7 @@
         verifyBeepLooped();
         verify(mAccessibilityService, times(2)).sendAccessibilityEvent(any(), anyInt());
         assertTrue(r.isInterruptive());
-        assertTrue(r.getAudiblyAlerted());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -549,7 +547,7 @@
 
         verifyNeverStopAudio();
         assertTrue(r.isInterruptive());
-        assertTrue(r.getAudiblyAlerted());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -563,9 +561,9 @@
 
         verifyNeverStopAudio();
         assertTrue(r.isInterruptive());
-        assertTrue(r.getAudiblyAlerted());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
         assertFalse(s.isInterruptive());
-        assertFalse(s.getAudiblyAlerted());
+        assertEquals(-1, s.getLastAudiblyAlertedMs());
     }
 
     /**
@@ -602,7 +600,7 @@
         mService.buzzBeepBlinkLocked(s); // this no longer owns the stream
         verifyNeverStopAudio();
         assertTrue(other.isInterruptive());
-        assertTrue(other.getAudiblyAlerted());
+        assertNotEquals(-1, other.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -628,14 +626,14 @@
         // set up internal state
         mService.buzzBeepBlinkLocked(r);
         assertTrue(r.isInterruptive());
-        assertTrue(r.getAudiblyAlerted());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
         Mockito.reset(mRingtonePlayer);
 
         // quiet update should stop making noise
         mService.buzzBeepBlinkLocked(s);
         verifyStopAudio();
         assertFalse(s.isInterruptive());
-        assertFalse(s.getAudiblyAlerted());
+        assertEquals(-1, s.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -647,14 +645,14 @@
         // set up internal state
         mService.buzzBeepBlinkLocked(r);
         assertTrue(r.isInterruptive());
-        assertTrue(r.getAudiblyAlerted());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
         Mockito.reset(mRingtonePlayer);
 
         // stop making noise - this is a weird corner case, but quiet should override once
         mService.buzzBeepBlinkLocked(s);
         verifyStopAudio();
         assertFalse(s.isInterruptive());
-        assertFalse(s.getAudiblyAlerted());
+        assertEquals(-1, s.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -671,7 +669,7 @@
         verify(mService, times(1)).playInCallNotification();
         verifyNeverBeep(); // doesn't play normal beep
         assertTrue(r.isInterruptive());
-        assertTrue(r.getAudiblyAlerted());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -691,7 +689,7 @@
                 eq(effect), anyString(),
                 (AudioAttributes) anyObject());
         assertTrue(r.isInterruptive());
-        assertTrue(r.getAudiblyAlerted());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -709,7 +707,7 @@
         verifyNeverVibrate();
         verifyBeepLooped();
         assertTrue(r.isInterruptive());
-        assertTrue(r.getAudiblyAlerted());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -729,7 +727,7 @@
         verify(mRingtonePlayer, never()).playAsync
                 (anyObject(), anyObject(), anyBoolean(), anyObject());
         assertTrue(r.isInterruptive());
-        assertTrue(r.getAudiblyAlerted());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -746,7 +744,7 @@
 
         verifyDelayedVibrateLooped();
         assertTrue(r.isInterruptive());
-        assertTrue(r.getAudiblyAlerted());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -758,7 +756,7 @@
         verifyNeverBeep();
         verifyVibrate();
         assertTrue(r.isInterruptive());
-        assertTrue(r.getAudiblyAlerted());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -768,7 +766,7 @@
         mService.buzzBeepBlinkLocked(r);
         verifyVibrateLooped();
         assertTrue(r.isInterruptive());
-        assertTrue(r.getAudiblyAlerted());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -784,7 +782,7 @@
         mService.buzzBeepBlinkLocked(r);
         verifyVibrate();
         assertTrue(r.isInterruptive());
-        assertTrue(r.getAudiblyAlerted());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -795,7 +793,7 @@
 
         verifyNeverBeep();
         assertFalse(child.isInterruptive());
-        assertFalse(child.getAudiblyAlerted());
+        assertEquals(-1, child.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -808,7 +806,7 @@
         verifyBeepLooped();
         // summaries are never interruptive for notification counts
         assertFalse(summary.isInterruptive());
-        assertTrue(summary.getAudiblyAlerted());
+        assertNotEquals(-1, summary.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -819,7 +817,7 @@
 
         verifyBeepLooped();
         assertTrue(nonGroup.isInterruptive());
-        assertTrue(nonGroup.getAudiblyAlerted());
+        assertNotEquals(-1, nonGroup.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -831,7 +829,7 @@
 
         verifyNeverBeep();
         assertFalse(summary.isInterruptive());
-        assertFalse(summary.getAudiblyAlerted());
+        assertEquals(-1, summary.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -842,7 +840,7 @@
 
         verifyBeepLooped();
         assertTrue(child.isInterruptive());
-        assertTrue(child.getAudiblyAlerted());
+        assertNotEquals(-1, child.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -853,7 +851,7 @@
 
         verifyBeepLooped();
         assertTrue(nonGroup.isInterruptive());
-        assertTrue(nonGroup.getAudiblyAlerted());
+        assertNotEquals(-1, nonGroup.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -864,7 +862,7 @@
 
         verifyBeepLooped();
         assertTrue(group.isInterruptive());
-        assertTrue(group.getAudiblyAlerted());
+        assertNotEquals(-1, group.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -877,13 +875,13 @@
         mService.buzzBeepBlinkLocked(r);
         Mockito.reset(mVibrator);
         assertTrue(r.isInterruptive());
-        assertTrue(r.getAudiblyAlerted());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
 
         // update should not beep
         mService.buzzBeepBlinkLocked(s);
         verifyNeverVibrate();
         assertFalse(s.isInterruptive());
-        assertFalse(s.getAudiblyAlerted());
+        assertEquals(-1, s.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -896,7 +894,7 @@
 
         verifyNeverStopVibrate();
         assertTrue(r.isInterruptive());
-        assertTrue(r.getAudiblyAlerted());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -910,9 +908,9 @@
 
         verifyNeverStopVibrate();
         assertTrue(r.isInterruptive());
-        assertTrue(r.getAudiblyAlerted());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
         assertFalse(s.isInterruptive());
-        assertFalse(s.getAudiblyAlerted());
+        assertEquals(-1, s.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -931,11 +929,11 @@
         mService.buzzBeepBlinkLocked(s); // this no longer owns the stream
         verifyNeverStopVibrate();
         assertTrue(r.isInterruptive());
-        assertTrue(r.getAudiblyAlerted());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
         assertTrue(other.isInterruptive());
-        assertTrue(other.getAudiblyAlerted());
+        assertNotEquals(-1, other.getLastAudiblyAlertedMs());
         assertFalse(s.isInterruptive());
-        assertFalse(s.getAudiblyAlerted());
+        assertEquals(-1, s.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -951,7 +949,7 @@
         mService.buzzBeepBlinkLocked(other);
         verifyNeverStopVibrate();
         assertFalse(other.isInterruptive());
-        assertFalse(other.getAudiblyAlerted());
+        assertEquals(-1, other.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -968,9 +966,9 @@
         mService.buzzBeepBlinkLocked(s);
         verifyStopVibrate();
         assertTrue(r.isInterruptive());
-        assertTrue(r.getAudiblyAlerted());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
         assertFalse(s.isInterruptive());
-        assertFalse(s.getAudiblyAlerted());
+        assertEquals(-1, s.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -987,9 +985,9 @@
         mService.buzzBeepBlinkLocked(s);
         verifyStopVibrate();
         assertTrue(r.isInterruptive());
-        assertTrue(r.getAudiblyAlerted());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
         assertFalse(s.isInterruptive());
-        assertFalse(s.getAudiblyAlerted());
+        assertEquals(-1, s.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -1007,9 +1005,9 @@
         mService.buzzBeepBlinkLocked(s);
         verifyStopVibrate();
         assertTrue(r.isInterruptive());
-        assertTrue(r.getAudiblyAlerted());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
         assertFalse(s.isInterruptive());
-        assertFalse(s.getAudiblyAlerted());
+        assertEquals(-1, s.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -1027,7 +1025,7 @@
         mService.buzzBeepBlinkLocked(r);
         verifyNeverBeep();
         assertFalse(r.isInterruptive());
-        assertFalse(r.getAudiblyAlerted());
+        assertEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -1039,7 +1037,7 @@
         mService.buzzBeepBlinkLocked(r);
         verifyNeverBeep();
         assertFalse(r.isInterruptive());
-        assertFalse(r.getAudiblyAlerted());
+        assertEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -1082,7 +1080,7 @@
         mService.buzzBeepBlinkLocked(r);
         verifyNeverBeep();
         assertFalse(r.isInterruptive());
-        assertFalse(r.getAudiblyAlerted());
+        assertEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -1116,7 +1114,7 @@
         mService.buzzBeepBlinkLocked(r);
         verifyNeverLights();
         assertFalse(r.isInterruptive());
-        assertFalse(r.getAudiblyAlerted());
+        assertEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -1126,7 +1124,7 @@
         mService.buzzBeepBlinkLocked(r);
         verifyNeverLights();
         assertFalse(r.isInterruptive());
-        assertFalse(r.getAudiblyAlerted());
+        assertEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -1135,7 +1133,7 @@
         mService.buzzBeepBlinkLocked(r);
         verifyLights();
         assertTrue(r.isInterruptive());
-        assertFalse(r.getAudiblyAlerted());
+        assertEquals(-1, r.getLastAudiblyAlertedMs());
 
         r = getLightsOnceNotification();
         r.isUpdate = true;
@@ -1143,7 +1141,7 @@
         // checks that lights happened once, i.e. this new call didn't trigger them again
         verifyLights();
         assertFalse(r.isInterruptive());
-        assertFalse(r.getAudiblyAlerted());
+        assertEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -1153,7 +1151,7 @@
         mService.buzzBeepBlinkLocked(r);
         verifyNeverLights();
         assertFalse(r.isInterruptive());
-        assertFalse(r.getAudiblyAlerted());
+        assertEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -1162,7 +1160,7 @@
         mService.buzzBeepBlinkLocked(r);
         verifyNeverLights();
         assertFalse(r.isInterruptive());
-        assertFalse(r.getAudiblyAlerted());
+        assertEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -1172,7 +1170,7 @@
         mService.buzzBeepBlinkLocked(r);
         verifyNeverLights();
         assertFalse(r.isInterruptive());
-        assertFalse(r.getAudiblyAlerted());
+        assertEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -1182,7 +1180,7 @@
         mService.buzzBeepBlinkLocked(r);
         verifyNeverLights();
         assertFalse(r.isInterruptive());
-        assertFalse(r.getAudiblyAlerted());
+        assertEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -1192,7 +1190,7 @@
         mService.buzzBeepBlinkLocked(r);
         verifyNeverLights();
         assertFalse(r.isInterruptive());
-        assertFalse(r.getAudiblyAlerted());
+        assertEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -1203,7 +1201,7 @@
 
         verifyNeverLights();
         assertFalse(child.isInterruptive());
-        assertFalse(child.getAudiblyAlerted());
+        assertEquals(-1, child.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -1216,7 +1214,7 @@
         verifyLights();
         // summaries should never count for interruptiveness counts
         assertFalse(summary.isInterruptive());
-        assertFalse(summary.getAudiblyAlerted());
+        assertEquals(-1, summary.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -1227,7 +1225,7 @@
 
         verifyLights();
         assertTrue(nonGroup.isInterruptive());
-        assertFalse(nonGroup.getAudiblyAlerted());
+        assertEquals(-1, nonGroup.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -1239,7 +1237,7 @@
 
         verifyNeverLights();
         assertFalse(summary.isInterruptive());
-        assertFalse(summary.getAudiblyAlerted());
+        assertEquals(-1, summary.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -1250,7 +1248,7 @@
 
         verifyLights();
         assertTrue(child.isInterruptive());
-        assertFalse(child.getAudiblyAlerted());
+        assertEquals(-1, child.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -1261,7 +1259,7 @@
 
         verifyLights();
         assertTrue(nonGroup.isInterruptive());
-        assertFalse(nonGroup.getAudiblyAlerted());
+        assertEquals(-1, nonGroup.getLastAudiblyAlertedMs());
     }
 
     @Test
@@ -1272,7 +1270,65 @@
 
         verifyLights();
         assertTrue(group.isInterruptive());
-        assertFalse(group.getAudiblyAlerted());
+        assertEquals(-1, group.getLastAudiblyAlertedMs());
+    }
+
+    @Test
+    public void testListenerHintCall() throws Exception {
+        NotificationRecord r = getCallRecord(1, true);
+
+        mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyNeverBeep();
+    }
+
+    @Test
+    public void testListenerHintCall_notificationSound() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+
+        mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyBeepLooped();
+    }
+
+    @Test
+    public void testListenerHintNotification() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+
+        mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyNeverBeep();
+    }
+
+    @Test
+    public void testListenerHintBoth() throws Exception {
+        NotificationRecord r = getCallRecord(1, true);
+        NotificationRecord s = getBeepyNotification();
+
+        mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
+                | NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS);
+
+        mService.buzzBeepBlinkLocked(r);
+        mService.buzzBeepBlinkLocked(s);
+
+        verifyNeverBeep();
+    }
+
+    @Test
+    public void testListenerHintNotification_callSound() throws Exception {
+        NotificationRecord r = getCallRecord(1, true);
+
+        mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyBeepLooped();
     }
 
     static class VibrateRepeatMatcher implements ArgumentMatcher<VibrationEffect> {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 659c6e7..20f72bf 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -613,7 +613,7 @@
     }
 
     @Test
-    public void testGetAllowedPackages() throws Exception {
+    public void testGetAllowedPackages_byUser() throws Exception {
         for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
             ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
                     mIpm, approvalLevel);
@@ -681,6 +681,30 @@
     }
 
     @Test
+    public void testGetAllowedPackages() throws Exception {
+        ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
+                mIpm, APPROVAL_BY_COMPONENT);
+        loadXml(service);
+        service.mApprovalLevel = APPROVAL_BY_PACKAGE;
+        loadXml(service);
+
+        List<String> allowedPackages = new ArrayList<>();
+        allowedPackages.add("this.is.a.package.name");
+        allowedPackages.add("another.package");
+        allowedPackages.add("secondary");
+        allowedPackages.add("this.is.another.package");
+        allowedPackages.add("package");
+        allowedPackages.add("component");
+        allowedPackages.add("bananas!");
+
+        Set<String> actual = service.getAllowedPackages();
+        assertEquals(allowedPackages.size(), actual.size());
+        for (String pkg : allowedPackages) {
+            assertTrue(actual.contains(pkg));
+        }
+    }
+
+    @Test
     public void testOnUserRemoved() throws Exception {
         for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
             ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -859,6 +883,19 @@
         assertTrue(componentsToBind.get(10).contains(ComponentName.unflattenFromString("c/c")));
     }
 
+    @Test
+    public void testOnPackagesChanged_nullValuesPassed_noNullPointers() {
+        for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
+            ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
+                    mIpm, approvalLevel);
+            // null uid list
+            service.onPackagesChanged(true, new String[]{"this.is.a.package.name"}, null);
+
+            // null package list
+            service.onPackagesChanged(true, null, new int[]{103});
+        }
+    }
+
     private void loadXml(ManagedServices service) throws Exception {
         final StringBuffer xml = new StringBuffer();
         xml.append("<" + service.getConfig().xmlTag + ">\n");
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index bcba15d..daca9cb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -16,12 +16,9 @@
 
 package com.android.server.notification;
 
-import static android.service.notification.NotificationListenerService.Ranking
-        .USER_SENTIMENT_NEGATIVE;
-import static android.service.notification.NotificationListenerService.Ranking
-        .USER_SENTIMENT_NEUTRAL;
-import static android.service.notification.NotificationListenerService.Ranking
-        .USER_SENTIMENT_POSITIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -92,7 +89,7 @@
             assertEquals(getShowBadge(i), ranking.canShowBadge());
             assertEquals(getUserSentiment(i), ranking.getUserSentiment());
             assertEquals(getHidden(i), ranking.isSuspended());
-            assertEquals(audiblyAlerted(i), ranking.audiblyAlerted());
+            assertEquals(lastAudiblyAlerted(i), ranking.getLastAudiblyAlertedMillis());
             assertActionsEqual(getSmartActions(key, i), ranking.getSmartActions());
             assertEquals(getSmartReplies(key, i), ranking.getSmartReplies());
         }
@@ -113,7 +110,7 @@
         Bundle mHidden = new Bundle();
         Bundle smartActions = new Bundle();
         Bundle smartReplies = new Bundle();
-        Bundle audiblyAlerted = new Bundle();
+        Bundle lastAudiblyAlerted = new Bundle();
         Bundle noisy = new Bundle();
 
         for (int i = 0; i < mKeys.length; i++) {
@@ -134,14 +131,14 @@
             mHidden.putBoolean(key, getHidden(i));
             smartActions.putParcelableArrayList(key, getSmartActions(key, i));
             smartReplies.putCharSequenceArrayList(key, getSmartReplies(key, i));
-            audiblyAlerted.putBoolean(key, audiblyAlerted(i));
+            lastAudiblyAlerted.putLong(key, lastAudiblyAlerted(i));
             noisy.putBoolean(key, getNoisy(i));
         }
         NotificationRankingUpdate update = new NotificationRankingUpdate(mKeys,
                 interceptedKeys.toArray(new String[0]), visibilityOverrides,
                 suppressedVisualEffects, importance, explanation, overrideGroupKeys,
                 channels, overridePeople, snoozeCriteria, showBadge, userSentiment, mHidden,
-                smartActions, smartReplies, audiblyAlerted, noisy);
+                smartActions, smartReplies, lastAudiblyAlerted, noisy);
         return update;
     }
 
@@ -193,8 +190,8 @@
         return index % 2 == 0;
     }
 
-    private boolean audiblyAlerted(int index) {
-        return index < 2;
+    private long lastAudiblyAlerted(int index) {
+        return index * 2000;
     }
 
     private boolean getNoisy(int index) {
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 527a1ee..83c1c76 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -208,6 +208,7 @@
     private static class TestableNotificationManagerService extends NotificationManagerService {
         int countSystemChecks = 0;
         boolean isSystemUid = true;
+        int countLogSmartSuggestionsVisible = 0;
 
         public TestableNotificationManagerService(Context context) {
             super(context);
@@ -231,11 +232,6 @@
         }
 
         @Override
-        protected void reportSeen(NotificationRecord r) {
-            return;
-        }
-
-        @Override
         protected void reportUserInteraction(NotificationRecord r) {
             return;
         }
@@ -244,6 +240,14 @@
         protected void handleSavePolicyFile() {
             return;
         }
+
+        @Override
+        void logSmartSuggestionsVisible(NotificationRecord r) {
+            super.logSmartSuggestionsVisible(r);
+            countLogSmartSuggestionsVisible++;
+        }
+
+
     }
 
     private class TestableToastCallback extends ITransientNotification.Stub {
@@ -3507,6 +3511,12 @@
     }
 
     @Test
+    public void testAppOverlay() throws Exception {
+        mBinderService.setAppOverlaysAllowed(PKG, mUid, false);
+        assertFalse(mBinderService.areAppOverlaysAllowedForPackage(PKG, mUid));
+    }
+
+    @Test
     public void testIsCallerInstantApp_primaryUser() throws Exception {
         ApplicationInfo info = new ApplicationInfo();
         info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
@@ -3777,4 +3787,88 @@
         verify(mAssistants).notifyAssistantActionClicked(
                 eq(r.sbn), eq(actionIndex), eq(action), eq(generatedByAssistant));
     }
+
+    @Test
+    public void testLogSmartSuggestionsVisible_triggerOnExpandAndVisible() {
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addNotification(r);
+
+        mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true);
+        NotificationVisibility[] notificationVisibility = new NotificationVisibility[] {
+                NotificationVisibility.obtain(r.getKey(), 0, 0, true)
+        };
+        mService.mNotificationDelegate.onNotificationVisibilityChanged(notificationVisibility,
+                new NotificationVisibility[0]);
+
+        assertEquals(1, mService.countLogSmartSuggestionsVisible);
+    }
+
+    @Test
+    public void testLogSmartSuggestionsVisible_noTriggerOnExpand() {
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addNotification(r);
+
+        mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true);
+
+        assertEquals(0, mService.countLogSmartSuggestionsVisible);
+    }
+
+    @Test
+    public void testLogSmartSuggestionsVisible_noTriggerOnVisible() {
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addNotification(r);
+
+        NotificationVisibility[] notificationVisibility = new NotificationVisibility[]{
+                NotificationVisibility.obtain(r.getKey(), 0, 0, true)
+        };
+        mService.mNotificationDelegate.onNotificationVisibilityChanged(notificationVisibility,
+                new NotificationVisibility[0]);
+
+        assertEquals(0, mService.countLogSmartSuggestionsVisible);
+    }
+
+    public void testReportSeen_delegated() {
+        Notification.Builder nb =
+                new Notification.Builder(mContext, mTestNotificationChannel.getId())
+                        .setContentTitle("foo")
+                        .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+        StatusBarNotification sbn = new StatusBarNotification(PKG, "opPkg", 0, "tag", mUid, 0,
+                nb.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord r =  new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        mService.reportSeen(r);
+        verify(mAppUsageStats, never()).reportEvent(anyString(), anyInt(), anyInt());
+
+    }
+
+    @Test
+    public void testReportSeen_notDelegated() {
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+
+        mService.reportSeen(r);
+        verify(mAppUsageStats, times(1)).reportEvent(anyString(), anyInt(), anyInt());
+    }
+
+    @Test
+    public void testNotificationStats_notificationError() {
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addNotification(r);
+
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", mUid, 0,
+                new Notification.Builder(mContext, mTestNotificationChannel.getId()).build(),
+                new UserHandle(mUid), null, 0);
+        NotificationRecord update = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+        mService.addEnqueuedNotification(update);
+        assertNull(update.sbn.getNotification().getSmallIcon());
+
+        NotificationManagerService.PostNotificationRunnable runnable =
+                mService.new PostNotificationRunnable(update.getKey());
+        runnable.run();
+        waitForIdle();
+
+        ArgumentCaptor<NotificationStats> captor = ArgumentCaptor.forClass(NotificationStats.class);
+        verify(mListeners).notifyRemovedLocked(any(), anyInt(), captor.capture());
+        assertNotNull(captor.getValue());
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 0eeeeed..1458266 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -19,12 +19,9 @@
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
-import static android.service.notification.NotificationListenerService.Ranking
-        .USER_SENTIMENT_NEGATIVE;
-import static android.service.notification.NotificationListenerService.Ranking
-        .USER_SENTIMENT_NEUTRAL;
-import static android.service.notification.NotificationListenerService.Ranking
-        .USER_SENTIMENT_POSITIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
@@ -806,6 +803,18 @@
     }
 
     @Test
+    public void testSetDidNotAudiblyAlert() {
+        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
+                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+                false /* lights */, false /* defaultLights */, groupId /* group */);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        record.setAudiblyAlerted(false);
+
+        assertEquals(-1, record.getLastAudiblyAlertedMs());
+    }
+
+    @Test
     public void testSetAudiblyAlerted() {
         StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
@@ -814,6 +823,26 @@
 
         record.setAudiblyAlerted(true);
 
-        assertTrue(record.getAudiblyAlerted());
+        assertNotEquals(-1, record.getLastAudiblyAlertedMs());
+    }
+
+    @Test
+    public void testIsNewEnoughForAlerting_new() {
+        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
+                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+                false /* lights */, false /* defaultLights */, groupId /* group */);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertTrue(record.isNewEnoughForAlerting(record.mUpdateTimeMs));
+    }
+
+    @Test
+    public void testIsNewEnoughForAlerting_old() {
+        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
+                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+                false /* lights */, false /* defaultLights */, groupId /* group */);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertFalse(record.isNewEnoughForAlerting(record.mUpdateTimeMs + (1000 * 60 * 60)));
     }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java
new file mode 100644
index 0000000..fa90b29
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertSame;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+// this is a lazy way to do in/out/err but we're not particularly interested in the output
+import static java.io.FileDescriptor.err;
+import static java.io.FileDescriptor.in;
+import static java.io.FileDescriptor.out;
+
+import android.app.INotificationManager;
+import android.app.Notification;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.Icon;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.UserHandle;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class NotificationShellCmdTest extends UiServiceTestCase {
+    private final Binder mBinder = new Binder();
+    private final ShellCallback mCallback = new ShellCallback();
+    private final TestableContext mTestableContext = spy(getContext());
+    @Mock
+    NotificationManagerService mMockService;
+    @Mock
+    INotificationManager mMockBinderService;
+    private TestableLooper mTestableLooper;
+    private ResultReceiver mResultReceiver;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mTestableLooper = TestableLooper.get(this);
+        mResultReceiver = new ResultReceiver(new Handler(mTestableLooper.getLooper()));
+
+        when(mMockService.getContext()).thenReturn(mTestableContext);
+        when(mMockService.getBinderService()).thenReturn(mMockBinderService);
+    }
+
+    private Bitmap createTestBitmap() {
+        final Bitmap bits = Bitmap.createBitmap(512, 512, Bitmap.Config.ARGB_8888);
+        final Canvas canvas = new Canvas(bits);
+        final GradientDrawable grad = new GradientDrawable(GradientDrawable.Orientation.TL_BR,
+                new int[]{Color.RED, Color.YELLOW, Color.GREEN,
+                        Color.CYAN, Color.BLUE, Color.MAGENTA});
+        grad.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+        grad.draw(canvas);
+        return bits;
+    }
+
+    private void doCmd(String... args) {
+        new NotificationShellCmd(mMockService)
+                .exec(mBinder, in, out, err, args, mCallback, mResultReceiver);
+    }
+
+    @Test
+    public void testNoArgs() throws Exception {
+        doCmd();
+    }
+
+    @Test
+    public void testHelp() throws Exception {
+        doCmd("--help");
+    }
+
+    Notification captureNotification(String aTag) throws Exception {
+        ArgumentCaptor<Notification> notificationCaptor =
+                ArgumentCaptor.forClass(Notification.class);
+        verify(mMockBinderService).enqueueNotificationWithTag(
+                eq(NotificationShellCmd.NOTIFICATION_PACKAGE),
+                eq("android"),
+                eq(aTag),
+                eq(NotificationShellCmd.NOTIFICATION_ID),
+                notificationCaptor.capture(),
+                eq(UserHandle.getCallingUserId()));
+        return notificationCaptor.getValue();
+    }
+
+    @Test
+    public void testBasic() throws Exception {
+        final String aTag = "aTag";
+        final String aText = "someText";
+        final String aTitle = "theTitle";
+        doCmd("notify",
+                "--title", aTitle,
+                aTag, aText);
+        final Notification captured = captureNotification(aTag);
+        assertEquals(aText, captured.extras.getString(Notification.EXTRA_TEXT));
+        assertEquals(aTitle, captured.extras.getString(Notification.EXTRA_TITLE));
+    }
+
+    @Test
+    public void testIcon() throws Exception {
+        final String aTag = "aTag";
+        final String aText = "someText";
+        doCmd("notify", "--icon", "@android:drawable/stat_sys_adb", aTag, aText);
+        final Notification captured = captureNotification(aTag);
+        final Icon icon = captured.getSmallIcon();
+        assertEquals("android", icon.getResPackage());
+        assertEquals(com.android.internal.R.drawable.stat_sys_adb, icon.getResId());
+    }
+
+    @Test
+    public void testBigText() throws Exception {
+        final String aTag = "aTag";
+        final String aText = "someText";
+        final String bigText = "someBigText";
+        doCmd("notify",
+                "--style", "bigtext",
+                "--big-text", bigText,
+                aTag, aText);
+        final Notification captured = captureNotification(aTag);
+        assertSame(captured.getNotificationStyle(), Notification.BigTextStyle.class);
+        assertEquals(aText, captured.extras.getString(Notification.EXTRA_TEXT));
+        assertEquals(bigText, captured.extras.getString(Notification.EXTRA_BIG_TEXT));
+    }
+
+    @Test
+    public void testBigPicture() throws Exception {
+        final String aTag = "aTag";
+        final String aText = "someText";
+        final String bigPicture = "@android:drawable/default_wallpaper";
+        doCmd("notify",
+                "--style", "bigpicture",
+                "--picture", bigPicture,
+                aTag, aText);
+        final Notification captured = captureNotification(aTag);
+        assertSame(captured.getNotificationStyle(), Notification.BigPictureStyle.class);
+        final Object pic = captured.extras.get(Notification.EXTRA_PICTURE);
+        assertThat(pic, instanceOf(Bitmap.class));
+    }
+
+    @Test
+    public void testInbox() throws Exception {
+        final int n = 25;
+        final String aTag = "inboxTag";
+        final String aText = "inboxText";
+        ArrayList<String> args = new ArrayList<>();
+        args.add("notify");
+        args.add("--style");
+        args.add("inbox");
+        final int startOfLineArgs = args.size();
+        for (int i = 0; i < n; i++) {
+            args.add("--line");
+            args.add(String.format("Line %02d", i));
+        }
+        args.add(aTag);
+        args.add(aText);
+
+        doCmd(args.toArray(new String[0]));
+        final Notification captured = captureNotification(aTag);
+        assertSame(captured.getNotificationStyle(), Notification.InboxStyle.class);
+        final Notification.Builder builder =
+                Notification.Builder.recoverBuilder(mContext, captured);
+        final ArrayList<CharSequence> lines =
+                ((Notification.InboxStyle) (builder.getStyle())).getLines();
+        for (int i = 0; i < n; i++) {
+            assertEquals(lines.get(i), args.get(1 + 2 * i + startOfLineArgs));
+        }
+    }
+
+    static final String[] PEOPLE = {
+            "Alice",
+            "Bob",
+            "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.",
+    };
+
+    @Test
+    public void testMessaging() throws Exception {
+        final String aTag = "messagingTag";
+        final String aText = "messagingText";
+        ArrayList<String> args = new ArrayList<>();
+        args.add("notify");
+        args.add("--style");
+        args.add("messaging");
+        args.add("--conversation");
+        args.add("Sonnet 18");
+        final int startOfLineArgs = args.size();
+        for (int i = 0; i < MESSAGES.length; i++) {
+            args.add("--message");
+            args.add(String.format("%s:%s",
+                    PEOPLE[i % PEOPLE.length],
+                    MESSAGES[i % MESSAGES.length]));
+        }
+        args.add(aTag);
+        args.add(aText);
+
+        doCmd(args.toArray(new String[0]));
+        final Notification captured = captureNotification(aTag);
+        assertSame(Notification.MessagingStyle.class, captured.getNotificationStyle());
+        final Notification.Builder builder =
+                Notification.Builder.recoverBuilder(mContext, captured);
+        final Notification.MessagingStyle messagingStyle =
+                (Notification.MessagingStyle) (builder.getStyle());
+
+        assertEquals("Sonnet 18", messagingStyle.getConversationTitle());
+        final List<Notification.MessagingStyle.Message> messages = messagingStyle.getMessages();
+        for (int i = 0; i < messages.size(); i++) {
+            final Notification.MessagingStyle.Message m = messages.get(i);
+            assertEquals(MESSAGES[i], m.getText());
+            assertEquals(PEOPLE[i % PEOPLE.length], m.getSenderPerson().getName());
+        }
+
+    }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index b027935..0b73481 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -1584,39 +1584,6 @@
     }
 
     @Test
-    public void testUpdateGroup_fromSystem_appOverlay() {
-        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
-        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
-
-        // from system, allowed
-        NotificationChannelGroup update = ncg.clone();
-        update.setAllowAppOverlay(false);
-
-        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, update, false);
-        NotificationChannelGroup updated =
-                mHelper.getNotificationChannelGroup("group1", PKG_N_MR1, UID_N_MR1);
-        assertFalse(updated.canOverlayApps());
-        assertEquals(NotificationChannelGroup.USER_LOCKED_ALLOW_APP_OVERLAY,
-                updated.getUserLockedFields());
-    }
-
-    @Test
-    public void testUpdateGroup_fromApp_appOverlay() {
-        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
-        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
-
-        // from app, not allowed
-        NotificationChannelGroup update = new NotificationChannelGroup("group1", "name1");
-        update.setAllowAppOverlay(false);
-
-        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
-        NotificationChannelGroup updated =
-                mHelper.getNotificationChannelGroup("group1", PKG_N_MR1, UID_N_MR1);
-        assertTrue(updated.canOverlayApps());
-        assertEquals(0, updated.getUserLockedFields());
-    }
-
-    @Test
     public void testCannotCreateChannel_badGroup() {
         NotificationChannel channel1 =
                 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
@@ -2192,4 +2159,32 @@
         mHelper.toggleNotificationDelegate(PKG_O, UID_O, true);
         assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O));
     }
+
+    @Test
+    public void testAllowAppOverlay_defaults() throws Exception {
+        assertTrue(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
+
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+        loadStreamXml(baos, false);
+
+        assertTrue(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
+        assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O));
+    }
+
+    @Test
+    public void testAllowAppOverlay_xml() throws Exception {
+        mHelper.setAppOverlaysAllowed(PKG_O, UID_O, false);
+        assertFalse(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
+        assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_APP_OVERLAY,
+                mHelper.getAppLockedFields(PKG_O, UID_O));
+
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+        loadStreamXml(baos, false);
+
+        assertFalse(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
+        assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_APP_OVERLAY,
+                mHelper.getAppLockedFields(PKG_O, UID_O));
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index b955e56..174c5fa 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -76,7 +76,7 @@
         verify(mAm, times(1)).setExactAndAllowWhileIdle(
                 anyInt(), captor.capture(), any(PendingIntent.class));
         long actualSnoozedUntilDuration = captor.getValue() - SystemClock.elapsedRealtime();
-        assertTrue(Math.abs(actualSnoozedUntilDuration - 1000) < 25);
+        assertTrue(Math.abs(actualSnoozedUntilDuration - 1000) < 250);
         assertTrue(mSnoozeHelper.isSnoozed(
                 UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
     }
@@ -299,6 +299,59 @@
         assertEquals(1, mSnoozeHelper.getSnoozed(UserHandle.USER_SYSTEM, "pkg").size());
     }
 
+    @Test
+    public void testClearData() {
+        // snooze 2 from same package
+        NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+        NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
+        mSnoozeHelper.snooze(r, 1000);
+        mSnoozeHelper.snooze(r2, 1000);
+        assertTrue(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+        assertTrue(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+
+        // clear data
+        mSnoozeHelper.clearData(UserHandle.USER_SYSTEM, "pkg");
+
+        // nothing snoozed; alarms canceled
+        assertFalse(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+        assertFalse(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+        // twice for initial snooze, twice for canceling the snooze
+        verify(mAm, times(4)).cancel(any(PendingIntent.class));
+    }
+
+    @Test
+    public void testClearData_otherRecordsUntouched() {
+        // 2 packages, 2 users
+        NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+        NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.ALL);
+        NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM);
+        mSnoozeHelper.snooze(r, 1000);
+        mSnoozeHelper.snooze(r2, 1000);
+        mSnoozeHelper.snooze(r3, 1000);
+        assertTrue(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+        assertTrue(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_ALL, r2.sbn.getPackageName(), r2.getKey()));
+        assertTrue(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r3.sbn.getPackageName(), r3.getKey()));
+
+        // clear data
+        mSnoozeHelper.clearData(UserHandle.USER_SYSTEM, "pkg");
+
+        assertFalse(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+        assertTrue(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_ALL, r2.sbn.getPackageName(), r2.getKey()));
+        assertTrue(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r3.sbn.getPackageName(), r3.getKey()));
+        // once for each initial snooze, once for canceling one snooze
+        verify(mAm, times(4)).cancel(any(PendingIntent.class));
+    }
+
     private NotificationRecord getNotificationRecord(String pkg, int id, String tag,
             UserHandle user, String groupKey, boolean groupSummary) {
         Notification n = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index 9bd3f26..68d3e4c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -17,20 +17,33 @@
 package com.android.server.notification;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertNull;
 
 import android.app.NotificationManager.Policy;
+import android.content.ComponentName;
 import android.net.Uri;
+import android.provider.Settings;
+import android.service.notification.Condition;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeConfig.EventInfo;
 import android.service.notification.ZenPolicy;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Xml;
 
+import com.android.internal.util.FastXmlSerializer;
 import com.android.server.UiServiceTestCase;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -138,6 +151,54 @@
         assertEquals(event, eventParsed);
     }
 
+    @Test
+    public void testRuleXml() throws Exception {
+        String tag = "tag";
+
+        ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+        rule.configurationActivity = new ComponentName("a", "a");
+        rule.component = new ComponentName("a", "b");
+        rule.conditionId = new Uri.Builder().scheme("hello").build();
+        rule.condition = new Condition(rule.conditionId, "", Condition.STATE_TRUE);
+        rule.enabled = true;
+        rule.creationTime = 123;
+        rule.id = "id";
+        rule.zenMode = Settings.Global.ZEN_MODE_ALARMS;
+        rule.modified = true;
+        rule.name = "name";
+        rule.snoozing = true;
+
+        XmlSerializer out = new FastXmlSerializer();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        out.setOutput(new BufferedOutputStream(baos), "utf-8");
+        out.startDocument(null, true);
+        out.startTag(null, tag);
+        ZenModeConfig.writeRuleXml(rule, out);
+        out.endTag(null, tag);
+        out.endDocument();
+
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(baos.toByteArray())), null);
+        parser.nextTag();
+        ZenModeConfig.ZenRule fromXml = ZenModeConfig.readRuleXml(parser);
+        // read from backing component
+        assertEquals("a", fromXml.pkg);
+        // always resets on reboot
+        assertFalse(fromXml.snoozing);
+        //should all match original
+        assertEquals(rule.component, fromXml.component);
+        assertEquals(rule.configurationActivity, fromXml.configurationActivity);
+        assertNull(fromXml.enabler);
+        assertEquals(rule.condition, fromXml.condition);
+        assertEquals(rule.enabled, fromXml.enabled);
+        assertEquals(rule.creationTime, fromXml.creationTime);
+        assertEquals(rule.modified, fromXml.modified);
+        assertEquals(rule.conditionId, fromXml.conditionId);
+        assertEquals(rule.name, fromXml.name);
+        assertEquals(rule.zenMode, fromXml.zenMode);
+    }
+
     private ZenModeConfig getMutedNotificationsConfig() {
         ZenModeConfig config = new ZenModeConfig();
         // Allow alarms, media, and system
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 6c7ede3..a459b0a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -61,6 +61,7 @@
 import android.service.notification.Condition;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeConfig.ScheduleInfo;
+import android.service.notification.ZenPolicy;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -633,8 +634,8 @@
         mZenModeHelperSpy.mConfig.manualRule.zenMode =
                 Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         mZenModeHelperSpy.mConfig.manualRule.component = new ComponentName("a", "a");
+        mZenModeHelperSpy.mConfig.manualRule.pkg = "a";
         mZenModeHelperSpy.mConfig.manualRule.enabled = true;
-        mZenModeHelperSpy.mConfig.manualRule.snoozing = true;
 
         ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy();
 
@@ -645,7 +646,8 @@
         parser.nextTag();
         mZenModeHelperSpy.readXml(parser, false);
 
-        assertEquals(expected, mZenModeHelperSpy.mConfig);
+        assertEquals("Config mismatch: current vs expected: "
+                + mZenModeHelperSpy.mConfig.diff(expected), expected, mZenModeHelperSpy.mConfig);
     }
 
     @Test
@@ -662,7 +664,9 @@
         customRule.name = "Custom Rule";
         customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
-        customRule.component = new ComponentName("android", "ScheduleConditionProvider");
+        customRule.configurationActivity
+                = new ComponentName("android", "ScheduleConditionProvider");
+        customRule.pkg = customRule.configurationActivity.getPackageName();
         automaticRules.put("customRule", customRule);
         mZenModeHelperSpy.mConfig.automaticRules = automaticRules;
 
@@ -674,11 +678,98 @@
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
         mZenModeHelperSpy.readXml(parser, true);
-        assertEquals(original, mZenModeHelperSpy.mConfig);
+        assertEquals("Config mismatch: current vs original: "
+                + mZenModeHelperSpy.mConfig.diff(original), original, mZenModeHelperSpy.mConfig);
         assertEquals(original.hashCode(), mZenModeHelperSpy.mConfig.hashCode());
     }
 
     @Test
+    public void testWriteXmlWithZenPolicy() throws Exception {
+        final String ruleId = "customRule";
+        setupZenConfig();
+
+        // one enabled automatic rule with zen policy
+        ArrayMap<String, ZenModeConfig.ZenRule> automaticRules = new ArrayMap<>();
+        ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule();
+        final ScheduleInfo customRuleInfo = new ScheduleInfo();
+        customRule.enabled = true;
+        customRule.creationTime = 0;
+        customRule.id = "customRule";
+        customRule.name = "Custom Rule";
+        customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
+        customRule.configurationActivity =
+                new ComponentName("android", "ScheduleConditionProvider");
+        customRule.pkg = customRule.configurationActivity.getPackageName();
+        customRule.zenPolicy = new ZenPolicy.Builder()
+                .allowAlarms(false)
+                .allowMedia(false)
+                .allowRepeatCallers(false)
+                .allowCalls(ZenPolicy.PEOPLE_TYPE_NONE)
+                .allowMessages(ZenPolicy.PEOPLE_TYPE_CONTACTS)
+                .allowEvents(true)
+                .allowReminders(false)
+                .build();
+        automaticRules.put("customRule", customRule);
+        mZenModeHelperSpy.mConfig.automaticRules = automaticRules;
+
+        ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy();
+
+        ByteArrayOutputStream baos = writeXmlAndPurge(false, null);
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(baos.toByteArray())), null);
+        parser.nextTag();
+        mZenModeHelperSpy.readXml(parser, false);
+
+        ZenModeConfig.ZenRule original = expected.automaticRules.get(ruleId);
+        ZenModeConfig.ZenRule current = mZenModeHelperSpy.mConfig.automaticRules.get(ruleId);
+
+        assertEquals("Automatic rules mismatch", original, current);
+    }
+
+    @Test
+    public void testReadXmlRestoreWithZenPolicy() throws Exception {
+        final String ruleId = "customRule";
+        setupZenConfig();
+
+        // one enabled automatic rule with zen policy
+        ArrayMap<String, ZenModeConfig.ZenRule> automaticRules = new ArrayMap<>();
+        ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule();
+        final ScheduleInfo customRuleInfo = new ScheduleInfo();
+        customRule.enabled = true;
+        customRule.creationTime = 0;
+        customRule.id = ruleId;
+        customRule.name = "Custom Rule";
+        customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
+        customRule.configurationActivity =
+                new ComponentName("android", "ScheduleConditionProvider");
+        customRule.pkg = customRule.configurationActivity.getPackageName();
+        customRule.zenPolicy = new ZenPolicy.Builder()
+                .allowSystem(true)
+                .allowCalls(ZenPolicy.PEOPLE_TYPE_ANYONE)
+                .allowReminders(true)
+                .build();
+        automaticRules.put(ruleId, customRule);
+        mZenModeHelperSpy.mConfig.automaticRules = automaticRules;
+
+        ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy();
+
+        ByteArrayOutputStream baos = writeXmlAndPurge(false, null);
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(baos.toByteArray())), null);
+        parser.nextTag();
+        mZenModeHelperSpy.readXml(parser, true);
+
+        ZenModeConfig.ZenRule original = expected.automaticRules.get(ruleId);
+        ZenModeConfig.ZenRule current = mZenModeHelperSpy.mConfig.automaticRules.get(ruleId);
+
+        assertEquals("Automatic rules mismatch", original, current);
+    }
+
+    @Test
     public void testReadXmlRulesNotOverriden() throws Exception {
         setupZenConfig();
 
@@ -892,6 +983,10 @@
         customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
         customRule.component = new ComponentName("android", "ScheduleConditionProvider");
+        customRule.zenPolicy = new ZenPolicy.Builder()
+                .allowReminders(true)
+                .allowMessages(ZenPolicy.PEOPLE_TYPE_ANYONE)
+                .build();
         automaticRules.put("customRule", customRule);
 
         ZenModeConfig.ZenRule defaultScheduleRule = new ZenModeConfig.ZenRule();
@@ -939,6 +1034,10 @@
         customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
         customRule.component = new ComponentName("android", "ScheduleConditionProvider");
+        customRule.zenPolicy = new ZenPolicy.Builder()
+                .allowReminders(true)
+                .allowMessages(ZenPolicy.PEOPLE_TYPE_ANYONE)
+                .build();
         automaticRules.put("customRule", customRule);
 
         ZenModeConfig.ZenRule defaultScheduleRule = new ZenModeConfig.ZenRule();
@@ -949,6 +1048,10 @@
         defaultScheduleRule.conditionId = ZenModeConfig.toScheduleConditionId(
                 defaultScheduleRuleInfo);
         defaultScheduleRule.id = ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID;
+        defaultScheduleRule.zenPolicy = new ZenPolicy.Builder()
+                .allowEvents(true)
+                .allowMessages(ZenPolicy.PEOPLE_TYPE_ANYONE)
+                .build();
         automaticRules.put(ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID, defaultScheduleRule);
 
         ZenModeConfig.ZenRule defaultEventRule = new ZenModeConfig.ZenRule();
@@ -959,6 +1062,11 @@
         defaultEventRule.conditionId = ZenModeConfig.toScheduleConditionId(
                 defaultEventRuleInfo);
         defaultEventRule.id = ZenModeConfig.EVENTS_DEFAULT_RULE_ID;
+        defaultScheduleRule.zenPolicy = new ZenPolicy.Builder()
+                .allowAlarms(false)
+                .allowMedia(false)
+                .allowRepeatCallers(false)
+                .build();
         automaticRules.put(ZenModeConfig.EVENTS_DEFAULT_RULE_ID, defaultEventRule);
 
         mZenModeHelperSpy.mConfig.automaticRules = automaticRules;
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java
index dc057d5..b315e51 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java
@@ -101,4 +101,34 @@
         assertTrue(FileUtils.deleteContentsAndDir(sliceDir));
     }
 
-}
\ No newline at end of file
+    @Test
+    public void testInvalid() throws Exception {
+        File sliceDir = new File(mContext.getCacheDir(), "slices-test");
+        if (!sliceDir.exists()) {
+            sliceDir.mkdir();
+        }
+        SlicePermissionManager permissions = new SlicePermissionManager(mContext,
+                TestableLooper.get(this).getLooper(), sliceDir);
+
+        DirtyTracker.Persistable junk = new DirtyTracker.Persistable() {
+            @Override
+            public String getFileName() {
+                return "invalidData";
+            }
+
+            @Override
+            public void writeTo(XmlSerializer out) throws IOException {
+                throw new RuntimeException("this doesn't work");
+            }
+        };
+
+        // let's put something bad in here
+        permissions.addDirtyImmediate(junk);
+        // force a persist. if this throws, it would take down system_server
+        permissions.handlePersist();
+
+        // Cleanup.
+        assertTrue(FileUtils.deleteContentsAndDir(sliceDir));
+    }
+
+}
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 85410f5..b2a2869 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -797,7 +797,7 @@
         public boolean mChanged = false;
 
         @Override
-        public void onStackOrderChanged() {
+        public void onStackOrderChanged(ActivityStack stack) {
             mChanged = true;
         }
     }
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 7c43cf3..7a6b2b5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -414,10 +414,10 @@
                 .setActivityOptions(new SafeActivityOptions(options))
                 .execute();
 
-        // verify that values are passed to the modifier. Values are passed twice -- once for
+        // verify that values are passed to the modifier. Values are passed thrice -- two for
         // setting initial state, another when task is created.
-        verify(modifier, times(2)).onCalculate(any(), eq(windowLayout), any(), any(), eq(options),
-                any(), any());
+        verify(modifier, times(3)).onCalculate(any(), eq(windowLayout), any(), any(), eq(options),
+                anyInt(), any(), any());
     }
 
     /**
@@ -565,7 +565,7 @@
         runAndVerifyBackgroundActivityStartsSubtest("allowed_noStartsAborted", false,
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, false);
+                false, false, false);
     }
 
     /**
@@ -580,7 +580,7 @@
                 "disallowed_unsupportedUsecase_aborted", true,
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, false);
+                false, false, false);
     }
 
     /**
@@ -595,47 +595,53 @@
         runAndVerifyBackgroundActivityStartsSubtest("disallowed_rootUid_notAborted", false,
                 Process.ROOT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, false);
+                false, false, false);
         runAndVerifyBackgroundActivityStartsSubtest("disallowed_systemUid_notAborted", false,
                 Process.SYSTEM_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, false);
+                false, false, false);
         runAndVerifyBackgroundActivityStartsSubtest(
                 "disallowed_callingUidHasVisibleWindow_notAborted", false,
                 UNIMPORTANT_UID, true, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, false);
+                false, false, false);
         runAndVerifyBackgroundActivityStartsSubtest(
                 "disallowed_callingUidProcessStateTop_notAborted", false,
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, false);
+                false, false, false);
         runAndVerifyBackgroundActivityStartsSubtest(
                 "disallowed_realCallingUidHasVisibleWindow_notAborted", false,
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, true, PROCESS_STATE_TOP + 1,
-                false, false);
+                false, false, false);
         runAndVerifyBackgroundActivityStartsSubtest(
                 "disallowed_realCallingUidProcessStateTop_notAborted", false,
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP,
-                false, false);
+                false, false, false);
         runAndVerifyBackgroundActivityStartsSubtest(
                 "disallowed_hasForegroundActivities_notAborted", false,
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                true, false);
+                true, false, false);
         runAndVerifyBackgroundActivityStartsSubtest(
                 "disallowed_callerIsRecents_notAborted", false,
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, true);
+                false, true, false);
+        runAndVerifyBackgroundActivityStartsSubtest(
+                "disallowed_callerIsWhitelisted_notAborted", false,
+                UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+                UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+                false, false, true);
     }
 
     private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted,
             int callingUid, boolean callingUidHasVisibleWindow, int callingUidProcState,
             int realCallingUid, boolean realCallingUidHasVisibleWindow, int realCallingUidProcState,
-            boolean hasForegroundActivities, boolean callerIsRecents) {
+            boolean hasForegroundActivities, boolean callerIsRecents,
+            boolean callerIsTempWhitelisted) {
         // window visibility
         doReturn(callingUidHasVisibleWindow).when(mService.mWindowManager).isAnyWindowVisibleForUid(
                 callingUid);
@@ -656,6 +662,8 @@
         RecentTasks recentTasks = mock(RecentTasks.class);
         mService.mStackSupervisor.setRecentTasks(recentTasks);
         doReturn(callerIsRecents).when(recentTasks).isCallerRecents(callingUid);
+        // caller is temp whitelisted
+        callerApp.setAllowBackgroundActivityStarts(callerIsTempWhitelisted);
 
         final ActivityOptions options = spy(ActivityOptions.makeBasic());
         ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK)
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 569c6d4..ee336a8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -31,6 +31,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -38,6 +39,9 @@
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doCallRealMethod;
+
 import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
@@ -115,6 +119,10 @@
     @After
     public void tearDownBase() {
         mTestInjector.tearDown();
+        if (mService != null) {
+            mService.setWindowManager(null);
+            mService = null;
+        }
     }
 
     ActivityTaskManagerService createActivityTaskManagerService() {
@@ -144,6 +152,13 @@
         return display;
     }
 
+    /** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */
+    TestActivityDisplay addNewActivityDisplayAt(DisplayInfo info, int position) {
+        final TestActivityDisplay display = createNewActivityDisplay(info);
+        mRootActivityContainer.addChild(display, position);
+        return display;
+    }
+
     /**
      * Builder for creating new activities.
      */
@@ -234,6 +249,10 @@
                     mService.mStackSupervisor, null /* options */, null /* sourceRecord */);
             spyOn(activity);
             activity.mAppWindowToken = mock(AppWindowToken.class);
+            doCallRealMethod().when(activity.mAppWindowToken).getOrientationIgnoreVisibility();
+            doCallRealMethod().when(activity.mAppWindowToken)
+                    .setOrientation(anyInt(), any(), any());
+            doCallRealMethod().when(activity.mAppWindowToken).setOrientation(anyInt());
             doNothing().when(activity).removeWindowContainer();
 
             if (mTaskRecord != null) {
@@ -346,6 +365,7 @@
                 mStack.addTask(task, true, "creating test task");
                 task.setStack(mStack);
                 task.setTask();
+                mStack.getWindowContainerController().mContainer.addChild(task.mTask, 0);
             }
 
             task.touchActiveTime();
@@ -365,7 +385,10 @@
                 setTask();
             }
 
-            private void setTask() {
+            void setTask() {
+                Task mockTask = mock(Task.class);
+                mockTask.mTaskRecord = this;
+                doCallRealMethod().when(mockTask).onDescendantOrientationChanged(any(), any());
                 setTask(mock(Task.class));
             }
         }
@@ -619,7 +642,13 @@
         }
     }
 
+    private static WindowManagerService sMockWindowManagerService;
+
     private static WindowManagerService prepareMockWindowManager() {
+        if (sMockWindowManagerService != null) {
+            return sMockWindowManagerService;
+        }
+
         final WindowManagerService service = mock(WindowManagerService.class);
         service.mRoot = mock(RootWindowContainer.class);
 
@@ -631,6 +660,7 @@
             return null;
         }).when(service).inSurfaceTransaction(any());
 
+        sMockWindowManagerService = service;
         return service;
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 92b4dbb..bc62de1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -155,15 +155,17 @@
 
         // Set initial orientation and update.
         mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
-        mWm.updateOrientationFromAppTokens(mDisplayContent.getRequestedOverrideConfiguration(),
-                null, mDisplayContent.getDisplayId());
+        mDisplayContent.updateOrientationFromAppTokens(
+                mDisplayContent.getRequestedOverrideConfiguration(),
+                null /* freezeThisOneIfNeeded */, false /* forceUpdate */);
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getLastOrientation());
         appWindow.mResizeReported = false;
 
         // Update the orientation to perform 180 degree rotation and check that resize was reported.
         mToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
-        mWm.updateOrientationFromAppTokens(mDisplayContent.getRequestedOverrideConfiguration(),
-                null, mDisplayContent.getDisplayId());
+        mDisplayContent.updateOrientationFromAppTokens(
+                mDisplayContent.getRequestedOverrideConfiguration(),
+                null /* freezeThisOneIfNeeded */, false /* forceUpdate */);
         mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
         assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, mDisplayContent.getLastOrientation());
         assertTrue(appWindow.mResizeReported);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 7c83ecc..3826fac 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -19,6 +19,8 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.os.Build.VERSION_CODES.P;
+import static android.os.Build.VERSION_CODES.Q;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
 import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
@@ -29,7 +31,13 @@
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.same;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -58,9 +66,11 @@
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
 import com.android.server.wm.utils.WmDisplayCutout;
 
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -309,9 +319,27 @@
 
     @Test
     public void testFocusedWindowMultipleDisplays() {
+        doTestFocusedWindowMultipleDisplays(false /* perDisplayFocusEnabled */, Q);
+    }
+
+    @Test
+    public void testFocusedWindowMultipleDisplaysPerDisplayFocusEnabled() {
+        doTestFocusedWindowMultipleDisplays(true /* perDisplayFocusEnabled */, Q);
+    }
+
+    @Test
+    public void testFocusedWindowMultipleDisplaysPerDisplayFocusEnabledLegacyApp() {
+        doTestFocusedWindowMultipleDisplays(true /* perDisplayFocusEnabled */, P);
+    }
+
+    private void doTestFocusedWindowMultipleDisplays(boolean perDisplayFocusEnabled,
+            int targetSdk) {
+        mWm.mPerDisplayFocusEnabled = perDisplayFocusEnabled;
+
         // Create a focusable window and check that focus is calculated correctly
         final WindowState window1 =
                 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "window1");
+        window1.mAppToken.mTargetSdk = targetSdk;
         updateFocusedWindow();
         assertTrue(window1.isFocused());
         assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
@@ -324,16 +352,17 @@
 
         // Add a window to the second display, and it should be focused
         final WindowState window2 = createWindow(null, TYPE_BASE_APPLICATION, dc, "window2");
+        window2.mAppToken.mTargetSdk = targetSdk;
         updateFocusedWindow();
-        assertTrue(window1.isFocused());
         assertTrue(window2.isFocused());
+        assertEquals(perDisplayFocusEnabled && targetSdk >= Q, window1.isFocused());
         assertEquals(window2, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
 
-        // Move the first window to the to including parents, and make sure focus is updated
+        // Move the first window to top including parents, and make sure focus is updated
         window1.getParent().positionChildAt(POSITION_TOP, window1, true);
         updateFocusedWindow();
         assertTrue(window1.isFocused());
-        assertTrue(window2.isFocused());
+        assertEquals(perDisplayFocusEnabled && targetSdk >= Q, window2.isFocused());
         assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
     }
 
@@ -454,6 +483,13 @@
     @SuppressLint("InlinedApi")
     public void testOrientationDefinedByKeyguard() {
         final DisplayContent dc = createNewDisplay();
+
+        // When display content is created its configuration is not yet initialized, which could
+        // cause unnecessary configuration propagation, so initialize it here.
+        final Configuration config = new Configuration();
+        dc.computeScreenConfiguration(config);
+        dc.onRequestedOverrideConfigurationChanged(config);
+
         // Create a window that requests landscape orientation. It will define device orientation
         // by default.
         final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
@@ -546,6 +582,52 @@
         assertNull("default display Ime target: ", mDisplayContent.mInputMethodTarget);
     }
 
+    @Test
+    public void testOnDescendantOrientationRequestChanged() {
+        final DisplayContent dc = createNewDisplay();
+        mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
+        final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
+                ? SCREEN_ORIENTATION_PORTRAIT
+                : SCREEN_ORIENTATION_LANDSCAPE;
+
+        final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
+        window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
+        window.mAppToken.setOrientation(newOrientation);
+
+        ActivityRecord activityRecord = mock(ActivityRecord.class);
+
+        assertTrue("Display should rotate to handle orientation request by default.",
+                dc.onDescendantOrientationChanged(window.mToken.token, activityRecord));
+
+        final ArgumentCaptor<Configuration> captor = ArgumentCaptor.forClass(Configuration.class);
+        verify(mWm.mAtmService).updateDisplayOverrideConfigurationLocked(captor.capture(),
+                same(activityRecord), anyBoolean(), eq(dc.getDisplayId()));
+        final Configuration newDisplayConfig = captor.getValue();
+        assertEquals(Configuration.ORIENTATION_PORTRAIT, newDisplayConfig.orientation);
+    }
+
+    @Test
+    public void testOnDescendantOrientationRequestChanged_FrozenToUserRotation() {
+        final DisplayContent dc = createNewDisplay();
+        dc.getDisplayRotation().setFixedToUserRotation(true);
+        mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
+        final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
+                ? SCREEN_ORIENTATION_PORTRAIT
+                : SCREEN_ORIENTATION_LANDSCAPE;
+
+        final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
+        window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
+        window.mAppToken.setOrientation(newOrientation);
+
+        ActivityRecord activityRecord = mock(ActivityRecord.class);
+
+        assertFalse("Display shouldn't rotate to handle orientation request if fixed to"
+                        + " user rotation.",
+                dc.onDescendantOrientationChanged(window.mToken.token, activityRecord));
+        verify(mWm.mAtmService, never()).updateDisplayOverrideConfigurationLocked(any(),
+                eq(activityRecord), anyBoolean(), eq(dc.getDisplayId()));
+    }
+
     private boolean isOptionsPanelAtRight(int displayId) {
         return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index e988994..198e7ce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -33,6 +33,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.content.ContentResolver;
@@ -52,7 +53,6 @@
 import android.provider.Settings;
 import android.view.Surface;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.util.test.FakeSettingsProvider;
@@ -63,6 +63,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
@@ -81,13 +82,12 @@
  */
 @SmallTest
 @Presubmit
-@FlakyTest(detail = "Confirm stable in post-submit before removing")
 public class DisplayRotationTests {
     private static final long UI_HANDLER_WAIT_TIMEOUT_MS = 50;
 
     private StatusBarManagerInternal mPreviousStatusBarManagerInternal;
 
-    private WindowManagerService mMockWm;
+    private static WindowManagerService sMockWm;
     private DisplayContent mMockDisplayContent;
     private DisplayPolicy mMockDisplayPolicy;
     private Context mMockContext;
@@ -109,13 +109,16 @@
 
     private DisplayRotation mTarget;
 
+    @BeforeClass
+    public static void setUpOnce() {
+        sMockWm = mock(WindowManagerService.class);
+        sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
+    }
+
     @Before
     public void setUp() {
         FakeSettingsProvider.clearSettingsProvider();
 
-        mMockWm = mock(WindowManagerService.class);
-        mMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
-
         mPreviousStatusBarManagerInternal = LocalServices.getService(
                 StatusBarManagerInternal.class);
         LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
@@ -236,7 +239,7 @@
     }
 
     @Test
-    public void testReturnsSidesays_UserRotationLocked_IncompatibleAppRequest()
+    public void testReturnsSideways_UserRotationLocked_IncompatibleAppRequest()
             throws Exception {
         mBuilder.build();
         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
@@ -453,7 +456,7 @@
         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
         assertTrue(waitForUiHandler());
 
-        verify(mMockWm).updateRotation(false, false);
+        verify(sMockWm).updateRotation(false, false);
     }
 
     @Test
@@ -606,6 +609,26 @@
                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
     }
 
+    // ========================
+    // Non-rotation API Tests
+    // ========================
+    @Test
+    public void testRespectsAppRequestedOrientationByDefault() throws Exception {
+        mBuilder.build();
+
+        assertTrue("Display rotation should respect app requested orientation by"
+                + " default.", mTarget.respectAppRequestedOrientation());
+    }
+
+    @Test
+    public void testNotRespectAppRequestedOrientation_FixedToUserRotation() throws Exception {
+        mBuilder.build();
+        mTarget.setFixedToUserRotation(true);
+
+        assertFalse("Display rotation shouldn't respect app requested orientation if"
+                + " fixed to user rotation.", mTarget.respectAppRequestedOrientation());
+    }
+
     /**
      * Call {@link DisplayRotation#configure(int, int, int, int)} to configure {@link #mTarget}
      * according to given parameters.
@@ -814,8 +837,9 @@
                     .thenReturn(mFakeSettingsProvider.getIContentProvider());
 
             mMockDisplayWindowSettings = mock(DisplayWindowSettings.class);
-            mTarget = new DisplayRotation(mMockWm, mMockDisplayContent, mMockDisplayPolicy,
+            mTarget = new DisplayRotation(sMockWm, mMockDisplayContent, mMockDisplayPolicy,
                     mMockDisplayWindowSettings, mMockContext, new Object());
+            reset(sMockWm);
 
             captureObservers();
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index 8e881b5..992d017 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -22,10 +22,10 @@
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -43,11 +43,13 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
 import com.android.server.policy.WindowManagerPolicy;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.MockitoSession;
 
 import java.io.File;
 
@@ -94,6 +96,12 @@
     @After
     public void tearDown() {
         deleteRecursively(TEST_FOLDER);
+
+        // TODO(b/121296525): We may want to restore other display settings (not only overscans in
+        // testPersistOverscan*test) on mPrimaryDisplay and mSecondaryDisplay back to default
+        // values after each test finishes, since we are going to reuse a singleton
+        // WindowManagerService instance among all tests that extend {@link WindowTestsBase} class
+        // (b/113239988).
     }
 
     @Test
@@ -245,21 +253,35 @@
     @Test
     public void testPersistOverscanInSameInstance() {
         final DisplayInfo info = mPrimaryDisplay.getDisplayInfo();
-        mTarget.setOverscanLocked(info, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */);
+        try {
+            mTarget.setOverscanLocked(info, 1 /* left */, 2 /* top */, 3 /* right */,
+                    4 /* bottom */);
 
-        mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
+            mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
 
-        assertOverscan(mPrimaryDisplay, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */);
+            assertOverscan(mPrimaryDisplay, 1 /* left */, 2 /* top */, 3 /* right */,
+                    4 /* bottom */);
+        } finally {
+            mTarget.setOverscanLocked(info, 0, 0, 0, 0);
+            mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
+        }
     }
 
     @Test
     public void testPersistOverscanAcrossInstances() {
         final DisplayInfo info = mPrimaryDisplay.getDisplayInfo();
-        mTarget.setOverscanLocked(info, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */);
+        try {
+            mTarget.setOverscanLocked(info, 10 /* left */, 20 /* top */, 30 /* right */,
+                    40 /* bottom */);
 
-        applySettingsToDisplayByNewInstance(mPrimaryDisplay);
+            applySettingsToDisplayByNewInstance(mPrimaryDisplay);
 
-        assertOverscan(mPrimaryDisplay, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */);
+            assertOverscan(mPrimaryDisplay, 10 /* left */, 20 /* top */, 30 /* right */,
+                    40 /* bottom */);
+        } finally {
+            mTarget.setOverscanLocked(info, 0, 0, 0, 0);
+            mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
+        }
     }
 
     @Test
@@ -389,26 +411,32 @@
         mTarget.setUserRotation(mPrimaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED,
                 Surface.ROTATION_0);
 
+        final MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
+                .startMocking();
         final DisplayRotation displayRotation = mock(DisplayRotation.class);
-        mPrimaryDisplay = spy(mPrimaryDisplay);
-        when(mPrimaryDisplay.getDisplayRotation()).thenReturn(displayRotation);
+        spyOn(mPrimaryDisplay);
+        doReturn(displayRotation).when(mPrimaryDisplay).getDisplayRotation();
 
         mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
 
         verify(displayRotation).restoreSettings(anyInt(), anyInt(), eq(false));
+        mockitoSession.finishMocking();
     }
 
     @Test
     public void testSetFixedToUserRotation() {
         mTarget.setFixedToUserRotation(mPrimaryDisplay, true);
 
+        final MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
+                .startMocking();
         final DisplayRotation displayRotation = mock(DisplayRotation.class);
-        mPrimaryDisplay = spy(mPrimaryDisplay);
-        when(mPrimaryDisplay.getDisplayRotation()).thenReturn(displayRotation);
+        spyOn(mPrimaryDisplay);
+        doReturn(displayRotation).when(mPrimaryDisplay).getDisplayRotation();
 
         applySettingsToDisplayByNewInstance(mPrimaryDisplay);
 
         verify(displayRotation).restoreSettings(anyInt(), anyInt(), eq(true));
+        mockitoSession.finishMocking();
     }
 
     private static void assertOverscan(DisplayContent display, int left, int top, int right,
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index 88215449..3740786 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -19,13 +19,19 @@
 import static android.view.InsetsState.TYPE_TOP_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.view.InsetsSource;
+import android.view.InsetsState;
 
+import org.junit.Before;
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
@@ -81,4 +87,41 @@
         mProvider.onPostLayout();
         assertEquals(new Rect(10, 10, 20, 20), mProvider.getSource().getFrame());
     }
+
+    @Test
+    public void testUpdateControlForTarget() {
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
+        topBar.getFrameLw().set(0, 0, 500, 100);
+        mProvider.setWindow(topBar, null);
+        mProvider.updateControlForTarget(target);
+        assertNotNull(mProvider.getControl());
+        mProvider.updateControlForTarget(null);
+        assertNull(mProvider.getControl());
+    }
+
+    @Test
+    public void testInsetsModified() {
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
+        topBar.getFrameLw().set(0, 0, 500, 100);
+        mProvider.setWindow(topBar, null);
+        mProvider.updateControlForTarget(target);
+        InsetsState state = new InsetsState();
+        state.getSource(TYPE_TOP_BAR).setVisible(false);
+        mProvider.onInsetsModified(target, state.getSource(TYPE_TOP_BAR));
+        assertFalse(mSource.isVisible());
+    }
+
+    @Test
+    public void testInsetsModified_noControl() {
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
+        topBar.getFrameLw().set(0, 0, 500, 100);
+        mProvider.setWindow(topBar, null);
+        InsetsState state = new InsetsState();
+        state.getSource(TYPE_TOP_BAR).setVisible(false);
+        mProvider.onInsetsModified(target, state.getSource(TYPE_TOP_BAR));
+        assertTrue(mSource.isVisible());
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java b/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java
new file mode 100644
index 0000000..ce22788
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.os.Process.FIRST_APPLICATION_UID;
+import static android.os.Process.NFC_UID;
+import static android.os.Process.SYSTEM_UID;
+import static android.os.UserHandle.USER_ALL;
+import static android.os.UserHandle.USER_SYSTEM;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.UserHandle;
+import android.util.SparseBooleanArray;
+
+import com.android.server.wm.LockTaskController.LockTaskToken;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.lang.reflect.Constructor;
+
+public class KeyguardDisableHandlerTest {
+
+    private KeyguardDisableHandler mKeyguardDisable;
+
+    private boolean mKeyguardEnabled;
+    private SparseBooleanArray mKeyguardSecure = new SparseBooleanArray();
+    private SparseBooleanArray mDpmRequiresPassword = new SparseBooleanArray();
+
+    @Before
+    public void setUp() throws Exception {
+        mKeyguardEnabled = true;
+
+        mKeyguardDisable = new KeyguardDisableHandler(new KeyguardDisableHandler.Injector() {
+            @Override
+            public boolean dpmRequiresPassword(int userId) {
+                return mDpmRequiresPassword.get(userId);
+            }
+
+            @Override
+            public boolean isKeyguardSecure(int userId) {
+                return mKeyguardSecure.get(userId);
+            }
+
+            @Override
+            public int getProfileParentId(int userId) {
+                return userId;
+            }
+
+            @Override
+            public void enableKeyguard(boolean enabled) {
+                mKeyguardEnabled = enabled;
+            }
+        }, mock(Handler.class)) {
+            @Override
+            public void disableKeyguard(IBinder token, String tag, int callingUid, int userId) {
+                super.disableKeyguard(token, tag, callingUid, userId);
+                // In the actual code, the update is posted to the handler thread. Eagerly update
+                // here to simplify the test.
+                updateKeyguardEnabled(userId);
+            }
+
+            @Override
+            public void reenableKeyguard(IBinder token, int callingUid, int userId) {
+                super.reenableKeyguard(token, callingUid, userId);
+                // In the actual code, the update is posted to the handler thread. Eagerly update
+                // here to simplify the test.
+                updateKeyguardEnabled(userId);
+            }
+        };
+    }
+
+    @Test
+    public void starts_enabled() {
+        assertTrue(mKeyguardEnabled);
+        mKeyguardDisable.updateKeyguardEnabled(USER_ALL);
+        assertTrue(mKeyguardEnabled);
+    }
+
+    @Test
+    public void disable_fromApp_disables() {
+        mKeyguardDisable.disableKeyguard(new Binder(), "Tag", FIRST_APPLICATION_UID, USER_SYSTEM);
+        assertFalse(mKeyguardEnabled);
+    }
+
+    @Test
+    public void disable_fromApp_secondaryUser_disables() {
+        mKeyguardDisable.setCurrentUser(1);
+        mKeyguardDisable.disableKeyguard(new Binder(), "Tag",
+                UserHandle.getUid(1, FIRST_APPLICATION_UID), 1);
+        assertFalse(mKeyguardEnabled);
+    }
+
+    @Test
+    public void disable_fromSystem_LockTask_disables() {
+        mKeyguardDisable.disableKeyguard(createLockTaskToken(), "Tag", SYSTEM_UID, USER_SYSTEM);
+        assertFalse(mKeyguardEnabled);
+    }
+
+    @Test
+    public void disable_fromSystem_genericToken_fails() {
+        try {
+            mKeyguardDisable.disableKeyguard(new Binder(), "Tag", SYSTEM_UID, USER_SYSTEM);
+            fail("Expected exception not thrown");
+        } catch (UnsupportedOperationException e) {
+            assertThat(e.getMessage(), containsString("Only apps can use the KeyguardLock API"));
+        }
+        assertTrue(mKeyguardEnabled);
+    }
+
+    @Test
+    public void disable_fromNonApp_genericToken_fails() {
+        try {
+            mKeyguardDisable.disableKeyguard(new Binder(), "Tag", NFC_UID, USER_SYSTEM);
+            fail("Expected exception not thrown");
+        } catch (UnsupportedOperationException e) {
+            assertThat(e.getMessage(), containsString("Only apps can use the KeyguardLock API"));
+        }
+        assertTrue(mKeyguardEnabled);
+    }
+
+    @Test
+    public void disable_fromApp_secure_staysEnabled() {
+        configureIsSecure(true, USER_SYSTEM);
+        mKeyguardDisable.disableKeyguard(new Binder(), "Tag", FIRST_APPLICATION_UID, USER_SYSTEM);
+        assertTrue(mKeyguardEnabled);
+    }
+
+    @Test
+    public void disable_fromApp_dpmRequiresPassword_staysEnabled() {
+        configureDpmRequiresPassword(true, USER_SYSTEM);
+        mKeyguardDisable.disableKeyguard(new Binder(), "Tag", FIRST_APPLICATION_UID, USER_SYSTEM);
+        assertTrue(mKeyguardEnabled);
+    }
+
+    @Test
+    public void disable_fromSystem_LockTask_secure_disables() {
+        configureIsSecure(true, USER_SYSTEM);
+        mKeyguardDisable.disableKeyguard(createLockTaskToken(), "Tag", SYSTEM_UID, USER_SYSTEM);
+        assertFalse(mKeyguardEnabled);
+    }
+
+    @Test
+    public void disable_fromSystem_LockTask_requiresDpm_staysEnabled() {
+        configureDpmRequiresPassword(true, USER_SYSTEM);
+        mKeyguardDisable.disableKeyguard(createLockTaskToken(), "Tag", SYSTEM_UID, USER_SYSTEM);
+        assertTrue(mKeyguardEnabled);
+    }
+
+    @Test
+    public void disable_fromApp_thenSecure_reenables() {
+        mKeyguardDisable.disableKeyguard(new Binder(), "Tag", FIRST_APPLICATION_UID, USER_SYSTEM);
+        configureIsSecure(true, USER_SYSTEM);
+        assertTrue(mKeyguardEnabled);
+    }
+
+    @Test
+    public void disable_fromSystem_LockTask_thenRequiresDpm_reenables() {
+        mKeyguardDisable.disableKeyguard(createLockTaskToken(), "Tag", SYSTEM_UID, USER_SYSTEM);
+        configureDpmRequiresPassword(true, USER_SYSTEM);
+        assertTrue(mKeyguardEnabled);
+    }
+
+    @Test
+    public void user_switch_to_enabledUser_applies_enabled() {
+        mKeyguardDisable.disableKeyguard(createLockTaskToken(), "Tag", SYSTEM_UID, USER_SYSTEM);
+        assertFalse("test setup failed", mKeyguardEnabled);
+        mKeyguardDisable.setCurrentUser(1);
+        assertTrue(mKeyguardEnabled);
+    }
+
+    @Test
+    public void user_switch_to_disabledUser_applies_disabled() {
+        mKeyguardDisable.disableKeyguard(createLockTaskToken(), "Tag",
+                SYSTEM_UID, 1);
+        assertTrue("test setup failed", mKeyguardEnabled);
+        mKeyguardDisable.setCurrentUser(1);
+        assertFalse(mKeyguardEnabled);
+    }
+
+    private void configureIsSecure(boolean secure, int userId) {
+        mKeyguardSecure.put(userId, secure);
+        mKeyguardDisable.updateKeyguardEnabled(userId);
+    }
+
+    private void configureDpmRequiresPassword(boolean requiresPassword, int userId) {
+        mDpmRequiresPassword.put(userId, requiresPassword);
+        mKeyguardDisable.updateKeyguardEnabled(userId);
+    }
+
+    private LockTaskToken createLockTaskToken() {
+        try {
+            final Constructor<LockTaskToken> constructor =
+                    LockTaskToken.class.getDeclaredConstructor();
+            constructor.setAccessible(true);
+            return constructor.newInstance();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
index 3720c85..8c3dec7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -31,6 +31,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
@@ -89,10 +90,10 @@
         final WindowLayout layout = new WindowLayout(0, 0, 0, 0, 0, 0, 0);
         final ActivityOptions options = mock(ActivityOptions.class);
 
-        mController.calculate(record.getTaskRecord(), layout, record, source, options,
+        mController.calculate(record.getTaskRecord(), layout, record, source, options, PHASE_BOUNDS,
                 new LaunchParams());
         verify(positioner, times(1)).onCalculate(eq(record.getTaskRecord()), eq(layout), eq(record),
-                eq(source), eq(options), any(), any());
+                eq(source), eq(options), anyInt(), any(), any());
     }
 
     /**
@@ -115,9 +116,9 @@
         mPersister.putLaunchParams(userId, name, expected);
 
         mController.calculate(activity.getTaskRecord(), null /*layout*/, activity, null /*source*/,
-                null /*options*/, new LaunchParams());
-        verify(positioner, times(1)).onCalculate(any(), any(), any(), any(), any(), eq(expected),
-                any());
+                null /*options*/, PHASE_BOUNDS, new LaunchParams());
+        verify(positioner, times(1)).onCalculate(any(), any(), any(), any(), any(), anyInt(),
+                eq(expected), any());
     }
 
     /**
@@ -128,14 +129,15 @@
         final LaunchParamsModifier
                 ignoredPositioner = mock(LaunchParamsModifier.class);
         final LaunchParamsModifier earlyExitPositioner =
-                (task, layout, activity, source, options, currentParams, outParams) -> RESULT_DONE;
+                (task, layout, activity, source, options, phase, currentParams, outParams)
+                        -> RESULT_DONE;
 
         mController.registerModifier(ignoredPositioner);
         mController.registerModifier(earlyExitPositioner);
 
         mController.calculate(null /*task*/, null /*layout*/, null /*activity*/,
-                null /*source*/, null /*options*/, new LaunchParams());
-        verify(ignoredPositioner, never()).onCalculate(any(), any(), any(), any(), any(),
+                null /*source*/, null /*options*/, PHASE_BOUNDS, new LaunchParams());
+        verify(ignoredPositioner, never()).onCalculate(any(), any(), any(), any(), any(), anyInt(),
                 any(), any());
     }
 
@@ -152,20 +154,20 @@
         mController.registerModifier(firstPositioner);
 
         mController.calculate(null /*task*/, null /*layout*/, null /*activity*/,
-                null /*source*/, null /*options*/, new LaunchParams());
-        verify(firstPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), any(),
-                any());
+                null /*source*/, null /*options*/, PHASE_BOUNDS, new LaunchParams());
+        verify(firstPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), anyInt(),
+                any(), any());
 
         final LaunchParamsModifier secondPositioner = spy(earlyExitPositioner);
 
         mController.registerModifier(secondPositioner);
 
         mController.calculate(null /*task*/, null /*layout*/, null /*activity*/,
-                null /*source*/, null /*options*/, new LaunchParams());
-        verify(firstPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), any(),
-                any());
-        verify(secondPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), any(),
-                any());
+                null /*source*/, null /*options*/, PHASE_BOUNDS, new LaunchParams());
+        verify(firstPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), anyInt(),
+                any(), any());
+        verify(secondPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), anyInt(),
+                any(), any());
     }
 
     /**
@@ -187,9 +189,9 @@
         mController.registerModifier(positioner2);
 
         mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
-                null /*options*/, new LaunchParams());
+                null /*options*/, PHASE_BOUNDS, new LaunchParams());
 
-        verify(positioner1, times(1)).onCalculate(any(), any(), any(), any(), any(),
+        verify(positioner1, times(1)).onCalculate(any(), any(), any(), any(), any(), anyInt(),
                 eq(positioner2.getLaunchParams()), any());
     }
 
@@ -213,7 +215,7 @@
         final LaunchParams result = new LaunchParams();
 
         mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
-                null /*options*/, result);
+                null /*options*/, PHASE_BOUNDS, result);
 
         assertEquals(result, positioner2.getLaunchParams());
     }
@@ -232,21 +234,42 @@
 
         // VR activities should always land on default display.
         mController.calculate(null /*task*/, null /*layout*/, vrActivity /*activity*/,
-                null /*source*/, null /*options*/, result);
+                null /*source*/, null /*options*/, PHASE_BOUNDS, result);
         assertEquals(DEFAULT_DISPLAY, result.mPreferredDisplayId);
 
         // Otherwise, always lands on VR 2D display.
         final ActivityRecord vr2dActivity = new ActivityBuilder(mService).build();
         mController.calculate(null /*task*/, null /*layout*/, vr2dActivity /*activity*/,
-                null /*source*/, null /*options*/, result);
+                null /*source*/, null /*options*/, PHASE_BOUNDS, result);
         assertEquals(vr2dDisplayId, result.mPreferredDisplayId);
         mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
-                null /*options*/, result);
+                null /*options*/, PHASE_BOUNDS, result);
         assertEquals(vr2dDisplayId, result.mPreferredDisplayId);
 
         mService.mVr2dDisplayId = INVALID_DISPLAY;
     }
 
+
+    /**
+     * Ensures that {@link LaunchParamsController} calculates to {@link PHASE_BOUNDS} phase by
+     * default.
+     */
+    @Test
+    public void testCalculatePhase() {
+        final LaunchParamsModifier positioner = mock(LaunchParamsModifier.class);
+        mController.registerModifier(positioner);
+
+        final ActivityRecord record = new ActivityBuilder(mService).build();
+        final ActivityRecord source = new ActivityBuilder(mService).build();
+        final WindowLayout layout = new WindowLayout(0, 0, 0, 0, 0, 0, 0);
+        final ActivityOptions options = mock(ActivityOptions.class);
+
+        mController.calculate(record.getTaskRecord(), layout, record, source, options, PHASE_BOUNDS,
+                new LaunchParams());
+        verify(positioner, times(1)).onCalculate(eq(record.getTaskRecord()), eq(layout), eq(record),
+                eq(source), eq(options), eq(PHASE_BOUNDS), any(), any());
+    }
+
     /**
      * Ensures that {@link LaunchParamsModifier} requests specifying display id during
      * layout are honored.
@@ -348,7 +371,7 @@
 
         @Override
         public int onCalculate(TaskRecord task, WindowLayout layout, ActivityRecord activity,
-                   ActivityRecord source, ActivityOptions options,
+                   ActivityRecord source, ActivityOptions options, int phase,
                    LaunchParams currentParams, LaunchParams outParams) {
             outParams.set(mParams);
             return mReturnVal;
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index f3a125b..8635364 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -40,7 +40,6 @@
 import android.view.DisplayInfo;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
 
 import com.android.server.LocalServices;
@@ -65,7 +64,6 @@
  */
 @MediumTest
 @Presubmit
-@FlakyTest(detail = "Confirm stable in post-submit before removing")
 public class LaunchParamsPersisterTests extends ActivityTestsBase {
     private static final int TEST_USER_ID = 3;
     private static final int ALTERNATIVE_USER_ID = 0;
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index 6259fa6..e24eb75 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -34,7 +34,12 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.isNull;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
@@ -47,10 +52,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
 
 import android.app.StatusBarManager;
 import android.app.admin.DevicePolicyManager;
@@ -115,6 +116,7 @@
 
     private LockTaskController mLockTaskController;
     private Context mContext;
+    private String mPackageName;
     private String mLockToAppSetting;
 
     @Before
@@ -122,6 +124,7 @@
         MockitoAnnotations.initMocks(this);
 
         mContext = getInstrumentation().getTargetContext();
+        mPackageName = mContext.getPackageName();
         mLockToAppSetting = Settings.Secure.getString(mContext.getContentResolver(),
                 Settings.Secure.LOCK_TO_APP_EXIT_LOCKED);
 
@@ -146,6 +149,7 @@
 
     @After
     public void tearDown() throws Exception {
+        mLockTaskController.setWindowManager(null);
         Settings.Secure.putString(mContext.getContentResolver(),
                 Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, mLockToAppSetting);
     }
@@ -223,7 +227,7 @@
         // THEN lock task mode should be started
         verifyLockTaskStarted(STATUS_BAR_MASK_PINNED, DISABLE2_NONE);
         // THEN screen pinning toast should be shown
-        verify(mStatusBarService).showPinningEnterExitToast(true /* entering */);
+        verify(mStatusBarService).showPinningEnterExitToast(eq(true /* entering */));
     }
 
     @Test
@@ -390,9 +394,9 @@
         // THEN lock task mode should have been finished
         verifyLockTaskStopped(times(1));
         // THEN the keyguard should be shown
-        verify(mLockPatternUtils).requireCredentialEntry(UserHandle.USER_ALL);
+        verify(mLockPatternUtils).requireCredentialEntry(eq(UserHandle.USER_ALL));
         // THEN screen pinning toast should be shown
-        verify(mStatusBarService).showPinningEnterExitToast(false /* entering */);
+        verify(mStatusBarService).showPinningEnterExitToast(eq(false /* entering */));
     }
 
     @Test
@@ -509,9 +513,9 @@
                 & ~DISABLE_HOME;
         int expectedFlags2 = DISABLE2_MASK;
         verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
         verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
 
         // reset invocation counter
         reset(mStatusBarService);
@@ -526,9 +530,9 @@
         expectedFlags2 = DISABLE2_MASK
                 & ~DISABLE2_NOTIFICATION_SHADE;
         verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
         verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
     }
 
     @Test
@@ -548,9 +552,9 @@
 
         // THEN status bar shouldn't change
         verify(mStatusBarService, never()).disable(anyInt(), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
         verify(mStatusBarService, never()).disable2(anyInt(), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
     }
 
     @Test
@@ -560,19 +564,20 @@
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
 
         // THEN keyguard should be disabled
-        verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString());
+        verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString(), eq(TEST_USER_ID));
 
         // WHEN keyguard is enabled for lock task mode
         mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_KEYGUARD);
 
         // THEN keyguard should be enabled
-        verify(mWindowManager).reenableKeyguard(any(IBinder.class));
+        verify(mWindowManager).reenableKeyguard(any(IBinder.class), eq(TEST_USER_ID));
 
         // WHEN keyguard is disabled again for lock task mode
         mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_NONE);
 
         // THEN keyguard should be disabled
-        verify(mWindowManager, times(2)).disableKeyguard(any(IBinder.class), anyString());
+        verify(mWindowManager, times(2)).disableKeyguard(any(IBinder.class), anyString(),
+                eq(TEST_USER_ID));
     }
 
     @Test
@@ -653,29 +658,30 @@
 
     private void verifyLockTaskStarted(int statusBarMask, int statusBarMask2) throws Exception {
         // THEN the keyguard should have been disabled
-        verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString());
+        verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString(), eq(TEST_USER_ID));
         // THEN the status bar should have been disabled
         verify(mStatusBarService).disable(eq(statusBarMask), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
         verify(mStatusBarService).disable2(eq(statusBarMask2), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
         // THEN recents should have been notified
         verify(mRecentTasks).onLockTaskModeStateChanged(anyInt(), eq(TEST_USER_ID));
         // THEN the DO/PO should be informed about the operation
-        verify(mDevicePolicyManager).notifyLockTaskModeChanged(true, TEST_PACKAGE_NAME,
-                TEST_USER_ID);
+        verify(mDevicePolicyManager).notifyLockTaskModeChanged(eq(true), eq(TEST_PACKAGE_NAME),
+                eq(TEST_USER_ID));
     }
 
     private void verifyLockTaskStopped(VerificationMode mode) throws Exception {
         // THEN the keyguard should have been disabled
-        verify(mWindowManager, mode).reenableKeyguard(any(IBinder.class));
+        verify(mWindowManager, mode).reenableKeyguard(any(IBinder.class), eq(TEST_USER_ID));
         // THEN the status bar should have been disabled
         verify(mStatusBarService, mode).disable(eq(StatusBarManager.DISABLE_NONE),
-                any(IBinder.class), eq(mContext.getPackageName()));
+                any(IBinder.class), eq(mPackageName));
         verify(mStatusBarService, mode).disable2(eq(StatusBarManager.DISABLE2_NONE),
-                any(IBinder.class), eq(mContext.getPackageName()));
+                any(IBinder.class), eq(mPackageName));
         // THEN the DO/PO should be informed about the operation
-        verify(mDevicePolicyManager, mode).notifyLockTaskModeChanged(false, null, TEST_USER_ID);
+        verify(mDevicePolicyManager, mode).notifyLockTaskModeChanged(eq(false), isNull(),
+                eq(TEST_USER_ID));
     }
 
     /**
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 0ff67d7..5f3a290 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -24,7 +24,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -65,22 +64,26 @@
     }
 
     @Test
-    public void testCancelAnimationOnStackOrderChange() {
-        ActivityStack fullscreenStack =
-                mService.mRootActivityContainer.getDefaultDisplay().createStack(
-                        WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        ActivityStack recentsStack = mService.mRootActivityContainer.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */);
-        ActivityRecord recentsActivity = new ActivityBuilder(mService)
+    public void testCancelAnimationOnVisibleStackOrderChange() {
+        ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
+        ActivityStack fullscreenStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        new ActivityBuilder(mService)
+                .setComponent(new ComponentName(mContext.getPackageName(), "App1"))
+                .setCreateTask(true)
+                .setStack(fullscreenStack)
+                .build();
+        ActivityStack recentsStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_RECENTS, true /* onTop */);
+        new ActivityBuilder(mService)
                 .setComponent(mRecentsComponent)
                 .setCreateTask(true)
                 .setStack(recentsStack)
                 .build();
-        ActivityStack fullscreenStack2 =
-                mService.mRootActivityContainer.getDefaultDisplay().createStack(
-                        WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        ActivityRecord fsActivity = new ActivityBuilder(mService)
-                .setComponent(new ComponentName(mContext.getPackageName(), "App1"))
+        ActivityStack fullscreenStack2 = display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        new ActivityBuilder(mService)
+                .setComponent(new ComponentName(mContext.getPackageName(), "App2"))
                 .setCreateTask(true)
                 .setStack(fullscreenStack2)
                 .build();
@@ -97,4 +100,42 @@
         verify(mService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
                 eq(REORDER_KEEP_IN_PLACE), any());
     }
+
+    @Test
+    public void testKeepAnimationOnHiddenStackOrderChange() {
+        ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
+        ActivityStack fullscreenStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        new ActivityBuilder(mService)
+                .setComponent(new ComponentName(mContext.getPackageName(), "App1"))
+                .setCreateTask(true)
+                .setStack(fullscreenStack)
+                .build();
+        ActivityStack recentsStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_RECENTS, true /* onTop */);
+        new ActivityBuilder(mService)
+                .setComponent(mRecentsComponent)
+                .setCreateTask(true)
+                .setStack(recentsStack)
+                .build();
+        ActivityStack fullscreenStack2 = display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        new ActivityBuilder(mService)
+                .setComponent(new ComponentName(mContext.getPackageName(), "App2"))
+                .setCreateTask(true)
+                .setStack(fullscreenStack2)
+                .build();
+        doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation();
+
+        // Start the recents animation
+        Intent recentsIntent = new Intent();
+        recentsIntent.setComponent(mRecentsComponent);
+        mService.startRecentsActivity(recentsIntent, null, mock(IRecentsAnimationRunner.class));
+
+        fullscreenStack.remove();
+
+        // Ensure that the recents animation was NOT canceled
+        verify(mService.mWindowManager, times(0)).cancelRecentsAnimationSynchronously(
+                eq(REORDER_KEEP_IN_PLACE), any());
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index ad2a708..413b6f4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -143,8 +143,8 @@
 
     @Test
     public void testTimeout_scaled() throws Exception {
-        mWm.setAnimationScale(2, 5.0f);
         try {
+            mWm.setAnimationScale(2, 5.0f);
             final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
                     "testWin");
             final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
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 9b18388..58302d6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -23,8 +23,8 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
-import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
 import static android.view.Display.DEFAULT_DISPLAY;
+
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -36,7 +36,7 @@
 import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
-import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -47,18 +47,27 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.contains;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.refEq;
 
 import android.app.ActivityOptions;
+import android.content.ComponentName;
+import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
 import android.graphics.Rect;
-import android.os.Build;
 import android.platform.test.annotations.Presubmit;
+import android.util.Pair;
+
 import androidx.test.filters.MediumTest;
+
+import com.android.internal.app.ResolverActivity;
+
 import org.junit.Before;
 import org.junit.Test;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Tests for the {@link ActivityStackSupervisor} class.
@@ -385,31 +394,10 @@
     }
 
     /**
-     * Tests home activities that targeted sdk before Q cannot start on secondary display.
-     */
-    @Test
-    public void testStartHomeTargetSdkBeforeQ() throws Exception {
-        final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
-        mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
-        doReturn(true).when(secondDisplay).supportsSystemDecorations();
-
-        final ActivityInfo info = new ActivityInfo();
-        info.launchMode = LAUNCH_MULTIPLE;
-        info.applicationInfo = new ApplicationInfo();
-        info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
-        assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
-                false /* allowInstrumenting */));
-
-        info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.P;
-        assertFalse(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
-                false /* allowInstrumenting */));
-    }
-
-    /**
      * Tests that home activities can be started on the displays that supports system decorations.
      */
-    @Test
-    public void testStartHomeOnAllDisplays() {
+    // TODO (b/118206886): Will add it back once launcher's patch is merged into master.
+    private void testStartHomeOnAllDisplays() {
         // Create secondary displays.
         final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
         mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
@@ -477,4 +465,142 @@
         assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
                 true /* allowInstrumenting*/));
     }
+
+    /**
+     * Tests that secondary home should be selected if default home not set.
+     */
+    @Test
+    public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSet() {
+        final Intent defaultHomeIntent = mService.getHomeIntent();
+        final ActivityInfo aInfoDefault = new ActivityInfo();
+        aInfoDefault.name = ResolverActivity.class.getName();
+        doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+                refEq(defaultHomeIntent));
+
+        final String secondaryHomeComponent = mService.mContext.getResources().getString(
+                com.android.internal.R.string.config_secondaryHomeComponent);
+        final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent);
+        final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
+        final ActivityInfo aInfoSecondary = new ActivityInfo();
+        aInfoSecondary.name = comp.getClassName();
+        doReturn(aInfoSecondary).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+                refEq(secondaryHomeIntent));
+
+        // Should fallback to secondary home if default home not set.
+        final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
+                .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
+
+        assertEquals(comp.getClassName(), resolvedInfo.first.name);
+    }
+
+    /**
+     * Tests that secondary home should be selected if default home not support secondary displays
+     * or there is no matched activity in the same package as selected default home.
+     */
+    @Test
+    public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSupportMultiDisplay() {
+        final Intent defaultHomeIntent = mService.getHomeIntent();
+        final ActivityInfo aInfoDefault = new ActivityInfo();
+        aInfoDefault.name = "fakeHomeActivity";
+        aInfoDefault.applicationInfo = new ApplicationInfo();
+        aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
+        doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+                refEq(defaultHomeIntent));
+
+        final List<ResolveInfo> resolutions = new ArrayList<>();
+        doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any());
+
+        final String secondaryHomeComponent = mService.mContext.getResources().getString(
+                com.android.internal.R.string.config_secondaryHomeComponent);
+        final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent);
+        final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
+        final ActivityInfo aInfoSecondary = new ActivityInfo();
+        aInfoSecondary.name = comp.getClassName();
+        doReturn(aInfoSecondary).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+                refEq(secondaryHomeIntent));
+
+        // Should fallback to secondary home if selected default home not support secondary displays
+        // or there is no matched activity in the same package as selected default home.
+        final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
+                .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
+
+        assertEquals(comp.getClassName(), resolvedInfo.first.name);
+    }
+
+    /**
+     * Tests that default home activity should be selected if it already support secondary displays.
+     */
+    @Test
+    public void testResolveSecondaryHomeActivityWhenDefaultHomeSupportMultiDisplay() {
+        final Intent homeIntent = mService.getHomeIntent();
+        final ActivityInfo aInfoDefault = new ActivityInfo();
+        aInfoDefault.name = "fakeHomeActivity";
+        aInfoDefault.applicationInfo = new ApplicationInfo();
+        aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
+        doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+                refEq(homeIntent));
+
+        final List<ResolveInfo> resolutions = new ArrayList<>();
+        final ResolveInfo infoFake1 = new ResolveInfo();
+        infoFake1.activityInfo = new ActivityInfo();
+        infoFake1.activityInfo.name = "fakeActivity1";
+        infoFake1.activityInfo.applicationInfo = new ApplicationInfo();
+        infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1";
+        final ResolveInfo infoFake2 = new ResolveInfo();
+        infoFake2.activityInfo = aInfoDefault;
+        resolutions.add(infoFake1);
+        resolutions.add(infoFake2);
+        doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any());
+
+        doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
+                any(), anyInt(), anyBoolean());
+
+        // Use default home activity if it support secondary displays.
+        final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
+                .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
+
+        assertEquals(aInfoDefault.applicationInfo.packageName,
+                resolvedInfo.first.applicationInfo.packageName);
+        assertEquals(aInfoDefault.name, resolvedInfo.first.name);
+    }
+
+    /**
+     * Tests that the first one that matches should be selected if there are multiple activities.
+     */
+    @Test
+    public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() {
+        final Intent homeIntent = mService.getHomeIntent();
+        final ActivityInfo aInfoDefault = new ActivityInfo();
+        aInfoDefault.name = "fakeHomeActivity";
+        aInfoDefault.applicationInfo = new ApplicationInfo();
+        aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
+        doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+                refEq(homeIntent));
+
+        final List<ResolveInfo> resolutions = new ArrayList<>();
+        final ResolveInfo infoFake1 = new ResolveInfo();
+        infoFake1.activityInfo = new ActivityInfo();
+        infoFake1.activityInfo.name = "fakeActivity1";
+        infoFake1.activityInfo.applicationInfo = new ApplicationInfo();
+        infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1";
+        final ResolveInfo infoFake2 = new ResolveInfo();
+        infoFake2.activityInfo = new ActivityInfo();
+        infoFake2.activityInfo.name = "fakeActivity2";
+        infoFake2.activityInfo.applicationInfo = new ApplicationInfo();
+        infoFake2.activityInfo.applicationInfo.packageName = "fakePackage2";
+        resolutions.add(infoFake1);
+        resolutions.add(infoFake2);
+        doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any());
+
+        doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
+                any(), anyInt(), anyBoolean());
+
+        // Use the first one of matched activities in the same package as selected default home.
+        final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
+                .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
+
+        assertEquals(infoFake1.activityInfo.applicationInfo.packageName,
+                resolvedInfo.first.applicationInfo.packageName);
+        assertEquals(infoFake1.activityInfo.name, resolvedInfo.first.name);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
index 36eccd1..03aba39 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -33,6 +33,8 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
@@ -58,7 +60,6 @@
 import android.view.WindowManager;
 import android.widget.TextView;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.After;
@@ -80,8 +81,8 @@
 @Presubmit
 public class ScreenDecorWindowTests {
 
-    private final Context mContext = InstrumentationRegistry.getTargetContext();
-    private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    private final Context mContext = getInstrumentation().getTargetContext();
+    private final Instrumentation mInstrumentation = getInstrumentation();
 
     private WindowManager mWm;
     private ArrayList<View> mWindows = new ArrayList<>();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
index 83e7ee7..dfdbf32 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
@@ -49,12 +49,9 @@
 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
 
 import java.util.concurrent.CountDownLatch;
 
@@ -72,7 +69,6 @@
     @Mock Transaction mMockTransaction;
     @Mock AnimationSpec mMockAnimationSpec;
     @Mock PowerManagerInternal mMockPowerManager;
-    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
     private SurfaceAnimationRunner mSurfaceAnimationRunner;
     private CountDownLatch mFinishCallbackLatch;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index fe632d4..53e99fa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -47,7 +47,6 @@
 import android.view.Display;
 import android.view.Gravity;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import com.android.server.wm.LaunchParamsController.LaunchParams;
@@ -64,7 +63,6 @@
  *  atest WmTests:TaskLaunchParamsModifierTests
  */
 @SmallTest
-@FlakyTest(detail = "Confirm stable in post-submit before removing")
 @Presubmit
 public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
 
@@ -173,6 +171,23 @@
     }
 
     @Test
+    public void testUsesTasksDisplayIdPriorToSourceIfSet() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+        final TestActivityDisplay fullscreenDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FULLSCREEN);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+        ActivityRecord reusableActivity = createSourceActivity(fullscreenDisplay);
+        ActivityRecord source = createSourceActivity(freeformDisplay);
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(reusableActivity.getTaskRecord(),
+                /* layout */ null, mActivity, source, /* options */ null, mCurrent, mResult));
+
+        assertEquals(fullscreenDisplay.mDisplayId, mResult.mPreferredDisplayId);
+    }
+
+    @Test
     public void testUsesTaskDisplayIdIfSet() {
         final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
                 WINDOWING_MODE_FREEFORM);
@@ -184,6 +199,25 @@
         assertEquals(freeformDisplay.mDisplayId, mResult.mPreferredDisplayId);
     }
 
+    @Test
+    public void testUsesNoDisplaySourceHandoverDisplayIdIfSet() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+        final TestActivityDisplay fullscreenDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FULLSCREEN);
+
+        mCurrent.mPreferredDisplayId = fullscreenDisplay.mDisplayId;
+        ActivityRecord reusableActivity = createSourceActivity(fullscreenDisplay);
+        ActivityRecord source = createSourceActivity(freeformDisplay);
+        source.mHandoverLaunchDisplayId = freeformDisplay.mDisplayId;
+        source.noDisplay = true;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(reusableActivity.getTaskRecord(),
+                null /* layout */, mActivity, source, null /* options */, mCurrent, mResult));
+
+        assertEquals(freeformDisplay.mDisplayId, mResult.mPreferredDisplayId);
+    }
+
     // =====================================
     // Launch Windowing Mode Related Tests
     // =====================================
@@ -753,6 +787,21 @@
     }
 
     @Test
+    public void testReturnBoundsForFullscreenWindowingMode() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+        mCurrent.mWindowingMode = WINDOWING_MODE_FULLSCREEN;
+        mCurrent.mBounds.set(0, 0, 200, 100);
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquals(new Rect(0, 0, 200, 100), mResult.mBounds);
+    }
+
+    @Test
     public void testUsesDisplayOrientationForNoSensorOrientation() {
         final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
                 WINDOWING_MODE_FREEFORM);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
index 3991e06..c343fe7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -20,6 +20,7 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 
 import static org.junit.Assert.assertFalse;
@@ -66,6 +67,10 @@
         synchronized (mWm.mGlobalLock) {
             mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
         }
+
+        spyOn(mDisplayContent);
+        InputMonitor inputMonitor = mock(InputMonitor.class);
+        when(mDisplayContent.getInputMonitor()).thenReturn(inputMonitor);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 6b6b33c..00bec3f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -21,6 +21,11 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static org.hamcrest.Matchers.not;
 import static org.hamcrest.Matchers.sameInstance;
@@ -34,6 +39,7 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
@@ -132,17 +138,6 @@
         assertTrue(task.returnsToHomeStack());
     }
 
-    /** Ensures that bounds are clipped to their parent. */
-    @Test
-    public void testAppBounds_BoundsClipping() {
-        final Rect shiftedBounds = new Rect(mParentBounds);
-        shiftedBounds.offset(10, 10);
-        final Rect expectedBounds = new Rect(mParentBounds);
-        expectedBounds.intersect(shiftedBounds);
-        testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, shiftedBounds,
-                expectedBounds);
-    }
-
     /** Ensures that empty bounds are not propagated to the configuration. */
     @Test
     public void testAppBounds_EmptyBounds() {
@@ -166,18 +161,126 @@
         final Rect insetBounds = new Rect(mParentBounds);
         insetBounds.inset(5, 5, 5, 5);
         testStackBoundsConfiguration(
-                WINDOWING_MODE_FULLSCREEN, mParentBounds, insetBounds, insetBounds);
+                WINDOWING_MODE_FREEFORM, mParentBounds, insetBounds, insetBounds);
     }
 
-    /** Ensures that full screen free form bounds are clipped */
+    /** Tests that the task bounds adjust properly to changes between FULLSCREEN and FREEFORM */
     @Test
-    public void testAppBounds_FullScreenFreeFormBounds() {
+    public void testBoundsOnModeChangeFreeformToFullscreen() {
         ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
+        ActivityStack stack = new StackBuilder(mRootActivityContainer).setDisplay(display)
+                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        TaskRecord task = stack.getChildAt(0);
+        task.getRootActivity().mAppWindowToken.setOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
         DisplayInfo info = new DisplayInfo();
         display.mDisplay.getDisplayInfo(info);
         final Rect fullScreenBounds = new Rect(0, 0, info.logicalWidth, info.logicalHeight);
-        testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, fullScreenBounds,
-                mParentBounds);
+        final Rect freeformBounds = new Rect(fullScreenBounds);
+        freeformBounds.inset((int) (freeformBounds.width() * 0.2),
+                (int) (freeformBounds.height() * 0.2));
+        task.setBounds(freeformBounds);
+
+        assertEquals(freeformBounds, task.getBounds());
+
+        // FULLSCREEN inherits bounds
+        stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        assertEquals(fullScreenBounds, task.getBounds());
+        assertEquals(freeformBounds, task.mLastNonFullscreenBounds);
+
+        // FREEFORM restores bounds
+        stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        assertEquals(freeformBounds, task.getBounds());
+    }
+
+    /**
+     * This is a temporary hack to trigger an onConfigurationChange at the task level after an
+     * orientation is requested. Normally this is done by the onDescendentOrientationChanged call
+     * up the WM hierarchy, but since the WM hierarchy is mocked out, it doesn't happen here.
+     * TODO: remove this when we either get a WM hierarchy or when hierarchies are merged.
+     */
+    private void setActivityRequestedOrientation(ActivityRecord activity, int orientation) {
+        activity.setRequestedOrientation(orientation);
+        ConfigurationContainer taskRecord = activity.getParent();
+        taskRecord.onConfigurationChanged(taskRecord.getParent().getConfiguration());
+    }
+
+    /**
+     * Tests that a task with forced orientation has orientation-consistent bounds within the
+     * parent.
+     */
+    @Test
+    public void testFullscreenBoundsForcedOrientation() {
+        final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
+        final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920);
+        DisplayInfo info = new DisplayInfo();
+        info.logicalWidth = fullScreenBounds.width();
+        info.logicalHeight = fullScreenBounds.height();
+        ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP);
+        assertTrue(mRootActivityContainer.getActivityDisplay(display.mDisplayId) != null);
+        ActivityStack stack = new StackBuilder(mRootActivityContainer)
+                .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
+        TaskRecord task = stack.getChildAt(0);
+        ActivityRecord root = task.getRootActivity();
+        ActivityRecord top = new ActivityBuilder(mService).setTask(task).setStack(stack).build();
+        assertEquals(root, task.getRootActivity());
+
+        assertEquals(fullScreenBounds, task.getBounds());
+
+        // Setting app to fixed portrait fits within parent
+        setActivityRequestedOrientation(root, SCREEN_ORIENTATION_PORTRAIT);
+        assertEquals(root, task.getRootActivity());
+        assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
+        assertTrue(task.getBounds().width() < task.getBounds().height());
+        assertEquals(fullScreenBounds.height(), task.getBounds().height());
+
+        // Setting non-root app has no effect
+        setActivityRequestedOrientation(root, SCREEN_ORIENTATION_LANDSCAPE);
+        assertTrue(task.getBounds().width() < task.getBounds().height());
+
+        // Setting app to unspecified restores
+        setActivityRequestedOrientation(root, SCREEN_ORIENTATION_UNSPECIFIED);
+        assertEquals(fullScreenBounds, task.getBounds());
+
+        // Setting app to fixed landscape and changing display
+        setActivityRequestedOrientation(root, SCREEN_ORIENTATION_LANDSCAPE);
+        display.setBounds(fullScreenBoundsPort);
+        assertTrue(task.getBounds().width() > task.getBounds().height());
+        assertEquals(fullScreenBoundsPort.width(), task.getBounds().width());
+
+        // in FREEFORM, no constraint
+        final Rect freeformBounds = new Rect(display.getBounds());
+        freeformBounds.inset((int) (freeformBounds.width() * 0.2),
+                (int) (freeformBounds.height() * 0.2));
+        stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        task.setBounds(freeformBounds);
+        assertEquals(freeformBounds, task.getBounds());
+
+        // FULLSCREEN letterboxes bounds
+        stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        assertTrue(task.getBounds().width() > task.getBounds().height());
+        assertEquals(fullScreenBoundsPort.width(), task.getBounds().width());
+
+        // FREEFORM restores bounds as before
+        stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        assertEquals(freeformBounds, task.getBounds());
+    }
+
+    /** Ensures that the alias intent won't have target component resolved. */
+    @Test
+    public void testTaskIntentActivityAlias() {
+        final String aliasActivity = DEFAULT_COMPONENT_PACKAGE_NAME + ".aliasActivity";
+        final String targetActivity = DEFAULT_COMPONENT_PACKAGE_NAME + ".targetActivity";
+        final Intent intent = new Intent();
+        intent.setComponent(new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, aliasActivity));
+        final ActivityInfo info = new ActivityInfo();
+        info.applicationInfo = new ApplicationInfo();
+        info.packageName = DEFAULT_COMPONENT_PACKAGE_NAME;
+        info.targetActivity = targetActivity;
+
+        final TaskRecord task = TaskRecord.create(mService, 1 /* taskId */, info, intent,
+                null /* taskDescription */);
+        assertEquals("The alias activity component should be saved in task intent.", aliasActivity,
+                task.intent.getComponent().getClassName());
     }
 
     private void testStackBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds,
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index 624ef9b..ca815ec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -39,6 +39,7 @@
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.view.Surface;
+import android.view.SurfaceControl;
 
 import androidx.test.filters.SmallTest;
 
@@ -65,7 +66,7 @@
         final TaskSnapshot snapshot = new TaskSnapshot(new ComponentName("", ""), buffer,
                 ORIENTATION_PORTRAIT, contentInsets, false, 1.0f, true /* isRealSnapshot */,
                 WINDOWING_MODE_FULLSCREEN, 0 /* systemUiVisibility */, false /* isTranslucent */);
-        mSurface = new TaskSnapshotSurface(mWm, new Window(), new Surface(), snapshot, "Test",
+        mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test",
                 Color.WHITE, Color.RED, Color.BLUE, sysuiVis, windowFlags, 0, taskBounds,
                 ORIENTATION_PORTRAIT);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index 29738ff..c5df85c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -64,8 +64,7 @@
     }
 
     @Override
-    public void windowFocusChanged(boolean hasFocus, boolean inTouchMode, boolean reportToClient)
-            throws RemoteException {
+    public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) throws RemoteException {
     }
 
     @Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index ba81bd1..d1fe48a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -170,6 +170,10 @@
     }
 
     @Override
+    public void setTopFocusedDisplay(int displayId) {
+    }
+
+    @Override
     public void applyKeyguardPolicyLw(WindowState win, WindowState imeTarget) {
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 60f957f..e156143 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -27,6 +27,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
@@ -38,8 +39,10 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
@@ -559,8 +562,7 @@
         builder.setLayer(2).setIsVisible(true);
         final TestWindowContainer visibleUnspecifiedRootChildChildFillsParent =
                 visibleUnspecifiedRootChild.addChildWindow(builder);
-        visibleUnspecifiedRootChildChildFillsParent.setOrientation(
-                SCREEN_ORIENTATION_PORTRAIT);
+        visibleUnspecifiedRootChildChildFillsParent.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
         assertEquals(SCREEN_ORIENTATION_PORTRAIT,
                 visibleUnspecifiedRootChildChildFillsParent.getOrientation());
         assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnspecifiedRootChild.getOrientation());
@@ -724,6 +726,19 @@
         verify(grandChild, times(1)).onParentResize();
     }
 
+    @Test
+    public void testOnDescendantOrientationRequestChangedPropagation() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
+        final TestWindowContainer root = spy(builder.build());
+
+        final IBinder binder = mock(IBinder.class);
+        final ActivityRecord activityRecord = mock(ActivityRecord.class);
+        final TestWindowContainer child = root.addChildWindow();
+
+        child.setOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED, binder, activityRecord);
+        verify(root).onDescendantOrientationChanged(binder, activityRecord);
+    }
+
     /* Used so we can gain access to some protected members of the {@link WindowContainer} class */
     private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
         private final int mLayer;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
deleted file mode 100644
index 522ab9f..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManagerInternal;
-import android.content.Context;
-import android.hardware.display.DisplayManagerInternal;
-import android.os.PowerManagerInternal;
-import android.os.PowerSaveState;
-import android.view.Display;
-import android.view.InputChannel;
-import android.view.SurfaceControl;
-import android.view.SurfaceControl.Transaction;
-
-import com.android.server.LocalServices;
-import com.android.server.input.InputManagerService;
-import com.android.server.policy.WindowManagerPolicy;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-import org.mockito.invocation.InvocationOnMock;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A test rule that sets up a fresh WindowManagerService instance before each test and makes sure
- * to properly tear it down after.
- *
- * <p>
- * Usage:
- * <pre>
- * {@literal @}Rule
- *  public final WindowManagerServiceRule mWmRule = new WindowManagerServiceRule();
- * </pre>
- */
-public class WindowManagerServiceRule implements TestRule {
-
-    private WindowManagerService mService;
-    private TestWindowManagerPolicy mPolicy;
-    // Record all {@link SurfaceControl.Transaction} created while testing and releases native
-    // resources when test finishes.
-    private final List<WeakReference<Transaction>> mSurfaceTransactions = new ArrayList<>();
-    // Record all {@link SurfaceControl} created while testing and releases native resources when
-    // test finishes.
-    private final List<WeakReference<SurfaceControl>> mSurfaceControls = new ArrayList<>();
-
-    @Override
-    public Statement apply(Statement base, Description description) {
-        return new Statement() {
-            @Override
-            public void evaluate() throws Throwable {
-                runWithDexmakerShareClassLoader(this::setUp);
-                try {
-                    base.evaluate();
-                } finally {
-                    tearDown();
-                }
-            }
-
-            private void setUp() {
-                final Context context = getInstrumentation().getTargetContext();
-
-                removeServices();
-
-                LocalServices.addService(DisplayManagerInternal.class,
-                        mock(DisplayManagerInternal.class));
-
-                LocalServices.addService(PowerManagerInternal.class,
-                        mock(PowerManagerInternal.class));
-                final PowerManagerInternal pm =
-                        LocalServices.getService(PowerManagerInternal.class);
-                doNothing().when(pm).registerLowPowerModeObserver(any());
-                PowerSaveState state = new PowerSaveState.Builder().build();
-                doReturn(state).when(pm).getLowPowerState(anyInt());
-
-                LocalServices.addService(ActivityManagerInternal.class,
-                        mock(ActivityManagerInternal.class));
-                LocalServices.addService(ActivityTaskManagerInternal.class,
-                        mock(ActivityTaskManagerInternal.class));
-                final ActivityTaskManagerInternal atm =
-                        LocalServices.getService(ActivityTaskManagerInternal.class);
-                doAnswer((InvocationOnMock invocationOnMock) -> {
-                    final Runnable runnable = invocationOnMock.<Runnable>getArgument(0);
-                    if (runnable != null) {
-                        runnable.run();
-                    }
-                    return null;
-                }).when(atm).notifyKeyguardFlagsChanged(any(), anyInt());
-
-                InputManagerService ims = mock(InputManagerService.class);
-                // InputChannel is final and can't be mocked.
-                InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM);
-                if (input != null && input.length > 1) {
-                    doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt());
-                }
-                ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class);
-                when(atms.getGlobalLock()).thenReturn(new WindowManagerGlobalLock());
-
-                mService = WindowManagerService.main(context, ims, false, false,
-                        mPolicy = new TestWindowManagerPolicy(
-                                WindowManagerServiceRule.this::getWindowManagerService), atms);
-                mService.mTransactionFactory = () -> {
-                    final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
-                    mSurfaceTransactions.add(new WeakReference<>(transaction));
-                    return transaction;
-                };
-                mService.mSurfaceBuilderFactory = session -> new SurfaceControl.Builder(session) {
-                    @Override
-                    public SurfaceControl build() {
-                        final SurfaceControl control = super.build();
-                        mSurfaceControls.add(new WeakReference<>(control));
-                        return control;
-                    }
-                };
-
-                mService.onInitReady();
-
-                final Display display = mService.mDisplayManager.getDisplay(DEFAULT_DISPLAY);
-                // Display creation is driven by the ActivityManagerService via
-                // ActivityStackSupervisor. We emulate those steps here.
-                mService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class));
-            }
-
-            private void removeServices() {
-                LocalServices.removeServiceForTest(DisplayManagerInternal.class);
-                LocalServices.removeServiceForTest(PowerManagerInternal.class);
-                LocalServices.removeServiceForTest(ActivityManagerInternal.class);
-                LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
-                LocalServices.removeServiceForTest(WindowManagerInternal.class);
-                LocalServices.removeServiceForTest(WindowManagerPolicy.class);
-            }
-
-            private void tearDown() {
-                waitUntilWindowManagerHandlersIdle();
-                destroyAllSurfaceTransactions();
-                destroyAllSurfaceControls();
-                removeServices();
-                mService = null;
-                mPolicy = null;
-            }
-        };
-    }
-
-    WindowManagerService getWindowManagerService() {
-        return mService;
-    }
-
-    void waitUntilWindowManagerHandlersIdle() {
-        final WindowManagerService wm = getWindowManagerService();
-        if (wm == null) {
-            return;
-        }
-        wm.mH.removeCallbacksAndMessages(null);
-        wm.mAnimationHandler.removeCallbacksAndMessages(null);
-        SurfaceAnimationThread.getHandler().removeCallbacksAndMessages(null);
-        wm.mH.runWithScissors(() -> { }, 0);
-        wm.mAnimationHandler.runWithScissors(() -> { }, 0);
-        SurfaceAnimationThread.getHandler().runWithScissors(() -> { }, 0);
-    }
-
-    private void destroyAllSurfaceTransactions() {
-        for (final WeakReference<Transaction> reference : mSurfaceTransactions) {
-            final Transaction transaction = reference.get();
-            if (transaction != null) {
-                reference.clear();
-                transaction.close();
-            }
-        }
-    }
-
-    private void destroyAllSurfaceControls() {
-        for (final WeakReference<SurfaceControl> reference : mSurfaceControls) {
-            final SurfaceControl control = reference.get();
-            if (control != null) {
-                reference.clear();
-                control.destroy();
-            }
-        }
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
deleted file mode 100644
index 343d359..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.junit.Assert.assertThat;
-
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Rule;
-import org.junit.Test;
-
-/**
- * Build/InstallRun:
- *  atest FrameworksServicesTests:WindowManagerServiceRuleTest
- */
-@Presubmit
-@SmallTest
-public class WindowManagerServiceRuleTest {
-
-    @Rule
-    public final WindowManagerServiceRule mRule = new WindowManagerServiceRule();
-
-    @Test
-    public void testWindowManagerSetUp() {
-        assertThat(mRule.getWindowManagerService(), notNullValue());
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 7f78034..c09cd46 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.hardware.camera2.params.OutputConfiguration.ROTATION_90;
+import static android.view.InsetsState.TYPE_TOP_BAR;
 import static android.view.Surface.ROTATION_0;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -31,6 +32,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
@@ -56,6 +58,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.Size;
 import android.view.DisplayCutout;
+import android.view.InsetsSource;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 
@@ -326,6 +329,20 @@
     }
 
     @Test
+    public void testVisibleWithInsetsProvider() throws Exception {
+        final WindowState topBar = createWindow(null, TYPE_STATUS_BAR, "topBar");
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        topBar.mHasSurface = true;
+        assertTrue(topBar.isVisible());
+        mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
+                .setWindow(topBar, null);
+        mDisplayContent.getInsetsStateController().onBarControllingWindowChanged(app);
+        mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
+                .onInsetsModified(app, new InsetsSource(TYPE_TOP_BAR));
+        assertFalse(topBar.isVisible());
+    }
+
+    @Test
     public void testIsSelfOrAncestorWindowAnimating() {
         final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
         final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1");
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index 65cde77..20fc5ae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -30,6 +30,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.Binder;
+import android.os.Build;
 import android.os.IBinder;
 import android.view.Display;
 import android.view.IApplicationToken;
@@ -161,6 +162,7 @@
                     return null;
                 }
             }, new ComponentName("", ""), false, dc, true /* fillsParent */);
+            mTargetSdk = Build.VERSION_CODES.CUR_DEVELOPMENT;
         }
 
         TestAppWindowToken(WindowManagerService service, IApplicationToken token,
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 5c3368b..3813891 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -53,6 +53,7 @@
 import com.android.server.AttributeCache;
 
 import org.junit.After;
+import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Rule;
@@ -95,17 +96,22 @@
     public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
             new DexmakerShareClassLoaderRule();
 
-    @Rule
-    public final WindowManagerServiceRule mWmRule = new WindowManagerServiceRule();
-
-    static WindowState.PowerManagerWrapper sPowerManagerWrapper;  // TODO(roosa): make non-static.
+    static WindowState.PowerManagerWrapper sPowerManagerWrapper;
 
     @BeforeClass
     public static void setUpOnceBase() {
         AttributeCache.init(getInstrumentation().getTargetContext());
+
+        WmServiceUtils.setUpWindowManagerService();
+
         sPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class);
     }
 
+    @AfterClass
+    public static void tearDonwOnceBase() {
+        WmServiceUtils.tearDownWindowManagerService();
+    }
+
     @Before
     public void setUpBase() {
         // If @Before throws an exception, the error isn't logged. This will make sure any failures
@@ -115,7 +121,7 @@
 
             final Context context = getInstrumentation().getTargetContext();
 
-            mWm = mWmRule.getWindowManagerService();
+            mWm = WmServiceUtils.getWindowManagerService();
             beforeCreateDisplay();
 
             context.getDisplay().getDisplayInfo(mDisplayInfo);
@@ -214,7 +220,7 @@
      * Waits until the main handler for WM has processed all messages.
      */
     void waitUntilHandlersIdle() {
-        mWmRule.waitUntilWindowManagerHandlersIdle();
+        WmServiceUtils.waitUntilWindowManagerHandlersIdle();
     }
 
     private WindowToken createWindowToken(
diff --git a/services/tests/wmtests/src/com/android/server/wm/WmServiceUtils.java b/services/tests/wmtests/src/com/android/server/wm/WmServiceUtils.java
new file mode 100644
index 0000000..2465e5d8
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/WmServiceUtils.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.nullable;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.app.ActivityManagerInternal;
+import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.hardware.display.DisplayManagerInternal;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
+import android.os.UserHandle;
+import android.view.Display;
+import android.view.InputChannel;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.server.LocalServices;
+import com.android.server.LockGuard;
+import com.android.server.Watchdog;
+import com.android.server.input.InputManagerService;
+import com.android.server.policy.WindowManagerPolicy;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.quality.Strictness;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * A Test utility class to create a mock {@link WindowManagerService} instance for tests.
+ */
+class WmServiceUtils {
+    private static StaticMockitoSession sMockitoSession;
+    private static WindowManagerService sService;
+    private static TestWindowManagerPolicy sPolicy;
+
+    static void setUpWindowManagerService() {
+        sMockitoSession = mockitoSession()
+                .spyStatic(LockGuard.class)
+                .spyStatic(Watchdog.class)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
+
+        runWithDexmakerShareClassLoader(WmServiceUtils::setUpTestWindowService);
+    }
+
+    static void tearDownWindowManagerService() {
+        waitUntilWindowManagerHandlersIdle();
+        removeLocalServices();
+        sService = null;
+        sPolicy = null;
+
+        sMockitoSession.finishMocking();
+    }
+
+    private static void setUpTestWindowService() {
+        doReturn(null).when(() -> LockGuard.installLock(any(), anyInt()));
+        doReturn(mock(Watchdog.class)).when(Watchdog::getInstance);
+
+        final Context context = getInstrumentation().getTargetContext();
+        spyOn(context);
+
+        doReturn(null).when(context)
+                .registerReceiver(nullable(BroadcastReceiver.class), any(IntentFilter.class));
+        doReturn(null).when(context)
+                .registerReceiverAsUser(any(BroadcastReceiver.class), any(UserHandle.class),
+                        any(IntentFilter.class), nullable(String.class), nullable(Handler.class));
+
+        final ContentResolver contentResolver = context.getContentResolver();
+        spyOn(contentResolver);
+        doNothing().when(contentResolver)
+                .registerContentObserver(any(Uri.class), anyBoolean(), any(ContentObserver.class),
+                        anyInt());
+
+        final AppOpsManager appOpsManager = mock(AppOpsManager.class);
+        doReturn(appOpsManager).when(context)
+                .getSystemService(eq(Context.APP_OPS_SERVICE));
+
+        removeLocalServices();
+
+        final DisplayManagerInternal dmi = mock(DisplayManagerInternal.class);
+        LocalServices.addService(DisplayManagerInternal.class, dmi);
+
+        final PowerManagerInternal pmi = mock(PowerManagerInternal.class);
+        LocalServices.addService(PowerManagerInternal.class, pmi);
+        final PowerSaveState state = new PowerSaveState.Builder().build();
+        doReturn(state).when(pmi).getLowPowerState(anyInt());
+
+        final ActivityManagerInternal ami = mock(ActivityManagerInternal.class);
+        LocalServices.addService(ActivityManagerInternal.class, ami);
+
+        final ActivityTaskManagerInternal atmi = mock(ActivityTaskManagerInternal.class);
+        LocalServices.addService(ActivityTaskManagerInternal.class, atmi);
+        doAnswer((InvocationOnMock invocationOnMock) -> {
+            final Runnable runnable = invocationOnMock.getArgument(0);
+            if (runnable != null) {
+                runnable.run();
+            }
+            return null;
+        }).when(atmi).notifyKeyguardFlagsChanged(nullable(Runnable.class), anyInt());
+
+        final InputManagerService ims = mock(InputManagerService.class);
+        // InputChannel is final and can't be mocked.
+        final InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM);
+        if (input != null && input.length > 1) {
+            doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt());
+        }
+
+        final ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class);
+        final WindowManagerGlobalLock wmLock = new WindowManagerGlobalLock();
+        doReturn(wmLock).when(atms).getGlobalLock();
+
+        sPolicy = new TestWindowManagerPolicy(WmServiceUtils::getWindowManagerService);
+        sService = WindowManagerService.main(context, ims, false, false, sPolicy, atms);
+
+        sService.onInitReady();
+
+        final Display display = sService.mDisplayManager.getDisplay(DEFAULT_DISPLAY);
+        // Display creation is driven by the ActivityManagerService via
+        // ActivityStackSupervisor. We emulate those steps here.
+        sService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class));
+    }
+
+    private static void removeLocalServices() {
+        LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+        LocalServices.removeServiceForTest(PowerManagerInternal.class);
+        LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+        LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
+        LocalServices.removeServiceForTest(WindowManagerInternal.class);
+        LocalServices.removeServiceForTest(WindowManagerPolicy.class);
+    }
+
+    static WindowManagerService getWindowManagerService() {
+        return sService;
+    }
+
+    static void waitUntilWindowManagerHandlersIdle() {
+        final WindowManagerService wm = getWindowManagerService();
+        if (wm == null) {
+            return;
+        }
+        // Removing delayed FORCE_GC message decreases time for waiting idle.
+        wm.mH.removeMessages(WindowManagerService.H.FORCE_GC);
+        waitHandlerIdle(wm.mH);
+        waitHandlerIdle(wm.mAnimationHandler);
+        waitHandlerIdle(SurfaceAnimationThread.getHandler());
+    }
+
+    private static void waitHandlerIdle(Handler handler) {
+        if (!handler.hasMessagesOrCallbacks()) {
+            return;
+        }
+        final CountDownLatch latch = new CountDownLatch(1);
+        // Wait for delayed messages are processed.
+        handler.getLooper().getQueue().addIdleHandler(() -> {
+            if (handler.hasMessagesOrCallbacks()) {
+                return true; // keep idle handler.
+            }
+            latch.countDown();
+            return false; // remove idle handler.
+        });
+        try {
+            latch.await();
+        } catch (InterruptedException e) {
+        }
+    }
+}
diff --git a/services/usage/java/com/android/server/usage/AppTimeLimitController.java b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
index 8e1ede1..2ed11fe 100644
--- a/services/usage/java/com/android/server/usage/AppTimeLimitController.java
+++ b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
@@ -23,7 +23,6 @@
 import android.os.Message;
 import android.os.SystemClock;
 import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -62,6 +61,8 @@
 
     private static final long ONE_MINUTE = 60_000L;
 
+    private static final Integer ONE = new Integer(1);
+
     /** Collection of data for each user that has reported usage */
     @GuardedBy("mLock")
     private final SparseArray<UserData> mUsers = new SparseArray<>();
@@ -79,11 +80,11 @@
         private @UserIdInt
         int userId;
 
-        /** Set of the currently active entities */
-        private final ArraySet<String> currentlyActive = new ArraySet<>();
+        /** Count of the currently active entities */
+        public final ArrayMap<String, Integer> currentlyActive = new ArrayMap<>();
 
         /** Map from entity name for quick lookup */
-        private final ArrayMap<String, ArrayList<UsageGroup>> observedMap = new ArrayMap<>();
+        public final ArrayMap<String, ArrayList<UsageGroup>> observedMap = new ArrayMap<>();
 
         private UserData(@UserIdInt int userId) {
             this.userId = userId;
@@ -94,7 +95,7 @@
             // TODO: Consider using a bloom filter here if number of actives becomes large
             final int size = entities.length;
             for (int i = 0; i < size; i++) {
-                if (currentlyActive.contains(entities[i])) {
+                if (currentlyActive.containsKey(entities[i])) {
                     return true;
                 }
             }
@@ -137,7 +138,7 @@
             pw.print(" Currently Active:");
             final int nActive = currentlyActive.size();
             for (int i = 0; i < nActive; i++) {
-                pw.print(currentlyActive.valueAt(i));
+                pw.print(currentlyActive.keyAt(i));
                 pw.print(", ");
             }
             pw.println();
@@ -233,6 +234,7 @@
         protected long mUsageTimeMs;
         protected int mActives;
         protected long mLastKnownUsageTimeMs;
+        protected long mLastUsageEndTimeMs;
         protected WeakReference<UserData> mUserRef;
         protected WeakReference<ObserverAppData> mObserverAppRef;
         protected PendingIntent mLimitReachedCallback;
@@ -271,9 +273,15 @@
         @GuardedBy("mLock")
         void noteUsageStart(long startTimeMs, long currentTimeMs) {
             if (mActives++ == 0) {
+                // If last known usage ended after the start of this usage, there is overlap
+                // between the last usage session and this one. Avoid double counting by only
+                // counting from the end of the last session. This has a rare side effect that some
+                // usage will not be accounted for if the previous session started and stopped
+                // within this current usage.
+                startTimeMs = mLastUsageEndTimeMs > startTimeMs ? mLastUsageEndTimeMs : startTimeMs;
                 mLastKnownUsageTimeMs = startTimeMs;
                 final long timeRemaining =
-                        mTimeLimitMs - mUsageTimeMs + currentTimeMs - startTimeMs;
+                        mTimeLimitMs - mUsageTimeMs - currentTimeMs + startTimeMs;
                 if (timeRemaining > 0) {
                     if (DEBUG) {
                         Slog.d(TAG, "Posting timeout for " + mObserverId + " for "
@@ -287,7 +295,7 @@
                     mActives = mObserved.length;
                     final UserData user = mUserRef.get();
                     if (user == null) return;
-                    final Object[] array = user.currentlyActive.toArray();
+                    final Object[] array = user.currentlyActive.keySet().toArray();
                     Slog.e(TAG,
                             "Too many noted usage starts! Observed entities: " + Arrays.toString(
                                     mObserved) + "   Active Entities: " + Arrays.toString(array));
@@ -300,6 +308,8 @@
             if (--mActives == 0) {
                 final boolean limitNotCrossed = mUsageTimeMs < mTimeLimitMs;
                 mUsageTimeMs += stopTimeMs - mLastKnownUsageTimeMs;
+
+                mLastUsageEndTimeMs = stopTimeMs;
                 if (limitNotCrossed && mUsageTimeMs >= mTimeLimitMs) {
                     // Crossed the limit
                     if (DEBUG) Slog.d(TAG, "MTB informing group obs=" + mObserverId);
@@ -312,7 +322,7 @@
                     mActives = 0;
                     final UserData user = mUserRef.get();
                     if (user == null) return;
-                    final Object[] array = user.currentlyActive.toArray();
+                    final Object[] array = user.currentlyActive.keySet().toArray();
                     Slog.e(TAG,
                             "Too many noted usage stops! Observed entities: " + Arrays.toString(
                                     mObserved) + "   Active Entities: " + Arrays.toString(array));
@@ -409,7 +419,6 @@
     }
 
     class SessionUsageGroup extends UsageGroup {
-        private long mLastUsageEndTimeMs;
         private long mNewSessionThresholdMs;
         private PendingIntent mSessionEndCallback;
 
@@ -451,7 +460,6 @@
         public void noteUsageStop(long stopTimeMs) {
             super.noteUsageStop(stopTimeMs);
             if (mActives == 0) {
-                mLastUsageEndTimeMs = stopTimeMs;
                 if (mUsageTimeMs >= mTimeLimitMs) {
                     // Usage has ended. Schedule the session end callback to be triggered once
                     // the new session threshold has been reached
@@ -467,7 +475,10 @@
             UserData user = mUserRef.get();
             if (user == null) return;
             if (mListener != null) {
-                mListener.onSessionEnd(mObserverId, user.userId, mUsageTimeMs, mSessionEndCallback);
+                mListener.onSessionEnd(mObserverId,
+                                       user.userId,
+                                       mUsageTimeMs,
+                                       mSessionEndCallback);
             }
         }
 
@@ -599,7 +610,7 @@
         // TODO: Consider using a bloom filter here if number of actives becomes large
         final int size = group.mObserved.length;
         for (int i = 0; i < size; i++) {
-            if (user.currentlyActive.contains(group.mObserved[i])) {
+            if (user.currentlyActive.containsKey(group.mObserved[i])) {
                 // Entity is currently active. Start group's usage.
                 group.noteUsageStart(currentTimeMs);
             }
@@ -717,21 +728,28 @@
     /**
      * Called when an entity becomes active.
      *
-     * @param name   The entity that became active
-     * @param userId The user
+     * @param name      The entity that became active
+     * @param userId    The user
+     * @param timeAgoMs Time since usage was started
      */
-    public void noteUsageStart(String name, int userId) throws IllegalArgumentException {
+    public void noteUsageStart(String name, int userId, long timeAgoMs)
+            throws IllegalArgumentException {
         synchronized (mLock) {
             UserData user = getOrCreateUserDataLocked(userId);
             if (DEBUG) Slog.d(TAG, "Usage entity " + name + " became active");
-            if (user.currentlyActive.contains(name)) {
-                throw new IllegalArgumentException(
-                        "Unable to start usage for " + name + ", already in use");
+
+            final int index = user.currentlyActive.indexOfKey(name);
+            if (index >= 0) {
+                final Integer count = user.currentlyActive.valueAt(index);
+                if (count != null) {
+                    // There are multiple instances of this entity. Just increment the count.
+                    user.currentlyActive.setValueAt(index, count + 1);
+                    return;
+                }
             }
             final long currentTime = getUptimeMillis();
 
-            // Add to the list of active entities
-            user.currentlyActive.add(name);
+            user.currentlyActive.put(name, ONE);
 
             ArrayList<UsageGroup> groups = user.observedMap.get(name);
             if (groups == null) return;
@@ -739,12 +757,22 @@
             final int size = groups.size();
             for (int i = 0; i < size; i++) {
                 UsageGroup group = groups.get(i);
-                group.noteUsageStart(currentTime);
+                group.noteUsageStart(currentTime - timeAgoMs, currentTime);
             }
         }
     }
 
     /**
+     * Called when an entity becomes active.
+     *
+     * @param name   The entity that became active
+     * @param userId The user
+     */
+    public void noteUsageStart(String name, int userId) throws IllegalArgumentException {
+        noteUsageStart(name, userId, 0);
+    }
+
+    /**
      * Called when an entity becomes inactive.
      *
      * @param name   The entity that became inactive
@@ -754,10 +782,21 @@
         synchronized (mLock) {
             UserData user = getOrCreateUserDataLocked(userId);
             if (DEBUG) Slog.d(TAG, "Usage entity " + name + " became inactive");
-            if (!user.currentlyActive.remove(name)) {
+
+            final int index = user.currentlyActive.indexOfKey(name);
+            if (index < 0) {
                 throw new IllegalArgumentException(
                         "Unable to stop usage for " + name + ", not in use");
             }
+
+            final Integer count = user.currentlyActive.valueAt(index);
+            if (!count.equals(ONE)) {
+                // There are multiple instances of this entity. Just decrement the count.
+                user.currentlyActive.setValueAt(index, count - 1);
+                return;
+            }
+
+            user.currentlyActive.removeAt(index);
             final long currentTime = getUptimeMillis();
 
             // Check if any of the groups need to watch for this entity
@@ -769,6 +808,7 @@
                 UsageGroup group = groups.get(i);
                 group.noteUsageStop(currentTime);
             }
+
         }
     }
 
@@ -780,7 +820,8 @@
 
     @GuardedBy("mLock")
     private void postInformSessionEndListenerLocked(SessionUsageGroup group, long timeout) {
-        mHandler.sendMessageDelayed(mHandler.obtainMessage(MyHandler.MSG_INFORM_SESSION_END, group),
+        mHandler.sendMessageDelayed(
+                mHandler.obtainMessage(MyHandler.MSG_INFORM_SESSION_END, group),
                 timeout);
     }
 
@@ -800,7 +841,27 @@
         mHandler.removeMessages(MyHandler.MSG_CHECK_TIMEOUT, group);
     }
 
-    void dump(PrintWriter pw) {
+    void dump(String[] args, PrintWriter pw) {
+        if (args != null) {
+            for (int i = 0; i < args.length; i++) {
+                String arg = args[i];
+                if ("actives".equals(arg)) {
+                    synchronized (mLock) {
+                        final int nUsers = mUsers.size();
+                        for (int user = 0; user < nUsers; user++) {
+                            final ArrayMap<String, Integer> actives =
+                                    mUsers.valueAt(user).currentlyActive;
+                            final int nActive = actives.size();
+                            for (int active = 0; active < nActive; active++) {
+                                pw.println(actives.keyAt(active));
+                            }
+                        }
+                    }
+                    return;
+                }
+            }
+        }
+
         synchronized (mLock) {
             pw.println("\n  App Time Limits");
             final int nUsers = mUsers.size();
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 57dc08f..f146370 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -53,6 +53,7 @@
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.IDeviceIdleController;
 import android.os.Looper;
 import android.os.Message;
@@ -105,6 +106,8 @@
     private static final boolean ENABLE_KERNEL_UPDATES = true;
     private static final File KERNEL_COUNTER_FILE = new File("/proc/uid_procstat/set");
 
+    private static final char TOKEN_DELIMITER = '/';
+
     // Handler message types.
     static final int MSG_REPORT_EVENT = 0;
     static final int MSG_FLUSH_TO_DISK = 1;
@@ -135,6 +138,10 @@
     /** Manages app time limit observers */
     AppTimeLimitController mAppTimeLimit;
 
+    final SparseArray<ArraySet<String>> mUsageReporters = new SparseArray();
+    final SparseArray<String> mVisibleActivities = new SparseArray();
+
+
     private UsageStatsManagerInternal.AppIdleStateChangeListener mStandbyChangeListener =
             new UsageStatsManagerInternal.AppIdleStateChangeListener() {
                 @Override
@@ -270,7 +277,7 @@
                     mHandler.obtainMessage(MSG_REMOVE_USER, userId, 0).sendToTarget();
                 }
             } else if (Intent.ACTION_USER_STARTED.equals(action)) {
-                if (userId >=0) {
+                if (userId >= 0) {
                     mAppStandby.postCheckIdleStates(userId);
                 }
             }
@@ -434,17 +441,46 @@
             mAppStandby.reportEvent(event, elapsedRealtime, userId);
             switch (event.mEventType) {
                 case Event.ACTIVITY_RESUMED:
-                    try {
-                        mAppTimeLimit.noteUsageStart(event.getPackageName(), userId);
-                    } catch (IllegalArgumentException iae) {
-                        Slog.e(TAG, "Failed to note usage start", iae);
+                    synchronized (mVisibleActivities) {
+                        mVisibleActivities.put(event.mInstanceId, event.getClassName());
+                        try {
+                            mAppTimeLimit.noteUsageStart(event.getPackageName(), userId);
+                        } catch (IllegalArgumentException iae) {
+                            Slog.e(TAG, "Failed to note usage start", iae);
+                        }
                     }
                     break;
-                case Event.ACTIVITY_PAUSED:
-                    try {
-                        mAppTimeLimit.noteUsageStop(event.getPackageName(), userId);
-                    } catch (IllegalArgumentException iae) {
-                        Slog.e(TAG, "Failed to note usage stop", iae);
+                case Event.ACTIVITY_STOPPED:
+                case Event.ACTIVITY_DESTROYED:
+                    ArraySet<String> tokens;
+                    synchronized (mUsageReporters) {
+                        tokens = mUsageReporters.removeReturnOld(event.mInstanceId);
+                    }
+                    if (tokens != null) {
+                        synchronized (tokens) {
+                            final int size = tokens.size();
+                            // Stop usage on behalf of a UsageReporter that stopped
+                            for (int i = 0; i < size; i++) {
+                                final String token = tokens.valueAt(i);
+                                try {
+                                    mAppTimeLimit.noteUsageStop(
+                                            buildFullToken(event.getPackageName(), token), userId);
+                                } catch (IllegalArgumentException iae) {
+                                    Slog.w(TAG, "Failed to stop usage for during reporter death: "
+                                            + iae);
+                                }
+                            }
+                        }
+                    }
+
+                    synchronized (mVisibleActivities) {
+                        if (mVisibleActivities.removeReturnOld(event.mInstanceId) != null) {
+                            try {
+                                mAppTimeLimit.noteUsageStop(event.getPackageName(), userId);
+                            } catch (IllegalArgumentException iae) {
+                                Slog.w(TAG, "Failed to note usage stop", iae);
+                            }
+                        }
                     }
                     break;
             }
@@ -599,6 +635,14 @@
         return beginTime <= currentTime && beginTime < endTime;
     }
 
+    private String buildFullToken(String packageName, String token) {
+        final StringBuilder sb = new StringBuilder(packageName.length() + token.length() + 1);
+        sb.append(packageName);
+        sb.append(TOKEN_DELIMITER);
+        sb.append(token);
+        return sb.toString();
+    }
+
     private void flushToDiskLocked() {
         final int userCount = mUserState.size();
         for (int i = 0; i < userCount; i++) {
@@ -627,8 +671,7 @@
                     String arg = args[i];
                     if ("--checkin".equals(arg)) {
                         checkin = true;
-                    } else
-                    if ("-c".equals(arg)) {
+                    } else if ("-c".equals(arg)) {
                         compact = true;
                     } else if ("flush".equals(arg)) {
                         flushToDiskLocked();
@@ -637,6 +680,15 @@
                     } else if ("is-app-standby-enabled".equals(arg)) {
                         pw.println(mAppStandby.mAppIdleEnabled);
                         return;
+                    } else if ("apptimelimit".equals(arg)) {
+                        if (i + 1 >= args.length) {
+                            mAppTimeLimit.dump(null, pw);
+                        } else {
+                            final String[] remainingArgs =
+                                    Arrays.copyOfRange(args, i + 1, args.length);
+                            mAppTimeLimit.dump(remainingArgs, pw);
+                        }
+                        return;
                     } else if (arg != null && !arg.startsWith("-")) {
                         // Anything else that doesn't start with '-' is a pkg to filter
                         pkg = arg;
@@ -666,7 +718,7 @@
                 mAppStandby.dumpState(args, pw);
             }
 
-            mAppTimeLimit.dump(pw);
+            mAppTimeLimit.dump(null, pw);
         }
     }
 
@@ -1231,16 +1283,82 @@
             final int userId = UserHandle.getUserId(callingUid);
             final long token = Binder.clearCallingIdentity();
             try {
-                UsageStatsService.this.unregisterUsageSessionObserver(callingUid, sessionObserverId, userId);
+                UsageStatsService.this.unregisterUsageSessionObserver(callingUid, sessionObserverId,
+                        userId);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
         }
+
+        @Override
+        public void reportUsageStart(IBinder activity, String token, String callingPackage) {
+            reportPastUsageStart(activity, token, 0, callingPackage);
+        }
+
+        @Override
+        public void reportPastUsageStart(IBinder activity, String token, long timeAgoMs,
+                String callingPackage) {
+
+            final int callingUid = Binder.getCallingUid();
+            final int userId = UserHandle.getUserId(callingUid);
+            final long binderToken = Binder.clearCallingIdentity();
+            try {
+                ArraySet<String> tokens;
+                synchronized (mUsageReporters) {
+                    tokens = mUsageReporters.get(activity.hashCode());
+                    if (tokens == null) {
+                        tokens = new ArraySet();
+                        mUsageReporters.put(activity.hashCode(), tokens);
+                    }
+                }
+
+                synchronized (tokens) {
+                    if (!tokens.add(token)) {
+                        throw new IllegalArgumentException(token + " for " + callingPackage
+                                + " is already reported as started for this activity");
+                    }
+                }
+
+                mAppTimeLimit.noteUsageStart(buildFullToken(callingPackage, token),
+                        userId, timeAgoMs);
+            } finally {
+                Binder.restoreCallingIdentity(binderToken);
+            }
+        }
+
+        @Override
+        public void reportUsageStop(IBinder activity, String token, String callingPackage) {
+            final int callingUid = Binder.getCallingUid();
+            final int userId = UserHandle.getUserId(callingUid);
+            final long binderToken = Binder.clearCallingIdentity();
+            try {
+                ArraySet<String> tokens;
+                synchronized (mUsageReporters) {
+                    tokens = mUsageReporters.get(activity.hashCode());
+                    if (tokens == null) {
+                        throw new IllegalArgumentException(
+                                "Unknown reporter trying to stop token " + token + " for "
+                                        + callingPackage);
+                    }
+                }
+
+                synchronized (tokens) {
+                    if (!tokens.remove(token)) {
+                        throw new IllegalArgumentException(token + " for " + callingPackage
+                                + " is already reported as stopped for this activity");
+                    }
+                }
+                mAppTimeLimit.noteUsageStop(buildFullToken(callingPackage, token), userId);
+            } finally {
+                Binder.restoreCallingIdentity(binderToken);
+            }
+        }
     }
 
     void registerAppUsageObserver(int callingUid, int observerId, String[] packages,
             long timeLimitMs, PendingIntent callbackIntent, int userId) {
-        mAppTimeLimit.addAppUsageObserver(callingUid, observerId, packages, timeLimitMs, callbackIntent,
+        mAppTimeLimit.addAppUsageObserver(callingUid, observerId, packages, timeLimitMs,
+                callbackIntent,
                 userId);
     }
 
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index ec475bf..eddf8f9 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -98,14 +98,14 @@
             stats.mLastTimeVisible = statsOut.beginTime + XmlUtils.readLongAttribute(
                     parser, LAST_TIME_VISIBLE_ATTR);
         } catch (IOException e) {
-            Log.e(TAG, "Failed to parse mLastTimeVisible", e);
+            Log.i(TAG, "Failed to parse mLastTimeVisible");
         }
 
         try {
             stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
                     parser, LAST_TIME_SERVICE_USED_ATTR);
         } catch (IOException e) {
-            Log.e(TAG, "Failed to parse mLastTimeForegroundServiceUsed", e);
+            Log.i(TAG, "Failed to parse mLastTimeForegroundServiceUsed");
         }
 
         stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
@@ -113,14 +113,14 @@
         try {
             stats.mTotalTimeVisible = XmlUtils.readLongAttribute(parser, TOTAL_TIME_VISIBLE_ATTR);
         } catch (IOException e) {
-            Log.e(TAG, "Failed to parse mTotalTimeVisible", e);
+            Log.i(TAG, "Failed to parse mTotalTimeVisible");
         }
 
         try {
             stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser,
                     TOTAL_TIME_SERVICE_USED_ATTR);
         } catch (IOException e) {
-            Log.e(TAG, "Failed to parse mTotalTimeForegroundServiceUsed", e);
+            Log.i(TAG, "Failed to parse mTotalTimeForegroundServiceUsed");
         }
 
         stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 60cb08f..f1e2281 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -16,6 +16,12 @@
 
 package com.android.server.usb;
 
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.MODE_AUDIO_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+
 import static com.android.internal.usb.DumpUtils.writeAccessory;
 import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
 
@@ -36,6 +42,7 @@
 import android.content.res.Resources;
 import android.debug.AdbManagerInternal;
 import android.debug.IAdbTransport;
+import android.hardware.usb.ParcelableUsbPort;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbConfiguration;
 import android.hardware.usb.UsbConstants;
@@ -294,9 +301,10 @@
         BroadcastReceiver portReceiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
-                UsbPort port = intent.getParcelableExtra(UsbManager.EXTRA_PORT);
+                ParcelableUsbPort port = intent.getParcelableExtra(UsbManager.EXTRA_PORT);
                 UsbPortStatus status = intent.getParcelableExtra(UsbManager.EXTRA_PORT_STATUS);
-                mHandler.updateHostState(port, status);
+                mHandler.updateHostState(
+                        port.getUsbPort(context.getSystemService(UsbManager.class)), status);
             }
         };
 
@@ -821,23 +829,20 @@
                     boolean prevHostConnected = mHostConnected;
                     UsbPort port = (UsbPort) args.arg1;
                     UsbPortStatus status = (UsbPortStatus) args.arg2;
-                    mHostConnected = status.getCurrentDataRole() == UsbPort.DATA_ROLE_HOST;
-                    mSourcePower = status.getCurrentPowerRole() == UsbPort.POWER_ROLE_SOURCE;
-                    mSinkPower = status.getCurrentPowerRole() == UsbPort.POWER_ROLE_SINK;
-                    mAudioAccessoryConnected =
-                            (status.getCurrentMode() == UsbPort.MODE_AUDIO_ACCESSORY);
-                    mAudioAccessorySupported = port.isModeSupported(UsbPort.MODE_AUDIO_ACCESSORY);
+                    mHostConnected = status.getCurrentDataRole() == DATA_ROLE_HOST;
+                    mSourcePower = status.getCurrentPowerRole() == POWER_ROLE_SOURCE;
+                    mSinkPower = status.getCurrentPowerRole() == POWER_ROLE_SINK;
+                    mAudioAccessoryConnected = (status.getCurrentMode() == MODE_AUDIO_ACCESSORY);
+                    mAudioAccessorySupported = port.isModeSupported(MODE_AUDIO_ACCESSORY);
                     // Ideally we want to see if PR_SWAP and DR_SWAP is supported.
                     // But, this should be suffice, since, all four combinations are only supported
                     // when PR_SWAP and DR_SWAP are supported.
                     mSupportsAllCombinations = status.isRoleCombinationSupported(
-                            UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_HOST)
-                            && status.isRoleCombinationSupported(UsbPort.POWER_ROLE_SINK,
-                            UsbPort.DATA_ROLE_HOST)
-                            && status.isRoleCombinationSupported(UsbPort.POWER_ROLE_SOURCE,
-                            UsbPort.DATA_ROLE_DEVICE)
-                            && status.isRoleCombinationSupported(UsbPort.POWER_ROLE_SINK,
-                            UsbPort.DATA_ROLE_HOST);
+                            POWER_ROLE_SOURCE, DATA_ROLE_HOST)
+                            && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_HOST)
+                            && status.isRoleCombinationSupported(POWER_ROLE_SOURCE,
+                            DATA_ROLE_DEVICE)
+                            && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_HOST);
 
                     args.recycle();
                     updateUsbNotification(false);
@@ -1739,8 +1744,8 @@
                     mGadgetProxy.linkToDeath(new UsbGadgetDeathRecipient(),
                             USB_GADGET_HAL_DEATH_COOKIE);
                     mCurrentFunctions = UsbManager.FUNCTION_NONE;
-                    mGadgetProxy.getCurrentUsbFunctions(new UsbGadgetCallback());
                     mCurrentUsbFunctionsRequested = true;
+                    mGadgetProxy.getCurrentUsbFunctions(new UsbGadgetCallback());
                 }
                 String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
                 updateState(state);
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index 96618f5..6f210e3 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -16,12 +16,22 @@
 
 package com.android.server.usb;
 
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+
 import static com.android.internal.usb.DumpUtils.writePort;
 import static com.android.internal.usb.DumpUtils.writePortStatus;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.content.Context;
 import android.content.Intent;
+import android.hardware.usb.ParcelableUsbPort;
 import android.hardware.usb.UsbManager;
 import android.hardware.usb.UsbPort;
 import android.hardware.usb.UsbPortStatus;
@@ -78,13 +88,13 @@
 
     // All non-trivial role combinations.
     private static final int COMBO_SOURCE_HOST =
-            UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_HOST);
-    private static final int COMBO_SOURCE_DEVICE =
-            UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_DEVICE);
+            UsbPort.combineRolesAsBit(POWER_ROLE_SOURCE, DATA_ROLE_HOST);
+    private static final int COMBO_SOURCE_DEVICE = UsbPort.combineRolesAsBit(
+            POWER_ROLE_SOURCE, DATA_ROLE_DEVICE);
     private static final int COMBO_SINK_HOST =
-            UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_HOST);
-    private static final int COMBO_SINK_DEVICE =
-            UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_DEVICE);
+            UsbPort.combineRolesAsBit(POWER_ROLE_SINK, DATA_ROLE_HOST);
+    private static final int COMBO_SINK_DEVICE = UsbPort.combineRolesAsBit(
+            POWER_ROLE_SINK, DATA_ROLE_DEVICE);
 
     // The system context.
     private final Context mContext;
@@ -217,12 +227,12 @@
             final int newMode;
             if ((!canChangePowerRole && currentPowerRole != newPowerRole)
                     || (!canChangeDataRole && currentDataRole != newDataRole)) {
-                if (canChangeMode && newPowerRole == UsbPort.POWER_ROLE_SOURCE
-                        && newDataRole == UsbPort.DATA_ROLE_HOST) {
-                    newMode = UsbPort.MODE_DFP;
-                } else if (canChangeMode && newPowerRole == UsbPort.POWER_ROLE_SINK
-                        && newDataRole == UsbPort.DATA_ROLE_DEVICE) {
-                    newMode = UsbPort.MODE_UFP;
+                if (canChangeMode && newPowerRole == POWER_ROLE_SOURCE
+                        && newDataRole == DATA_ROLE_HOST) {
+                    newMode = MODE_DFP;
+                } else if (canChangeMode && newPowerRole == POWER_ROLE_SINK
+                        && newDataRole == DATA_ROLE_DEVICE) {
+                    newMode = MODE_UFP;
                 } else {
                     logAndPrint(Log.ERROR, pw, "Found mismatch in supported USB role combinations "
                             + "while attempting to change role: " + portInfo
@@ -607,7 +617,7 @@
             IndentingPrintWriter pw) {
         // Only allow mode switch capability for dual role ports.
         // Validate that the current mode matches the supported modes we expect.
-        if ((supportedModes & UsbPort.MODE_DUAL) != UsbPort.MODE_DUAL) {
+        if ((supportedModes & MODE_DUAL) != MODE_DUAL) {
             canChangeMode = false;
             if (currentMode != 0 && currentMode != supportedModes) {
                 logAndPrint(Log.WARN, pw, "Ignoring inconsistent current mode from USB "
@@ -633,16 +643,16 @@
                 // Can only change power role.
                 // Assume data role must remain at its current value.
                 supportedRoleCombinations |= UsbPort.combineRolesAsBit(
-                        UsbPort.POWER_ROLE_SOURCE, currentDataRole);
+                        POWER_ROLE_SOURCE, currentDataRole);
                 supportedRoleCombinations |= UsbPort.combineRolesAsBit(
-                        UsbPort.POWER_ROLE_SINK, currentDataRole);
+                        POWER_ROLE_SINK, currentDataRole);
             } else if (canChangeDataRole) {
                 // Can only change data role.
                 // Assume power role must remain at its current value.
                 supportedRoleCombinations |= UsbPort.combineRolesAsBit(
-                        currentPowerRole, UsbPort.DATA_ROLE_HOST);
+                        currentPowerRole, DATA_ROLE_HOST);
                 supportedRoleCombinations |= UsbPort.combineRolesAsBit(
-                        currentPowerRole, UsbPort.DATA_ROLE_DEVICE);
+                        currentPowerRole, DATA_ROLE_DEVICE);
             } else if (canChangeMode) {
                 // Can only change the mode.
                 // Assume both standard UFP and DFP configurations will become available
@@ -654,7 +664,8 @@
         // Update the port data structures.
         PortInfo portInfo = mPorts.get(portId);
         if (portInfo == null) {
-            portInfo = new PortInfo(portId, supportedModes);
+            portInfo = new PortInfo(mContext.getSystemService(UsbManager.class), portId,
+                    supportedModes);
             portInfo.setStatus(currentMode, canChangeMode,
                     currentPowerRole, canChangePowerRole,
                     currentDataRole, canChangeDataRole,
@@ -701,12 +712,13 @@
         intent.addFlags(
                 Intent.FLAG_RECEIVER_FOREGROUND |
                         Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-        intent.putExtra(UsbManager.EXTRA_PORT, portInfo.mUsbPort);
+        intent.putExtra(UsbManager.EXTRA_PORT, ParcelableUsbPort.of(portInfo.mUsbPort));
         intent.putExtra(UsbManager.EXTRA_PORT_STATUS, portInfo.mUsbPortStatus);
 
         // Guard against possible reentrance by posting the broadcast from the handler
         // instead of from within the critical section.
-        mHandler.post(() -> mContext.sendBroadcastAsUser(intent, UserHandle.ALL));
+        mHandler.post(() -> mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                Manifest.permission.MANAGE_USB));
 
         // Log to statsd
         if (!mConnected.containsKey(portInfo.mUsbPort.getId())
@@ -772,8 +784,8 @@
         // 0 when port is connected. Else reports the last connected duration
         public long mLastConnectDurationMillis;
 
-        public PortInfo(String portId, int supportedModes) {
-            mUsbPort = new UsbPort(portId, supportedModes);
+        PortInfo(@NonNull UsbManager usbManager, @NonNull String portId, int supportedModes) {
+            mUsbPort = new UsbPort(usbManager, portId, supportedModes);
         }
 
         public boolean setStatus(int currentMode, boolean canChangeMode,
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index f9abedf..9115477 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -16,6 +16,14 @@
 
 package com.android.server.usb;
 
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.PendingIntent;
@@ -27,6 +35,7 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.hardware.usb.IUsbManager;
+import android.hardware.usb.ParcelableUsbPort;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbManager;
@@ -52,7 +61,9 @@
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 
 /**
  * UsbService manages all USB related state, including both host and device support.
@@ -489,12 +500,25 @@
     }
 
     @Override
-    public UsbPort[] getPorts() {
+    public List<ParcelableUsbPort> getPorts() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
 
         final long ident = Binder.clearCallingIdentity();
         try {
-            return mPortManager != null ? mPortManager.getPorts() : null;
+            if (mPortManager == null) {
+                return null;
+            } else {
+                final UsbPort[] ports = mPortManager.getPorts();
+
+                final int numPorts = ports.length;
+                ArrayList<ParcelableUsbPort> parcelablePorts = new ArrayList<>();
+                for (int i = 0; i < numPorts; i++) {
+                    parcelablePorts.add(ParcelableUsbPort.of(ports[i]));
+                }
+
+                return parcelablePorts;
+            }
+
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -588,10 +612,10 @@
                 final int powerRole;
                 switch (args[2]) {
                     case "source":
-                        powerRole = UsbPort.POWER_ROLE_SOURCE;
+                        powerRole = POWER_ROLE_SOURCE;
                         break;
                     case "sink":
-                        powerRole = UsbPort.POWER_ROLE_SINK;
+                        powerRole = POWER_ROLE_SINK;
                         break;
                     case "no-power":
                         powerRole = 0;
@@ -603,10 +627,10 @@
                 final int dataRole;
                 switch (args[3]) {
                     case "host":
-                        dataRole = UsbPort.DATA_ROLE_HOST;
+                        dataRole = DATA_ROLE_HOST;
                         break;
                     case "device":
-                        dataRole = UsbPort.DATA_ROLE_DEVICE;
+                        dataRole = DATA_ROLE_DEVICE;
                         break;
                     case "no-data":
                         dataRole = 0;
@@ -631,13 +655,13 @@
                 final int supportedModes;
                 switch (args[2]) {
                     case "ufp":
-                        supportedModes = UsbPort.MODE_UFP;
+                        supportedModes = MODE_UFP;
                         break;
                     case "dfp":
-                        supportedModes = UsbPort.MODE_DFP;
+                        supportedModes = MODE_DFP;
                         break;
                     case "dual":
-                        supportedModes = UsbPort.MODE_DUAL;
+                        supportedModes = MODE_DUAL;
                         break;
                     case "none":
                         supportedModes = 0;
@@ -658,10 +682,10 @@
                 final boolean canChangeMode = args[2].endsWith("?");
                 switch (canChangeMode ? removeLastChar(args[2]) : args[2]) {
                     case "ufp":
-                        mode = UsbPort.MODE_UFP;
+                        mode = MODE_UFP;
                         break;
                     case "dfp":
-                        mode = UsbPort.MODE_DFP;
+                        mode = MODE_DFP;
                         break;
                     default:
                         pw.println("Invalid mode: " + args[2]);
@@ -671,10 +695,10 @@
                 final boolean canChangePowerRole = args[3].endsWith("?");
                 switch (canChangePowerRole ? removeLastChar(args[3]) : args[3]) {
                     case "source":
-                        powerRole = UsbPort.POWER_ROLE_SOURCE;
+                        powerRole = POWER_ROLE_SOURCE;
                         break;
                     case "sink":
-                        powerRole = UsbPort.POWER_ROLE_SINK;
+                        powerRole = POWER_ROLE_SINK;
                         break;
                     default:
                         pw.println("Invalid power role: " + args[3]);
@@ -684,10 +708,10 @@
                 final boolean canChangeDataRole = args[4].endsWith("?");
                 switch (canChangeDataRole ? removeLastChar(args[4]) : args[4]) {
                     case "host":
-                        dataRole = UsbPort.DATA_ROLE_HOST;
+                        dataRole = DATA_ROLE_HOST;
                         break;
                     case "device":
-                        dataRole = UsbPort.DATA_ROLE_DEVICE;
+                        dataRole = DATA_ROLE_DEVICE;
                         break;
                     default:
                         pw.println("Invalid data role: " + args[4]);
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index d6b40ae..38efc74 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -622,6 +622,7 @@
                     onRecognitionFailureLocked();
                     break;
                 case SoundTrigger.RECOGNITION_STATUS_SUCCESS:
+                case SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE:
                     if (isKeyphraseRecognitionEvent(event)) {
                         onKeyphraseRecognitionSuccessLocked((KeyphraseRecognitionEvent) event);
                     } else {
@@ -638,7 +639,8 @@
 
     private void onGenericRecognitionSuccessLocked(GenericRecognitionEvent event) {
         MetricsLogger.count(mContext, "sth_generic_recognition_event", 1);
-        if (event.status != SoundTrigger.RECOGNITION_STATUS_SUCCESS) {
+        if (event.status != SoundTrigger.RECOGNITION_STATUS_SUCCESS
+                && event.status != SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE) {
             return;
         }
         ModelData model = getModelDataForLocked(event.soundModelHandle);
@@ -655,7 +657,9 @@
             return;
         }
 
-        model.setStopped();
+        if (event.status != SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE) {
+            model.setStopped();
+        }
         try {
             callback.onGenericSoundTriggerDetected((GenericRecognitionEvent) event);
         } catch (DeadObjectException e) {
@@ -797,7 +801,10 @@
             Slog.w(TAG, "Received onRecognition event without callback for keyphrase model.");
             return;
         }
-        modelData.setStopped();
+
+        if (event.status != SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE) {
+            modelData.setStopped();
+        }
 
         try {
             modelData.getCallback().onKeyphraseDetected((KeyphraseRecognitionEvent) event);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 99ad1f4..bbf3d45 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -356,7 +356,12 @@
             }
 
             // No voice interactor, we'll just set up a simple recognizer.
-            curRecognizer = findAvailRecognizer(null, userHandle);
+            initSimpleRecognizer(curInteractorInfo, userHandle);
+        }
+
+        public void initSimpleRecognizer(VoiceInteractionServiceInfo curInteractorInfo,
+                int userHandle) {
+            ComponentName curRecognizer = findAvailRecognizer(null, userHandle);
             if (curRecognizer != null) {
                 if (curInteractorInfo == null) {
                     setCurInteractor(null, userHandle);
@@ -1236,34 +1241,46 @@
                 int userHandle = UserHandle.getUserId(uid);
                 ComponentName curInteractor = getCurInteractor(userHandle);
                 ComponentName curRecognizer = getCurRecognizer(userHandle);
-                boolean hit = false;
+                boolean hitInt = false;
+                boolean hitRec = false;
                 for (String pkg : packages) {
                     if (curInteractor != null && pkg.equals(curInteractor.getPackageName())) {
-                        hit = true;
+                        hitInt = true;
                         break;
                     } else if (curRecognizer != null
                             && pkg.equals(curRecognizer.getPackageName())) {
-                        hit = true;
+                        hitRec = true;
                         break;
                     }
                 }
-                if (hit && doit) {
-                    // The user is force stopping our current interactor/recognizer.
+                if (hitInt && doit) {
+                    // The user is force stopping our current interactor.
                     // Clear the current settings and restore default state.
                     synchronized (VoiceInteractionManagerServiceStub.this) {
+                        Slog.i(TAG, "Force stopping current voice interactor: "
+                                + getCurInteractor(userHandle));
                         unloadAllKeyphraseModels();
                         if (mImpl != null) {
                             mImpl.shutdownLocked();
                             setImplLocked(null);
                         }
+
                         setCurInteractor(null, userHandle);
                         setCurRecognizer(null, userHandle);
                         resetCurAssistant(userHandle);
                         initForUser(userHandle);
                         switchImplementationIfNeededLocked(true);
                     }
+                } else if (hitRec && doit) {
+                    // We are just force-stopping the current recognizer, which is not
+                    // also the current interactor.
+                    synchronized (VoiceInteractionManagerServiceStub.this) {
+                        Slog.i(TAG, "Force stopping current voice recognizer: "
+                                + getCurRecognizer(userHandle));
+                        initSimpleRecognizer(null, userHandle);
+                    }
                 }
-                return hit;
+                return hitInt || hitRec;
             }
 
             @Override
diff --git a/startop/iorap/Android.bp b/startop/iorap/Android.bp
index b3b0900..59a80fb 100644
--- a/startop/iorap/Android.bp
+++ b/startop/iorap/Android.bp
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 java_library_static {
-  name: "libiorap-java",
+  name: "services.startop.iorap",
 
   aidl: {
     include_dirs: [
@@ -21,6 +21,8 @@
     ],
   },
 
+  libs: ["services.core"],
+
   srcs: [
       ":iorap-aidl",
       "**/*.java",
diff --git a/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java
new file mode 100644
index 0000000..c2e4581
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.startop.iorap;
+
+import android.annotation.LongDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+// TODO: fix this. either move this class into system server or add a dependency on
+// these wm classes to libiorap-java and libiorap-java-tests (somehow).
+import com.android.server.wm.ActivityMetricsLaunchObserver;
+import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto;
+import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Objects;
+
+/**
+ * Provide a hint to iorapd that an app launch sequence has transitioned state.<br /><br />
+ *
+ * Knowledge of when an activity starts/stops can be used by iorapd to increase system
+ * performance (e.g. by launching perfetto tracing to record an io profile, or by
+ * playing back an ioprofile via readahead) over the long run.<br /><br />
+ *
+ * /@see com.google.android.startop.iorap.IIorap#onAppLaunchEvent <br /><br />
+ * @see com.android.server.wm.ActivityMetricsLaunchObserver
+ *      ActivityMetricsLaunchObserver for the possible event states.
+ * @hide
+ */
+public abstract class AppLaunchEvent implements Parcelable {
+    @LongDef
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SequenceId {}
+
+    public final @SequenceId
+    long sequenceId;
+
+    protected AppLaunchEvent(@SequenceId long sequenceId) {
+        this.sequenceId = sequenceId;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof AppLaunchEvent) {
+            return equals((AppLaunchEvent) other);
+        }
+        return false;
+    }
+
+    protected boolean equals(AppLaunchEvent other) {
+        return sequenceId == other.sequenceId;
+    }
+
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() +
+                "{" + "sequenceId=" + Long.toString(sequenceId) +
+                toStringBody() + "}";
+    }
+
+    protected String toStringBody() { return ""; };
+
+    // List of possible variants:
+
+    public static final class IntentStarted extends AppLaunchEvent {
+        @NonNull
+        public final Intent intent;
+
+        public IntentStarted(@SequenceId long sequenceId, Intent intent) {
+            super(sequenceId);
+            this.intent = intent;
+
+            Objects.requireNonNull(intent, "intent");
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof IntentStarted) {
+                return intent.equals(((IntentStarted)other).intent) &&
+                        super.equals(other);
+            }
+            return false;
+        }
+
+        @Override
+        protected String toStringBody() {
+            return ", intent=" + intent.toString();
+        }
+
+
+        @Override
+        protected void writeToParcelImpl(Parcel p, int flags) {
+            super.writeToParcelImpl(p, flags);
+            intent.writeToParcel(p, flags);
+        }
+
+        IntentStarted(Parcel p) {
+            super(p);
+            intent = Intent.CREATOR.createFromParcel(p);
+        }
+    }
+
+    public static final class IntentFailed extends AppLaunchEvent {
+        public IntentFailed(@SequenceId long sequenceId) {
+            super(sequenceId);
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof IntentFailed) {
+                return super.equals(other);
+            }
+            return false;
+        }
+
+        IntentFailed(Parcel p) {
+            super(p);
+        }
+    }
+
+    public static abstract class BaseWithActivityRecordData extends AppLaunchEvent {
+        public final @NonNull
+        @ActivityRecordProto byte[] activityRecordSnapshot;
+
+        protected BaseWithActivityRecordData(@SequenceId long sequenceId,
+                @NonNull @ActivityRecordProto byte[] snapshot) {
+            super(sequenceId);
+            activityRecordSnapshot = snapshot;
+
+            Objects.requireNonNull(snapshot, "snapshot");
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof BaseWithActivityRecordData) {
+                return activityRecordSnapshot.equals(
+                        ((BaseWithActivityRecordData)other).activityRecordSnapshot) &&
+                        super.equals(other);
+            }
+            return false;
+        }
+
+        @Override
+        protected String toStringBody() {
+            return ", " + activityRecordSnapshot.toString();
+        }
+
+        @Override
+        protected void writeToParcelImpl(Parcel p, int flags) {
+           super.writeToParcelImpl(p, flags);
+           ActivityRecordProtoParcelable.write(p, activityRecordSnapshot, flags);
+        }
+
+        BaseWithActivityRecordData(Parcel p) {
+            super(p);
+            activityRecordSnapshot = ActivityRecordProtoParcelable.create(p);
+        }
+    }
+
+    public static final class ActivityLaunched extends BaseWithActivityRecordData {
+        public final @ActivityMetricsLaunchObserver.Temperature
+        int temperature;
+
+        public ActivityLaunched(@SequenceId long sequenceId,
+                @NonNull @ActivityRecordProto byte[] snapshot, 
+                @ActivityMetricsLaunchObserver.Temperature int temperature) {
+            super(sequenceId, snapshot);
+            this.temperature = temperature;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof ActivityLaunched) {
+                return temperature == ((ActivityLaunched)other).temperature &&
+                        super.equals(other);
+            }
+            return false;
+        }
+
+        @Override
+        protected String toStringBody() {
+            return ", temperature=" + Integer.toString(temperature);
+        }
+
+        @Override
+        protected void writeToParcelImpl(Parcel p, int flags) {
+           super.writeToParcelImpl(p, flags);
+           p.writeInt(temperature);
+        }
+
+        ActivityLaunched(Parcel p) {
+            super(p);
+            temperature = p.readInt();
+        }
+    }
+
+    public static final class ActivityLaunchFinished extends BaseWithActivityRecordData {
+        public ActivityLaunchFinished(@SequenceId long sequenceId,
+                @NonNull @ActivityRecordProto byte[] snapshot) {
+            super(sequenceId, snapshot);
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof ActivityLaunched) {
+                return super.equals(other);
+            }
+            return false;
+        }
+    }
+
+     public static class ActivityLaunchCancelled extends AppLaunchEvent {
+        public final @Nullable
+        @ActivityRecordProto byte[] activityRecordSnapshot;
+
+        public ActivityLaunchCancelled(@SequenceId long sequenceId,
+                @Nullable @ActivityRecordProto byte[] snapshot) {
+            super(sequenceId);
+            activityRecordSnapshot = snapshot;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof ActivityLaunchCancelled) {
+                return Objects.equals(activityRecordSnapshot,
+                        ((ActivityLaunchCancelled)other).activityRecordSnapshot) &&
+                        super.equals(other);
+            }
+            return false;
+        }
+
+        @Override
+        protected String toStringBody() {
+            return ", " + activityRecordSnapshot.toString();
+        }
+
+        @Override
+        protected void writeToParcelImpl(Parcel p, int flags) {
+           super.writeToParcelImpl(p, flags);
+           if (activityRecordSnapshot != null) {
+               p.writeBoolean(true);
+               ActivityRecordProtoParcelable.write(p, activityRecordSnapshot, flags);
+           } else {
+               p.writeBoolean(false);
+           }
+        }
+
+        ActivityLaunchCancelled(Parcel p) {
+            super(p);
+            if (p.readBoolean()) {
+                activityRecordSnapshot = ActivityRecordProtoParcelable.create(p);
+            } else {
+                activityRecordSnapshot = null;
+            }
+        }
+    }
+
+    @Override
+    public @ContentsFlags int describeContents() { return 0; }
+
+    @Override
+    public void writeToParcel(Parcel p, @WriteFlags int flags) {
+        p.writeInt(getTypeIndex());
+
+        writeToParcelImpl(p, flags);
+    }
+
+
+    public static Creator<AppLaunchEvent> CREATOR =
+            new Creator<AppLaunchEvent>() {
+        @Override
+        public AppLaunchEvent createFromParcel(Parcel source) {
+            int typeIndex = source.readInt();
+
+            Class<?> kls = getClassFromTypeIndex(typeIndex);
+            if (kls == null) {
+                throw new IllegalArgumentException("Invalid type index: " + typeIndex);
+            }
+
+            try {
+                return (AppLaunchEvent) kls.getConstructor(Parcel.class).newInstance(source);
+            } catch (InstantiationException e) {
+                throw new AssertionError(e);
+            } catch (IllegalAccessException e) {
+                throw new AssertionError(e);
+            } catch (InvocationTargetException e) {
+                throw new AssertionError(e);
+            } catch (NoSuchMethodException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public AppLaunchEvent[] newArray(int size) {
+            return new AppLaunchEvent[0];
+        }
+    };
+
+    protected void writeToParcelImpl(Parcel p, int flags) {
+        p.writeLong(sequenceId);
+    }
+
+    protected AppLaunchEvent(Parcel p) {
+        sequenceId = p.readLong();
+    }
+
+    private int getTypeIndex() {
+        for (int i = 0; i < sTypes.length; ++i) {
+            if (sTypes[i].equals(this.getClass())) {
+                return i;
+            }
+        }
+        throw new AssertionError("sTypes did not include this type: " + this.getClass());
+    }
+
+    private static @Nullable Class<?> getClassFromTypeIndex(int typeIndex) {
+        if (typeIndex >= 0 && typeIndex < sTypes.length) {
+            return sTypes[typeIndex];
+        }
+        return null;
+    }
+
+    // Index position matters: It is used to encode the specific type in parceling.
+    // Keep up-to-date with C++ side.
+    private static Class<?>[] sTypes = new Class[] {
+            IntentStarted.class,
+            IntentFailed.class,
+            ActivityLaunched.class,
+            ActivityLaunchFinished.class,
+            ActivityLaunchCancelled.class,
+    };
+
+    // TODO: move to @ActivityRecordProto byte[] once we have unit tests.
+    public static class ActivityRecordProtoParcelable {
+        public static void write(Parcel p, @ActivityRecordProto byte[] activityRecordSnapshot,
+                int flags) {
+            p.writeByteArray(activityRecordSnapshot);
+        }
+
+        public static @ActivityRecordProto byte[] create(Parcel p) {
+            byte[] data = p.createByteArray();
+
+            return data;
+        }
+    }
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
new file mode 100644
index 0000000..7fcad36
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.startop.iorap;
+// TODO: rename to com.android.server.startop.iorap
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.wm.ActivityMetricsLaunchObserver;
+import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto;
+import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature;
+import com.android.server.wm.ActivityMetricsLaunchObserverRegistry;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+/**
+ * System-server-local proxy into the {@code IIorap} native service.
+ */
+public class IorapForwardingService extends SystemService {
+
+    public static final boolean DEBUG = true; // TODO: read from a getprop?
+    public static final String TAG = "IorapForwardingService";
+
+    private IIorap mIorapRemote;
+
+    /**
+     * Initializes the system service.
+     * <p>
+     * Subclasses must define a single argument constructor that accepts the context
+     * and passes it to super.
+     * </p>
+     *
+     * @param context The system server context.
+     */
+    public IorapForwardingService(Context context) {
+       super(context);
+    }
+
+    //<editor-fold desc="Providers">
+    /*
+     * Providers for external dependencies:
+     * - These are marked as protected to allow tests to inject different values via mocks.
+     */
+
+    @VisibleForTesting
+    protected ActivityMetricsLaunchObserverRegistry provideLaunchObserverRegistry() {
+        ActivityTaskManagerInternal amtInternal =
+                LocalServices.getService(ActivityTaskManagerInternal.class);
+        ActivityMetricsLaunchObserverRegistry launchObserverRegistry =
+                amtInternal.getLaunchObserverRegistry();
+        return launchObserverRegistry;
+    }
+
+    @VisibleForTesting
+    protected IIorap provideIorapRemote() {
+        try {
+            return IIorap.Stub.asInterface(ServiceManager.getServiceOrThrow("iorapd"));
+        } catch (ServiceManager.ServiceNotFoundException e) {
+            // TODO: how do we handle service being missing?
+            throw new AssertionError(e);
+        }
+    }
+
+    //</editor-fold>
+
+    @Override
+    public void onStart() {
+        if (DEBUG) {
+            Log.v(TAG, "onStart");
+        }
+
+        // Connect to the native binder service.
+        mIorapRemote = provideIorapRemote();
+        invokeRemote( () -> mIorapRemote.setTaskListener(new RemoteTaskListener()) );
+
+        // Listen to App Launch Sequence events from ActivityTaskManager,
+        // and forward them to the native binder service.
+        ActivityMetricsLaunchObserverRegistry launchObserverRegistry =
+                provideLaunchObserverRegistry();
+        launchObserverRegistry.registerLaunchObserver(new AppLaunchObserver());
+    }
+
+    private class AppLaunchObserver implements ActivityMetricsLaunchObserver {
+        // We add a synthetic sequence ID here to make it easier to differentiate new
+        // launch sequences on the native side.
+        private @AppLaunchEvent.SequenceId long mSequenceId = -1;
+
+        @Override
+        public void onIntentStarted(@NonNull Intent intent) {
+            // #onIntentStarted [is the only transition that] initiates a new launch sequence.
+            ++mSequenceId;
+
+            if (DEBUG) {
+                Log.v(TAG, String.format("AppLaunchObserver#onIntentStarted(%d, %s)",
+                        mSequenceId, intent));
+            }
+
+            invokeRemote(() ->
+                    mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+                        new AppLaunchEvent.IntentStarted(mSequenceId, intent))
+            );
+        }
+
+        @Override
+        public void onIntentFailed() {
+            if (DEBUG) {
+                Log.v(TAG, String.format("AppLaunchObserver#onIntentFailed(%d)", mSequenceId));
+            }
+
+            invokeRemote(() ->
+                    mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+                        new AppLaunchEvent.IntentFailed(mSequenceId))
+            );
+        }
+
+        @Override
+        public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity,
+                @Temperature int temperature) {
+            if (DEBUG) {
+                Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunched(%d, %s, %d)",
+                        mSequenceId, activity, temperature));
+            }
+
+            invokeRemote(() ->
+                    mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+                            new AppLaunchEvent.ActivityLaunched(mSequenceId, activity, temperature))
+            );
+        }
+
+        @Override
+        public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] activity) {
+            if (DEBUG) {
+                Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchCancelled(%d, %s)",
+                        mSequenceId, activity));
+            }
+
+            invokeRemote(() ->
+                    mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+                            new AppLaunchEvent.ActivityLaunchCancelled(mSequenceId,
+                                    activity)));
+        }
+
+        @Override
+        public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity) {
+            if (DEBUG) {
+                Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchFinished(%d, %s)",
+                        mSequenceId, activity));
+            }
+
+            invokeRemote(() ->
+                mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+                        new AppLaunchEvent.ActivityLaunchCancelled(mSequenceId, activity))
+            );
+        }
+    }
+
+    private class RemoteTaskListener extends ITaskListener.Stub {
+        @Override
+        public void onProgress(RequestId requestId, TaskResult result) throws RemoteException {
+            if (DEBUG) {
+                Log.v(TAG,
+                        String.format("RemoteTaskListener#onProgress(%s, %s)", requestId, result));
+            }
+
+            // TODO: implement rest.
+        }
+
+        @Override
+        public void onComplete(RequestId requestId, TaskResult result) throws RemoteException {
+            if (DEBUG) {
+                Log.v(TAG,
+                        String.format("RemoteTaskListener#onComplete(%s, %s)", requestId, result));
+            }
+
+            // TODO: implement rest.
+        }
+    }
+
+    private interface RemoteRunnable {
+        void run() throws RemoteException;
+    }
+
+    private static void invokeRemote(RemoteRunnable r) {
+       try {
+           r.run();
+       } catch (RemoteException e) {
+           // TODO: what do we do with exceptions?
+           throw new AssertionError("not implemented", e);
+       }
+    }
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/RequestId.java b/startop/iorap/src/com/google/android/startop/iorap/RequestId.java
index 2c79319..adb3a91 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/RequestId.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/RequestId.java
@@ -71,7 +71,7 @@
 
     @Override
     public String toString() {
-        return String.format("{requestId: %ld}", requestId);
+        return String.format("{requestId: %d}", requestId);
     }
 
     @Override
diff --git a/startop/iorap/tests/Android.bp b/startop/iorap/tests/Android.bp
index 7605784..5ac4a46 100644
--- a/startop/iorap/tests/Android.bp
+++ b/startop/iorap/tests/Android.bp
@@ -18,8 +18,15 @@
     srcs: ["src/**/*.kt"],
 
     static_libs: [
-      // non-test dependencies
-      "libiorap-java",
+      // Non-test dependencies
+
+      // library under test
+      "services.startop.iorap",
+      // need the system_server code to be on the classpath,
+      "services.core",
+
+      // Test Dependencies
+
       // test android dependencies
       "platform-test-annotations",
       "android-support-test",
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index de40e0d..82056e9 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -24,6 +24,9 @@
         "libdexfile",
         "slicer",
     ],
+    static_libs: [
+        "libtinyxml2",
+    ],
 }
 
 cc_library_host_static {
@@ -31,8 +34,11 @@
     defaults: ["viewcompiler_defaults"],
     srcs: [
         "dex_builder.cc",
+        "dex_layout_compiler.cc",
         "java_lang_builder.cc",
+        "tinyxml_layout_parser.cc",
         "util.cc",
+        "layout_validation.cc",
     ],
 }
 
@@ -43,7 +49,6 @@
         "main.cc",
     ],
     static_libs: [
-        "libtinyxml2",
         "libgflags",
         "libviewcompiler",
     ],
@@ -54,6 +59,7 @@
     defaults: ["viewcompiler_defaults"],
     srcs: [
         "dex_builder_test.cc",
+        "layout_validation_test.cc",
         "util_test.cc",
     ],
     static_libs: [
diff --git a/startop/view_compiler/TEST_MAPPING b/startop/view_compiler/TEST_MAPPING
index 5d675b7..7006075 100644
--- a/startop/view_compiler/TEST_MAPPING
+++ b/startop/view_compiler/TEST_MAPPING
@@ -2,6 +2,10 @@
   "presubmit": [
     {
       "name": "dex-builder-test"
+    },
+    {
+      "name": "view-compiler-tests",
+      "host": true
     }
   ]
 }
diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc
index 906d64c..a78f7d5 100644
--- a/startop/view_compiler/dex_builder.cc
+++ b/startop/view_compiler/dex_builder.cc
@@ -61,18 +61,49 @@
     case Instruction::Op::kInvokeDirect:
       out << "kInvokeDirect";
       return out;
+    case Instruction::Op::kInvokeStatic:
+      out << "kInvokeStatic";
+      return out;
+    case Instruction::Op::kInvokeInterface:
+      out << "kInvokeInterface";
+      return out;
     case Instruction::Op::kBindLabel:
       out << "kBindLabel";
       return out;
     case Instruction::Op::kBranchEqz:
       out << "kBranchEqz";
       return out;
+    case Instruction::Op::kBranchNEqz:
+      out << "kBranchNEqz";
+      return out;
     case Instruction::Op::kNew:
       out << "kNew";
       return out;
+    case Instruction::Op::kCheckCast:
+      out << "kCheckCast";
+      return out;
   }
 }
 
+std::ostream& operator<<(std::ostream& out, const Value& value) {
+  if (value.is_register()) {
+    out << "Register(" << value.value() << ")";
+  } else if (value.is_parameter()) {
+    out << "Parameter(" << value.value() << ")";
+  } else if (value.is_immediate()) {
+    out << "Immediate(" << value.value() << ")";
+  } else if (value.is_string()) {
+    out << "String(" << value.value() << ")";
+  } else if (value.is_label()) {
+    out << "Label(" << value.value() << ")";
+  } else if (value.is_type()) {
+    out << "Type(" << value.value() << ")";
+  } else {
+    out << "UnknownValue";
+  }
+  return out;
+}
+
 void* TrackingAllocator::Allocate(size_t size) {
   std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(size);
   void* raw_buffer = buffer.get();
@@ -289,12 +320,20 @@
       return EncodeInvoke(instruction, art::Instruction::INVOKE_VIRTUAL);
     case Instruction::Op::kInvokeDirect:
       return EncodeInvoke(instruction, art::Instruction::INVOKE_DIRECT);
+    case Instruction::Op::kInvokeStatic:
+      return EncodeInvoke(instruction, art::Instruction::INVOKE_STATIC);
+    case Instruction::Op::kInvokeInterface:
+      return EncodeInvoke(instruction, art::Instruction::INVOKE_INTERFACE);
     case Instruction::Op::kBindLabel:
       return BindLabel(instruction.args()[0]);
     case Instruction::Op::kBranchEqz:
       return EncodeBranch(art::Instruction::IF_EQZ, instruction);
+    case Instruction::Op::kBranchNEqz:
+      return EncodeBranch(art::Instruction::IF_NEZ, instruction);
     case Instruction::Op::kNew:
       return EncodeNew(instruction);
+    case Instruction::Op::kCheckCast:
+      return EncodeCast(instruction);
   }
 }
 
@@ -353,7 +392,9 @@
 
   // If there is a return value, add a move-result instruction
   if (instruction.dest().has_value()) {
-    Encode11x(art::Instruction::MOVE_RESULT, RegisterValue(*instruction.dest()));
+    Encode11x(instruction.result_is_object() ? art::Instruction::MOVE_RESULT_OBJECT
+                                             : art::Instruction::MOVE_RESULT,
+              RegisterValue(*instruction.dest()));
   }
 
   max_args_ = std::max(max_args_, instruction.args().size());
@@ -386,6 +427,18 @@
   Encode21c(::art::Instruction::NEW_INSTANCE, RegisterValue(*instruction.dest()), type.value());
 }
 
+void MethodBuilder::EncodeCast(const Instruction& instruction) {
+  DCHECK_EQ(Instruction::Op::kCheckCast, instruction.opcode());
+  DCHECK(instruction.dest().has_value());
+  DCHECK(instruction.dest()->is_variable());
+  DCHECK_EQ(1, instruction.args().size());
+
+  const Value& type = instruction.args()[0];
+  DCHECK_LT(RegisterValue(*instruction.dest()), 256);
+  DCHECK(type.is_type());
+  Encode21c(::art::Instruction::CHECK_CAST, RegisterValue(*instruction.dest()), type.value());
+}
+
 size_t MethodBuilder::RegisterValue(const Value& value) const {
   if (value.is_register()) {
     return value.value();
@@ -447,7 +500,7 @@
     auto& ir_node = dex_file_->methods_map[new_index];
     SLICER_CHECK(ir_node == nullptr);
     ir_node = decl;
-    decl->orig_index = new_index;
+    decl->orig_index = decl->index = new_index;
 
     entry = {id, decl};
   }
diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h
index adf82bf..757d863 100644
--- a/startop/view_compiler/dex_builder.h
+++ b/startop/view_compiler/dex_builder.h
@@ -73,7 +73,7 @@
   bool operator<(const TypeDescriptor& rhs) const { return descriptor_ < rhs.descriptor_; }
 
  private:
-  TypeDescriptor(std::string descriptor) : descriptor_{descriptor} {}
+  explicit TypeDescriptor(std::string descriptor) : descriptor_{descriptor} {}
 
   const std::string descriptor_;
 };
@@ -83,7 +83,7 @@
 class Prototype {
  public:
   template <typename... TypeDescriptors>
-  Prototype(TypeDescriptor return_type, TypeDescriptors... param_types)
+  explicit Prototype(TypeDescriptor return_type, TypeDescriptors... param_types)
       : return_type_{return_type}, param_types_{param_types...} {}
 
   // Encode this prototype into the dex file.
@@ -142,14 +142,18 @@
   // The operation performed by this instruction. These are virtual instructions that do not
   // correspond exactly to DEX instructions.
   enum class Op {
-    kReturn,
-    kReturnObject,
-    kMove,
-    kInvokeVirtual,
-    kInvokeDirect,
     kBindLabel,
     kBranchEqz,
-    kNew
+    kBranchNEqz,
+    kCheckCast,
+    kInvokeDirect,
+    kInvokeInterface,
+    kInvokeStatic,
+    kInvokeVirtual,
+    kMove,
+    kNew,
+    kReturn,
+    kReturnObject,
   };
 
   ////////////////////////
@@ -163,19 +167,60 @@
   // For most instructions, which take some number of arguments and have an optional return value.
   template <typename... T>
   static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest, T... args) {
-    return Instruction{opcode, /*method_id*/ 0, dest, args...};
+    return Instruction{opcode, /*method_id=*/0, /*result_is_object=*/false, dest, args...};
   }
+
+  // A cast instruction. Basically, `(type)val`
+  static inline Instruction Cast(Value val, Value type) {
+    DCHECK(type.is_type());
+    return OpWithArgs(Op::kCheckCast, val, type);
+  }
+
   // For method calls.
   template <typename... T>
   static inline Instruction InvokeVirtual(size_t method_id, std::optional<const Value> dest,
                                           Value this_arg, T... args) {
-    return Instruction{Op::kInvokeVirtual, method_id, dest, this_arg, args...};
+    return Instruction{
+        Op::kInvokeVirtual, method_id, /*result_is_object=*/false, dest, this_arg, args...};
+  }
+  // Returns an object
+  template <typename... T>
+  static inline Instruction InvokeVirtualObject(size_t method_id, std::optional<const Value> dest,
+                                                Value this_arg, T... args) {
+    return Instruction{
+        Op::kInvokeVirtual, method_id, /*result_is_object=*/true, dest, this_arg, args...};
   }
   // For direct calls (basically, constructors).
   template <typename... T>
   static inline Instruction InvokeDirect(size_t method_id, std::optional<const Value> dest,
                                          Value this_arg, T... args) {
-    return Instruction{Op::kInvokeDirect, method_id, dest, this_arg, args...};
+    return Instruction{
+        Op::kInvokeDirect, method_id, /*result_is_object=*/false, dest, this_arg, args...};
+  }
+  // Returns an object
+  template <typename... T>
+  static inline Instruction InvokeDirectObject(size_t method_id, std::optional<const Value> dest,
+                                               Value this_arg, T... args) {
+    return Instruction{
+        Op::kInvokeDirect, method_id, /*result_is_object=*/true, dest, this_arg, args...};
+  }
+  // For static calls.
+  template <typename... T>
+  static inline Instruction InvokeStatic(size_t method_id, std::optional<const Value> dest,
+                                         T... args) {
+    return Instruction{Op::kInvokeStatic, method_id, /*result_is_object=*/false, dest, args...};
+  }
+  // Returns an object
+  template <typename... T>
+  static inline Instruction InvokeStaticObject(size_t method_id, std::optional<const Value> dest,
+                                               T... args) {
+    return Instruction{Op::kInvokeStatic, method_id, /*result_is_object=*/true, dest, args...};
+  }
+  // For static calls.
+  template <typename... T>
+  static inline Instruction InvokeInterface(size_t method_id, std::optional<const Value> dest,
+                                            T... args) {
+    return Instruction{Op::kInvokeInterface, method_id, /*result_is_object=*/false, dest, args...};
   }
 
   ///////////////
@@ -184,21 +229,27 @@
 
   Op opcode() const { return opcode_; }
   size_t method_id() const { return method_id_; }
+  bool result_is_object() const { return result_is_object_; }
   const std::optional<const Value>& dest() const { return dest_; }
   const std::vector<const Value>& args() const { return args_; }
 
  private:
   inline Instruction(Op opcode, size_t method_id, std::optional<const Value> dest)
-      : opcode_{opcode}, method_id_{method_id}, dest_{dest}, args_{} {}
+      : opcode_{opcode}, method_id_{method_id}, result_is_object_{false}, dest_{dest}, args_{} {}
 
   template <typename... T>
-  inline constexpr Instruction(Op opcode, size_t method_id, std::optional<const Value> dest,
-                               T... args)
-      : opcode_{opcode}, method_id_{method_id}, dest_{dest}, args_{args...} {}
+  inline constexpr Instruction(Op opcode, size_t method_id, bool result_is_object,
+                               std::optional<const Value> dest, T... args)
+      : opcode_{opcode},
+        method_id_{method_id},
+        result_is_object_{result_is_object},
+        dest_{dest},
+        args_{args...} {}
 
   const Op opcode_;
   // The index of the method to invoke, for kInvokeVirtual and similar opcodes.
   const size_t method_id_{0};
+  const bool result_is_object_;
   const std::optional<const Value> dest_;
   const std::vector<const Value> args_;
 };
@@ -244,6 +295,8 @@
 
   // TODO: add builders for more instructions
 
+  DexBuilder* dex_file() const { return dex_; }
+
  private:
   void EncodeInstructions();
   void EncodeInstruction(const Instruction& instruction);
@@ -257,6 +310,7 @@
   void EncodeInvoke(const Instruction& instruction, ::art::Instruction::Code opcode);
   void EncodeBranch(art::Instruction::Code op, const Instruction& instruction);
   void EncodeNew(const Instruction& instruction);
+  void EncodeCast(const Instruction& instruction);
 
   // Low-level instruction format encoding. See
   // https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of
diff --git a/startop/view_compiler/dex_builder_test/Android.bp b/startop/view_compiler/dex_builder_test/Android.bp
index 4449ea0..d4f38ed 100644
--- a/startop/view_compiler/dex_builder_test/Android.bp
+++ b/startop/view_compiler/dex_builder_test/Android.bp
@@ -14,16 +14,30 @@
 // limitations under the License.
 //
 
+genrule {
+    name: "generate_compiled_layout",
+    tools: [":viewcompiler"],
+    cmd: "$(location :viewcompiler) $(in) --dex --out $(out) --package android.startop.test",
+    srcs: ["res/layout/layout1.xml"],
+    out: [
+        "layout1.dex",
+    ],
+}
+
 android_test {
     name: "dex-builder-test",
-    srcs: ["src/android/startop/test/DexBuilderTest.java"],
+    srcs: [
+        "src/android/startop/test/DexBuilderTest.java",
+        "src/android/startop/test/LayoutCompilerTest.java",
+    ],
     sdk_version: "current",
-    data: [":generate_dex_testcases"],
+    data: [":generate_dex_testcases", ":generate_compiled_layout"],
     static_libs: [
         "android-support-test",
         "guava",
     ],
     manifest: "AndroidManifest.xml",
+    resource_dirs: ["res"],
     test_config: "AndroidTest.xml",
     test_suites: ["general-tests"],
 }
diff --git a/startop/view_compiler/dex_builder_test/AndroidTest.xml b/startop/view_compiler/dex_builder_test/AndroidTest.xml
index 6f90cf3..68d8fdc 100644
--- a/startop/view_compiler/dex_builder_test/AndroidTest.xml
+++ b/startop/view_compiler/dex_builder_test/AndroidTest.xml
@@ -25,6 +25,7 @@
         <option name="cleanup" value="true" />
         <option name="push" value="trivial.dex->/data/local/tmp/dex-builder-test/trivial.dex" />
         <option name="push" value="simple.dex->/data/local/tmp/dex-builder-test/simple.dex" />
+        <option name="push" value="layout1.dex->/data/local/tmp/dex-builder-test/layout1.dex" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/startop/view_compiler/dex_builder_test/res/layout/layout1.xml b/startop/view_compiler/dex_builder_test/res/layout/layout1.xml
new file mode 100644
index 0000000..0f9375c
--- /dev/null
+++ b/startop/view_compiler/dex_builder_test/res/layout/layout1.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+   android:layout_width="match_parent"
+   android:layout_height="match_parent"
+   android:paddingLeft="16dp"
+   android:paddingRight="16dp"
+   android:orientation="vertical"
+   android:gravity="center">
+
+    <Button
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+    <Button
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
+ </LinearLayout>
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
index e20f3a9..42d4161 100644
--- a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
@@ -20,6 +20,7 @@
 import dalvik.system.InMemoryDexClassLoader;
 import dalvik.system.PathClassLoader;
 import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.nio.ByteBuffer;
 import org.junit.Assert;
@@ -84,6 +85,15 @@
   }
 
   @Test
+  public void returnIfNotZero() throws Exception {
+    ClassLoader loader = loadDexFile("simple.dex");
+    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+    Method method = clazz.getMethod("returnIfNotZero", int.class);
+    Assert.assertEquals(3, method.invoke(null, 0));
+    Assert.assertEquals(5, method.invoke(null, 17));
+  }
+
+  @Test
   public void backwardsBranch() throws Exception {
     ClassLoader loader = loadDexFile("simple.dex");
     Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
@@ -124,4 +134,41 @@
     Assert.assertEquals("b", method.invoke(null, 0));
     Assert.assertEquals("a", method.invoke(null, 1));
   }
+
+  @Test
+  public void invokeStaticReturnObject() throws Exception {
+    ClassLoader loader = loadDexFile("simple.dex");
+    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+    Method method = clazz.getMethod("invokeStaticReturnObject", int.class, int.class);
+    Assert.assertEquals("10", method.invoke(null, 10, 10));
+    Assert.assertEquals("a", method.invoke(null, 10, 16));
+    Assert.assertEquals("5", method.invoke(null, 5, 16));
+  }
+
+  @Test
+  public void invokeVirtualReturnObject() throws Exception {
+    ClassLoader loader = loadDexFile("simple.dex");
+    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+    Method method = clazz.getMethod("invokeVirtualReturnObject", String.class, int.class);
+    Assert.assertEquals("bc", method.invoke(null, "abc", 1));
+  }
+
+  @Test
+  public void castObjectToString() throws Exception {
+    ClassLoader loader = loadDexFile("simple.dex");
+    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+    Method method = clazz.getMethod("castObjectToString", Object.class);
+    Assert.assertEquals("abc", method.invoke(null, "abc"));
+    boolean castFailed = false;
+    try {
+      method.invoke(null, 5);
+    } catch (InvocationTargetException e) {
+      if (e.getCause() instanceof ClassCastException) {
+        castFailed = true;
+      } else {
+        throw e;
+      }
+    }
+    Assert.assertTrue(castFailed);
+  }
 }
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java
new file mode 100644
index 0000000..ce3ce83
--- /dev/null
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.startop.test;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.view.View;
+import com.google.common.io.ByteStreams;
+import dalvik.system.InMemoryDexClassLoader;
+import dalvik.system.PathClassLoader;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import org.junit.Assert;
+import org.junit.Test;
+
+// Adding tests here requires changes in several other places. See README.md in
+// the view_compiler directory for more information.
+public class LayoutCompilerTest {
+    static ClassLoader loadDexFile(String filename) throws Exception {
+        return new PathClassLoader("/data/local/tmp/dex-builder-test/" + filename,
+                ClassLoader.getSystemClassLoader());
+    }
+
+    @Test
+    public void loadAndInflaterLayout1() throws Exception {
+        ClassLoader dex_file = loadDexFile("layout1.dex");
+        Class compiled_view = dex_file.loadClass("android.startop.test.CompiledView");
+        Method layout1 = compiled_view.getMethod("layout1", Context.class, int.class);
+        Context context = InstrumentationRegistry.getTargetContext();
+        layout1.invoke(null, context, R.layout.layout1);
+    }
+}
diff --git a/startop/view_compiler/dex_layout_compiler.cc b/startop/view_compiler/dex_layout_compiler.cc
new file mode 100644
index 0000000..c68793d
--- /dev/null
+++ b/startop/view_compiler/dex_layout_compiler.cc
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dex_layout_compiler.h"
+#include "layout_validation.h"
+
+#include "android-base/stringprintf.h"
+
+namespace startop {
+
+using android::base::StringPrintf;
+
+void LayoutValidationVisitor::VisitStartTag(const std::u16string& name) {
+  if (0 == name.compare(u"merge")) {
+    message_ = "Merge tags are not supported";
+    can_compile_ = false;
+  }
+  if (0 == name.compare(u"include")) {
+    message_ = "Include tags are not supported";
+    can_compile_ = false;
+  }
+  if (0 == name.compare(u"view")) {
+    message_ = "View tags are not supported";
+    can_compile_ = false;
+  }
+  if (0 == name.compare(u"fragment")) {
+    message_ = "Fragment tags are not supported";
+    can_compile_ = false;
+  }
+}
+
+DexViewBuilder::DexViewBuilder(dex::MethodBuilder* method)
+    : method_{method},
+      context_{dex::Value::Parameter(0)},
+      resid_{dex::Value::Parameter(1)},
+      inflater_{method->MakeRegister()},
+      xml_{method->MakeRegister()},
+      attrs_{method->MakeRegister()},
+      classname_tmp_{method->MakeRegister()},
+      xml_next_{method->dex_file()->GetOrDeclareMethod(
+          dex::TypeDescriptor::FromClassname("android.content.res.XmlResourceParser"), "next",
+          dex::Prototype{dex::TypeDescriptor::Int()})},
+      try_create_view_{method->dex_file()->GetOrDeclareMethod(
+          dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"), "tryCreateView",
+          dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.View"),
+                         dex::TypeDescriptor::FromClassname("android.view.View"),
+                         dex::TypeDescriptor::FromClassname("java.lang.String"),
+                         dex::TypeDescriptor::FromClassname("android.content.Context"),
+                         dex::TypeDescriptor::FromClassname("android.util.AttributeSet")})},
+      generate_layout_params_{method->dex_file()->GetOrDeclareMethod(
+          dex::TypeDescriptor::FromClassname("android.view.ViewGroup"), "generateLayoutParams",
+          dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams"),
+                         dex::TypeDescriptor::FromClassname("android.util.AttributeSet")})},
+      add_view_{method->dex_file()->GetOrDeclareMethod(
+          dex::TypeDescriptor::FromClassname("android.view.ViewGroup"), "addView",
+          dex::Prototype{
+              dex::TypeDescriptor::Void(),
+              dex::TypeDescriptor::FromClassname("android.view.View"),
+              dex::TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams")})},
+      // The register stack starts with one register, which will be null for the root view.
+      register_stack_{{method->MakeRegister()}} {}
+
+void DexViewBuilder::Start() {
+  dex::DexBuilder* const dex = method_->dex_file();
+
+  // LayoutInflater inflater = LayoutInflater.from(context);
+  auto layout_inflater_from = dex->GetOrDeclareMethod(
+      dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"),
+      "from",
+      dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"),
+                     dex::TypeDescriptor::FromClassname("android.content.Context")});
+  method_->AddInstruction(
+      dex::Instruction::InvokeStaticObject(layout_inflater_from.id, /*dest=*/inflater_, context_));
+
+  // Resources res = context.getResources();
+  auto context_type = dex::TypeDescriptor::FromClassname("android.content.Context");
+  auto resources_type = dex::TypeDescriptor::FromClassname("android.content.res.Resources");
+  auto get_resources =
+      dex->GetOrDeclareMethod(context_type, "getResources", dex::Prototype{resources_type});
+  method_->AddInstruction(dex::Instruction::InvokeVirtualObject(get_resources.id, xml_, context_));
+
+  // XmlResourceParser xml = res.getLayout(resid);
+  auto xml_resource_parser_type =
+      dex::TypeDescriptor::FromClassname("android.content.res.XmlResourceParser");
+  auto get_layout =
+      dex->GetOrDeclareMethod(resources_type,
+                              "getLayout",
+                              dex::Prototype{xml_resource_parser_type, dex::TypeDescriptor::Int()});
+  method_->AddInstruction(dex::Instruction::InvokeVirtualObject(get_layout.id, xml_, xml_, resid_));
+
+  // AttributeSet attrs = Xml.asAttributeSet(xml);
+  auto as_attribute_set = dex->GetOrDeclareMethod(
+      dex::TypeDescriptor::FromClassname("android.util.Xml"),
+      "asAttributeSet",
+      dex::Prototype{dex::TypeDescriptor::FromClassname("android.util.AttributeSet"),
+                     dex::TypeDescriptor::FromClassname("org.xmlpull.v1.XmlPullParser")});
+  method_->AddInstruction(dex::Instruction::InvokeStaticObject(as_attribute_set.id, attrs_, xml_));
+
+  // xml.next(); // start document
+  method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_));
+}
+
+void DexViewBuilder::Finish() {}
+
+namespace {
+std::string ResolveName(const std::string& name) {
+  if (name == "View") return "android.view.View";
+  if (name == "ViewGroup") return "android.view.ViewGroup";
+  if (name.find(".") == std::string::npos) {
+    return StringPrintf("android.widget.%s", name.c_str());
+  }
+  return name;
+}
+}  // namespace
+
+void DexViewBuilder::StartView(const std::string& name, bool is_viewgroup) {
+  bool const is_root_view = view_stack_.empty();
+
+  // xml.next(); // start tag
+  method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_));
+
+  dex::Value view = AcquireRegister();
+  // try to create the view using the factories
+  method_->BuildConstString(classname_tmp_,
+                            name);  // TODO: the need to fully qualify the classname
+  if (is_root_view) {
+    dex::Value null = AcquireRegister();
+    method_->BuildConst4(null, 0);
+    method_->AddInstruction(dex::Instruction::InvokeVirtualObject(
+        try_create_view_.id, view, inflater_, null, classname_tmp_, context_, attrs_));
+    ReleaseRegister();
+  } else {
+    method_->AddInstruction(dex::Instruction::InvokeVirtualObject(
+        try_create_view_.id, view, inflater_, GetCurrentView(), classname_tmp_, context_, attrs_));
+  }
+  auto label = method_->MakeLabel();
+  // branch if not null
+  method_->AddInstruction(
+      dex::Instruction::OpWithArgs(dex::Instruction::Op::kBranchNEqz, /*dest=*/{}, view, label));
+
+  // If null, create the class directly.
+  method_->BuildNew(view,
+                    dex::TypeDescriptor::FromClassname(ResolveName(name)),
+                    dex::Prototype{dex::TypeDescriptor::Void(),
+                                   dex::TypeDescriptor::FromClassname("android.content.Context"),
+                                   dex::TypeDescriptor::FromClassname("android.util.AttributeSet")},
+                    context_,
+                    attrs_);
+
+  method_->AddInstruction(
+      dex::Instruction::OpWithArgs(dex::Instruction::Op::kBindLabel, /*dest=*/{}, label));
+
+  if (is_viewgroup) {
+    // Cast to a ViewGroup so we can add children later.
+    const ir::Type* view_group_def = method_->dex_file()->GetOrAddType(
+        dex::TypeDescriptor::FromClassname("android.view.ViewGroup").descriptor());
+    method_->AddInstruction(dex::Instruction::Cast(view, dex::Value::Type(view_group_def->orig_index)));
+  }
+
+  if (!is_root_view) {
+    // layout_params = parent.generateLayoutParams(attrs);
+    dex::Value layout_params{AcquireRegister()};
+    method_->AddInstruction(dex::Instruction::InvokeVirtualObject(
+        generate_layout_params_.id, layout_params, GetCurrentView(), attrs_));
+    view_stack_.push_back({view, layout_params});
+  } else {
+    view_stack_.push_back({view, {}});
+  }
+}
+
+void DexViewBuilder::FinishView() {
+  if (view_stack_.size() == 1) {
+    method_->BuildReturn(GetCurrentView(), /*is_object=*/true);
+  } else {
+    // parent.add(view, layout_params)
+    method_->AddInstruction(dex::Instruction::InvokeVirtual(
+        add_view_.id, /*dest=*/{}, GetParentView(), GetCurrentView(), GetCurrentLayoutParams()));
+    // xml.next(); // end tag
+    method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_));
+  }
+  PopViewStack();
+}
+
+dex::Value DexViewBuilder::AcquireRegister() {
+  top_register_++;
+  if (register_stack_.size() == top_register_) {
+    register_stack_.push_back(method_->MakeRegister());
+  }
+  return register_stack_[top_register_];
+}
+
+void DexViewBuilder::ReleaseRegister() { top_register_--; }
+
+dex::Value DexViewBuilder::GetCurrentView() const { return view_stack_.back().view; }
+dex::Value DexViewBuilder::GetCurrentLayoutParams() const {
+  return view_stack_.back().layout_params.value();
+}
+dex::Value DexViewBuilder::GetParentView() const {
+  return view_stack_[view_stack_.size() - 2].view;
+}
+
+void DexViewBuilder::PopViewStack() {
+  const auto& top = view_stack_.back();
+  // release the layout params if we have them
+  if (top.layout_params.has_value()) {
+    ReleaseRegister();
+  }
+  // Unconditionally release the view register.
+  ReleaseRegister();
+  view_stack_.pop_back();
+}
+
+}  // namespace startop
\ No newline at end of file
diff --git a/startop/view_compiler/dex_layout_compiler.h b/startop/view_compiler/dex_layout_compiler.h
new file mode 100644
index 0000000..170a1a6
--- /dev/null
+++ b/startop/view_compiler/dex_layout_compiler.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DEX_LAYOUT_COMPILER_H_
+#define DEX_LAYOUT_COMPILER_H_
+
+#include "dex_builder.h"
+
+#include <codecvt>
+#include <locale>
+#include <string>
+#include <vector>
+
+namespace startop {
+
+// This visitor does the actual view compilation, using a supplied builder.
+template <typename Builder>
+class LayoutCompilerVisitor {
+ public:
+  explicit LayoutCompilerVisitor(Builder* builder) : builder_{builder} {}
+
+  void VisitStartDocument() { builder_->Start(); }
+  void VisitEndDocument() { builder_->Finish(); }
+  void VisitStartTag(const std::u16string& name) {
+    parent_stack_.push_back(ViewEntry{
+        std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.to_bytes(name), {}});
+  }
+  void VisitEndTag() {
+    auto entry = parent_stack_.back();
+    parent_stack_.pop_back();
+
+    if (parent_stack_.empty()) {
+      GenerateCode(entry);
+    } else {
+      parent_stack_.back().children.push_back(entry);
+    }
+  }
+
+ private:
+  struct ViewEntry {
+    std::string name;
+    std::vector<ViewEntry> children;
+  };
+
+  void GenerateCode(const ViewEntry& view) {
+    builder_->StartView(view.name, !view.children.empty());
+    for (const auto& child : view.children) {
+      GenerateCode(child);
+    }
+    builder_->FinishView();
+  }
+
+  Builder* builder_;
+
+  std::vector<ViewEntry> parent_stack_;
+};
+
+class DexViewBuilder {
+ public:
+  DexViewBuilder(dex::MethodBuilder* method);
+
+  void Start();
+  void Finish();
+  void StartView(const std::string& name, bool is_viewgroup);
+  void FinishView();
+
+ private:
+  // Accessors for the stack of views that are under construction.
+  dex::Value AcquireRegister();
+  void ReleaseRegister();
+  dex::Value GetCurrentView() const;
+  dex::Value GetCurrentLayoutParams() const;
+  dex::Value GetParentView() const;
+  void PopViewStack();
+
+  dex::MethodBuilder* method_;
+
+  // Registers used for code generation
+  dex::Value const context_;
+  dex::Value const resid_;
+  const dex::Value inflater_;
+  const dex::Value xml_;
+  const dex::Value attrs_;
+  const dex::Value classname_tmp_;
+
+  const dex::MethodDeclData xml_next_;
+  const dex::MethodDeclData try_create_view_;
+  const dex::MethodDeclData generate_layout_params_;
+  const dex::MethodDeclData add_view_;
+
+  // used for keeping track of which registers are in use
+  size_t top_register_{0};
+  std::vector<dex::Value> register_stack_;
+
+  // Keep track of the views currently in progress.
+  struct ViewEntry {
+    dex::Value view;
+    std::optional<dex::Value> layout_params;
+  };
+  std::vector<ViewEntry> view_stack_;
+};
+
+}  // namespace startop
+
+#endif  // DEX_LAYOUT_COMPILER_H_
diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc
index e2bf43bc..f62ec5dd 100644
--- a/startop/view_compiler/dex_testcase_generator.cc
+++ b/startop/view_compiler/dex_testcase_generator.cc
@@ -108,6 +108,27 @@
   }
   returnIfZero.Encode();
 
+  // int returnIfNotZero(int x) { if (x != 0) { return 5; } else { return 3; } }
+  MethodBuilder returnIfNotZero{cbuilder.CreateMethod(
+      "returnIfNotZero", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
+  {
+    Value resultIfNotZero{returnIfNotZero.MakeRegister()};
+    Value else_target{returnIfNotZero.MakeLabel()};
+    returnIfNotZero.AddInstruction(Instruction::OpWithArgs(
+        Instruction::Op::kBranchNEqz, /*dest=*/{}, Value::Parameter(0), else_target));
+    // else branch
+    returnIfNotZero.BuildConst4(resultIfNotZero, 3);
+    returnIfNotZero.AddInstruction(
+        Instruction::OpWithArgs(Instruction::Op::kReturn, /*dest=*/{}, resultIfNotZero));
+    // then branch
+    returnIfNotZero.AddInstruction(
+        Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, else_target));
+    returnIfNotZero.BuildConst4(resultIfNotZero, 5);
+    returnIfNotZero.AddInstruction(
+        Instruction::OpWithArgs(Instruction::Op::kReturn, /*dest=*/{}, resultIfNotZero));
+  }
+  returnIfNotZero.Encode();
+
   // Make sure backwards branches work too.
   //
   // Pseudo code for test:
@@ -216,6 +237,51 @@
     method.Encode();
   }(returnStringIfZeroBA);
 
+  // Make sure we can invoke static methods that return an object
+  // String invokeStaticReturnObject(int n, int radix) { return java.lang.Integer.toString(n,
+  // radix); }
+  MethodBuilder invokeStaticReturnObject{
+      cbuilder.CreateMethod("invokeStaticReturnObject",
+                            Prototype{string_type, TypeDescriptor::Int(), TypeDescriptor::Int()})};
+  [&](MethodBuilder& method) {
+    Value result{method.MakeRegister()};
+    MethodDeclData to_string{dex_file.GetOrDeclareMethod(
+        TypeDescriptor::FromClassname("java.lang.Integer"),
+        "toString",
+        Prototype{string_type, TypeDescriptor::Int(), TypeDescriptor::Int()})};
+    method.AddInstruction(Instruction::InvokeStaticObject(
+        to_string.id, result, Value::Parameter(0), Value::Parameter(1)));
+    method.BuildReturn(result, /*is_object=*/true);
+    method.Encode();
+  }(invokeStaticReturnObject);
+
+  // Make sure we can invoke virtual methods that return an object
+  // String invokeVirtualReturnObject(String s, int n) { return s.substring(n); }
+  MethodBuilder invokeVirtualReturnObject{cbuilder.CreateMethod(
+      "invokeVirtualReturnObject", Prototype{string_type, string_type, TypeDescriptor::Int()})};
+  [&](MethodBuilder& method) {
+    Value result{method.MakeRegister()};
+    MethodDeclData substring{dex_file.GetOrDeclareMethod(
+        string_type, "substring", Prototype{string_type, TypeDescriptor::Int()})};
+    method.AddInstruction(Instruction::InvokeVirtualObject(
+        substring.id, result, Value::Parameter(0), Value::Parameter(1)));
+    method.BuildReturn(result, /*is_object=*/true);
+    method.Encode();
+  }(invokeVirtualReturnObject);
+
+  // Make sure we can cast objects
+  // String castObjectToString(Object o) { return (String)o; }
+  MethodBuilder castObjectToString{cbuilder.CreateMethod(
+      "castObjectToString",
+      Prototype{string_type, TypeDescriptor::FromClassname("java.lang.Object")})};
+  [&](MethodBuilder& method) {
+    const ir::Type* type_def = dex_file.GetOrAddType(string_type.descriptor());
+    method.AddInstruction(
+        Instruction::Cast(Value::Parameter(0), Value::Type(type_def->orig_index)));
+    method.BuildReturn(Value::Parameter(0), /*is_object=*/true);
+    method.Encode();
+  }(castObjectToString);
+
   slicer::MemView image{dex_file.CreateImage()};
   std::ofstream out_file(outdir + "/simple.dex");
   out_file.write(image.ptr<const char>(), image.size());
diff --git a/startop/view_compiler/java_lang_builder.cc b/startop/view_compiler/java_lang_builder.cc
index 0b8754f..920caee 100644
--- a/startop/view_compiler/java_lang_builder.cc
+++ b/startop/view_compiler/java_lang_builder.cc
@@ -67,7 +67,7 @@
           "}\n";     // end CompiledView
 }
 
-void JavaLangViewBuilder::StartView(const string& class_name) {
+void JavaLangViewBuilder::StartView(const string& class_name, bool /*is_viewgroup*/) {
   const string view_var = MakeVar("view");
   const string layout_var = MakeVar("layout");
   std::string parent = "null";
diff --git a/startop/view_compiler/java_lang_builder.h b/startop/view_compiler/java_lang_builder.h
index c8d20b2..69356d3 100644
--- a/startop/view_compiler/java_lang_builder.h
+++ b/startop/view_compiler/java_lang_builder.h
@@ -35,7 +35,7 @@
   void Finish() const;
 
   // Begin creating a view (i.e. process the opening tag)
-  void StartView(const std::string& class_name);
+  void StartView(const std::string& class_name, bool is_viewgroup);
   // Finish a view, after all of its child nodes have been processed.
   void FinishView();
 
diff --git a/startop/view_compiler/layout_validation.cc b/startop/view_compiler/layout_validation.cc
new file mode 100644
index 0000000..8c77377
--- /dev/null
+++ b/startop/view_compiler/layout_validation.cc
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "layout_validation.h"
+
+#include "android-base/stringprintf.h"
+
+namespace startop {
+
+void LayoutValidationVisitor::VisitStartTag(const std::u16string& name) {
+  if (0 == name.compare(u"merge")) {
+    message_ = "Merge tags are not supported";
+    can_compile_ = false;
+  }
+  if (0 == name.compare(u"include")) {
+    message_ = "Include tags are not supported";
+    can_compile_ = false;
+  }
+  if (0 == name.compare(u"view")) {
+    message_ = "View tags are not supported";
+    can_compile_ = false;
+  }
+  if (0 == name.compare(u"fragment")) {
+    message_ = "Fragment tags are not supported";
+    can_compile_ = false;
+  }
+}
+
+}  // namespace startop
\ No newline at end of file
diff --git a/startop/view_compiler/layout_validation.h b/startop/view_compiler/layout_validation.h
new file mode 100644
index 0000000..bed34bb
--- /dev/null
+++ b/startop/view_compiler/layout_validation.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LAYOUT_VALIDATION_H_
+#define LAYOUT_VALIDATION_H_
+
+#include "dex_builder.h"
+
+#include <string>
+
+namespace startop {
+
+// This visitor determines whether a layout can be compiled. Since we do not currently support all
+// features, such as includes and merges, we need to pre-validate the layout before we start
+// compiling.
+class LayoutValidationVisitor {
+ public:
+  void VisitStartDocument() const {}
+  void VisitEndDocument() const {}
+  void VisitStartTag(const std::u16string& name);
+  void VisitEndTag() const {}
+
+  const std::string& message() const { return message_; }
+  bool can_compile() const { return can_compile_; }
+
+ private:
+  std::string message_{"Okay"};
+  bool can_compile_{true};
+};
+
+}  // namespace startop
+
+#endif  // LAYOUT_VALIDATION_H_
diff --git a/startop/view_compiler/layout_validation_test.cc b/startop/view_compiler/layout_validation_test.cc
new file mode 100644
index 0000000..b74cdae
--- /dev/null
+++ b/startop/view_compiler/layout_validation_test.cc
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "tinyxml_layout_parser.h"
+
+#include "gtest/gtest.h"
+
+using startop::CanCompileLayout;
+using std::string;
+
+namespace {
+void ValidateXmlText(const string& xml, bool expected) {
+  tinyxml2::XMLDocument doc;
+  doc.Parse(xml.c_str());
+  EXPECT_EQ(CanCompileLayout(doc), expected);
+}
+}  // namespace
+
+TEST(LayoutValidationTest, SingleButtonLayout) {
+  const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:text="Hello, World!">
+
+</Button>)";
+  ValidateXmlText(xml, /*expected=*/true);
+}
+
+TEST(LayoutValidationTest, SmallConstraintLayout) {
+  const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <Button
+        android:id="@+id/button6"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="16dp"
+        android:layout_marginBottom="16dp"
+        android:text="Button"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent" />
+
+    <Button
+        android:id="@+id/button7"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="8dp"
+        android:layout_marginBottom="16dp"
+        android:text="Button2"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toStartOf="@+id/button6" />
+
+    <Button
+        android:id="@+id/button8"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="8dp"
+        android:layout_marginBottom="16dp"
+        android:text="Button1"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toStartOf="@+id/button7" />
+</android.support.constraint.ConstraintLayout>)";
+  ValidateXmlText(xml, /*expected=*/true);
+}
+
+TEST(LayoutValidationTest, MergeNode) {
+  const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <TextView
+        android:id="@+id/textView3"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="TextView" />
+
+    <Button
+        android:id="@+id/button9"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Button" />
+</merge>)";
+  ValidateXmlText(xml, /*expected=*/false);
+}
+
+TEST(LayoutValidationTest, IncludeLayout) {
+  const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <include
+        layout="@layout/single_button_layout"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+</android.support.constraint.ConstraintLayout>)";
+  ValidateXmlText(xml, /*expected=*/false);
+}
+
+TEST(LayoutValidationTest, ViewNode) {
+  const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <view
+        class="android.support.design.button.MaterialButton"
+        id="@+id/view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+</android.support.constraint.ConstraintLayout>)";
+  ValidateXmlText(xml, /*expected=*/false);
+}
+
+TEST(LayoutValidationTest, FragmentNode) {
+  // This test case is from https://developer.android.com/guide/components/fragments
+  const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <fragment android:name="com.example.news.ArticleListFragment"
+            android:id="@+id/list"
+            android:layout_weight="1"
+            android:layout_width="0dp"
+            android:layout_height="match_parent" />
+    <fragment android:name="com.example.news.ArticleReaderFragment"
+            android:id="@+id/viewer"
+            android:layout_weight="2"
+            android:layout_width="0dp"
+            android:layout_height="match_parent" />
+</LinearLayout>)";
+  ValidateXmlText(xml, /*expected=*/false);
+}
diff --git a/startop/view_compiler/main.cc b/startop/view_compiler/main.cc
index 7d791c2..ae00187 100644
--- a/startop/view_compiler/main.cc
+++ b/startop/view_compiler/main.cc
@@ -16,8 +16,12 @@
 
 #include "gflags/gflags.h"
 
+#include "android-base/stringprintf.h"
 #include "dex_builder.h"
+#include "dex_layout_compiler.h"
 #include "java_lang_builder.h"
+#include "layout_validation.h"
+#include "tinyxml_layout_parser.h"
 #include "util.h"
 
 #include "tinyxml2.h"
@@ -31,6 +35,13 @@
 namespace {
 
 using namespace tinyxml2;
+using android::base::StringPrintf;
+using startop::dex::ClassBuilder;
+using startop::dex::DexBuilder;
+using startop::dex::MethodBuilder;
+using startop::dex::Prototype;
+using startop::dex::TypeDescriptor;
+using namespace startop::util;
 using std::string;
 
 constexpr char kStdoutFilename[]{"stdout"};
@@ -39,34 +50,44 @@
 DEFINE_string(out, kStdoutFilename, "Where to write the generated class");
 DEFINE_string(package, "", "The package name for the generated class (required)");
 
-class ViewCompilerXmlVisitor : public XMLVisitor {
+template <typename Visitor>
+class XmlVisitorAdapter : public XMLVisitor {
  public:
-  ViewCompilerXmlVisitor(JavaLangViewBuilder* builder) : builder_(builder) {}
+  explicit XmlVisitorAdapter(Visitor* visitor) : visitor_{visitor} {}
 
   bool VisitEnter(const XMLDocument& /*doc*/) override {
-    builder_->Start();
+    visitor_->VisitStartDocument();
     return true;
   }
 
   bool VisitExit(const XMLDocument& /*doc*/) override {
-    builder_->Finish();
+    visitor_->VisitEndDocument();
     return true;
   }
 
   bool VisitEnter(const XMLElement& element, const XMLAttribute* /*firstAttribute*/) override {
-    builder_->StartView(element.Name());
+    visitor_->VisitStartTag(
+        std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(
+            element.Name()));
     return true;
   }
 
   bool VisitExit(const XMLElement& /*element*/) override {
-    builder_->FinishView();
+    visitor_->VisitEndTag();
     return true;
   }
 
  private:
-  JavaLangViewBuilder* builder_;
+  Visitor* visitor_;
 };
 
+template <typename Builder>
+void CompileLayout(XMLDocument* xml, Builder* builder) {
+  startop::LayoutCompilerVisitor visitor{builder};
+  XmlVisitorAdapter<decltype(visitor)> adapter{&visitor};
+  xml->Accept(&adapter);
+}
+
 }  // end namespace
 
 int main(int argc, char** argv) {
@@ -86,29 +107,46 @@
     return 1;
   }
 
-  if (FLAGS_dex) {
-    startop::dex::WriteTestDexFile("test.dex");
-    return 0;
-  }
-
   const char* const filename = argv[kFileNameParam];
-  const string layout_name = FindLayoutNameFromFilename(filename);
-
-  // We want to generate Java language code to inflate exactly this layout. This means
-  // generating code to walk the resource XML too.
+  const string layout_name = startop::util::FindLayoutNameFromFilename(filename);
 
   XMLDocument xml;
   xml.LoadFile(filename);
 
+  string message{};
+  if (!startop::CanCompileLayout(xml, &message)) {
+    LOG(ERROR) << "Layout not supported: " << message;
+    return 1;
+  }
+
+  const bool is_stdout = FLAGS_out == kStdoutFilename;
+
   std::ofstream outfile;
-  if (FLAGS_out != kStdoutFilename) {
+  if (!is_stdout) {
     outfile.open(FLAGS_out);
   }
-  JavaLangViewBuilder builder{
-      FLAGS_package, layout_name, FLAGS_out == kStdoutFilename ? std::cout : outfile};
 
-  ViewCompilerXmlVisitor visitor{&builder};
-  xml.Accept(&visitor);
+  if (FLAGS_dex) {
+    DexBuilder dex_file;
+    string class_name = StringPrintf("%s.CompiledView", FLAGS_package.c_str());
+    ClassBuilder compiled_view{dex_file.MakeClass(class_name)};
+    MethodBuilder method{compiled_view.CreateMethod(
+        layout_name,
+        Prototype{TypeDescriptor::FromClassname("android.view.View"),
+                  TypeDescriptor::FromClassname("android.content.Context"),
+                  TypeDescriptor::Int()})};
+    startop::DexViewBuilder builder{&method};
+    CompileLayout(&xml, &builder);
+    method.Encode();
 
+    slicer::MemView image{dex_file.CreateImage()};
+
+    (is_stdout ? std::cout : outfile).write(image.ptr<const char>(), image.size());
+  } else {
+    // Generate Java language output.
+    JavaLangViewBuilder builder{FLAGS_package, layout_name, is_stdout ? std::cout : outfile};
+
+    CompileLayout(&xml, &builder);
+  }
   return 0;
 }
diff --git a/startop/view_compiler/tinyxml_layout_parser.cc b/startop/view_compiler/tinyxml_layout_parser.cc
new file mode 100644
index 0000000..1b3a81f
--- /dev/null
+++ b/startop/view_compiler/tinyxml_layout_parser.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "tinyxml_layout_parser.h"
+
+#include "layout_validation.h"
+
+namespace startop {
+
+bool CanCompileLayout(const tinyxml2::XMLDocument& xml, std::string* message) {
+  LayoutValidationVisitor validator;
+  TinyXmlVisitorAdapter adapter{&validator};
+  xml.Accept(&adapter);
+
+  if (message != nullptr) {
+    *message = validator.message();
+  }
+
+  return validator.can_compile();
+}
+
+}  // namespace startop
diff --git a/startop/view_compiler/tinyxml_layout_parser.h b/startop/view_compiler/tinyxml_layout_parser.h
new file mode 100644
index 0000000..8f714a2
--- /dev/null
+++ b/startop/view_compiler/tinyxml_layout_parser.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef TINYXML_LAYOUT_PARSER_H_
+#define TINYXML_LAYOUT_PARSER_H_
+
+#include "tinyxml2.h"
+
+#include <codecvt>
+#include <locale>
+#include <string>
+
+namespace startop {
+
+template <typename Visitor>
+class TinyXmlVisitorAdapter : public tinyxml2::XMLVisitor {
+ public:
+  explicit TinyXmlVisitorAdapter(Visitor* visitor) : visitor_{visitor} {}
+
+  bool VisitEnter(const tinyxml2::XMLDocument& /*doc*/) override {
+    visitor_->VisitStartDocument();
+    return true;
+  }
+
+  bool VisitExit(const tinyxml2::XMLDocument& /*doc*/) override {
+    visitor_->VisitEndDocument();
+    return true;
+  }
+
+  bool VisitEnter(const tinyxml2::XMLElement& element,
+                  const tinyxml2::XMLAttribute* /*firstAttribute*/) override {
+    visitor_->VisitStartTag(
+        std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(
+            element.Name()));
+    return true;
+  }
+
+  bool VisitExit(const tinyxml2::XMLElement& /*element*/) override {
+    visitor_->VisitEndTag();
+    return true;
+  }
+
+ private:
+  Visitor* visitor_;
+};
+
+// Returns whether a layout resource represented by a TinyXML document is supported by the layout
+// compiler.
+bool CanCompileLayout(const tinyxml2::XMLDocument& xml, std::string* message = nullptr);
+
+}  // namespace startop
+
+#endif  // TINYXML_LAYOUT_PARSER_H_
diff --git a/startop/view_compiler/util.cc b/startop/view_compiler/util.cc
index 69df41d..a0637e6 100644
--- a/startop/view_compiler/util.cc
+++ b/startop/view_compiler/util.cc
@@ -18,6 +18,9 @@
 
 using std::string;
 
+namespace startop {
+namespace util {
+
 // TODO: see if we can borrow this from somewhere else, like aapt2.
 string FindLayoutNameFromFilename(const string& filename) {
   size_t start = filename.rfind("/");
@@ -30,3 +33,6 @@
 
   return filename.substr(start, end - start);
 }
+
+}  // namespace util
+}  // namespace startop
diff --git a/startop/view_compiler/util.h b/startop/view_compiler/util.h
index 03e0939..0176175 100644
--- a/startop/view_compiler/util.h
+++ b/startop/view_compiler/util.h
@@ -13,11 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#ifndef UTIL_H_
-#define UTIL_H_
+#ifndef VIEW_COMPILER_UTIL_H_
+#define VIEW_COMPILER_UTIL_H_
 
 #include <string>
 
+namespace startop {
+namespace util {
+
 std::string FindLayoutNameFromFilename(const std::string& filename);
 
-#endif  // UTIL_H_
+}  // namespace util
+}  // namespace startop
+
+#endif  // VIEW_COMPILER_UTIL_H_
diff --git a/startop/view_compiler/util_test.cc b/startop/view_compiler/util_test.cc
index d1540d3..50682a0 100644
--- a/startop/view_compiler/util_test.cc
+++ b/startop/view_compiler/util_test.cc
@@ -20,9 +20,15 @@
 
 using std::string;
 
+namespace startop {
+namespace util {
+
 TEST(UtilTest, FindLayoutNameFromFilename) {
-  EXPECT_EQ("bar", ::FindLayoutNameFromFilename("foo/bar.xml"));
-  EXPECT_EQ("bar", ::FindLayoutNameFromFilename("bar.xml"));
-  EXPECT_EQ("bar", ::FindLayoutNameFromFilename("./foo/bar.xml"));
-  EXPECT_EQ("bar", ::FindLayoutNameFromFilename("/foo/bar.xml"));
+  EXPECT_EQ("bar", startop::util::FindLayoutNameFromFilename("foo/bar.xml"));
+  EXPECT_EQ("bar", startop::util::FindLayoutNameFromFilename("bar.xml"));
+  EXPECT_EQ("bar", startop::util::FindLayoutNameFromFilename("./foo/bar.xml"));
+  EXPECT_EQ("bar", startop::util::FindLayoutNameFromFilename("/foo/bar.xml"));
 }
+
+}  // namespace util
+}  // namespace startop
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 36d0188..2820836 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -518,6 +518,7 @@
         private final Bundle mExtras;
         private final Bundle mIntentExtras;
         private final long mCreationTimeMillis;
+        private final CallIdentification mCallIdentification;
 
         /**
          * Whether the supplied capabilities  supports the specified capability.
@@ -699,6 +700,12 @@
         }
 
         /**
+         * The display name for the caller.
+         * <p>
+         * This is the name as reported by the {@link ConnectionService} associated with this call.
+         * The name reported by a {@link CallScreeningService} can be retrieved using
+         * {@link CallIdentification#getName()}.
+         *
          * @return The display name for the caller.
          */
         public String getCallerDisplayName() {
@@ -814,6 +821,23 @@
             return mCreationTimeMillis;
         }
 
+        /**
+         * Returns {@link CallIdentification} information provided by a
+         * {@link CallScreeningService} for this call.
+         * <p>
+         * {@link InCallService} implementations should display the {@link CallIdentification} for
+         * calls.  The name of the call screening service is provided in
+         * {@link CallIdentification#getCallScreeningAppName()} and should be used to attribute the
+         * call identification information.
+         *
+         * @return The {@link CallIdentification} if it was provided by a
+         * {@link CallScreeningService}, or {@code null} if no {@link CallScreeningService} has
+         * provided {@link CallIdentification} information for the call.
+         */
+        public @Nullable CallIdentification getCallIdentification() {
+            return mCallIdentification;
+        }
+
         @Override
         public boolean equals(Object o) {
             if (o instanceof Details) {
@@ -834,7 +858,8 @@
                         Objects.equals(mStatusHints, d.mStatusHints) &&
                         areBundlesEqual(mExtras, d.mExtras) &&
                         areBundlesEqual(mIntentExtras, d.mIntentExtras) &&
-                        Objects.equals(mCreationTimeMillis, d.mCreationTimeMillis);
+                        Objects.equals(mCreationTimeMillis, d.mCreationTimeMillis) &&
+                        Objects.equals(mCallIdentification, d.mCallIdentification);
             }
             return false;
         }
@@ -855,7 +880,8 @@
                             mStatusHints,
                             mExtras,
                             mIntentExtras,
-                            mCreationTimeMillis);
+                            mCreationTimeMillis,
+                            mCallIdentification);
         }
 
         /** {@hide} */
@@ -875,7 +901,8 @@
                 StatusHints statusHints,
                 Bundle extras,
                 Bundle intentExtras,
-                long creationTimeMillis) {
+                long creationTimeMillis,
+                CallIdentification callIdentification) {
             mTelecomCallId = telecomCallId;
             mHandle = handle;
             mHandlePresentation = handlePresentation;
@@ -892,6 +919,7 @@
             mExtras = extras;
             mIntentExtras = intentExtras;
             mCreationTimeMillis = creationTimeMillis;
+            mCallIdentification = callIdentification;
         }
 
         /** {@hide} */
@@ -912,7 +940,8 @@
                     parcelableCall.getStatusHints(),
                     parcelableCall.getExtras(),
                     parcelableCall.getIntentExtras(),
-                    parcelableCall.getCreationTimeMillis());
+                    parcelableCall.getCreationTimeMillis(),
+                    parcelableCall.getCallIdentification());
         }
 
         @Override
diff --git a/core/java/android/service/contentcapture/InteractionContext.aidl b/telecomm/java/android/telecom/CallIdentification.aidl
similarity index 81%
copy from core/java/android/service/contentcapture/InteractionContext.aidl
copy to telecomm/java/android/telecom/CallIdentification.aidl
index 982e095..532535c 100644
--- a/core/java/android/service/contentcapture/InteractionContext.aidl
+++ b/telecomm/java/android/telecom/CallIdentification.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
+/*
+ * Copyright 2018, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
-package android.service.contentcapture;
+package android.telecom;
 
-parcelable InteractionContext;
+/**
+ * {@hide}
+ */
+parcelable CallIdentification;
diff --git a/telecomm/java/android/telecom/CallIdentification.java b/telecomm/java/android/telecom/CallIdentification.java
new file mode 100644
index 0000000..97af06c
--- /dev/null
+++ b/telecomm/java/android/telecom/CallIdentification.java
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Encapsulates information about an incoming or outgoing {@link Call} provided by a
+ * {@link CallScreeningService}.
+ * <p>
+ * Call identified information is consumed by the {@link InCallService dialer} app to provide the
+ * user with more information about a call.  This can include information such as the name of the
+ * caller, address, etc.  Call identification information is persisted to the
+ * {@link android.provider.CallLog}.
+ */
+public final class CallIdentification implements Parcelable {
+    /**
+     * Builder for {@link CallIdentification} instances.
+     * <p>
+     * A {@link CallScreeningService} uses this class to create new instances of
+     * {@link CallIdentification} for a screened call.
+     */
+    public static class Builder {
+        private String mName;
+        private String mDescription;
+        private String mDetails;
+        private Icon mPhoto;
+        private int mNuisanceConfidence = CallIdentification.CONFIDENCE_UNKNOWN;
+        private String mPackageName;
+        private String mAppName;
+
+        /**
+         * Default builder constructor.
+         */
+        public Builder() {
+            // Default constructor
+        }
+
+        /**
+         * Create instance of call identification with specified package/app name.
+         *
+         * @param callIdPackageName The package name.
+         * @param callIdAppName The app name.
+         * @hide
+         */
+        public Builder(String callIdPackageName, String callIdAppName) {
+            mPackageName = callIdPackageName;
+            mAppName = callIdAppName;
+        }
+
+        /**
+         * Sets the name associated with the {@link CallIdentification} being built.
+         * <p>
+         * Could be a business name, for example.
+         *
+         * @param name The name associated with the call, or {@code null} if none is provided.
+         * @return Builder instance.
+         */
+        public Builder setName(@Nullable String name) {
+            mName = name;
+            return this;
+        }
+
+        /**
+         * Sets the description associated with the {@link CallIdentification} being built.
+         * <p>
+         * A description of the call as identified by a {@link CallScreeningService}.  The
+         * description is typically presented by Dialer apps after the
+         * {@link CallIdentification#getName() name} to provide a short piece of relevant
+         * information about the call.  This could include a location, address, or a message
+         * regarding the potential nature of the call (e.g. potential telemarketer).
+         *
+         * @param description The call description, or {@code null} if none is provided.
+         * @return Builder instance.
+         */
+        public Builder setDescription(@Nullable String description) {
+            mDescription = description;
+            return this;
+        }
+
+        /**
+         * Sets the details associated with the {@link CallIdentification} being built.
+         * <p>
+         * The details is typically presented by Dialer apps after the
+         * {@link CallIdentification#getName() name} and
+         * {@link CallIdentification#getDescription() description} to provide further clarifying
+         * information about the call. This could include, for example, the opening hours of a
+         * business, or a stats about the number of times a call has been reported as spam.
+         *
+         * @param details The call details, or {@code null} if none is provided.
+         * @return Builder instance.
+         */
+        public Builder setDetails(@Nullable String details) {
+            mDetails = details;
+            return this;
+        }
+
+        /**
+         * Sets the photo associated with the {@link CallIdentification} being built.
+         * <p>
+         * This could be, for example, a business logo, or a photo of the caller.
+         *
+         * @param photo The photo associated with the call, or {@code null} if none was provided.
+         * @return Builder instance.
+         */
+        public Builder setPhoto(@Nullable Icon photo) {
+            mPhoto = photo;
+            return this;
+        }
+
+        /**
+         * Sets the nuisance confidence with the {@link CallIdentification} being built.
+         * <p>
+         * This can be used to specify how confident the {@link CallScreeningService} is that a call
+         * is or is not a nuisance call.
+         *
+         * @param nuisanceConfidence The nuisance confidence.
+         * @return The builder.
+         */
+        public Builder setNuisanceConfidence(@NuisanceConfidence int nuisanceConfidence) {
+            mNuisanceConfidence = nuisanceConfidence;
+            return this;
+        }
+
+        /**
+         * Creates a new instance of {@link CallIdentification} based on the parameters set in this
+         * builder.
+         *
+         * @return {@link CallIdentification} instance.
+         */
+        public CallIdentification build() {
+            return new CallIdentification(mName, mDescription, mDetails, mPhoto,
+                    mNuisanceConfidence, mPackageName, mAppName);
+        }
+    }
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            prefix = { "CONFIDENCE_" },
+            value = {CONFIDENCE_NUISANCE, CONFIDENCE_LIKELY_NUISANCE, CONFIDENCE_UNKNOWN,
+                    CONFIDENCE_LIKELY_NOT_NUISANCE, CONFIDENCE_NOT_NUISANCE})
+    public @interface NuisanceConfidence {}
+
+    /**
+     * Call has been identified as a nuisance call.
+     * <p>
+     * Returned from {@link #getNuisanceConfidence()} to indicate that a
+     * {@link CallScreeningService} to indicate how confident it is that a call is or is not a
+     * nuisance call.
+     */
+    public static final int CONFIDENCE_NUISANCE = 2;
+
+    /**
+     * Call has been identified as a likely nuisance call.
+     * <p>
+     * Returned from {@link #getNuisanceConfidence()} to indicate that a
+     * {@link CallScreeningService} to indicate how confident it is that a call is or is not a
+     * nuisance call.
+     */
+    public static final int CONFIDENCE_LIKELY_NUISANCE = 1;
+
+    /**
+     * Call could not be classified as nuisance or non-nuisance.
+     * <p>
+     * Returned from {@link #getNuisanceConfidence()} to indicate that a
+     * {@link CallScreeningService} to indicate how confident it is that a call is or is not a
+     * nuisance call.
+     */
+    public static final int CONFIDENCE_UNKNOWN = 0;
+
+    /**
+     * Call has been identified as not likely to be a nuisance call.
+     * <p>
+     * Returned from {@link #getNuisanceConfidence()} to indicate that a
+     * {@link CallScreeningService} to indicate how confident it is that a call is or is not a
+     * nuisance call.
+     */
+    public static final int CONFIDENCE_LIKELY_NOT_NUISANCE = -1;
+
+    /**
+     * Call has been identified as not a nuisance call.
+     * <p>
+     * Returned from {@link #getNuisanceConfidence()} to indicate that a
+     * {@link CallScreeningService} to indicate how confident it is that a call is or is not a
+     * nuisance call.
+     */
+    public static final int CONFIDENCE_NOT_NUISANCE = -2;
+
+    /**
+     * Default constructor for {@link CallIdentification}.
+     *
+     * @param name The name.
+     * @param description The description.
+     * @param details The details.
+     * @param photo The photo.
+     * @param nuisanceConfidence Confidence that this is a nuisance call.
+     * @hide
+     */
+    private CallIdentification(@Nullable String name, @Nullable String description,
+            @Nullable String details, @Nullable Icon photo,
+            @NuisanceConfidence int nuisanceConfidence) {
+        this(name, description, details, photo, nuisanceConfidence, null, null);
+    }
+
+    /**
+     * Default constructor for {@link CallIdentification}.
+     *
+     * @param name The name.
+     * @param description The description.
+     * @param details The details.
+     * @param photo The photo.
+     * @param nuisanceConfidence Confidence that this is a nuisance call.
+     * @param callScreeningPackageName Package name of the {@link CallScreeningService} which
+     *                                 provided the call identification.
+     * @param callScreeningAppName App name of the {@link CallScreeningService} which provided the
+     *                             call identification.
+     * @hide
+     */
+    private CallIdentification(@Nullable String name, @Nullable String description,
+            @Nullable String details, @Nullable Icon photo,
+            @NuisanceConfidence int nuisanceConfidence, @NonNull String callScreeningPackageName,
+            @NonNull String callScreeningAppName) {
+        mName = name;
+        mDescription = description;
+        mDetails = details;
+        mPhoto = photo;
+        mNuisanceConfidence = nuisanceConfidence;
+        mCallScreeningAppName = callScreeningPackageName;
+        mCallScreeningPackageName = callScreeningAppName;
+    }
+
+    private String mName;
+    private String mDescription;
+    private String mDetails;
+    private Icon mPhoto;
+    private int mNuisanceConfidence;
+    private String mCallScreeningPackageName;
+    private String mCallScreeningAppName;
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int i) {
+        parcel.writeString(mName);
+        parcel.writeString(mDescription);
+        parcel.writeString(mDetails);
+        parcel.writeParcelable(mPhoto, 0);
+        parcel.writeInt(mNuisanceConfidence);
+        parcel.writeString(mCallScreeningPackageName);
+        parcel.writeString(mCallScreeningAppName);
+    }
+
+    /**
+     * Responsible for creating CallIdentification objects for deserialized Parcels.
+     */
+    public static final Parcelable.Creator<CallIdentification> CREATOR =
+            new Parcelable.Creator<CallIdentification> () {
+
+                @Override
+                public CallIdentification createFromParcel(Parcel source) {
+                    String name = source.readString();
+                    String description = source.readString();
+                    String details = source.readString();
+                    Icon photo = source.readParcelable(ClassLoader.getSystemClassLoader());
+                    int nuisanceConfidence = source.readInt();
+                    String callScreeningPackageName = source.readString();
+                    String callScreeningAppName = source.readString();
+                    return new CallIdentification(name, description, details, photo,
+                            nuisanceConfidence, callScreeningPackageName, callScreeningAppName);
+                }
+
+                @Override
+                public CallIdentification[] newArray(int size) {
+                    return new CallIdentification[size];
+                }
+            };
+
+    /**
+     * The name associated with the number.
+     * <p>
+     * The name of the call as identified by a {@link CallScreeningService}.  Could be a business
+     * name, for example.
+     *
+     * @return The name associated with the number, or {@code null} if none was provided.
+     */
+    public final @Nullable String getName() {
+        return mName;
+    }
+
+    /**
+     * Description of the call.
+     * <p>
+     * A description of the call as identified by a {@link CallScreeningService}.  The description
+     * is typically presented by Dialer apps after the {@link #getName() name} to provide a short
+     * piece of relevant information about the call.  This could include a location, address, or a
+     * message regarding the potential nature of the call (e.g. potential telemarketer).
+     *
+     * @return The call description, or {@code null} if none was provided.
+     */
+    public final @Nullable String getDescription() {
+        return mDescription;
+    }
+
+    /**
+     * Details of the call.
+     * <p>
+     * Details of the call as identified by a {@link CallScreeningService}.  The details
+     * are typically presented by Dialer apps after the {@link #getName() name} and
+     * {@link #getDescription() description} to provide further clarifying information about the
+     * call. This could include, for example, the opening hours of a business, or stats about
+     * the number of times a call has been reported as spam.
+     *
+     * @return The call details, or {@code null} if none was provided.
+     */
+    public final @Nullable String getDetails() {
+        return mDetails;
+    }
+
+    /**
+     * Photo associated with the call.
+     * <p>
+     * A photo associated with the call as identified by a {@link CallScreeningService}.  This
+     * could be, for example, a business logo, or a photo of the caller.
+     *
+     * @return The photo associated with the call, or {@code null} if none was provided.
+     */
+    public final @Nullable Icon getPhoto() {
+        return mPhoto;
+    }
+
+    /**
+     * Indicates the likelihood that this call is a nuisance call.
+     * <p>
+     * How likely the call is a nuisance call, as identified by a {@link CallScreeningService}.
+     *
+     * @return The nuisance confidence.
+     */
+    public final @NuisanceConfidence
+    int getNuisanceConfidence() {
+        return mNuisanceConfidence;
+    }
+
+    /**
+     * The package name of the {@link CallScreeningService} which provided the
+     * {@link CallIdentification}.
+     * <p>
+     * A {@link CallScreeningService} may not set this property; it is set by the system.
+     * @return the package name
+     */
+    public final @NonNull String getCallScreeningPackageName() {
+        return mCallScreeningPackageName;
+    }
+
+    /**
+     * The {@link android.content.pm.PackageManager#getApplicationLabel(ApplicationInfo) name} of
+     * the {@link CallScreeningService} which provided the {@link CallIdentification}.
+     * <p>
+     * A {@link CallScreeningService} may not set this property; it is set by the system.
+     *
+     * @return The name of the app.
+     */
+    public final @NonNull String getCallScreeningAppName() {
+        return mCallScreeningAppName;
+    }
+
+    /**
+     * Set the package name of the {@link CallScreeningService} which provided this information.
+     *
+     * @param callScreeningPackageName The package name.
+     * @hide
+     */
+    public void setCallScreeningPackageName(@NonNull String callScreeningPackageName) {
+        mCallScreeningPackageName = callScreeningPackageName;
+    }
+
+    /**
+     * Set the app name of the {@link CallScreeningService} which provided this information.
+     *
+     * @param callScreeningAppName The app name.
+     * @hide
+     */
+    public void setCallScreeningAppName(@NonNull String callScreeningAppName) {
+        mCallScreeningAppName = callScreeningAppName;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        CallIdentification that = (CallIdentification) o;
+        // Note: mPhoto purposely omit as no good comparison exists.
+        return mNuisanceConfidence == that.mNuisanceConfidence
+                && Objects.equals(mName, that.mName)
+                && Objects.equals(mDescription, that.mDescription)
+                && Objects.equals(mDetails, that.mDetails)
+                && Objects.equals(mCallScreeningAppName, that.mCallScreeningAppName)
+                && Objects.equals(mCallScreeningPackageName, that.mCallScreeningPackageName);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mName, mDescription, mDetails, mPhoto, mNuisanceConfidence,
+                mCallScreeningAppName, mCallScreeningPackageName);
+    }
+}
diff --git a/telecomm/java/android/telecom/CallRedirectionService.java b/telecomm/java/android/telecom/CallRedirectionService.java
index b906d0b..3299117 100644
--- a/telecomm/java/android/telecom/CallRedirectionService.java
+++ b/telecomm/java/android/telecom/CallRedirectionService.java
@@ -16,6 +16,7 @@
 
 package android.telecom;
 
+import android.annotation.NonNull;
 import android.annotation.SdkConstant;
 import android.app.Service;
 import android.content.Intent;
@@ -27,8 +28,8 @@
 import android.os.RemoteException;
 
 import com.android.internal.os.SomeArgs;
-import com.android.internal.telecom.ICallRedirectionService;
 import com.android.internal.telecom.ICallRedirectionAdapter;
+import com.android.internal.telecom.ICallRedirectionService;
 
 /**
  * This service can be implemented to interact between Telecom and its implementor
@@ -62,22 +63,35 @@
 
     /**
      * Telecom calls this method to inform the implemented {@link CallRedirectionService} of
-     * a new outgoing call which is being placed.
+     * a new outgoing call which is being placed. Telecom does not request to redirect emergency
+     * calls and does not request to redirect calls with gateway information.
      *
-     * The implemented {@link CallRedirectionService} can call {@link #placeCallUnmodified()},
-     * {@link #redirectCall(Uri, PhoneAccountHandle)}, and {@link #cancelCall()} only from here.
+     * <p>Telecom will cancel the call if Telecom does not receive a response in 5 seconds from
+     * the implemented {@link CallRedirectionService} set by users.
      *
-     * @param handle the phone number dialed by the user
-     * @param targetPhoneAccount the {@link PhoneAccountHandle} on which the call will be placed.
+     * <p>The implemented {@link CallRedirectionService} can call {@link #placeCallUnmodified()},
+     * {@link #redirectCall(Uri, PhoneAccountHandle, boolean)}, and {@link #cancelCall()} only
+     * from here.
+     *
+     * @param handle the phone number dialed by the user, represented in E.164 format if possible
+     * @param initialPhoneAccount the {@link PhoneAccountHandle} on which the call will be placed.
+     * @param allowInteractiveResponse a boolean to tell if the implemented
+     *                                 {@link CallRedirectionService} should allow interactive
+     *                                 responses with users. Will be {@code false} if, for example
+     *                                 the device is in car mode and the user would not be able to
+     *                                 interact with their device.
      */
-    public abstract void onPlaceCall(Uri handle, PhoneAccountHandle targetPhoneAccount);
+    public abstract void onPlaceCall(@NonNull Uri handle,
+                                     @NonNull PhoneAccountHandle initialPhoneAccount,
+                                     boolean allowInteractiveResponse);
 
     /**
      * The implemented {@link CallRedirectionService} calls this method to response a request
-     * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that no changes
-     * are required to the outgoing call, and that the call should be placed as-is.
+     * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that
+     * no changes are required to the outgoing call, and that the call should be placed as-is.
      *
-     * This can only be called from implemented {@link #onPlaceCall(Uri, PhoneAccountHandle)}.
+     * <p>This can only be called from implemented
+     * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}.
      *
      */
     public final void placeCallUnmodified() {
@@ -89,29 +103,39 @@
 
     /**
      * The implemented {@link CallRedirectionService} calls this method to response a request
-     * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that changes
-     * are required to the phone number or/and {@link PhoneAccountHandle} for the outgoing call.
+     * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that
+     * changes are required to the phone number or/and {@link PhoneAccountHandle} for the outgoing
+     * call. Telecom will cancel the call if the implemented {@link CallRedirectionService}
+     * replies Telecom a handle for an emergency number.
      *
-     * This can only be called from implemented {@link #onPlaceCall(Uri, PhoneAccountHandle)}.
+     * <p>This can only be called from implemented
+     * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}.
      *
      * @param handle the new phone number to dial
      * @param targetPhoneAccount the {@link PhoneAccountHandle} to use when placing the call.
      *                           If {@code null}, no change will be made to the
      *                           {@link PhoneAccountHandle} used to place the call.
+     * @param confirmFirst Telecom will ask users to confirm the redirection via a yes/no dialog
+     *                     if the confirmFirst is true, and if the redirection request of this
+     *                     response was sent with a true flag of allowInteractiveResponse via
+     *                     {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}
      */
-    public final void redirectCall(Uri handle, PhoneAccountHandle targetPhoneAccount) {
+    public final void redirectCall(@NonNull Uri handle,
+                                   @NonNull PhoneAccountHandle targetPhoneAccount,
+                                   boolean confirmFirst) {
         try {
-            mCallRedirectionAdapter.redirectCall(handle, targetPhoneAccount);
+            mCallRedirectionAdapter.redirectCall(handle, targetPhoneAccount, confirmFirst);
         } catch (RemoteException e) {
         }
     }
 
     /**
      * The implemented {@link CallRedirectionService} calls this method to response a request
-     * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that an outgoing
-     * call should be canceled entirely.
+     * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that
+     * an outgoing call should be canceled entirely.
      *
-     * This can only be called from implemented {@link #onPlaceCall(Uri, PhoneAccountHandle)}.
+     * <p>This can only be called from implemented
+     * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}.
      *
      */
     public final void cancelCall() {
@@ -137,7 +161,8 @@
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
                         mCallRedirectionAdapter = (ICallRedirectionAdapter) args.arg1;
-                        onPlaceCall((Uri) args.arg2, (PhoneAccountHandle) args.arg3);
+                        onPlaceCall((Uri) args.arg2, (PhoneAccountHandle) args.arg3,
+                                (boolean) args.arg4);
                     } finally {
                         args.recycle();
                     }
@@ -152,15 +177,20 @@
          * Telecom calls this method to inform the CallRedirectionService of a new outgoing call
          * which is about to be placed.
          * @param handle the phone number dialed by the user
-         * @param targetPhoneAccount the URI of the number the user dialed
+         * @param initialPhoneAccount the URI of the number the user dialed
+         * @param allowInteractiveResponse a boolean to tell if the implemented
+         *                                 {@link CallRedirectionService} should allow interactive
+         *                                 responses with users.
          */
         @Override
-        public void placeCall(ICallRedirectionAdapter adapter, Uri handle,
-                              PhoneAccountHandle targetPhoneAccount) {
+        public void placeCall(@NonNull ICallRedirectionAdapter adapter, @NonNull Uri handle,
+                              @NonNull PhoneAccountHandle initialPhoneAccount,
+                              boolean allowInteractiveResponse) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = adapter;
             args.arg2 = handle;
-            args.arg3 = targetPhoneAccount;
+            args.arg3 = initialPhoneAccount;
+            args.arg4 = allowInteractiveResponse;
             mHandler.obtainMessage(MSG_PLACE_CALL, args).sendToTarget();
         }
     }
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index 6628743..be96b3c 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -16,6 +16,7 @@
 
 package android.telecom;
 
+import android.annotation.NonNull;
 import android.annotation.SdkConstant;
 import android.app.Service;
 import android.content.ComponentName;
@@ -46,6 +47,15 @@
  * </service>
  * }
  * </pre>
+ * <p>
+ * A CallScreeningService performs two functions:
+ * <ol>
+ *     <li>Call blocking/screening - the service can choose which calls will ring on the user's
+ *     device, and which will be silently sent to voicemail.</li>
+ *     <li>Call identification - the service can optionally provide {@link CallIdentification}
+ *     information about a {@link Call.Details call} which will be shown to the user in the
+ *     Dialer app.</li>
+ * </ol>
  */
 public abstract class CallScreeningService extends Service {
     /**
@@ -229,16 +239,23 @@
      *
      * @param callDetails Information about a new incoming call, see {@link Call.Details}.
      */
-    public abstract void onScreenCall(Call.Details callDetails);
+    public abstract void onScreenCall(@NonNull Call.Details callDetails);
 
     /**
      * Responds to the given call, either allowing it or disallowing it.
+     * <p>
+     * The {@link CallScreeningService} calls this method to inform the system whether the call
+     * should be silently blocked or not.
      *
      * @param callDetails The call to allow.
+     *                    <p>
+     *                    Must be the same {@link Call.Details call} which was provided to the
+     *                    {@link CallScreeningService} via {@link #onScreenCall(Call.Details)}.
      * @param response The {@link CallScreeningService.CallResponse} which contains information
      * about how to respond to a call.
      */
-    public final void respondToCall(Call.Details callDetails, CallResponse response) {
+    public final void respondToCall(@NonNull Call.Details callDetails,
+            @NonNull CallResponse response) {
         try {
             if (response.getDisallowCall()) {
                 mCallScreeningAdapter.disallowCall(
@@ -253,4 +270,32 @@
         } catch (RemoteException e) {
         }
     }
+
+    /**
+     * Provide {@link CallIdentification} information about a {@link Call.Details call}.
+     * <p>
+     * The {@link CallScreeningService} calls this method to provide information it has identified
+     * about a {@link Call.Details call}.  This information will potentially be shown to the user
+     * in the {@link InCallService dialer} app.  It will be logged to the
+     * {@link android.provider.CallLog}.
+     * <p>
+     * A {@link CallScreeningService} should only call this method for calls for which it is able to
+     * provide some {@link CallIdentification} for.  {@link CallIdentification} instances with no
+     * fields set will be ignored by the system.
+     *
+     * @param callDetails The call to provide information for.
+     *                    <p>
+     *                    Must be the same {@link Call.Details call} which was provided to the
+     *                    {@link CallScreeningService} via {@link #onScreenCall(Call.Details)}.
+     * @param identification An instance of {@link CallIdentification} with information about the
+     *                       {@link Call.Details call}.
+     */
+    public final void provideCallIdentification(@NonNull Call.Details callDetails,
+            @NonNull CallIdentification identification) {
+        try {
+            mCallScreeningAdapter.provideCallIdentification(callDetails.getTelecomCallId(),
+                    identification);
+        } catch (RemoteException e) {
+        }
+    }
 }
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 0589cd4..8425603 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -614,6 +614,11 @@
     public static final String EVENT_HANDOVER_FAILED =
             "android.telecom.event.HANDOVER_FAILED";
 
+    /**
+     * Connection extra key used to store SIP invite fields for an incoming call for IMS calls
+     */
+    public static final String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE";
+
     // Flag controlling whether PII is emitted into the logs
     private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
 
diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java
index 1806aee..2680af7 100644
--- a/telecomm/java/android/telecom/DefaultDialerManager.java
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -74,7 +74,7 @@
         }
 
         // Only make the change if the new package belongs to a valid phone application
-        List<String> packageNames = getInstalledDialerApplications(context);
+        List<String> packageNames = getInstalledDialerApplications(context, user);
 
         if (packageNames.contains(packageName)) {
             // Update the secure setting.
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index 3ad0f0c..911786e 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -16,6 +16,7 @@
 
 package android.telecom;
 
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.net.Uri;
 import android.os.Build;
@@ -62,6 +63,7 @@
     private final Bundle mIntentExtras;
     private final Bundle mExtras;
     private final long mCreationTimeMillis;
+    private final CallIdentification mCallIdentification;
 
     public ParcelableCall(
             String id,
@@ -89,7 +91,8 @@
             List<String> conferenceableCallIds,
             Bundle intentExtras,
             Bundle extras,
-            long creationTimeMillis) {
+            long creationTimeMillis,
+            CallIdentification callIdentification) {
         mId = id;
         mState = state;
         mDisconnectCause = disconnectCause;
@@ -116,6 +119,7 @@
         mIntentExtras = intentExtras;
         mExtras = extras;
         mCreationTimeMillis = creationTimeMillis;
+        mCallIdentification = callIdentification;
     }
 
     /** The unique ID of the call. */
@@ -133,7 +137,7 @@
      * Reason for disconnection, as described by {@link android.telecomm.DisconnectCause}. Valid
      * when call state is {@link CallState#DISCONNECTED}.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public DisconnectCause getDisconnectCause() {
         return mDisconnectCause;
     }
@@ -159,13 +163,13 @@
     }
 
     /** The time that the call switched to the active state. */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public long getConnectTimeMillis() {
         return mConnectTimeMillis;
     }
 
     /** The endpoint to which the call is connected. */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public Uri getHandle() {
         return mHandle;
     }
@@ -305,8 +309,17 @@
         return mCreationTimeMillis;
     }
 
+    /**
+     * Contains call identification information returned by a {@link CallScreeningService}.
+     * @return The {@link CallIdentification} for this call, or {@code null} if a
+     * {@link CallScreeningService} did not provide information.
+     */
+    public @Nullable CallIdentification getCallIdentification() {
+        return mCallIdentification;
+    }
+
     /** Responsible for creating ParcelableCall objects for deserialized Parcels. */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static final Parcelable.Creator<ParcelableCall> CREATOR =
             new Parcelable.Creator<ParcelableCall> () {
         @Override
@@ -342,6 +355,7 @@
             boolean isRttCallChanged = source.readByte() == 1;
             ParcelableRttCall rttCall = source.readParcelable(classLoader);
             long creationTimeMillis = source.readLong();
+            CallIdentification callIdentification = source.readParcelable(classLoader);
             return new ParcelableCall(
                     id,
                     state,
@@ -368,7 +382,8 @@
                     conferenceableCallIds,
                     intentExtras,
                     extras,
-                    creationTimeMillis);
+                    creationTimeMillis,
+                    callIdentification);
         }
 
         @Override
@@ -413,6 +428,7 @@
         destination.writeByte((byte) (mIsRttCallChanged ? 1 : 0));
         destination.writeParcelable(mRttCall, 0);
         destination.writeLong(mCreationTimeMillis);
+        destination.writeParcelable(mCallIdentification, 0);
     }
 
     @Override
diff --git a/telecomm/java/android/telecom/PhoneAccountHandle.java b/telecomm/java/android/telecom/PhoneAccountHandle.java
index 097e352..5dedb04 100644
--- a/telecomm/java/android/telecom/PhoneAccountHandle.java
+++ b/telecomm/java/android/telecom/PhoneAccountHandle.java
@@ -168,7 +168,7 @@
         }
     };
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private PhoneAccountHandle(Parcel in) {
         this(ComponentName.CREATOR.createFromParcel(in),
                 in.readString(),
diff --git a/core/java/android/service/contentcapture/InteractionContext.aidl b/telecomm/java/android/telecom/PhoneAccountSuggestion.aidl
similarity index 73%
copy from core/java/android/service/contentcapture/InteractionContext.aidl
copy to telecomm/java/android/telecom/PhoneAccountSuggestion.aidl
index 982e095..e2fa7e4 100644
--- a/core/java/android/service/contentcapture/InteractionContext.aidl
+++ b/telecomm/java/android/telecom/PhoneAccountSuggestion.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
+/*
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
-package android.service.contentcapture;
+package android.telecom;
 
-parcelable InteractionContext;
+/**
+ * {@hide}
+  */
+parcelable PhoneAccountSuggestion;
\ No newline at end of file
diff --git a/telecomm/java/android/telecom/PhoneAccountSuggestion.java b/telecomm/java/android/telecom/PhoneAccountSuggestion.java
index 4e6a178..b401bcf 100644
--- a/telecomm/java/android/telecom/PhoneAccountSuggestion.java
+++ b/telecomm/java/android/telecom/PhoneAccountSuggestion.java
@@ -24,6 +24,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 
 public final class PhoneAccountSuggestion implements Parcelable {
 
@@ -132,4 +133,19 @@
         dest.writeInt(mReason);
         dest.writeByte((byte) (mShouldAutoSelect ? 1 : 0));
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        PhoneAccountSuggestion that = (PhoneAccountSuggestion) o;
+        return mReason == that.mReason
+                && mShouldAutoSelect == that.mShouldAutoSelect
+                && Objects.equals(mHandle, that.mHandle);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mHandle, mReason, mShouldAutoSelect);
+    }
 }
diff --git a/telecomm/java/android/telecom/PhoneAccountSuggestionService.java b/telecomm/java/android/telecom/PhoneAccountSuggestionService.java
new file mode 100644
index 0000000..ba3822c
--- /dev/null
+++ b/telecomm/java/android/telecom/PhoneAccountSuggestionService.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+import android.annotation.NonNull;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.internal.telecom.IPhoneAccountSuggestionCallback;
+import com.android.internal.telecom.IPhoneAccountSuggestionService;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Base class for service that allows system apps to suggest phone accounts for outgoing calls.
+ *
+ * Phone account suggestions allow OEMs to intelligently select phone accounts based on knowledge
+ * about the user's past behavior, carrier billing patterns, or other factors unknown to the AOSP
+ * Telecom system.
+ * OEMs who wish to provide a phone account suggestion service on their device should implement this
+ * service in an app that resides in the /system/priv-app/ directory on their device. For security
+ * reasons, the service's entry {@code AndroidManifest.xml} file must declare the
+ * {@link android.Manifest.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE} permission:
+ * <pre>
+ * {@code
+ * <service android:name="your.package.YourServiceName"
+ *          android:permission="android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE">
+ *      <intent-filter>
+ *          <action android:name="android.telecom.PhoneAccountSuggestionService"/>
+ *      </intent-filter>
+ * </service>
+ * }
+ * </pre>
+ * Only one system app on each device may implement this service. If multiple system apps implement
+ * this service, none of them will be queried for suggestions.
+ * @hide
+ */
+@SystemApi
+@TestApi
+public class PhoneAccountSuggestionService extends Service {
+    /**
+     * The {@link Intent} that must be declared in the {@code intent-filter} element of the
+     * service's manifest entry.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE = "android.telecom.PhoneAccountSuggestionService";
+
+    private IPhoneAccountSuggestionService mInterface = new IPhoneAccountSuggestionService.Stub() {
+        @Override
+        public void onAccountSuggestionRequest(IPhoneAccountSuggestionCallback callback,
+                String number) {
+            mCallbackMap.put(number, callback);
+            PhoneAccountSuggestionService.this.onAccountSuggestionRequest(number);
+        }
+    };
+
+    private final Map<String, IPhoneAccountSuggestionCallback> mCallbackMap =
+            new HashMap<>();
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mInterface.asBinder();
+    }
+
+    /**
+     * The system calls this method during the outgoing call flow if it needs account suggestions.
+     *
+     * The implementer of this service must override this method to implement its account suggestion
+     * logic. After preparing the suggestions, the implementation of the service must call
+     * {@link #suggestPhoneAccounts(String, List)} to deliver the suggestions back to the system.
+     *
+     * Note that the system will suspend the outgoing call process after it calls this method until
+     * this service calls {@link #suggestPhoneAccounts}.
+     *
+     * @param number The phone number to provide suggestions for.
+     */
+    public void onAccountSuggestionRequest(@NonNull String number) {}
+
+    /**
+     * The implementation of this service calls this method to deliver suggestions to the system.
+     *
+     * The implementation of this service must call this method after receiving a call to
+     * {@link #onAccountSuggestionRequest(String)}. If no suggestions are available, pass an empty
+     * list as the {@code suggestions} argument.
+     *
+     * @param number The phone number to provide suggestions for.
+     * @param suggestions The list of suggestions.
+     */
+    public final void suggestPhoneAccounts(@NonNull String number,
+            @NonNull List<PhoneAccountSuggestion> suggestions) {
+        IPhoneAccountSuggestionCallback callback = mCallbackMap.remove(number);
+        if (callback == null) {
+            Log.w(this, "No suggestions requested for the number %s", Log.pii(number));
+            return;
+        }
+        try {
+            callback.suggestPhoneAccounts(number, suggestions);
+        } catch (RemoteException e) {
+            Log.w(this, "Remote exception calling suggestPhoneAccounts");
+        }
+    }
+}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index fb371c1..6c4b1af8 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -23,6 +23,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.UnsupportedAppUsage;
+import android.app.role.RoleManagerCallback;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -31,6 +32,7 @@
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -42,6 +44,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * Provides access to information about active calls and registration/call-management functionality.
@@ -1188,8 +1191,12 @@
      * Requires permission: {@link android.Manifest.permission#WRITE_SECURE_SETTINGS}
      *
      * @hide
+     * @deprecated Use
+     * {@link android.app.role.RoleManager#addRoleHolderAsUser(String, String, UserHandle, Executor,
+     * RoleManagerCallback)} instead.
      */
     @SystemApi
+    @Deprecated
     @RequiresPermission(allOf = {
             android.Manifest.permission.MODIFY_PHONE_STATE,
             android.Manifest.permission.WRITE_SECURE_SETTINGS})
@@ -1221,79 +1228,6 @@
     }
 
     /**
-     * Used to trigger display of the ChangeDefaultCallScreeningApp activity to prompt the user to
-     * change the call screening app.
-     *
-     * A {@link SecurityException} will be thrown if calling package name doesn't match the package
-     * of the passed {@link ComponentName}
-     *
-     * @param componentName to verify that the calling package name matches the package of the
-     * passed ComponentName.
-     */
-    public void requestChangeDefaultCallScreeningApp(@NonNull ComponentName componentName) {
-        try {
-            if (isServiceConnected()) {
-                getTelecomService().requestChangeDefaultCallScreeningApp(componentName, mContext
-                    .getOpPackageName());
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG,
-                "RemoteException calling ITelecomService#requestChangeDefaultCallScreeningApp.",
-                e);
-        }
-    }
-
-    /**
-     * Used to verify that the passed ComponentName is default call screening app.
-     *
-     * @param componentName to verify that the package of the passed ComponentName matched the default
-     * call screening packageName.
-     *
-     * @return {@code true} if the passed componentName matches the default call screening's, {@code
-     * false} if the passed componentName is null, or it doesn't match default call screening's.
-     */
-    public boolean isDefaultCallScreeningApp(ComponentName componentName) {
-        try {
-            if (isServiceConnected()) {
-                return getTelecomService().isDefaultCallScreeningApp(componentName);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG,
-                "RemoteException calling ITelecomService#isDefaultCallScreeningApp.",
-                e);
-        }
-        return false;
-    }
-
-    /**
-     * Used to set the default call screening package.
-     *
-     * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} Requires
-     * permission: {@link android.Manifest.permission#WRITE_SECURE_SETTINGS}
-     *
-     * A {@link IllegalArgumentException} will be thrown if the specified package and component name
-     * of {@link ComponentName} does't exist, or the specified component of {@link ComponentName}
-     * does't have {@link android.Manifest.permission#BIND_SCREENING_SERVICE}.
-     *
-     * @param componentName to set the default call screening to.
-     * @hide
-     */
-    @RequiresPermission(anyOf = {
-        android.Manifest.permission.MODIFY_PHONE_STATE,
-        android.Manifest.permission.WRITE_SECURE_SETTINGS
-    })
-    public void setDefaultCallScreeningApp(ComponentName componentName) {
-        try {
-            if (isServiceConnected()) {
-                getTelecomService().setDefaultCallScreeningApp(componentName);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG,
-                "RemoteException calling ITelecomService#setDefaultCallScreeningApp.", e);
-        }
-    }
-
-    /**
      * Return whether a given phone number is the configured voicemail number for a
      * particular phone account.
      *
diff --git a/telecomm/java/android/telecom/VideoProfile.java b/telecomm/java/android/telecom/VideoProfile.java
index bbac8eb..7b23061 100644
--- a/telecomm/java/android/telecom/VideoProfile.java
+++ b/telecomm/java/android/telecom/VideoProfile.java
@@ -369,16 +369,13 @@
         }
 
         /**
-         * Create a call camera capabilities instance that optionally
-         * supports zoom.
+         * Create a call camera capabilities instance that optionally supports zoom.
          *
          * @param width The width of the camera video (in pixels).
          * @param height The height of the camera video (in pixels).
          * @param zoomSupported True when camera supports zoom.
          * @param maxZoom Maximum zoom supported by camera.
-         * @hide
          */
-        @UnsupportedAppUsage
         public CameraCapabilities(int width, int height, boolean zoomSupported, float maxZoom) {
             mWidth = width;
             mHeight = height;
@@ -455,16 +452,14 @@
         }
 
         /**
-         * Whether the camera supports zoom.
-         * @hide
+         * Returns {@code true} is zoom is supported, {@code false} otherwise.
          */
         public boolean isZoomSupported() {
             return mZoomSupported;
         }
 
         /**
-         * The maximum zoom supported by the camera.
-         * @hide
+         * Returns the maximum zoom supported by the camera.
          */
         public float getMaxZoom() {
             return mMaxZoom;
diff --git a/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl b/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl
index 46bf983..0a42a3f 100644
--- a/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl
@@ -31,5 +31,6 @@
 
     void placeCallUnmodified();
 
-    void redirectCall(in Uri handle, in PhoneAccountHandle targetPhoneAccount);
+    void redirectCall(in Uri handle, in PhoneAccountHandle targetPhoneAccount,
+            boolean confirmFirst);
 }
diff --git a/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl b/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl
index d8d360b..c1bc440 100644
--- a/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl
@@ -30,5 +30,5 @@
  */
 oneway interface ICallRedirectionService {
     void placeCall(in ICallRedirectionAdapter adapter, in Uri handle,
-            in PhoneAccountHandle targetPhoneAccount);
+            in PhoneAccountHandle initialPhoneAccount, boolean allowInteractiveResponse);
 }
diff --git a/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl b/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl
index d255ed1..a86c830 100644
--- a/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl
@@ -17,6 +17,7 @@
 package com.android.internal.telecom;
 
 import android.content.ComponentName;
+import android.telecom.CallIdentification;
 
 /**
  * Internal remote callback interface for call screening services.
@@ -34,4 +35,8 @@
             boolean shouldAddToCallLog,
             boolean shouldShowNotification,
             in ComponentName componentName);
+
+    void provideCallIdentification(
+            String callId,
+            in CallIdentification callIdentification);
 }
diff --git a/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.aidl b/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.aidl
new file mode 100644
index 0000000..cb14241
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telecom;
+
+import android.telecom.PhoneAccountSuggestion;
+/**
+ * Internal remote callback interface for a phone acct suggestion service.
+ * @hide
+ */
+oneway interface IPhoneAccountSuggestionCallback{
+    void suggestPhoneAccounts(in String number, in List<PhoneAccountSuggestion> suggestions);
+}
diff --git a/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl b/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl
new file mode 100644
index 0000000..0ffab93
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telecom;
+
+import com.android.internal.telecom.IPhoneAccountSuggestionCallback;
+
+/**
+ * Internal remote interface for a phone acct suggestion service.
+ * @hide
+ */
+oneway interface IPhoneAccountSuggestionService {
+    void onAccountSuggestionRequest(in IPhoneAccountSuggestionCallback callback,
+            in String number);
+}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index d64efea..954a709 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -256,21 +256,6 @@
     boolean setDefaultDialer(in String packageName);
 
     /**
-     * @see TelecomServiceImpl#requestChangeDefaultCallScreeningApp
-     */
-    void requestChangeDefaultCallScreeningApp(in ComponentName componentNamem, String callingPackage);
-
-    /**
-     * @see TelecomServiceImpl#isDefaultCallScreeningApp
-     */
-    boolean isDefaultCallScreeningApp(in ComponentName componentName);
-
-    /**
-     * @see TelecomServiceImpl#setDefaultCallScreeningApp
-     */
-    void setDefaultCallScreeningApp(in ComponentName componentName);
-
-    /**
     * @see TelecomServiceImpl#createManageBlockedNumbersIntent
     **/
     Intent createManageBlockedNumbersIntent();
@@ -307,6 +292,8 @@
 
     void setTestDefaultCallRedirectionApp(String packageName);
 
+    void setTestPhoneAcctSuggestionComponent(String flattenedComponentName);
+
     void setTestDefaultCallScreeningApp(String packageName);
 
     void addOrRemoveTestCallCompanionApp(String packageName, boolean isAdded);
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 1cbe5a2..c115a4b 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -2732,10 +2732,26 @@
 
         /**
          * The {@code content://} style URL for this table.
+         * For MSIM, this will return APNs for the default subscription
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}. To specify subId for MSIM,
+         * use {@link Uri#withAppendedPath(Uri, String)} to append with subscription id.
          */
         public static final Uri CONTENT_URI = Uri.parse("content://telephony/carriers");
 
         /**
+         * The {@code content://} style URL for this table. Used for APN query based on current
+         * subscription. Instead of specifying carrier matching information in the selection,
+         * this API will return all matching APNs from current subscription carrier and queries
+         * will be applied on top of that. If there is no match for MVNO (Mobile Virtual Network
+         * Operator) APNs, return APNs from its MNO (based on mccmnc) instead. For MSIM, this will
+         * return APNs for the default subscription
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}. To specify subId for MSIM,
+         * use {@link Uri#withAppendedPath(Uri, String)} to append with subscription id.
+         */
+        public static final Uri SIM_APN_URI = Uri.parse(
+                "content://telephony/carriers/sim_apn_list");
+
+        /**
          * The {@code content://} style URL to be called from DevicePolicyManagerService,
          * can manage DPC-owned APNs.
          * @hide
@@ -2745,7 +2761,9 @@
         /**
          * The {@code content://} style URL to be called from Telephony to query APNs.
          * When DPC-owned APNs are enforced, only DPC-owned APNs are returned, otherwise only
-         * non-DPC-owned APNs are returned.
+         * non-DPC-owned APNs are returned. For MSIM, this will return APNs for the default
+         * subscription {@link SubscriptionManager#getDefaultSubscriptionId()}. To specify subId
+         * for MSIM, use {@link Uri#withAppendedPath(Uri, String)} to append with subscription id.
          * @hide
          */
         public static final Uri FILTERED_URI = Uri.parse("content://telephony/carriers/filtered");
@@ -2759,13 +2777,6 @@
                 "content://telephony/carriers/enforce_managed");
 
         /**
-         * The {@code content://} style URL to be called from Telephony to query current APNs.
-         * @hide
-         */
-        public static final Uri SIM_APN_LIST = Uri.parse(
-                "content://telephony/carriers/sim_apn_list");
-
-        /**
          * The column name for ENFORCE_MANAGED_URI, indicates whether DPC-owned APNs are enforced.
          * @hide
          */
@@ -2839,18 +2850,30 @@
         /**
          * Mobile Country Code (MCC).
          * <P>Type: TEXT</P>
+         * @deprecated Use {@link #SIM_APN_URI} to query APN instead, this API will return
+         * matching APNs based on current subscription carrier, thus no need to specify MCC and
+         * other carrier matching information. In the future, Android will not support MCC for
+         * APN query.
          */
         public static final String MCC = "mcc";
 
         /**
          * Mobile Network Code (MNC).
          * <P>Type: TEXT</P>
+         * @deprecated Use {@link #SIM_APN_URI} to query APN instead, this API will return
+         * matching APNs based on current subscription carrier, thus no need to specify MNC and
+         * other carrier matching information. In the future, Android will not support MNC for
+         * APN query.
          */
         public static final String MNC = "mnc";
 
         /**
          * Numeric operator ID (as String). Usually {@code MCC + MNC}.
          * <P>Type: TEXT</P>
+         * @deprecated Use {@link #SIM_APN_URI} to query APN instead, this API will return
+         * matching APNs based on current subscription carrier, thus no need to specify Numeric
+         * and other carrier matching information. In the future, Android will not support Numeric
+         * for APN query.
          */
         public static final String NUMERIC = "numeric";
 
@@ -2931,6 +2954,10 @@
          * MVNO type:
          * {@code SPN (Service Provider Name), IMSI, GID (Group Identifier Level 1)}.
          * <P>Type: TEXT</P>
+         * @deprecated Use {@link #SIM_APN_URI} to query APN instead, this API will return
+         * matching APNs based on current subscription carrier, thus no need to specify MVNO_TYPE
+         * and other carrier matching information. In the future, Android will not support MVNO_TYPE
+         * for APN query.
          */
         public static final String MVNO_TYPE = "mvno_type";
 
@@ -2943,6 +2970,10 @@
          *     <li>GID: 4E, 33, ...</li>
          * </ul>
          * <P>Type: TEXT</P>
+         * @deprecated Use {@link #SIM_APN_URI} to query APN instead, this API will return
+         * matching APNs based on current subscription carrier, thus no need to specify
+         * MVNO_MATCH_DATA and other carrier matching information. In the future, Android will not
+         * support MVNO_MATCH_DATA for APN query.
          */
         public static final String MVNO_MATCH_DATA = "mvno_match_data";
 
@@ -3151,7 +3182,6 @@
         })
         @Retention(RetentionPolicy.SOURCE)
         public @interface EditStatus {}
-
     }
 
     /**
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 388b5fb..312b318 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1793,17 +1793,6 @@
             "editable_wfc_roaming_mode_bool";
 
     /**
-     * Determine whether current lpp_mode used for E-911 needs to be kept persistently.
-     * {@code false} - not keeping the lpp_mode means using default configuration of gps.conf
-     *                 when sim is not presented.
-     * {@code true}  - current lpp_profile of carrier will be kepted persistently
-     *                 even after sim is removed.
-     *
-     * @hide
-     */
-    public static final String KEY_PERSIST_LPP_MODE_BOOL = "persist_lpp_mode_bool";
-
-    /**
      * Carrier specified WiFi networks.
      * @hide
      */
@@ -2000,6 +1989,8 @@
      * Determine whether to use only RSRP for the number of LTE signal bars.
      * @hide
      */
+    // FIXME: this key and related keys must not be exposed without a consistent philosophy for
+    // all RATs.
     public static final String KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL =
             "use_only_rsrp_for_lte_signal_bar_bool";
 
@@ -2240,9 +2231,11 @@
      * e.g.) To use RSCP by default, set the value to "rscp". The signal strength level will
      * then be determined by #KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY
      * <p>
-     * Currently this only supports the value "rscp"
+     * Currently this supports the value "rscp" and "rssi".
      * @hide
      */
+    // FIXME: this key and related keys must not be exposed without a consistent philosophy for
+    // all RATs.
     public static final String KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING =
             "wcdma_default_signal_strength_measurement_string";
 
@@ -2400,6 +2393,142 @@
     public static final String KEY_5G_ICON_CONFIGURATION_STRING =
             "5g_icon_configuration_string";
 
+    /**
+     * Controls RSRP threshold at which AlternativeNetworkService will decide whether
+     * the opportunistic network is good enough for internet data.
+     */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT =
+            "opportunistic_network_entry_threshold_rsrp_int";
+
+    /**
+     * Controls RSSNR threshold at which AlternativeNetworkService will decide whether
+     * the opportunistic network is good enough for internet data.
+     */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT =
+            "opportunistic_network_entry_threshold_rssnr_int";
+
+    /**
+     * Controls RSRP threshold below which AlternativeNetworkService will decide whether
+     * the opportunistic network available is not good enough for internet data.
+     */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT =
+            "opportunistic_network_exit_threshold_rsrp_int";
+
+    /**
+     * Controls RSSNR threshold below which AlternativeNetworkService will decide whether
+     * the opportunistic network available is not good enough for internet data.
+     */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT =
+            "opportunistic_network_exit_threshold_rssnr_int";
+
+    /**
+     * GPS configs. See android.hardware.gnss@1.0 IGnssConfiguration.
+     * @hide
+     */
+    public static final class Gps {
+        /** Prefix of all Gps.KEY_* constants. */
+        public static final String KEY_PREFIX = "gps.";
+
+        /**
+         * Determine whether current lpp_mode used for E-911 needs to be kept persistently.
+         * {@code false} - not keeping the lpp_mode means using default configuration of gps.conf
+         *                 when sim is not presented.
+         * {@code true}  - current lpp_profile of carrier will be kepted persistently
+         *                 even after sim is removed. This is default.
+         */
+        public static final String KEY_PERSIST_LPP_MODE_BOOL = KEY_PREFIX + "persist_lpp_mode_bool";
+
+        /**
+         * SUPL server host for SET Initiated & non-ES Network-Initiated SUPL requests.
+         * Default to supl.google.com
+         */
+        public static final String KEY_SUPL_HOST_STRING = KEY_PREFIX + "supl_host";
+
+        /** SUPL server port. Default to 7275. */
+        public static final String KEY_SUPL_PORT_STRING = KEY_PREFIX + "supl_port";
+
+        /**
+         * The SUPL version requested by Carrier. This is a bit mask
+         * with bits 0:7 representing a service indicator field, bits 8:15
+         * representing the minor version and bits 16:23 representing the
+         * major version. Default to 0x20000.
+         */
+        public static final String KEY_SUPL_VER_STRING = KEY_PREFIX + "supl_ver";
+
+        /**
+         * SUPL_MODE configuration bit mask
+         * 1 - Mobile Station Based. This is default.
+         * 2 - Mobile Station Assisted.
+         */
+        public static final String KEY_SUPL_MODE_STRING = KEY_PREFIX + "supl_mode";
+
+        /**
+         * Whether to limit responses to SUPL ES mode requests only during user emergency sessions
+         * (e.g. E911), and SUPL non-ES requests to only outside of non user emergency sessions.
+         * 0 - no.
+         * 1 - yes. This is default.
+         */
+        // TODO(b/119567985): name this key properly
+        public static final String KEY_SUPL_ES_STRING = KEY_PREFIX + "supl_es";
+
+        /**
+         * LTE Positioning Profile settings bit mask.
+         * 0 - Radio Resource Location Protocol in user plane and control plane. This is default.
+         * 1 - Enable LTE Positioning Protocol in user plane.
+         * 2 - Enable LTE Positioning Protocol in control plane.
+         */
+        public static final String KEY_LPP_PROFILE_STRING = KEY_PREFIX + "lpp_profile";
+
+        /**
+         * Determine whether to use emergency PDN for emergency SUPL.
+         * 0 - no.
+         * 1 - yes. This is default.
+         */
+        public static final String KEY_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL_STRING =
+                KEY_PREFIX + "use_emergency_pdn_for_emergency_supl";
+
+        /**
+         * A_GLONASS_POS_PROTOCOL_SELECT bit mask.
+         * 0 - Don't use A-GLONASS. This is default.
+         * 1 - Use A-GLONASS in Radio Resource Control(RRC) control-plane.
+         * 2 - Use A-GLONASS in Radio Resource Location user-plane.
+         * 4 - Use A-GLONASS in LTE Positioning Protocol User plane.
+         */
+        public static final String KEY_A_GLONASS_POS_PROTOCOL_SELECT_STRING =
+                KEY_PREFIX + "a_glonass_pos_protocol_select";
+
+        /**
+         * GPS_LOCK configuration bit mask to specify GPS device behavior toward other services,
+         * when Location Settings are off.
+         * "0" - No lock.
+         * "1" - Lock Mobile Originated GPS functionalities.
+         * "2" - Lock Network initiated GPS functionalities.
+         * "3" - Lock both. This is default.
+         */
+        public static final String KEY_GPS_LOCK_STRING = KEY_PREFIX + "gps_lock";
+
+        /**
+         * SUPL NI emergency extension time in seconds. Default to "0".
+         */
+        public static final String KEY_ES_EXTENSION_SEC = KEY_PREFIX + "es_extension_sec";
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            defaults.putBoolean(KEY_PERSIST_LPP_MODE_BOOL, true);
+            defaults.putString(KEY_SUPL_HOST_STRING, "supl.google.com");
+            defaults.putString(KEY_SUPL_PORT_STRING, "7275");
+            defaults.putString(KEY_SUPL_VER_STRING, "0x20000");
+            defaults.putString(KEY_SUPL_MODE_STRING, "1");
+            defaults.putString(KEY_SUPL_ES_STRING, "1");
+            defaults.putString(KEY_LPP_PROFILE_STRING, "0");
+            defaults.putString(KEY_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL_STRING, "1");
+            defaults.putString(KEY_A_GLONASS_POS_PROTOCOL_SELECT_STRING, "0");
+            defaults.putString(KEY_GPS_LOCK_STRING, "3");
+            defaults.putString(KEY_ES_EXTENSION_SEC, "0");
+            return defaults;
+        }
+    }
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -2658,12 +2787,9 @@
                 //6: CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS
                 //8: CARRIER_ACTION_DISABLE_DEFAULT_URL_HANDLER
                 });
-        sDefaults.putStringArray(KEY_CARRIER_DEFAULT_ACTIONS_ON_DEFAULT_NETWORK_AVAILABLE,
-                new String[] {
-                        String.valueOf(false) + ": 7",
-                        //7: CARRIER_ACTION_ENABLE_DEFAULT_URL_HANDLER
-                        String.valueOf(true) + ": 8"
-                        //8: CARRIER_ACTION_DISABLE_DEFAULT_URL_HANDLER
+        sDefaults.putStringArray(KEY_CARRIER_DEFAULT_ACTIONS_ON_DEFAULT_NETWORK_AVAILABLE, new String[] {
+                String.valueOf(false) + ": 7", //7: CARRIER_ACTION_ENABLE_DEFAULT_URL_HANDLER
+                String.valueOf(true) + ": 8"  //8: CARRIER_ACTION_DISABLE_DEFAULT_URL_HANDLER
                 });
         sDefaults.putStringArray(KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY, null);
 
@@ -2695,7 +2821,6 @@
         sDefaults.putStringArray(KEY_FILTERED_CNAP_NAMES_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_EDITABLE_WFC_ROAMING_MODE_BOOL, false);
         sDefaults.putBoolean(KEY_STK_DISABLE_LAUNCH_BROWSER_BOOL, false);
-        sDefaults.putBoolean(KEY_PERSIST_LPP_MODE_BOOL, true);
         sDefaults.putStringArray(KEY_CARRIER_WIFI_STRING_ARRAY, null);
         sDefaults.putInt(KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT, -1);
         sDefaults.putInt(KEY_EMERGENCY_NOTIFICATION_DELAY_INT, -1);
@@ -2750,7 +2875,7 @@
                         -95, /* SIGNAL_STRENGTH_GOOD */
                         -85  /* SIGNAL_STRENGTH_GREAT */
                 });
-        sDefaults.putString(KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING, "");
+        sDefaults.putString(KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING, "rssi");
         sDefaults.putBoolean(KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL, false);
         sDefaults.putBoolean(KEY_CALL_FORWARDING_OVER_UT_WARNING_BOOL, false);
@@ -2764,6 +2889,15 @@
         sDefaults.putInt(KEY_CALL_WAITING_SERVICE_CLASS_INT, 1 /* SERVICE_CLASS_VOICE */);
         sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING,
                 "connected_mmwave:None,connected:5G,not_restricted:None,restricted:None");
+        /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
+        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108);
+        /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */
+        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT, -118);
+        /* Default value is minimum RSSNR level needed for SIGNAL_STRENGTH_GOOD */
+        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT, 45);
+        /* Default value is minimum RSSNR level needed for SIGNAL_STRENGTH_MODERATE */
+        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT, 10);
+        sDefaults.putAll(Gps.getDefaults());
     }
 
     /**
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 598f567..fa19867 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -16,7 +16,6 @@
 
 package android.telephony;
 
-import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.telephony.cdma.CdmaCellLocation;
 
@@ -71,30 +70,13 @@
      *        to 2592000
      * @param lat Latitude is a decimal number ranges from -1296000
      *        to 1296000
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public CellIdentityCdma(int nid, int sid, int bid, int lon, int lat) {
-        this(nid, sid, bid, lon, lat, null, null);
-    }
-
-    /**
-     * public constructor
-     * @param nid Network Id 0..65535
-     * @param sid CDMA System Id 0..32767
-     * @param bid Base Station Id 0..65535
-     * @param lon Longitude is a decimal number ranges from -2592000
-     *        to 2592000
-     * @param lat Latitude is a decimal number ranges from -1296000
-     *        to 1296000
      * @param alphal long alpha Operator Name String or Enhanced Operator Name String
      * @param alphas short alpha Operator Name String or Enhanced Operator Name String
      *
      * @hide
      */
-    public CellIdentityCdma(int nid, int sid, int bid, int lon, int lat, String alphal,
-                             String alphas) {
+    public CellIdentityCdma(
+            int nid, int sid, int bid, int lon, int lat, String alphal, String alphas) {
         super(TAG, CellInfo.TYPE_CDMA, null, null, alphal, alphas);
         mNetworkId = nid;
         mSystemId = sid;
@@ -107,6 +89,17 @@
         }
     }
 
+    /** @hide */
+    public CellIdentityCdma(android.hardware.radio.V1_0.CellIdentityCdma cid) {
+        this(cid.networkId, cid.systemId, cid.baseStationId, cid.longitude, cid.latitude, "", "");
+    }
+
+    /** @hide */
+    public CellIdentityCdma(android.hardware.radio.V1_2.CellIdentityCdma cid) {
+        this(cid.base.networkId, cid.base.systemId, cid.base.baseStationId, cid.base.longitude,
+                cid.base.latitude, cid.operatorNames.alphaLong, cid.operatorNames.alphaShort);
+    }
+
     private CellIdentityCdma(CellIdentityCdma cid) {
         this(cid.mNetworkId, cid.mSystemId, cid.mBasestationId, cid.mLongitude, cid.mLatitude,
                 cid.mAlphaLong, cid.mAlphaShort);
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 04c28e5..9a24e47 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
@@ -35,10 +36,8 @@
     // 16-bit GSM Cell Identity described in TS 27.007, 0..65535
     private final int mCid;
     // 16-bit GSM Absolute RF Channel Number
-    @UnsupportedAppUsage
     private final int mArfcn;
     // 6-bit Base Station Identity Code
-    @UnsupportedAppUsage
     private final int mBsic;
 
     /**
@@ -52,34 +51,6 @@
         mArfcn = CellInfo.UNAVAILABLE;
         mBsic = CellInfo.UNAVAILABLE;
     }
-    /**
-     * public constructor
-     * @param mcc 3-digit Mobile Country Code, 0..999
-     * @param mnc 2 or 3-digit Mobile Network Code, 0..999
-     * @param lac 16-bit Location Area Code, 0..65535
-     * @param cid 16-bit GSM Cell Identity or 28-bit UMTS Cell Identity
-     *
-     * @hide
-     */
-    public CellIdentityGsm(int mcc, int mnc, int lac, int cid) {
-        this(lac, cid, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
-                String.valueOf(mcc), String.valueOf(mnc), null, null);
-    }
-
-    /**
-     * public constructor
-     * @param mcc 3-digit Mobile Country Code, 0..999
-     * @param mnc 2 or 3-digit Mobile Network Code, 0..999
-     * @param lac 16-bit Location Area Code, 0..65535
-     * @param cid 16-bit GSM Cell Identity or 28-bit UMTS Cell Identity
-     * @param arfcn 16-bit GSM Absolute RF Channel Number
-     * @param bsic 6-bit Base Station Identity Code
-     *
-     * @hide
-     */
-    public CellIdentityGsm(int mcc, int mnc, int lac, int cid, int arfcn, int bsic) {
-        this(lac, cid, arfcn, bsic, String.valueOf(mcc), String.valueOf(mnc), null, null);
-    }
 
     /**
      * public constructor
@@ -100,9 +71,21 @@
         mLac = lac;
         mCid = cid;
         mArfcn = arfcn;
-        // In RIL BSIC is a UINT8, so 0xFF is the 'INVALID' designator
-        // for inbound parcels
-        mBsic = (bsic == 0xFF) ? CellInfo.UNAVAILABLE : bsic;
+        mBsic = bsic;
+    }
+
+    /** @hide */
+    public CellIdentityGsm(android.hardware.radio.V1_0.CellIdentityGsm cid) {
+        this(cid.lac, cid.cid, cid.arfcn,
+                cid.bsic == (byte) 0xFF ? CellInfo.UNAVAILABLE : cid.bsic,
+                cid.mcc, cid.mnc, "", "");
+    }
+
+    /** @hide */
+    public CellIdentityGsm(android.hardware.radio.V1_2.CellIdentityGsm cid) {
+        this(cid.base.lac, cid.base.cid, cid.base.arfcn,
+                cid.base.bsic == (byte) 0xFF ? CellInfo.UNAVAILABLE : cid.base.bsic, cid.base.mcc,
+                cid.base.mnc, cid.operatorNames.alphaLong, cid.operatorNames.alphaShort);
     }
 
     private CellIdentityGsm(CellIdentityGsm cid) {
@@ -169,6 +152,7 @@
     /**
      * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown.
      */
+    @Nullable
     public String getMobileNetworkOperator() {
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
     }
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 04b6a6c..51393b9 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -16,7 +16,9 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
 import android.text.TextUtils;
@@ -37,7 +39,6 @@
     // 16-bit tracking area code
     private final int mTac;
     // 18-bit Absolute RF Channel Number
-    @UnsupportedAppUsage
     private final int mEarfcn;
     // cell bandwidth, in kHz
     private final int mBandwidth;
@@ -65,7 +66,7 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac) {
         this(ci, pci, tac, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, String.valueOf(mcc),
                 String.valueOf(mnc), null, null);
@@ -73,22 +74,6 @@
 
     /**
      *
-     * @param mcc 3-digit Mobile Country Code, 0..999
-     * @param mnc 2 or 3-digit Mobile Network Code, 0..999
-     * @param ci 28-bit Cell Identity
-     * @param pci Physical Cell Id 0..503
-     * @param tac 16-bit Tracking Area Code
-     * @param earfcn 18-bit LTE Absolute RF Channel Number
-     *
-     * @hide
-     */
-    public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac, int earfcn) {
-        this(ci, pci, tac, earfcn, CellInfo.UNAVAILABLE, String.valueOf(mcc), String.valueOf(mnc),
-                null, null);
-    }
-
-    /**
-     *
      * @param ci 28-bit Cell Identity
      * @param pci Physical Cell Id 0..503
      * @param tac 16-bit Tracking Area Code
@@ -111,6 +96,18 @@
         mBandwidth = bandwidth;
     }
 
+    /** @hide */
+    public CellIdentityLte(android.hardware.radio.V1_0.CellIdentityLte cid) {
+        this(cid.ci, cid.pci, cid.tac, cid.earfcn, CellInfo.UNAVAILABLE, cid.mcc, cid.mnc, "", "");
+    }
+
+    /** @hide */
+    public CellIdentityLte(android.hardware.radio.V1_2.CellIdentityLte cid) {
+        this(cid.base.ci, cid.base.pci, cid.base.tac, cid.base.earfcn, cid.bandwidth,
+                cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong,
+                cid.operatorNames.alphaShort);
+    }
+
     private CellIdentityLte(CellIdentityLte cid) {
         this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mBandwidth, cid.mMccStr,
                 cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort);
@@ -197,6 +194,7 @@
     /**
      * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown.
      */
+    @Nullable
     public String getMobileNetworkOperator() {
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
     }
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 8b1c1b9..3814333 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
 
@@ -50,22 +51,6 @@
     }
 
     /**
-     * @param mcc 3-digit Mobile Country Code, 0..999
-     * @param mnc 2 or 3-digit Mobile Network Code, 0..999
-     * @param lac 16-bit Location Area Code, 0..65535, CellInfo.UNAVAILABLE if unknown
-     * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, CellInfo.
-     *        UNAVAILABLE if unknown
-     * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, CellInfo.UNAVAILABLE
-     *        if unknown
-     * @param uarfcn 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
-     *
-     * @hide
-     */
-    public CellIdentityTdscdma(int mcc, int mnc, int lac, int cid, int cpid, int uarfcn) {
-        this(String.valueOf(mcc), String.valueOf(mnc), lac, cid, cpid, uarfcn, null, null);
-    }
-
-    /**
      * @param mcc 3-digit Mobile Country Code in string format
      * @param mnc 2 or 3-digit Mobile Network Code in string format
      * @param lac 16-bit Location Area Code, 0..65535, CellInfo.UNAVAILABLE if unknown
@@ -93,6 +78,17 @@
                 cid.mCpid, cid.mUarfcn, cid.mAlphaLong, cid.mAlphaShort);
     }
 
+    /** @hide */
+    public CellIdentityTdscdma(android.hardware.radio.V1_0.CellIdentityTdscdma cid) {
+        this(cid.mcc, cid.mnc, cid.lac, cid.cid, cid.cpid, CellInfo.UNAVAILABLE, "", "");
+    }
+
+    /** @hide */
+    public CellIdentityTdscdma(android.hardware.radio.V1_2.CellIdentityTdscdma cid) {
+        this(cid.base.mcc, cid.base.mnc, cid.base.lac, cid.base.cid, cid.base.cpid,
+                cid.uarfcn, cid.operatorNames.alphaLong, cid.operatorNames.alphaShort);
+    }
+
     CellIdentityTdscdma copy() {
         return new CellIdentityTdscdma(this);
     }
@@ -116,6 +112,7 @@
     /**
      * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown
      */
+    @Nullable
     public String getMobileNetworkOperator() {
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
     }
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 3416ffe..6e09784 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
@@ -50,35 +51,6 @@
         mPsc = CellInfo.UNAVAILABLE;
         mUarfcn = CellInfo.UNAVAILABLE;
     }
-    /**
-     * public constructor
-     * @param mcc 3-digit Mobile Country Code, 0..999
-     * @param mnc 2 or 3-digit Mobile Network Code, 0..999
-     * @param lac 16-bit Location Area Code, 0..65535
-     * @param cid 28-bit UMTS Cell Identity
-     * @param psc 9-bit UMTS Primary Scrambling Code
-     *
-     * @hide
-     */
-    public CellIdentityWcdma (int mcc, int mnc, int lac, int cid, int psc) {
-        this(lac, cid, psc, CellInfo.UNAVAILABLE, String.valueOf(mcc), String.valueOf(mnc),
-                null, null);
-    }
-
-    /**
-     * public constructor
-     * @param mcc 3-digit Mobile Country Code, 0..999
-     * @param mnc 2 or 3-digit Mobile Network Code, 0..999
-     * @param lac 16-bit Location Area Code, 0..65535
-     * @param cid 28-bit UMTS Cell Identity
-     * @param psc 9-bit UMTS Primary Scrambling Code
-     * @param uarfcn 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
-     *
-     * @hide
-     */
-    public CellIdentityWcdma (int mcc, int mnc, int lac, int cid, int psc, int uarfcn) {
-        this(lac, cid, psc, uarfcn, String.valueOf(mcc), String.valueOf(mnc), null, null);
-    }
 
     /**
      * public constructor
@@ -102,6 +74,18 @@
         mUarfcn = uarfcn;
     }
 
+    /** @hide */
+    public CellIdentityWcdma(android.hardware.radio.V1_0.CellIdentityWcdma cid) {
+        this(cid.lac, cid.cid, cid.psc, cid.uarfcn, cid.mcc, cid.mnc, "", "");
+    }
+
+    /** @hide */
+    public CellIdentityWcdma(android.hardware.radio.V1_2.CellIdentityWcdma cid) {
+        this(cid.base.lac, cid.base.cid, cid.base.psc, cid.base.uarfcn,
+                cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong,
+                cid.operatorNames.alphaShort);
+    }
+
     private CellIdentityWcdma(CellIdentityWcdma cid) {
         this(cid.mLac, cid.mCid, cid.mPsc, cid.mUarfcn, cid.mMccStr,
                 cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort);
@@ -173,6 +157,7 @@
     /**
      * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown
      */
+    @Nullable
     public String getMobileNetworkOperator() {
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
     }
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index d0b2687..b761bd7 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -132,7 +132,8 @@
     /** Connection status is unknown. */
     public static final int CONNECTION_UNKNOWN = Integer.MAX_VALUE;
 
-    private int mCellConnectionStatus = CONNECTION_NONE;
+    /** A cell connection status */
+    private int mCellConnectionStatus;
 
     // True if device is mRegistered to the mobile network
     private boolean mRegistered;
@@ -144,6 +145,7 @@
     protected CellInfo() {
         this.mRegistered = false;
         this.mTimeStamp = Long.MAX_VALUE;
+        mCellConnectionStatus = CONNECTION_NONE;
     }
 
     /** @hide */
@@ -300,4 +302,44 @@
             return new CellInfo[size];
         }
     };
+
+    /** @hide */
+    protected CellInfo(android.hardware.radio.V1_0.CellInfo ci) {
+        this.mRegistered = ci.registered;
+        this.mTimeStamp = ci.timeStamp;
+        this.mCellConnectionStatus = CONNECTION_UNKNOWN;
+    }
+
+    /** @hide */
+    protected CellInfo(android.hardware.radio.V1_2.CellInfo ci) {
+        this.mRegistered = ci.registered;
+        this.mTimeStamp = ci.timeStamp;
+        this.mCellConnectionStatus = ci.connectionStatus;
+    }
+
+    /** @hide */
+    public static CellInfo create(android.hardware.radio.V1_0.CellInfo ci) {
+        if (ci == null) return null;
+        switch(ci.cellInfoType) {
+            case android.hardware.radio.V1_0.CellInfoType.GSM: return new CellInfoGsm(ci);
+            case android.hardware.radio.V1_0.CellInfoType.CDMA: return new CellInfoCdma(ci);
+            case android.hardware.radio.V1_0.CellInfoType.LTE: return new CellInfoLte(ci);
+            case android.hardware.radio.V1_0.CellInfoType.WCDMA: return new CellInfoWcdma(ci);
+            case android.hardware.radio.V1_0.CellInfoType.TD_SCDMA: return new CellInfoTdscdma(ci);
+            default: return null;
+        }
+    }
+
+    /** @hide */
+    public static CellInfo create(android.hardware.radio.V1_2.CellInfo ci) {
+        if (ci == null) return null;
+        switch(ci.cellInfoType) {
+            case android.hardware.radio.V1_0.CellInfoType.GSM: return new CellInfoGsm(ci);
+            case android.hardware.radio.V1_0.CellInfoType.CDMA: return new CellInfoCdma(ci);
+            case android.hardware.radio.V1_0.CellInfoType.LTE: return new CellInfoLte(ci);
+            case android.hardware.radio.V1_0.CellInfoType.WCDMA: return new CellInfoWcdma(ci);
+            case android.hardware.radio.V1_0.CellInfoType.TD_SCDMA: return new CellInfoTdscdma(ci);
+            default: return null;
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java
index f67733d..c9f07da 100644
--- a/telephony/java/android/telephony/CellInfoCdma.java
+++ b/telephony/java/android/telephony/CellInfoCdma.java
@@ -17,6 +17,7 @@
 package android.telephony;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.Rlog;
@@ -41,13 +42,31 @@
     }
 
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public CellInfoCdma(CellInfoCdma ci) {
         super(ci);
         this.mCellIdentityCdma = ci.mCellIdentityCdma.copy();
         this.mCellSignalStrengthCdma = ci.mCellSignalStrengthCdma.copy();
     }
 
+    /** @hide */
+    public CellInfoCdma(android.hardware.radio.V1_0.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_0.CellInfoCdma cic = ci.cdma.get(0);
+        mCellIdentityCdma = new CellIdentityCdma(cic.cellIdentityCdma);
+        mCellSignalStrengthCdma =
+            new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo);
+    }
+
+    /** @hide */
+    public CellInfoCdma(android.hardware.radio.V1_2.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_2.CellInfoCdma cic = ci.cdma.get(0);
+        mCellIdentityCdma = new CellIdentityCdma(cic.cellIdentityCdma);
+        mCellSignalStrengthCdma =
+            new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo);
+    }
+
     @Override
     public CellIdentityCdma getCellIdentity() {
         return mCellIdentityCdma;
diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java
index 7211de1..ad16dfa 100644
--- a/telephony/java/android/telephony/CellInfoGsm.java
+++ b/telephony/java/android/telephony/CellInfoGsm.java
@@ -43,8 +43,24 @@
     /** @hide */
     public CellInfoGsm(CellInfoGsm ci) {
         super(ci);
-        this.mCellIdentityGsm = ci.mCellIdentityGsm.copy();
-        this.mCellSignalStrengthGsm = ci.mCellSignalStrengthGsm.copy();
+        mCellIdentityGsm = ci.mCellIdentityGsm.copy();
+        mCellSignalStrengthGsm = ci.mCellSignalStrengthGsm.copy();
+    }
+
+    /** @hide */
+    public CellInfoGsm(android.hardware.radio.V1_0.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_0.CellInfoGsm cig = ci.gsm.get(0);
+        mCellIdentityGsm = new CellIdentityGsm(cig.cellIdentityGsm);
+        mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm);
+    }
+
+    /** @hide */
+    public CellInfoGsm(android.hardware.radio.V1_2.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_2.CellInfoGsm cig = ci.gsm.get(0);
+        mCellIdentityGsm = new CellIdentityGsm(cig.cellIdentityGsm);
+        mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm);
     }
 
     @Override
diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java
index 7d5388b..7593831 100644
--- a/telephony/java/android/telephony/CellInfoLte.java
+++ b/telephony/java/android/telephony/CellInfoLte.java
@@ -17,6 +17,7 @@
 package android.telephony;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -51,13 +52,31 @@
         this.mCellConfig = new CellConfigLte(ci.mCellConfig);
     }
 
+    /** @hide */
+    public CellInfoLte(android.hardware.radio.V1_0.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_0.CellInfoLte cil = ci.lte.get(0);
+        mCellIdentityLte = new CellIdentityLte(cil.cellIdentityLte);
+        mCellSignalStrengthLte = new CellSignalStrengthLte(cil.signalStrengthLte);
+        mCellConfig = new CellConfigLte();
+    }
+
+    /** @hide */
+    public CellInfoLte(android.hardware.radio.V1_2.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_2.CellInfoLte cil = ci.lte.get(0);
+        mCellIdentityLte = new CellIdentityLte(cil.cellIdentityLte);
+        mCellSignalStrengthLte = new CellSignalStrengthLte(cil.signalStrengthLte);
+        mCellConfig = new CellConfigLte();
+    }
+
     @Override
     public CellIdentityLte getCellIdentity() {
         if (DBG) log("getCellIdentity: " + mCellIdentityLte);
         return mCellIdentityLte;
     }
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void setCellIdentity(CellIdentityLte cid) {
         if (DBG) log("setCellIdentity: " + cid);
         mCellIdentityLte = cid;
@@ -69,7 +88,7 @@
         return mCellSignalStrengthLte;
     }
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void setCellSignalStrength(CellSignalStrengthLte css) {
         if (DBG) log("setCellSignalStrength: " + css);
         mCellSignalStrengthLte = css;
diff --git a/telephony/java/android/telephony/CellInfoTdscdma.java b/telephony/java/android/telephony/CellInfoTdscdma.java
index 40cadde..a8c49b7 100644
--- a/telephony/java/android/telephony/CellInfoTdscdma.java
+++ b/telephony/java/android/telephony/CellInfoTdscdma.java
@@ -48,8 +48,23 @@
         this.mCellSignalStrengthTdscdma = ci.mCellSignalStrengthTdscdma.copy();
     }
 
-    @Override
-    public CellIdentityTdscdma getCellIdentity() {
+    /** @hide */
+    public CellInfoTdscdma(android.hardware.radio.V1_0.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_0.CellInfoTdscdma cit = ci.tdscdma.get(0);
+        mCellIdentityTdscdma = new CellIdentityTdscdma(cit.cellIdentityTdscdma);
+        mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma);
+    }
+
+    /** @hide */
+    public CellInfoTdscdma(android.hardware.radio.V1_2.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_2.CellInfoTdscdma cit = ci.tdscdma.get(0);
+        mCellIdentityTdscdma = new CellIdentityTdscdma(cit.cellIdentityTdscdma);
+        mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma);
+    }
+
+    @Override public CellIdentityTdscdma getCellIdentity() {
         return mCellIdentityTdscdma;
     }
     /** @hide */
diff --git a/telephony/java/android/telephony/CellInfoWcdma.java b/telephony/java/android/telephony/CellInfoWcdma.java
index 4f9dcb1..a427e80 100644
--- a/telephony/java/android/telephony/CellInfoWcdma.java
+++ b/telephony/java/android/telephony/CellInfoWcdma.java
@@ -47,6 +47,22 @@
         this.mCellSignalStrengthWcdma = ci.mCellSignalStrengthWcdma.copy();
     }
 
+    /** @hide */
+    public CellInfoWcdma(android.hardware.radio.V1_0.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_0.CellInfoWcdma ciw = ci.wcdma.get(0);
+        mCellIdentityWcdma = new CellIdentityWcdma(ciw.cellIdentityWcdma);
+        mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma);
+    }
+
+    /** @hide */
+    public CellInfoWcdma(android.hardware.radio.V1_2.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_2.CellInfoWcdma ciw = ci.wcdma.get(0);
+        mCellIdentityWcdma = new CellIdentityWcdma(ciw.cellIdentityWcdma);
+        mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma);
+    }
+
     @Override
     public CellIdentityWcdma getCellIdentity() {
         return mCellIdentityWcdma;
diff --git a/telephony/java/android/telephony/CellSignalStrength.java b/telephony/java/android/telephony/CellSignalStrength.java
index fd21d42..e6182ed 100644
--- a/telephony/java/android/telephony/CellSignalStrength.java
+++ b/telephony/java/android/telephony/CellSignalStrength.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import android.os.PersistableBundle;
+
 /**
  * Abstract base class for cell phone signal strength related information.
  */
@@ -40,6 +42,9 @@
     public static final int NUM_SIGNAL_STRENGTH_BINS = 5;
 
     /** @hide */
+    protected static final int NUM_SIGNAL_STRENGTH_THRESHOLDS = NUM_SIGNAL_STRENGTH_BINS - 1;
+
+    /** @hide */
     public static final String[] SIGNAL_STRENGTH_NAMES = {
         "none", "poor", "moderate", "good", "great"
     };
@@ -80,9 +85,74 @@
      */
     public abstract CellSignalStrength copy();
 
+    /**
+     * Checks and returns whether there are any non-default values in this CellSignalStrength.
+     *
+     * Checks all the values in the subclass of CellSignalStrength and returns true if any of them
+     * have been set to a value other than their default.
+     *
+     * @hide
+     */
+    public abstract boolean isValid();
+
     @Override
     public abstract int hashCode();
 
     @Override
     public abstract boolean equals (Object o);
+
+    /**
+     * Calculate and set the carrier-influenced values such as the signal "Level".
+     *
+     * @hide
+     */
+    public abstract void updateLevel(PersistableBundle cc, ServiceState ss);
+
+    // Range for RSSI in ASU (0-31, 99) as defined in TS 27.007 8.69
+    /** @hide */
+    protected static final int getRssiDbmFromAsu(int asu) {
+        if (asu > 31 || asu < 0) return CellInfo.UNAVAILABLE;
+        return -113 + (2 * asu);
+    }
+
+    // Range for RSSI in ASU (0-31, 99) as defined in TS 27.007 8.69
+    /** @hide */
+    protected static final int getAsuFromRssiDbm(int dbm) {
+        if (dbm == CellInfo.UNAVAILABLE) return 99;
+        return (dbm / 2) + 113;
+    }
+
+    // Range for RSCP in ASU (0-96, 255) as defined in TS 27.007 8.69
+    /** @hide */
+    protected static final int getRscpDbmFromAsu(int asu) {
+        if (asu > 96 || asu < 0) return CellInfo.UNAVAILABLE;
+        return asu - 120;
+    }
+
+    // Range for RSCP in ASU (0-96, 255) as defined in TS 27.007 8.69
+    /** @hide */
+    protected static final int getAsuFromRscpDbm(int dbm) {
+        if (dbm == CellInfo.UNAVAILABLE) return 255;
+        return dbm + 120;
+    }
+
+    // Range for SNR in ASU (0-49, 255) as defined in TS 27.007 8.69
+    /** @hide */
+    protected static final int getEcNoDbFromAsu(int asu) {
+        if (asu > 49 || asu < 0) return CellInfo.UNAVAILABLE;
+        return -24 + (asu / 2);
+    }
+
+    /** @hide */
+    protected static final int inRangeOrUnavailable(int value, int rangeMin, int rangeMax) {
+        if (value < rangeMin || value > rangeMax) return CellInfo.UNAVAILABLE;
+        return value;
+    }
+
+    /** @hide */
+    protected static final int inRangeOrUnavailable(
+            int value, int rangeMin, int rangeMax, int special) {
+        if ((value < rangeMin || value > rangeMax) && value != special) return CellInfo.UNAVAILABLE;
+        return value;
+    }
 }
diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java
index 5123052..47faf1e 100644
--- a/telephony/java/android/telephony/CellSignalStrengthCdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java
@@ -18,6 +18,7 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 import android.telephony.Rlog;
 
 import java.util.Objects;
@@ -35,6 +36,7 @@
     private int mEvdoDbm;   // This value is the EVDO RSSI value
     private int mEvdoEcio;  // This value is the EVDO Ec/Io
     private int mEvdoSnr;   // Valid values are 0-8.  8 is the highest signal to noise ratio
+    private int mLevel;
 
     /** @hide */
     public CellSignalStrengthCdma() {
@@ -55,23 +57,29 @@
      * rather than left as -1, which is a departure from SignalStrength, which is stuck with the
      * values it currently reports.
      *
-     * @param cdmaDbm negative of the CDMA signal strength value or -1 if invalid.
-     * @param cdmaEcio negative of the CDMA pilot/noise ratio or -1 if invalid.
-     * @param evdoDbm negative of the EvDO signal strength value or -1 if invalid.
-     * @param evdoEcio negative of the EvDO pilot/noise ratio or -1 if invalid.
-     * @param evdoSnr an SNR value 0..8 or -1 if invalid.
+     * @param cdmaDbm CDMA signal strength value or CellInfo.UNAVAILABLE if invalid.
+     * @param cdmaEcio CDMA pilot/noise ratio or CellInfo.UNAVAILABLE  if invalid.
+     * @param evdoDbm negative of the EvDO signal strength value or CellInfo.UNAVAILABLE if invalid.
+     * @param evdoEcio negative of the EvDO pilot/noise ratio or CellInfo.UNAVAILABLE if invalid.
+     * @param evdoSnr an SNR value 0..8 or CellInfo.UNVAILABLE if invalid.
      * @hide
      */
     public CellSignalStrengthCdma(int cdmaDbm, int cdmaEcio, int evdoDbm, int evdoEcio,
             int evdoSnr) {
-        // The values here were lifted from SignalStrength.validateInput()
-        // FIXME: Combine all checking and setting logic between this and SignalStrength.
-        mCdmaDbm = ((cdmaDbm > 0) && (cdmaDbm < 120))  ? -cdmaDbm : CellInfo.UNAVAILABLE;
-        mCdmaEcio = ((cdmaEcio > 0) && (cdmaEcio < 160)) ? -cdmaEcio : CellInfo.UNAVAILABLE;
+        mCdmaDbm = inRangeOrUnavailable(cdmaDbm, -120, 0);
+        mCdmaEcio = inRangeOrUnavailable(cdmaEcio, -160, 0);
+        mEvdoDbm = inRangeOrUnavailable(evdoDbm, -120, 0);
+        mEvdoEcio = inRangeOrUnavailable(evdoEcio, -160, 0);
+        mEvdoSnr = inRangeOrUnavailable(evdoSnr, 0, 8);
 
-        mEvdoDbm = ((evdoDbm > 0) && (evdoDbm < 120)) ? -evdoDbm : CellInfo.UNAVAILABLE;
-        mEvdoEcio = ((evdoEcio > 0) && (evdoEcio < 160)) ? -evdoEcio : CellInfo.UNAVAILABLE;
-        mEvdoSnr = ((evdoSnr > 0) && (evdoSnr <= 8)) ? evdoSnr : CellInfo.UNAVAILABLE;
+        updateLevel(null, null);
+    }
+
+    /** @hide */
+    public CellSignalStrengthCdma(android.hardware.radio.V1_0.CdmaSignalStrength cdma,
+            android.hardware.radio.V1_0.EvdoSignalStrength evdo) {
+        // Convert from HAL values as part of construction.
+        this(-cdma.dbm, -cdma.ecio, -evdo.dbm, -evdo.ecio, evdo.signalNoiseRatio);
     }
 
     /** @hide */
@@ -86,6 +94,7 @@
         mEvdoDbm = s.mEvdoDbm;
         mEvdoEcio = s.mEvdoEcio;
         mEvdoSnr = s.mEvdoSnr;
+        mLevel = s.mLevel;
     }
 
     /** @hide */
@@ -102,6 +111,7 @@
         mEvdoDbm = CellInfo.UNAVAILABLE;
         mEvdoEcio = CellInfo.UNAVAILABLE;
         mEvdoSnr = CellInfo.UNAVAILABLE;
+        mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
     }
 
     /**
@@ -112,26 +122,54 @@
      */
     @Override
     public int getLevel() {
-        int level;
+        return mLevel;
+    }
 
+    /** @hide */
+    @Override
+    public void updateLevel(PersistableBundle cc, ServiceState ss) {
         int cdmaLevel = getCdmaLevel();
         int evdoLevel = getEvdoLevel();
         if (evdoLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
             /* We don't know evdo, use cdma */
-            level = getCdmaLevel();
+            mLevel = getCdmaLevel();
         } else if (cdmaLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
             /* We don't know cdma, use evdo */
-            level = getEvdoLevel();
+            mLevel = getEvdoLevel();
         } else {
             /* We know both, use the lowest level */
-            level = cdmaLevel < evdoLevel ? cdmaLevel : evdoLevel;
+            mLevel = cdmaLevel < evdoLevel ? cdmaLevel : evdoLevel;
         }
-        if (DBG) log("getLevel=" + level);
-        return level;
     }
 
     /**
-     * Get the signal level as an asu value between 0..97, 99 is unknown
+     * Get the 1xRTT Level in (Android) ASU.
+     *
+     * There is no standard definition of ASU for CDMA; however, Android defines it as the
+     * the lesser of the following two results (for 1xRTT):
+     * <table>
+     *     <thead><tr><th>RSSI Range (dBm)</th><th>ASU Value</th></tr><thead>
+     *     <tbody>
+     *         <tr><td>-75..</td><td>16</td></tr>
+     *         <tr><td>-82..-76</td><td>8</td></tr>
+     *         <tr><td>-90..-83</td><td>4</td></tr>
+     *         <tr><td>-95..-91</td><td>2</td></tr>
+     *         <tr><td>-100..-96</td><td>1</td></tr>
+     *         <tr><td>..-101</td><td>99</td></tr>
+     *     </tbody>
+     * </table>
+     * <table>
+     *     <thead><tr><th>Ec/Io Range (dB)</th><th>ASU Value</th></tr><thead>
+     *     <tbody>
+     *         <tr><td>-90..</td><td>16</td></tr>
+     *         <tr><td>-100..-91</td><td>8</td></tr>
+     *         <tr><td>-115..-101</td><td>4</td></tr>
+     *         <tr><td>-130..-116</td><td>2</td></tr>
+     *         <tr><td>--150..-131</td><td>1</td></tr>
+     *         <tr><td>..-151</td><td>99</td></tr>
+     *     </tbody>
+     * </table>
+     * @return 1xRTT Level in Android ASU {1,2,4,8,16,99}
      */
     @Override
     public int getAsuLevel() {
@@ -220,6 +258,63 @@
     }
 
     /**
+     * Get the EVDO Level in (Android) ASU.
+     *
+     * There is no standard definition of ASU for CDMA; however, Android defines it as the
+     * the lesser of the following two results (for EVDO):
+     * <table>
+     *     <thead><tr><th>RSSI Range (dBm)</th><th>ASU Value</th></tr><thead>
+     *     <tbody>
+     *         <tr><td>-65..</td><td>16</td></tr>
+     *         <tr><td>-75..-66</td><td>8</td></tr>
+     *         <tr><td>-85..-76</td><td>4</td></tr>
+     *         <tr><td>-95..-86</td><td>2</td></tr>
+     *         <tr><td>-105..-96</td><td>1</td></tr>
+     *         <tr><td>..-106</td><td>99</td></tr>
+     *     </tbody>
+     * </table>
+     * <table>
+     *     <thead><tr><th>SNR Range (unitless)</th><th>ASU Value</th></tr><thead>
+     *     <tbody>
+     *         <tr><td>7..</td><td>16</td></tr>
+     *         <tr><td>6</td><td>8</td></tr>
+     *         <tr><td>5</td><td>4</td></tr>
+     *         <tr><td>3..4</td><td>2</td></tr>
+     *         <tr><td>1..2</td><td>1</td></tr>
+     *         <tr><td>0</td><td>99</td></tr>
+     *     </tbody>
+     * </table>
+     *
+     * @return EVDO Level in Android ASU {1,2,4,8,16,99}
+     *
+     * @hide
+     */
+    public int getEvdoAsuLevel() {
+        int evdoDbm = getEvdoDbm();
+        int evdoSnr = getEvdoSnr();
+        int levelEvdoDbm;
+        int levelEvdoSnr;
+
+        if (evdoDbm >= -65) levelEvdoDbm = 16;
+        else if (evdoDbm >= -75) levelEvdoDbm = 8;
+        else if (evdoDbm >= -85) levelEvdoDbm = 4;
+        else if (evdoDbm >= -95) levelEvdoDbm = 2;
+        else if (evdoDbm >= -105) levelEvdoDbm = 1;
+        else levelEvdoDbm = 99;
+
+        if (evdoSnr >= 7) levelEvdoSnr = 16;
+        else if (evdoSnr >= 6) levelEvdoSnr = 8;
+        else if (evdoSnr >= 5) levelEvdoSnr = 4;
+        else if (evdoSnr >= 3) levelEvdoSnr = 2;
+        else if (evdoSnr >= 1) levelEvdoSnr = 1;
+        else levelEvdoSnr = 99;
+
+        int level = (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
+        if (DBG) log("getEvdoAsuLevel=" + level);
+        return level;
+    }
+
+    /**
      * Get the signal strength as dBm
      */
     @Override
@@ -237,6 +332,7 @@
     public int getCdmaDbm() {
         return mCdmaDbm;
     }
+
     /** @hide */
     public void setCdmaDbm(int cdmaDbm) {
         mCdmaDbm = cdmaDbm;
@@ -248,6 +344,7 @@
     public int getCdmaEcio() {
         return mCdmaEcio;
     }
+
     /** @hide */
     public void setCdmaEcio(int cdmaEcio) {
         mCdmaEcio = cdmaEcio;
@@ -259,6 +356,7 @@
     public int getEvdoDbm() {
         return mEvdoDbm;
     }
+
     /** @hide */
     public void setEvdoDbm(int evdoDbm) {
         mEvdoDbm = evdoDbm;
@@ -270,6 +368,7 @@
     public int getEvdoEcio() {
         return mEvdoEcio;
     }
+
     /** @hide */
     public void setEvdoEcio(int evdoEcio) {
         mEvdoEcio = evdoEcio;
@@ -281,6 +380,7 @@
     public int getEvdoSnr() {
         return mEvdoSnr;
     }
+
     /** @hide */
     public void setEvdoSnr(int evdoSnr) {
         mEvdoSnr = evdoSnr;
@@ -288,28 +388,29 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mCdmaDbm, mCdmaEcio, mEvdoDbm, mEvdoEcio, mEvdoSnr);
+        return Objects.hash(mCdmaDbm, mCdmaEcio, mEvdoDbm, mEvdoEcio, mEvdoSnr, mLevel);
+    }
+
+    private static final CellSignalStrengthCdma sInvalid = new CellSignalStrengthCdma();
+
+    /** @hide */
+    @Override
+    public boolean isValid() {
+        return !this.equals(sInvalid);
     }
 
     @Override
     public boolean equals (Object o) {
         CellSignalStrengthCdma s;
-
-        try {
-            s = (CellSignalStrengthCdma) o;
-        } catch (ClassCastException ex) {
-            return false;
-        }
-
-        if (o == null) {
-            return false;
-        }
+        if (!(o instanceof CellSignalStrengthCdma)) return false;
+        s = (CellSignalStrengthCdma) o;
 
         return mCdmaDbm == s.mCdmaDbm
                 && mCdmaEcio == s.mCdmaEcio
                 && mEvdoDbm == s.mEvdoDbm
                 && mEvdoEcio == s.mEvdoEcio
-                && mEvdoSnr == s.mEvdoSnr;
+                && mEvdoSnr == s.mEvdoSnr
+                && mLevel == s.mLevel;
     }
 
     /**
@@ -322,7 +423,8 @@
                 + " cdmaEcio=" + mCdmaEcio
                 + " evdoDbm=" + mEvdoDbm
                 + " evdoEcio=" + mEvdoEcio
-                + " evdoSnr=" + mEvdoSnr;
+                + " evdoSnr=" + mEvdoSnr
+                + " level=" + mLevel;
     }
 
     /** Implement the Parcelable interface */
@@ -334,6 +436,7 @@
         dest.writeInt(mEvdoDbm);
         dest.writeInt(mEvdoEcio);
         dest.writeInt(mEvdoSnr);
+        dest.writeInt(mLevel);
     }
 
     /**
@@ -349,6 +452,7 @@
         mEvdoDbm = in.readInt();
         mEvdoEcio = in.readInt();
         mEvdoSnr = in.readInt();
+        mLevel = in.readInt();
         if (DBG) log("CellSignalStrengthCdma(Parcel): " + toString());
     }
 
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index e906f46..7b29f69 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -19,6 +19,7 @@
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 import android.telephony.Rlog;
 
 import java.util.Objects;
@@ -31,16 +32,18 @@
     private static final String LOG_TAG = "CellSignalStrengthGsm";
     private static final boolean DBG = false;
 
-    private static final int GSM_SIGNAL_STRENGTH_GREAT = 12;
-    private static final int GSM_SIGNAL_STRENGTH_GOOD = 8;
-    private static final int GSM_SIGNAL_STRENGTH_MODERATE = 5;
+    private static final int GSM_RSSI_MAX = -51;
+    private static final int GSM_RSSI_GREAT = -89;
+    private static final int GSM_RSSI_GOOD = -97;
+    private static final int GSM_RSSI_MODERATE = -103;
+    private static final int GSM_RSSI_POOR = -107;
 
+    private int mRssi; // in dBm [-113, -51] or UNAVAILABLE
     @UnsupportedAppUsage
-    private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
-    @UnsupportedAppUsage
-    private int mBitErrorRate;   // bit error rate (0-7, 99) as defined in TS 27.007 8.5
-    @UnsupportedAppUsage
+    private int mBitErrorRate; // bit error rate (0-7, 99) TS 27.007 8.5 or UNAVAILABLE
+    @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O)
     private int mTimingAdvance; // range from 0-219 or CellInfo.UNAVAILABLE if unknown
+    private int mLevel;
 
     /** @hide */
     @UnsupportedAppUsage
@@ -49,15 +52,17 @@
     }
 
     /** @hide */
-    public CellSignalStrengthGsm(int ss, int ber) {
-        this(ss, ber, CellInfo.UNAVAILABLE);
+    public CellSignalStrengthGsm(int rssi, int ber, int ta) {
+        mRssi = inRangeOrUnavailable(rssi, -113, -51);
+        mBitErrorRate = inRangeOrUnavailable(ber, 0, 7, 99);
+        mTimingAdvance = inRangeOrUnavailable(ta, 0, 219);
+        updateLevel(null, null);
     }
 
     /** @hide */
-    public CellSignalStrengthGsm(int ss, int ber, int ta) {
-        mSignalStrength = ss;
-        mBitErrorRate = ber;
-        mTimingAdvance = ta;
+    public CellSignalStrengthGsm(android.hardware.radio.V1_0.GsmSignalStrength gsm) {
+        // Convert from HAL values as part of construction.
+        this(getRssiDbmFromAsu(gsm.signalStrength), gsm.bitErrorRate, gsm.timingAdvance);
     }
 
     /** @hide */
@@ -67,9 +72,10 @@
 
     /** @hide */
     protected void copyFrom(CellSignalStrengthGsm s) {
-        mSignalStrength = s.mSignalStrength;
+        mRssi = s.mRssi;
         mBitErrorRate = s.mBitErrorRate;
         mTimingAdvance = s.mTimingAdvance;
+        mLevel = s.mLevel;
     }
 
     /** @hide */
@@ -81,9 +87,10 @@
     /** @hide */
     @Override
     public void setDefaultValues() {
-        mSignalStrength = CellInfo.UNAVAILABLE;
+        mRssi = CellInfo.UNAVAILABLE;
         mBitErrorRate = CellInfo.UNAVAILABLE;
         mTimingAdvance = CellInfo.UNAVAILABLE;
+        mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
     }
 
     /**
@@ -94,20 +101,18 @@
      */
     @Override
     public int getLevel() {
-        int level;
+        return mLevel;
+    }
 
-        // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
-        // asu = 0 (-113dB or less) is very weak
-        // signal, its better to show 0 bars to the user in such cases.
-        // asu = 99 is a special case, where the signal strength is unknown.
-        int asu = mSignalStrength;
-        if (asu <= 2 || asu == 99) level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-        else if (asu >= GSM_SIGNAL_STRENGTH_GREAT) level = SIGNAL_STRENGTH_GREAT;
-        else if (asu >= GSM_SIGNAL_STRENGTH_GOOD)  level = SIGNAL_STRENGTH_GOOD;
-        else if (asu >= GSM_SIGNAL_STRENGTH_MODERATE)  level = SIGNAL_STRENGTH_MODERATE;
-        else level = SIGNAL_STRENGTH_POOR;
-        if (DBG) log("getLevel=" + level);
-        return level;
+    /** @hide */
+    @Override
+    public void updateLevel(PersistableBundle cc, ServiceState ss) {
+        if (mRssi > GSM_RSSI_MAX) mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        else if (mRssi >= GSM_RSSI_GREAT) mLevel = SIGNAL_STRENGTH_GREAT;
+        else if (mRssi >= GSM_RSSI_GOOD)  mLevel = SIGNAL_STRENGTH_GOOD;
+        else if (mRssi >= GSM_RSSI_MODERATE)  mLevel = SIGNAL_STRENGTH_MODERATE;
+        else if (mRssi >= GSM_RSSI_POOR) mLevel = SIGNAL_STRENGTH_POOR;
+        else mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
     }
 
     /**
@@ -126,55 +131,52 @@
      */
     @Override
     public int getDbm() {
-        int dBm;
-
-        int level = mSignalStrength;
-        int asu = (level == 99 ? CellInfo.UNAVAILABLE : level);
-        if (asu != CellInfo.UNAVAILABLE) {
-            dBm = -113 + (2 * asu);
-        } else {
-            dBm = CellInfo.UNAVAILABLE;
-        }
-        if (DBG) log("getDbm=" + dBm);
-        return dBm;
+        return mRssi;
     }
 
     /**
-     * Get the signal level as an asu value between 0..31, 99 is unknown
+     * Get the RSSI in ASU.
+     *
      * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+     *
+     * @return RSSI in ASU 0..31, 99, or UNAVAILABLE
      */
     @Override
     public int getAsuLevel() {
-        // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
-        // asu = 0 (-113dB or less) is very weak
-        // signal, its better to show 0 bars to the user in such cases.
-        // asu = 99 is a special case, where the signal strength is unknown.
-        int level = mSignalStrength;
-        if (DBG) log("getAsuLevel=" + level);
-        return level;
+        return getAsuFromRssiDbm(mRssi);
+    }
+
+    /**
+     * Return the Bit Error Rate
+     * @returns the bit error rate (0-7, 99) as defined in TS 27.007 8.5 or UNAVAILABLE.
+     * @hide
+     */
+    public int getBitErrorRate() {
+        return mBitErrorRate;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mSignalStrength, mBitErrorRate, mTimingAdvance);
+        return Objects.hash(mRssi, mBitErrorRate, mTimingAdvance);
+    }
+
+    private static final CellSignalStrengthGsm sInvalid = new CellSignalStrengthGsm();
+
+    /** @hide */
+    @Override
+    public boolean isValid() {
+        return !this.equals(sInvalid);
     }
 
     @Override
-    public boolean equals (Object o) {
-        CellSignalStrengthGsm s;
+    public boolean equals(Object o) {
+        if (!(o instanceof CellSignalStrengthGsm)) return false;
+        CellSignalStrengthGsm s = (CellSignalStrengthGsm) o;
 
-        try {
-            s = (CellSignalStrengthGsm) o;
-        } catch (ClassCastException ex) {
-            return false;
-        }
-
-        if (o == null) {
-            return false;
-        }
-
-        return mSignalStrength == s.mSignalStrength && mBitErrorRate == s.mBitErrorRate &&
-                        s.mTimingAdvance == mTimingAdvance;
+        return mRssi == s.mRssi
+                && mBitErrorRate == s.mBitErrorRate
+                && mTimingAdvance == s.mTimingAdvance
+                && mLevel == s.mLevel;
     }
 
     /**
@@ -183,18 +185,20 @@
     @Override
     public String toString() {
         return "CellSignalStrengthGsm:"
-                + " ss=" + mSignalStrength
+                + " rssi=" + mRssi
                 + " ber=" + mBitErrorRate
-                + " mTa=" + mTimingAdvance;
+                + " mTa=" + mTimingAdvance
+                + " mLevel=" + mLevel;
     }
 
     /** Implement the Parcelable interface */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         if (DBG) log("writeToParcel(Parcel, int): " + toString());
-        dest.writeInt(mSignalStrength);
+        dest.writeInt(mRssi);
         dest.writeInt(mBitErrorRate);
         dest.writeInt(mTimingAdvance);
+        dest.writeInt(mLevel);
     }
 
     /**
@@ -202,9 +206,10 @@
      * where the token is already been processed.
      */
     private CellSignalStrengthGsm(Parcel in) {
-        mSignalStrength = in.readInt();
+        mRssi = in.readInt();
         mBitErrorRate = in.readInt();
         mTimingAdvance = in.readInt();
+        mLevel = in.readInt();
         if (DBG) log("CellSignalStrengthGsm(Parcel): " + toString());
     }
 
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index d075394..893dbe3 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -19,7 +19,9 @@
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 
+import java.util.Arrays;
 import java.util.Objects;
 
 /**
@@ -28,7 +30,7 @@
 public final class CellSignalStrengthLte extends CellSignalStrength implements Parcelable {
 
     private static final String LOG_TAG = "CellSignalStrengthLte";
-    private static final boolean DBG = false;
+    private static final boolean DBG = true;
 
     /**
      * Indicates the unknown or undetectable RSSI value in ASU.
@@ -49,18 +51,23 @@
      */
     private static final int SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MIN_VALUE = 0;
 
-    @UnsupportedAppUsage
-    private int mSignalStrength;
-    @UnsupportedAppUsage
+    private static final int MAX_LTE_RSRP = -44;
+    private static final int MIN_LTE_RSRP = -140;
+
+    @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
+    private int mSignalStrength; // To be removed
+    private int mRssi;
+    @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O)
     private int mRsrp;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O)
     private int mRsrq;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O)
     private int mRssnr;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O)
     private int mCqi;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O)
     private int mTimingAdvance;
+    private int mLevel;
 
     /** @hide */
     @UnsupportedAppUsage
@@ -68,15 +75,38 @@
         setDefaultValues();
     }
 
+    /**
+     * Construct a cell signal strength
+     *
+     * @param rssi in dBm [-113,-51], UNKNOWN
+     * @param rsrp in dBm [-140,-43], UNKNOWN
+     * @param rsrq in dB [-20,-3], UNKNOWN
+     * @param rssnr in 10*dB [-200, +300], UNKNOWN
+     * @param cqi [0, 15], UNKNOWN
+     * @param timingAdvance [0, 1282], UNKNOWN
+     *
+     */
     /** @hide */
-    public CellSignalStrengthLte(int signalStrength, int rsrp, int rsrq, int rssnr, int cqi,
+    public CellSignalStrengthLte(int rssi, int rsrp, int rsrq, int rssnr, int cqi,
             int timingAdvance) {
-        mSignalStrength = signalStrength;
-        mRsrp = rsrp;
-        mRsrq = rsrq;
-        mRssnr = rssnr;
-        mCqi = cqi;
-        mTimingAdvance = timingAdvance;
+
+        mRssi = inRangeOrUnavailable(rssi, -113, -51);
+        mSignalStrength = mRssi;
+        mRsrp = inRangeOrUnavailable(rsrp, -140, -43);
+        mRsrq = inRangeOrUnavailable(rsrq, -20, -3);
+        mRssnr = inRangeOrUnavailable(rssnr, -200, 300);
+        mCqi = inRangeOrUnavailable(cqi, 0, 15);
+        mTimingAdvance = inRangeOrUnavailable(timingAdvance, 0, 1282);
+        updateLevel(null, null);
+    }
+
+    /** @hide */
+    public CellSignalStrengthLte(android.hardware.radio.V1_0.LteSignalStrength lte) {
+        // Convert from HAL values as part of construction.
+        this(convertRssiAsuToDBm(lte.signalStrength),
+                lte.rsrp != CellInfo.UNAVAILABLE ? -lte.rsrp : lte.rsrp,
+                lte.rsrq != CellInfo.UNAVAILABLE ? -lte.rsrq : lte.rsrq,
+                lte.rssnr, lte.cqi, lte.timingAdvance);
     }
 
     /** @hide */
@@ -87,11 +117,13 @@
     /** @hide */
     protected void copyFrom(CellSignalStrengthLte s) {
         mSignalStrength = s.mSignalStrength;
+        mRssi = s.mRssi;
         mRsrp = s.mRsrp;
         mRsrq = s.mRsrq;
         mRssnr = s.mRssnr;
         mCqi = s.mCqi;
         mTimingAdvance = s.mTimingAdvance;
+        mLevel = s.mLevel;
     }
 
     /** @hide */
@@ -104,11 +136,13 @@
     @Override
     public void setDefaultValues() {
         mSignalStrength = CellInfo.UNAVAILABLE;
+        mRssi = CellInfo.UNAVAILABLE;
         mRsrp = CellInfo.UNAVAILABLE;
         mRsrq = CellInfo.UNAVAILABLE;
         mRssnr = CellInfo.UNAVAILABLE;
         mCqi = CellInfo.UNAVAILABLE;
         mTimingAdvance = CellInfo.UNAVAILABLE;
+        mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
     }
 
     /**
@@ -119,34 +153,106 @@
      */
     @Override
     public int getLevel() {
-        int levelRsrp = 0;
-        int levelRssnr = 0;
+        return mLevel;
+    }
 
-        if (mRsrp == CellInfo.UNAVAILABLE) levelRsrp = 0;
-        else if (mRsrp >= -95) levelRsrp = SIGNAL_STRENGTH_GREAT;
-        else if (mRsrp >= -105) levelRsrp = SIGNAL_STRENGTH_GOOD;
-        else if (mRsrp >= -115) levelRsrp = SIGNAL_STRENGTH_MODERATE;
-        else levelRsrp = SIGNAL_STRENGTH_POOR;
+    // Lifted from Default carrier configs and max range of RSRP
+    private static final int[] sThresholds = new int[]{-115, -105, -95, -85};
+    private static final int sRsrpBoost = 0;
 
-        // See RIL_LTE_SignalStrength in ril.h
-        if (mRssnr == CellInfo.UNAVAILABLE) levelRssnr = 0;
-        else if (mRssnr >= 45) levelRssnr = SIGNAL_STRENGTH_GREAT;
-        else if (mRssnr >= 10) levelRssnr = SIGNAL_STRENGTH_GOOD;
-        else if (mRssnr >= -30) levelRssnr = SIGNAL_STRENGTH_MODERATE;
-        else levelRssnr = SIGNAL_STRENGTH_POOR;
-
-        int level;
-        if (mRsrp == CellInfo.UNAVAILABLE) {
-            level = levelRssnr;
-        } else if (mRssnr == CellInfo.UNAVAILABLE) {
-            level = levelRsrp;
+    /** @hide */
+    @Override
+    public void updateLevel(PersistableBundle cc, ServiceState ss) {
+        int[] thresholds;
+        boolean rsrpOnly;
+        if (cc == null) {
+            thresholds = sThresholds;
+            rsrpOnly = false;
         } else {
-            level = (levelRssnr < levelRsrp) ? levelRssnr : levelRsrp;
+            rsrpOnly = cc.getBoolean(
+                    CarrierConfigManager.KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL, false);
+            thresholds = cc.getIntArray(
+                    CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY);
+            if (thresholds == null) thresholds = sThresholds;
+            if (DBG) log("updateLevel() carrierconfig - rsrpOnly="
+                    + rsrpOnly + ", thresholds=" + Arrays.toString(thresholds));
         }
 
-        if (DBG) log("Lte rsrp level: " + levelRsrp
-                + " snr level: " + levelRssnr + " level: " + level);
-        return level;
+
+        int rsrpBoost = 0;
+        if (ss != null) {
+            rsrpBoost = ss.getLteEarfcnRsrpBoost();
+        }
+
+        int rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        int rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        int snrIconLevel = -1;
+
+        int rsrp = mRsrp + rsrpBoost;
+
+        if (rsrp < MIN_LTE_RSRP || rsrp > MAX_LTE_RSRP) {
+            rsrpIconLevel = -1;
+        } else {
+            rsrpIconLevel = thresholds.length;
+            while (rsrpIconLevel > 0 && rsrp < thresholds[rsrpIconLevel - 1]) rsrpIconLevel--;
+        }
+
+        if (rsrpOnly) {
+            if (DBG) log("updateLevel() - rsrp = " + rsrpIconLevel);
+            if (rsrpIconLevel != -1) {
+                mLevel = rsrpIconLevel;
+                return;
+            }
+        }
+
+        /*
+         * Values are -200 dB to +300 (SNR*10dB) RS_SNR >= 13.0 dB =>4 bars 4.5
+         * dB <= RS_SNR < 13.0 dB => 3 bars 1.0 dB <= RS_SNR < 4.5 dB => 2 bars
+         * -3.0 dB <= RS_SNR < 1.0 dB 1 bar RS_SNR < -3.0 dB/No Service Antenna
+         * Icon Only
+         */
+        if (mRssnr > 300) snrIconLevel = -1;
+        else if (mRssnr >= 130) snrIconLevel = SIGNAL_STRENGTH_GREAT;
+        else if (mRssnr >= 45) snrIconLevel = SIGNAL_STRENGTH_GOOD;
+        else if (mRssnr >= 10) snrIconLevel = SIGNAL_STRENGTH_MODERATE;
+        else if (mRssnr >= -30) snrIconLevel = SIGNAL_STRENGTH_POOR;
+        else if (mRssnr >= -200)
+            snrIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+        if (DBG) log("updateLevel() - rsrp:" + mRsrp + " snr:" + mRssnr + " rsrpIconLevel:"
+                + rsrpIconLevel + " snrIconLevel:" + snrIconLevel
+                + " lteRsrpBoost:" + sRsrpBoost);
+
+        /* Choose a measurement type to use for notification */
+        if (snrIconLevel != -1 && rsrpIconLevel != -1) {
+            /*
+             * The number of bars displayed shall be the smaller of the bars
+             * associated with LTE RSRP and the bars associated with the LTE
+             * RS_SNR
+             */
+            mLevel = (rsrpIconLevel < snrIconLevel ? rsrpIconLevel : snrIconLevel);
+            return;
+        }
+
+        if (snrIconLevel != -1) {
+            mLevel = snrIconLevel;
+            return;
+        }
+
+        if (rsrpIconLevel != -1) {
+            mLevel = rsrpIconLevel;
+            return;
+        }
+
+        if (mRssi > -51) rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        else if (mRssi >= -89) rssiIconLevel = SIGNAL_STRENGTH_GREAT;
+        else if (mRssi >= -97) rssiIconLevel = SIGNAL_STRENGTH_GOOD;
+        else if (mRssi >= -103) rssiIconLevel = SIGNAL_STRENGTH_MODERATE;
+        else if (mRssi >= -113) rssiIconLevel = SIGNAL_STRENGTH_POOR;
+        else rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        if (DBG) log("getLteLevel - rssi:" + mRssi + " rssiIconLevel:"
+                + rssiIconLevel);
+        mLevel = rssiIconLevel;
     }
 
     /**
@@ -169,7 +275,7 @@
      * @return the RSSI if available or {@link CellInfo#UNAVAILABLE} if unavailable.
      */
     public int getRssi() {
-        return convertRssiAsuToDBm(mSignalStrength);
+        return mRssi;
     }
 
     /**
@@ -212,13 +318,16 @@
     }
 
     /**
-     * Get the LTE signal level as an asu value between 0..97, 99 is unknown
+     * Get the RSRP in ASU.
+     *
      * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+     *
+     * @return RSCP in ASU 0..97, 255, or UNAVAILABLE
      */
     @Override
     public int getAsuLevel() {
         int lteAsuLevel = 99;
-        int lteDbm = getDbm();
+        int lteDbm = mRsrp;
         if (lteDbm == CellInfo.UNAVAILABLE) lteAsuLevel = 99;
         else if (lteDbm <= -140) lteAsuLevel = 0;
         else if (lteDbm >= -43) lteAsuLevel = 97;
@@ -241,29 +350,31 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mSignalStrength, mRsrp, mRsrq, mRssnr, mCqi, mTimingAdvance);
+        return Objects.hash(mRssi, mRsrp, mRsrq, mRssnr, mCqi, mTimingAdvance, mLevel);
+    }
+
+    private static final CellSignalStrengthLte sInvalid = new CellSignalStrengthLte();
+
+    /** @hide */
+    @Override
+    public boolean isValid() {
+        return !this.equals(sInvalid);
     }
 
     @Override
     public boolean equals (Object o) {
         CellSignalStrengthLte s;
 
-        try {
-            s = (CellSignalStrengthLte) o;
-        } catch (ClassCastException ex) {
-            return false;
-        }
+        if (!(o instanceof CellSignalStrengthLte)) return false;
+        s = (CellSignalStrengthLte) o;
 
-        if (o == null) {
-            return false;
-        }
-
-        return mSignalStrength == s.mSignalStrength
+        return mRssi == s.mRssi
                 && mRsrp == s.mRsrp
                 && mRsrq == s.mRsrq
                 && mRssnr == s.mRssnr
                 && mCqi == s.mCqi
-                && mTimingAdvance == s.mTimingAdvance;
+                && mTimingAdvance == s.mTimingAdvance
+                && mLevel == s.mLevel;
     }
 
     /**
@@ -272,27 +383,29 @@
     @Override
     public String toString() {
         return "CellSignalStrengthLte:"
-                + " ss=" + mSignalStrength
+                + " rssi=" + mRssi
                 + " rsrp=" + mRsrp
                 + " rsrq=" + mRsrq
                 + " rssnr=" + mRssnr
                 + " cqi=" + mCqi
-                + " ta=" + mTimingAdvance;
+                + " ta=" + mTimingAdvance
+                + " level=" + mLevel;
     }
 
     /** Implement the Parcelable interface */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         if (DBG) log("writeToParcel(Parcel, int): " + toString());
-        dest.writeInt(mSignalStrength);
+        dest.writeInt(mRssi);
         // Need to multiply rsrp and rsrq by -1
         // to ensure consistency when reading values written here
         // unless the values are invalid
-        dest.writeInt(mRsrp * (mRsrp != CellInfo.UNAVAILABLE ? -1 : 1));
-        dest.writeInt(mRsrq * (mRsrq != CellInfo.UNAVAILABLE ? -1 : 1));
+        dest.writeInt(mRsrp);
+        dest.writeInt(mRsrq);
         dest.writeInt(mRssnr);
         dest.writeInt(mCqi);
         dest.writeInt(mTimingAdvance);
+        dest.writeInt(mLevel);
     }
 
     /**
@@ -300,16 +413,14 @@
      * where the token is already been processed.
      */
     private CellSignalStrengthLte(Parcel in) {
-        mSignalStrength = in.readInt();
-        // rsrp and rsrq are written into the parcel as positive values.
-        // Need to convert into negative values unless the values are invalid
+        mRssi = in.readInt();
+        mSignalStrength = mRssi;
         mRsrp = in.readInt();
-        if (mRsrp != CellInfo.UNAVAILABLE) mRsrp *= -1;
         mRsrq = in.readInt();
-        if (mRsrq != CellInfo.UNAVAILABLE) mRsrq *= -1;
         mRssnr = in.readInt();
         mCqi = in.readInt();
         mTimingAdvance = in.readInt();
+        mLevel = in.readInt();
         if (DBG) log("CellSignalStrengthLte(Parcel): " + toString());
     }
 
@@ -342,13 +453,12 @@
     }
 
     private static int convertRssiAsuToDBm(int rssiAsu) {
-        if (rssiAsu != SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN
-                && (rssiAsu < SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MIN_VALUE
-                || rssiAsu > SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MAX_VALUE)) {
-            Rlog.e(LOG_TAG, "convertRssiAsuToDBm: invalid RSSI in ASU=" + rssiAsu);
+        if (rssiAsu == SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN) {
             return CellInfo.UNAVAILABLE;
         }
-        if (rssiAsu == SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN) {
+        if ((rssiAsu < SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MIN_VALUE
+                || rssiAsu > SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MAX_VALUE)) {
+            Rlog.e(LOG_TAG, "convertRssiAsuToDBm: invalid RSSI in ASU=" + rssiAsu);
             return CellInfo.UNAVAILABLE;
         }
         return -113 + (2 * rssiAsu);
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
index 8079242..061cd4b 100644
--- a/telephony/java/android/telephony/CellSignalStrengthNr.java
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -18,6 +18,7 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 
 import java.util.Objects;
 
@@ -48,6 +49,12 @@
     private int mSsRsrp;
     private int mSsRsrq;
     private int mSsSinr;
+    private int mLevel;
+
+    /** @hide */
+    public CellSignalStrengthNr() {
+        setDefaultValues();
+    }
 
     /**
      * @param csiRsrp CSI reference signal received power.
@@ -60,12 +67,13 @@
      */
     public CellSignalStrengthNr(
             int csiRsrp, int csiRsrq, int csiSinr, int ssRsrp, int ssRsrq, int ssSinr) {
-        mCsiRsrp = csiRsrp;
-        mCsiRsrq = csiRsrq;
-        mCsiSinr = csiSinr;
-        mSsRsrp = ssRsrp;
-        mSsRsrq = ssRsrq;
-        mSsSinr = ssSinr;
+        mCsiRsrp = inRangeOrUnavailable(csiRsrp, -140, -44);
+        mCsiRsrq = inRangeOrUnavailable(csiRsrq, -20, -3);
+        mCsiSinr = inRangeOrUnavailable(csiSinr, -23, 23);
+        mSsRsrp = inRangeOrUnavailable(ssRsrp, -140, -44);
+        mSsRsrq = inRangeOrUnavailable(ssRsrq, -20, -3);
+        mSsSinr = inRangeOrUnavailable(ssSinr, -23, 40);
+        updateLevel(null, null);
     }
 
     /**
@@ -142,6 +150,7 @@
         dest.writeInt(mSsRsrp);
         dest.writeInt(mSsRsrq);
         dest.writeInt(mSsSinr);
+        dest.writeInt(mLevel);
     }
 
     private CellSignalStrengthNr(Parcel in) {
@@ -151,6 +160,7 @@
         mSsRsrp = in.readInt();
         mSsRsrq = in.readInt();
         mSsSinr = in.readInt();
+        mLevel = in.readInt();
     }
 
     /** @hide */
@@ -162,27 +172,36 @@
         mSsRsrp = CellInfo.UNAVAILABLE;
         mSsRsrq = CellInfo.UNAVAILABLE;
         mSsSinr = CellInfo.UNAVAILABLE;
+        mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
     }
 
     @Override
     public int getLevel() {
+        return mLevel;
+    }
+
+    /** @hide */
+    @Override
+    public void updateLevel(PersistableBundle cc, ServiceState ss) {
         if (mCsiRsrp == CellInfo.UNAVAILABLE) {
-            return SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+            mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
         } else if (mCsiRsrp >= SIGNAL_GREAT_THRESHOLD) {
-            return SIGNAL_STRENGTH_GREAT;
+            mLevel = SIGNAL_STRENGTH_GREAT;
         } else if (mCsiRsrp >= SIGNAL_GOOD_THRESHOLD) {
-            return SIGNAL_STRENGTH_GOOD;
+            mLevel = SIGNAL_STRENGTH_GOOD;
         } else if (mCsiRsrp >= SIGNAL_MODERATE_THRESHOLD) {
-            return SIGNAL_STRENGTH_MODERATE;
+            mLevel = SIGNAL_STRENGTH_MODERATE;
         } else {
-            return SIGNAL_STRENGTH_POOR;
+            mLevel = SIGNAL_STRENGTH_POOR;
         }
     }
 
     /**
-     * Calculates the NR signal as an asu value between 0..97, 99 is unknown.
-     * Asu is calculated based on 3GPP RSRP, refer to 3GPP TS 27.007 section 8.69.
-     * @return an integer represent the asu level of the signal strength.
+     * Get the RSRP in ASU.
+     *
+     * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+     *
+     * @return RSCP in ASU 0..97, 255, or UNAVAILABLE
      */
     @Override
     public int getAsuLevel() {
@@ -206,15 +225,33 @@
     }
 
     /** @hide */
+    public CellSignalStrengthNr(CellSignalStrengthNr s) {
+        mCsiRsrp = s.mCsiRsrp;
+        mCsiRsrq = s.mCsiRsrq;
+        mCsiSinr = s.mCsiSinr;
+        mSsRsrp = s.mSsRsrp;
+        mSsRsrq = s.mSsRsrq;
+        mSsSinr = s.mSsSinr;
+        mLevel = s.mLevel;
+    }
+
+    /** @hide */
     @Override
-    public CellSignalStrength copy() {
-        return new CellSignalStrengthNr(
-                mCsiRsrp, mCsiRsrq, mCsiSinr, mSsRsrp, mSsRsrq, mSsSinr);
+    public CellSignalStrengthNr copy() {
+        return new CellSignalStrengthNr(this);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mCsiRsrp, mCsiRsrq, mCsiSinr, mSsRsrp, mSsRsrq, mSsSinr);
+        return Objects.hash(mCsiRsrp, mCsiRsrq, mCsiSinr, mSsRsrp, mSsRsrq, mSsSinr, mLevel);
+    }
+
+    private static final CellSignalStrengthNr sInvalid = new CellSignalStrengthNr();
+
+    /** @hide */
+    @Override
+    public boolean isValid() {
+        return !this.equals(sInvalid);
     }
 
     @Override
@@ -222,7 +259,8 @@
         if (obj instanceof CellSignalStrengthNr) {
             CellSignalStrengthNr o = (CellSignalStrengthNr) obj;
             return mCsiRsrp == o.mCsiRsrp && mCsiRsrq == o.mCsiRsrq && mCsiSinr == o.mCsiSinr
-                    && mSsRsrp == o.mSsRsrp && mSsRsrq == o.mSsRsrq && mSsSinr == o.mSsSinr;
+                    && mSsRsrp == o.mSsRsrp && mSsRsrq == o.mSsRsrq && mSsSinr == o.mSsSinr
+                    && mLevel == o.mLevel;
         }
         return false;
     }
@@ -237,6 +275,7 @@
                 .append(" ssRsrp = " + mSsRsrp)
                 .append(" ssRsrq = " + mSsRsrq)
                 .append(" ssSinr = " + mSsSinr)
+                .append(" level = " + mLevel)
                 .append(" }")
                 .toString();
     }
diff --git a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
index 4d040cc..6f52b85 100644
--- a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
@@ -18,6 +18,7 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 
 import java.util.Objects;
 
@@ -31,27 +32,53 @@
     private static final String LOG_TAG = "CellSignalStrengthTdscdma";
     private static final boolean DBG = false;
 
-    private static final int TDSCDMA_SIGNAL_STRENGTH_GREAT = 12;
-    private static final int TDSCDMA_SIGNAL_STRENGTH_GOOD = 8;
-    private static final int TDSCDMA_SIGNAL_STRENGTH_MODERATE = 5;
+    private static final int TDSCDMA_RSSI_MAX = -51;
+    private static final int TDSCDMA_RSSI_GREAT = -77;
+    private static final int TDSCDMA_RSSI_GOOD = -87;
+    private static final int TDSCDMA_RSSI_MODERATE = -97;
+    private static final int TDSCDMA_RSSI_POOR = -107;
 
-    private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
-                                 // or CellInfo.UNAVAILABLE if unknown
+    private static final int TDSCDMA_RSCP_MIN = -120;
+    private static final int TDSCDMA_RSCP_MAX = -24;
+
+    private int mRssi; // in dBm [-113, -51], CellInfo.UNAVAILABLE
+
     private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
                                // CellInfo.UNAVAILABLE if unknown
-    private int mRscp; // Pilot power (0-96, 255) as defined in TS 27.007 8.69 or
+    private int mRscp; // Pilot Power in dBm [-120, -24] or CellInfo.UNAVAILABLE
                        // CellInfo.UNAVAILABLE if unknown
 
+    private int mLevel;
+
     /** @hide */
     public CellSignalStrengthTdscdma() {
         setDefaultValues();
     }
 
+    /**
+     * @param rssi in dBm [-113, -51] or UNAVAILABLE
+     * @param ber [0-7], 99 or UNAVAILABLE
+     * @param rscp in dBm [-120, -24] or UNAVAILABLE
+     * @hide */
+    public CellSignalStrengthTdscdma(int rssi, int ber, int rscp) {
+        mRssi = inRangeOrUnavailable(rssi, -113, -51);
+        mBitErrorRate = inRangeOrUnavailable(ber, 0, 7, 99);
+        mRscp = inRangeOrUnavailable(rscp, -120, -24);
+        updateLevel(null, null);
+    }
+
     /** @hide */
-    public CellSignalStrengthTdscdma(int ss, int ber, int rscp) {
-        mSignalStrength = ss;
-        mBitErrorRate = ber;
-        mRscp = rscp;
+    public CellSignalStrengthTdscdma(android.hardware.radio.V1_0.TdScdmaSignalStrength tdscdma) {
+        // Convert from HAL values as part of construction.
+        this(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
+                tdscdma.rscp != CellInfo.UNAVAILABLE ? -tdscdma.rscp : tdscdma.rscp);
+    }
+
+    /** @hide */
+    public CellSignalStrengthTdscdma(android.hardware.radio.V1_2.TdscdmaSignalStrength tdscdma) {
+        // Convert from HAL values as part of construction.
+        this(getRssiDbmFromAsu(tdscdma.signalStrength),
+                tdscdma.bitErrorRate, getRscpDbmFromAsu(tdscdma.rscp));
     }
 
     /** @hide */
@@ -61,9 +88,10 @@
 
     /** @hide */
     protected void copyFrom(CellSignalStrengthTdscdma s) {
-        mSignalStrength = s.mSignalStrength;
+        mRssi = s.mRssi;
         mBitErrorRate = s.mBitErrorRate;
         mRscp = s.mRscp;
+        mLevel = s.mLevel;
     }
 
     /** @hide */
@@ -75,9 +103,10 @@
     /** @hide */
     @Override
     public void setDefaultValues() {
-        mSignalStrength = CellInfo.UNAVAILABLE;
+        mRssi = CellInfo.UNAVAILABLE;
         mBitErrorRate = CellInfo.UNAVAILABLE;
         mRscp = CellInfo.UNAVAILABLE;
+        mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
     }
 
     /**
@@ -88,26 +117,18 @@
      */
     @Override
     public int getLevel() {
-        int level;
+        return mLevel;
+    }
 
-        // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
-        // asu = 0 (-113dB or less) is very weak
-        // signal, its better to show 0 bars to the user in such cases.
-        // asu = 99 is a special case, where the signal strength is unknown.
-        int asu = mSignalStrength;
-        if (asu <= 2 || asu == 99) {
-            level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-        } else if (asu >= TDSCDMA_SIGNAL_STRENGTH_GREAT) {
-            level = SIGNAL_STRENGTH_GREAT;
-        } else if (asu >= TDSCDMA_SIGNAL_STRENGTH_GOOD) {
-            level = SIGNAL_STRENGTH_GOOD;
-        } else if (asu >= TDSCDMA_SIGNAL_STRENGTH_MODERATE) {
-            level = SIGNAL_STRENGTH_MODERATE;
-        } else {
-            level = SIGNAL_STRENGTH_POOR;
-        }
-        if (DBG) log("getLevel=" + level);
-        return level;
+    /** @hide */
+    @Override
+    public void updateLevel(PersistableBundle cc, ServiceState ss) {
+        if (mRssi > TDSCDMA_RSSI_MAX) mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        else if (mRssi >= TDSCDMA_RSSI_GREAT) mLevel = SIGNAL_STRENGTH_GREAT;
+        else if (mRssi >= TDSCDMA_RSSI_GOOD)  mLevel = SIGNAL_STRENGTH_GOOD;
+        else if (mRssi >= TDSCDMA_RSSI_MODERATE)  mLevel = SIGNAL_STRENGTH_MODERATE;
+        else if (mRssi >= TDSCDMA_RSSI_POOR) mLevel = SIGNAL_STRENGTH_POOR;
+        else mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
     }
 
     /**
@@ -115,56 +136,55 @@
      */
     @Override
     public int getDbm() {
-        int dBm;
-
-        int level = mSignalStrength;
-        int asu = (level == 99 ? CellInfo.UNAVAILABLE : level);
-        if (asu != CellInfo.UNAVAILABLE) {
-            dBm = -113 + (2 * asu);
-        } else {
-            dBm = CellInfo.UNAVAILABLE;
-        }
-        if (DBG) log("getDbm=" + dBm);
-        return dBm;
+        return mRscp;
     }
 
     /**
-     * Get the signal level as an asu value between 0..31, 99 is unknown
+     * Get the RSCP as dBm
+     * @hide
+     */
+    public int getRscp() {
+        return mRscp;
+    }
+
+    /**
+     * Get the RSCP in ASU.
+     *
      * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+     *
+     * @return RSCP in ASU 0..96, 255, or UNAVAILABLE
      */
     @Override
     public int getAsuLevel() {
-        // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
-        // asu = 0 (-113dB or less) is very weak
-        // signal, its better to show 0 bars to the user in such cases.
-        // asu = 99 is a special case, where the signal strength is unknown.
-        int level = mSignalStrength;
-        if (DBG) log("getAsuLevel=" + level);
-        return level;
+        if (mRscp != CellInfo.UNAVAILABLE) return getAsuFromRscpDbm(mRscp);
+        // For historical reasons, if RSCP is unavailable, this API will very incorrectly return
+        // RSSI. This hackery will be removed when most devices are using Radio HAL 1.2+
+        if (mRssi != CellInfo.UNAVAILABLE) return getAsuFromRssiDbm(mRssi);
+        return getAsuFromRscpDbm(CellInfo.UNAVAILABLE);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mSignalStrength, mBitErrorRate);
+        return Objects.hash(mRssi, mBitErrorRate, mRscp, mLevel);
+    }
+
+    private static final CellSignalStrengthTdscdma sInvalid = new CellSignalStrengthTdscdma();
+
+    /** @hide */
+    @Override
+    public boolean isValid() {
+        return !this.equals(sInvalid);
     }
 
     @Override
     public boolean equals(Object o) {
-        CellSignalStrengthTdscdma s;
+        if (!(o instanceof CellSignalStrengthTdscdma)) return false;
+        CellSignalStrengthTdscdma s = (CellSignalStrengthTdscdma) o;
 
-        try {
-            s = (CellSignalStrengthTdscdma) o;
-        } catch (ClassCastException ex) {
-            return false;
-        }
-
-        if (o == null) {
-            return false;
-        }
-
-        return mSignalStrength == s.mSignalStrength
+        return mRssi == s.mRssi
                 && mBitErrorRate == s.mBitErrorRate
-                && mRscp == s.mRscp;
+                && mRscp == s.mRscp
+                && mLevel == s.mLevel;
     }
 
     /**
@@ -173,18 +193,20 @@
     @Override
     public String toString() {
         return "CellSignalStrengthTdscdma:"
-                + " ss=" + mSignalStrength
+                + " rssi=" + mRssi
                 + " ber=" + mBitErrorRate
-                + " rscp=" + mRscp;
+                + " rscp=" + mRscp
+                + " level=" + mLevel;
     }
 
     /** Implement the Parcelable interface */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         if (DBG) log("writeToParcel(Parcel, int): " + toString());
-        dest.writeInt(mSignalStrength);
+        dest.writeInt(mRssi);
         dest.writeInt(mBitErrorRate);
         dest.writeInt(mRscp);
+        dest.writeInt(mLevel);
     }
 
     /**
@@ -192,9 +214,10 @@
      * where the token is already been processed.
      */
     private CellSignalStrengthTdscdma(Parcel in) {
-        mSignalStrength = in.readInt();
+        mRssi = in.readInt();
         mBitErrorRate = in.readInt();
         mRscp = in.readInt();
+        mLevel = in.readInt();
         if (DBG) log("CellSignalStrengthTdscdma(Parcel): " + toString());
     }
 
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index 0048cbd..0760407 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -16,11 +16,15 @@
 
 package android.telephony;
 
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.StringDef;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 import android.telephony.Rlog;
+import android.text.TextUtils;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
 /**
@@ -31,20 +35,45 @@
     private static final String LOG_TAG = "CellSignalStrengthWcdma";
     private static final boolean DBG = false;
 
-    private static final int WCDMA_SIGNAL_STRENGTH_GREAT = 12;
-    private static final int WCDMA_SIGNAL_STRENGTH_GOOD = 8;
-    private static final int WCDMA_SIGNAL_STRENGTH_MODERATE = 5;
+    private static final int WCDMA_RSSI_MAX = -51;
+    private static final int WCDMA_RSSI_GREAT = -77;
+    private static final int WCDMA_RSSI_GOOD = -87;
+    private static final int WCDMA_RSSI_MODERATE = -97;
+    private static final int WCDMA_RSSI_POOR = -107;
+    private static final int WCDMA_RSSI_MIN = -113;
 
-    @UnsupportedAppUsage
-    private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
-                                 // or CellInfo.UNAVAILABLE if unknown
-    @UnsupportedAppUsage
+    private static final int[] sRssiThresholds = new int[]{
+            WCDMA_RSSI_POOR, WCDMA_RSSI_MODERATE, WCDMA_RSSI_GOOD, WCDMA_RSSI_GREAT};
+
+    private static final int WCDMA_RSCP_MAX = -24;
+    private static final int WCDMA_RSCP_GREAT = -85;
+    private static final int WCDMA_RSCP_GOOD = -95;
+    private static final int WCDMA_RSCP_MODERATE = -105;
+    private static final int WCDMA_RSCP_POOR = -115;
+    private static final int WCDMA_RSCP_MIN = -120;
+
+    private static final int[] sRscpThresholds = new int[] {
+            WCDMA_RSCP_POOR, WCDMA_RSCP_MODERATE, WCDMA_RSCP_GOOD, WCDMA_RSCP_GREAT};
+
+    // TODO: Because these are used as values in CarrierConfig, they should be exposed somehow.
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef({LEVEL_CALCULATION_METHOD_RSSI, LEVEL_CALCULATION_METHOD_RSCP})
+    public @interface LevelCalculationMethod {}
+    /** @hide */
+    public static final String LEVEL_CALCULATION_METHOD_RSSI = "rssi";
+    /** @hide */
+    public static final String LEVEL_CALCULATION_METHOD_RSCP = "rscp";
+
+    // Default to RSSI for backwards compatibility with older devices
+    private static final String sLevelCalculationMethod = LEVEL_CALCULATION_METHOD_RSSI;
+
+    private int mRssi; // in dBm [-113, 51] or CellInfo.UNAVAILABLE if unknown
     private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
                                // CellInfo.UNAVAILABLE if unknown
-    private int mRscp; // bit error rate (0-96, 255) as defined in TS 27.007 8.69 or
-                       // CellInfo.UNAVAILABLE if unknown
-    private int mEcNo; // signal to noise radio (0-49, 255) as defined in TS 27.007 8.69 or
-                       // CellInfo.UNAVAILABLE if unknown
+    private int mRscp; // in dBm [-120, -24]
+    private int mEcNo; // range -24, 1, CellInfo.UNAVAILABLE if unknown
+    private int mLevel;
 
     /** @hide */
     public CellSignalStrengthWcdma() {
@@ -52,11 +81,28 @@
     }
 
     /** @hide */
-    public CellSignalStrengthWcdma(int ss, int ber, int rscp, int ecno) {
-        mSignalStrength = ss;
-        mBitErrorRate = ber;
-        mRscp = rscp;
-        mEcNo = ecno;
+    public CellSignalStrengthWcdma(int rssi, int ber, int rscp, int ecno) {
+        mRssi = inRangeOrUnavailable(rssi, WCDMA_RSSI_MIN, WCDMA_RSSI_MAX);
+        mBitErrorRate = inRangeOrUnavailable(ber, 0, 7, 99);
+        mRscp = inRangeOrUnavailable(rscp, -120, -24);
+        mEcNo = inRangeOrUnavailable(ecno, -24, 1);
+        updateLevel(null, null);
+    }
+
+    /** @hide */
+    public CellSignalStrengthWcdma(android.hardware.radio.V1_0.WcdmaSignalStrength wcdma) {
+        // Convert from HAL values as part of construction.
+        this(getRssiDbmFromAsu(wcdma.signalStrength),
+                wcdma.bitErrorRate, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE);
+    }
+
+    /** @hide */
+    public CellSignalStrengthWcdma(android.hardware.radio.V1_2.WcdmaSignalStrength wcdma) {
+        // Convert from HAL values as part of construction.
+        this(getRssiDbmFromAsu(wcdma.base.signalStrength),
+                    wcdma.base.bitErrorRate,
+                    getRscpDbmFromAsu(wcdma.rscp),
+                    getEcNoDbFromAsu(wcdma.ecno));
     }
 
     /** @hide */
@@ -66,10 +112,11 @@
 
     /** @hide */
     protected void copyFrom(CellSignalStrengthWcdma s) {
-        mSignalStrength = s.mSignalStrength;
+        mRssi = s.mRssi;
         mBitErrorRate = s.mBitErrorRate;
         mRscp = s.mRscp;
         mEcNo = s.mEcNo;
+        mLevel = s.mLevel;
     }
 
     /** @hide */
@@ -81,10 +128,11 @@
     /** @hide */
     @Override
     public void setDefaultValues() {
-        mSignalStrength = CellInfo.UNAVAILABLE;
+        mRssi = CellInfo.UNAVAILABLE;
         mBitErrorRate = CellInfo.UNAVAILABLE;
         mRscp = CellInfo.UNAVAILABLE;
         mEcNo = CellInfo.UNAVAILABLE;
+        mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
     }
 
     /**
@@ -95,20 +143,54 @@
      */
     @Override
     public int getLevel() {
-        int level;
+        return mLevel;
+    }
 
-        // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
-        // asu = 0 (-113dB or less) is very weak
-        // signal, its better to show 0 bars to the user in such cases.
-        // asu = 99 is a special case, where the signal strength is unknown.
-        int asu = mSignalStrength;
-        if (asu <= 2 || asu == 99) level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-        else if (asu >= WCDMA_SIGNAL_STRENGTH_GREAT) level = SIGNAL_STRENGTH_GREAT;
-        else if (asu >= WCDMA_SIGNAL_STRENGTH_GOOD)  level = SIGNAL_STRENGTH_GOOD;
-        else if (asu >= WCDMA_SIGNAL_STRENGTH_MODERATE)  level = SIGNAL_STRENGTH_MODERATE;
-        else level = SIGNAL_STRENGTH_POOR;
-        if (DBG) log("getLevel=" + level);
-        return level;
+    /** @hide */
+    @Override
+    public void updateLevel(PersistableBundle cc, ServiceState ss) {
+        String calcMethod;
+        int[] rscpThresholds;
+
+        if (cc == null) {
+            calcMethod = sLevelCalculationMethod;
+            rscpThresholds = sRscpThresholds;
+        } else {
+            // TODO: abstract this entire thing into a series of functions
+            calcMethod = cc.getString(
+                    CarrierConfigManager.KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING,
+                    sLevelCalculationMethod);
+            if (TextUtils.isEmpty(calcMethod)) calcMethod = sLevelCalculationMethod;
+            rscpThresholds = cc.getIntArray(
+                    CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY);
+            if (rscpThresholds == null || rscpThresholds.length != NUM_SIGNAL_STRENGTH_THRESHOLDS) {
+                rscpThresholds = sRscpThresholds;
+            }
+        }
+
+        int level = NUM_SIGNAL_STRENGTH_THRESHOLDS;
+        switch (calcMethod) {
+            case LEVEL_CALCULATION_METHOD_RSCP:
+                if (mRscp < WCDMA_RSCP_MIN || mRscp > WCDMA_RSCP_MAX) {
+                    mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+                    return;
+                }
+                while (level > 0 && mRscp < rscpThresholds[level - 1]) level--;
+                mLevel = level;
+                return;
+            default:
+                loge("Invalid Level Calculation Method for CellSignalStrengthWcdma = "
+                        + calcMethod);
+                /** fall through */
+            case LEVEL_CALCULATION_METHOD_RSSI:
+                if (mRssi < WCDMA_RSSI_MIN || mRssi > WCDMA_RSSI_MAX) {
+                    mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+                    return;
+                }
+                while (level > 0 && mRssi < sRssiThresholds[level - 1]) level--;
+                mLevel = level;
+                return;
+        }
     }
 
     /**
@@ -116,57 +198,86 @@
      */
     @Override
     public int getDbm() {
-        int dBm;
-
-        int level = mSignalStrength;
-        int asu = (level == 99 ? CellInfo.UNAVAILABLE : level);
-        if (asu != CellInfo.UNAVAILABLE) {
-            dBm = -113 + (2 * asu);
-        } else {
-            dBm = CellInfo.UNAVAILABLE;
-        }
-        if (DBG) log("getDbm=" + dBm);
-        return dBm;
+        if (mRscp != CellInfo.UNAVAILABLE) return mRscp;
+        return mRssi;
     }
 
     /**
-     * Get the signal level as an asu value between 0..31, 99 is unknown
-     * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+     * Get the RSCP in ASU.
+     *
+     * Asu is calculated based on 3GPP RSCP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+     *
+     * @return RSCP in ASU 0..96, 255, or UNAVAILABLE
      */
     @Override
     public int getAsuLevel() {
-        // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
-        // asu = 0 (-113dB or less) is very weak
-        // signal, its better to show 0 bars to the user in such cases.
-        // asu = 99 is a special case, where the signal strength is unknown.
-        int level = mSignalStrength;
-        if (DBG) log("getAsuLevel=" + level);
-        return level;
+        if (mRscp != CellInfo.UNAVAILABLE) return getAsuFromRscpDbm(mRscp);
+        // For historical reasons, if RSCP is unavailable, this API will very incorrectly return
+        // RSSI. This hackery will be removed when most devices are using Radio HAL 1.2+
+        if (mRssi != CellInfo.UNAVAILABLE) return getAsuFromRssiDbm(mRssi);
+        return getAsuFromRscpDbm(CellInfo.UNAVAILABLE);
+    }
+
+    /**
+     * Get the RSSI as dBm
+     *
+     * @hide
+     */
+    public int getRssi() {
+        return mRssi;
+    }
+
+    /**
+     * Get the RSCP as dBm
+     *
+     * @hide
+     */
+    public int getRscp() {
+        return mRscp;
+    }
+
+    /**
+     * Get the Ec/No as dB
+     *
+     * @hide
+     */
+    public int getEcNo() {
+        return mEcNo;
+    }
+
+    /**
+     * Return the Bit Error Rate
+     *
+     * @returns the bit error rate (0-7, 99) as defined in TS 27.007 8.5 or UNAVAILABLE.
+     * @hide
+     */
+    public int getBitErrorRate() {
+        return mBitErrorRate;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mSignalStrength, mBitErrorRate);
+        return Objects.hash(mRssi, mBitErrorRate, mRscp, mEcNo, mLevel);
+    }
+
+    private static final CellSignalStrengthWcdma sInvalid = new CellSignalStrengthWcdma();
+
+    /** @hide */
+    @Override
+    public boolean isValid() {
+        return !this.equals(sInvalid);
     }
 
     @Override
-    public boolean equals (Object o) {
-        CellSignalStrengthWcdma s;
+    public boolean equals(Object o) {
+        if (!(o instanceof CellSignalStrengthWcdma)) return false;
+        CellSignalStrengthWcdma s = (CellSignalStrengthWcdma) o;
 
-        try {
-            s = (CellSignalStrengthWcdma) o;
-        } catch (ClassCastException ex) {
-            return false;
-        }
-
-        if (o == null) {
-            return false;
-        }
-
-        return mSignalStrength == s.mSignalStrength
+        return mRssi == s.mRssi
                 && mBitErrorRate == s.mBitErrorRate
                 && mRscp == s.mRscp
-                && mEcNo == s.mEcNo;
+                && mEcNo == s.mEcNo
+                && mLevel == s.mLevel;
     }
 
     /**
@@ -175,20 +286,22 @@
     @Override
     public String toString() {
         return "CellSignalStrengthWcdma:"
-                + " ss=" + mSignalStrength
+                + " ss=" + mRssi
                 + " ber=" + mBitErrorRate
                 + " rscp=" + mRscp
-                + " ecno=" + mEcNo;
+                + " ecno=" + mEcNo
+                + " level=" + mLevel;
     }
 
     /** Implement the Parcelable interface */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         if (DBG) log("writeToParcel(Parcel, int): " + toString());
-        dest.writeInt(mSignalStrength);
+        dest.writeInt(mRssi);
         dest.writeInt(mBitErrorRate);
         dest.writeInt(mRscp);
         dest.writeInt(mEcNo);
+        dest.writeInt(mLevel);
     }
 
     /**
@@ -196,10 +309,11 @@
      * where the token is already been processed.
      */
     private CellSignalStrengthWcdma(Parcel in) {
-        mSignalStrength = in.readInt();
+        mRssi = in.readInt();
         mBitErrorRate = in.readInt();
         mRscp = in.readInt();
         mEcNo = in.readInt();
+        mLevel = in.readInt();
         if (DBG) log("CellSignalStrengthWcdma(Parcel): " + toString());
     }
 
@@ -225,9 +339,16 @@
     };
 
     /**
-     * log
+     * log warning
      */
     private static void log(String s) {
         Rlog.w(LOG_TAG, s);
     }
+
+    /**
+     * log error
+     */
+    private static void loge(String s) {
+        Rlog.e(LOG_TAG, s);
+    }
 }
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index c6f7d0e..c53b37d 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -15,151 +15,383 @@
  */
 package android.telephony;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.content.Context;
 import android.os.PersistableBundle;
 
+import com.android.internal.util.ArrayUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
 
 /**
- * Returned as the reason for a connection failure as defined
- * by RIL_DataCallFailCause in ril.h and some local errors.
+ * Returned as the reason for a data connection failure as defined by modem and some local errors.
  * @hide
  */
-public enum DataFailCause {
-    NONE(0),
+public final class DataFailCause {
+    /** There is no failure */
+    public static final int NONE = 0;
 
     // This series of errors as specified by the standards
     // specified in ril.h
-    OPERATOR_BARRED(0x08),                  /* no retry */
-    NAS_SIGNALLING(0x0E),
-    LLC_SNDCP(0x19),
-    INSUFFICIENT_RESOURCES(0x1A),
-    MISSING_UNKNOWN_APN(0x1B),              /* no retry */
-    UNKNOWN_PDP_ADDRESS_TYPE(0x1C),         /* no retry */
-    USER_AUTHENTICATION(0x1D),              /* no retry */
-    ACTIVATION_REJECT_GGSN(0x1E),           /* no retry */
-    ACTIVATION_REJECT_UNSPECIFIED(0x1F),
-    SERVICE_OPTION_NOT_SUPPORTED(0x20),     /* no retry */
-    SERVICE_OPTION_NOT_SUBSCRIBED(0x21),    /* no retry */
-    SERVICE_OPTION_OUT_OF_ORDER(0x22),
-    NSAPI_IN_USE(0x23),                     /* no retry */
-    REGULAR_DEACTIVATION(0x24),             /* possibly restart radio, based on config */
-    QOS_NOT_ACCEPTED(0x25),
-    NETWORK_FAILURE(0x26),
-    UMTS_REACTIVATION_REQ(0x27),
-    FEATURE_NOT_SUPP(0x28),
-    TFT_SEMANTIC_ERROR(0x29),
-    TFT_SYTAX_ERROR(0x2A),
-    UNKNOWN_PDP_CONTEXT(0x2B),
-    FILTER_SEMANTIC_ERROR(0x2C),
-    FILTER_SYTAX_ERROR(0x2D),
-    PDP_WITHOUT_ACTIVE_TFT(0x2E),
-    ONLY_IPV4_ALLOWED(0x32),                /* no retry */
-    ONLY_IPV6_ALLOWED(0x33),                /* no retry */
-    ONLY_SINGLE_BEARER_ALLOWED(0x34),
-    ESM_INFO_NOT_RECEIVED(0x35),
-    PDN_CONN_DOES_NOT_EXIST(0x36),
-    MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED(0x37),
-    MAX_ACTIVE_PDP_CONTEXT_REACHED(0x41),
-    UNSUPPORTED_APN_IN_CURRENT_PLMN(0x42),
-    INVALID_TRANSACTION_ID(0x51),
-    MESSAGE_INCORRECT_SEMANTIC(0x5F),
-    INVALID_MANDATORY_INFO(0x60),
-    MESSAGE_TYPE_UNSUPPORTED(0x61),
-    MSG_TYPE_NONCOMPATIBLE_STATE(0x62),
-    UNKNOWN_INFO_ELEMENT(0x63),
-    CONDITIONAL_IE_ERROR(0x64),
-    MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE(0x65),
-    PROTOCOL_ERRORS(0x6F),                  /* no retry */
-    APN_TYPE_CONFLICT(0x70),
-    INVALID_PCSCF_ADDR(0x71),
-    INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN(0x72),
-    EMM_ACCESS_BARRED(0x73),
-    EMERGENCY_IFACE_ONLY(0x74),
-    IFACE_MISMATCH(0x75),
-    COMPANION_IFACE_IN_USE(0x76),
-    IP_ADDRESS_MISMATCH(0x77),
-    IFACE_AND_POL_FAMILY_MISMATCH(0x78),
-    EMM_ACCESS_BARRED_INFINITE_RETRY(0x79),
-    AUTH_FAILURE_ON_EMERGENCY_CALL(0x7A),
+    /** Operator determined barring. */
+    public static final int OPERATOR_BARRED = 0x08;
+    /** NAS signalling. */
+    public static final int NAS_SIGNALLING = 0x0E;
+    /** Logical Link Control (LLC) Sub Network Dependent Convergence Protocol (SNDCP). */
+    public static final int LLC_SNDCP = 0x19;
+    /** Insufficient resources. */
+    public static final int INSUFFICIENT_RESOURCES = 0x1A;
+    /** Missing or unknown APN. */
+    public static final int MISSING_UNKNOWN_APN = 0x1B;              /* no retry */
+    /** Unknown Packet Data Protocol (PDP) address type. */
+    public static final int UNKNOWN_PDP_ADDRESS_TYPE = 0x1C;         /* no retry */
+    /** User authentication. */
+    public static final int USER_AUTHENTICATION = 0x1D;              /* no retry */
+    /** Activation rejected by Gateway GPRS Support Node (GGSN), Serving Gateway or PDN Gateway. */
+    public static final int ACTIVATION_REJECT_GGSN = 0x1E;           /* no retry */
+    /** Activation rejected, unspecified. */
+    public static final int ACTIVATION_REJECT_UNSPECIFIED = 0x1F;
+    /** Service option not supported. */
+    public static final int SERVICE_OPTION_NOT_SUPPORTED = 0x20;     /* no retry */
+    /** Requested service option not subscribed. */
+    public static final int SERVICE_OPTION_NOT_SUBSCRIBED = 0x21;    /* no retry */
+    /** Service option temporarily out of order. */
+    public static final int SERVICE_OPTION_OUT_OF_ORDER = 0x22;
+    /** The Network Service Access Point Identifier (NSAPI) is in use. */
+    public static final int NSAPI_IN_USE = 0x23;                     /* no retry */
+    /* possibly restart radio, based on config */
+    /** Regular deactivation. */
+    public static final int REGULAR_DEACTIVATION = 0x24;
+    /** Quality of service (QoS) is not accepted. */
+    public static final int QOS_NOT_ACCEPTED = 0x25;
+    /** Network Failure. */
+    public static final int NETWORK_FAILURE = 0x26;
+    /** Universal Mobile Telecommunications System (UMTS) reactivation request. */
+    public static final int UMTS_REACTIVATION_REQ = 0x27;
+    /** Feature not supported. */
+    public static final int FEATURE_NOT_SUPP = 0x28;
+    /** Semantic error in the Traffic flow templates (TFT) operation. */
+    public static final int TFT_SEMANTIC_ERROR = 0x29;
+    /** Syntactical error in the Traffic flow templates (TFT) operation. */
+    public static final int TFT_SYTAX_ERROR = 0x2A;
+    /** Unknown Packet Data Protocol (PDP) context. */
+    public static final int UNKNOWN_PDP_CONTEXT = 0x2B;
+    /** Semantic errors in packet filter. */
+    public static final int FILTER_SEMANTIC_ERROR = 0x2C;
+    /** Syntactical errors in packet filter(s). */
+    public static final int FILTER_SYTAX_ERROR = 0x2D;
+    /** Packet Data Protocol (PDP) without active traffic flow template (TFT). */
+    public static final int PDP_WITHOUT_ACTIVE_TFT = 0x2E;
+    /** Packet Data Protocol (PDP) type IPv4 only allowed. */
+    public static final int ONLY_IPV4_ALLOWED = 0x32;                /* no retry */
+    /** Packet Data Protocol (PDP) type IPv6 only allowed. */
+    public static final int ONLY_IPV6_ALLOWED = 0x33;                /* no retry */
+    /** Single address bearers only allowed. */
+    public static final int ONLY_SINGLE_BEARER_ALLOWED = 0x34;
+    /** EPS Session Management (ESM) information is not received. */
+    public static final int ESM_INFO_NOT_RECEIVED = 0x35;
+    /** PDN connection does not exist. */
+    public static final int PDN_CONN_DOES_NOT_EXIST = 0x36;
+    /** Multiple connections to a same PDN is not allowed. */
+    public static final int MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED = 0x37;
+    /** Packet Data Protocol (PDP) */
+    public static final int MAX_ACTIVE_PDP_CONTEXT_REACHED = 0x41;
+    /** Unsupported APN in current public land mobile network (PLMN). */
+    public static final int UNSUPPORTED_APN_IN_CURRENT_PLMN = 0x42;
+    /** Invalid transaction id. */
+    public static final int INVALID_TRANSACTION_ID = 0x51;
+    /** Incorrect message semantic. */
+    public static final int MESSAGE_INCORRECT_SEMANTIC = 0x5F;
+    /** Invalid mandatory information. */
+    public static final int INVALID_MANDATORY_INFO = 0x60;
+    /** Unsupported message type. */
+    public static final int MESSAGE_TYPE_UNSUPPORTED = 0x61;
+    /** Message type uncompatible. */
+    public static final int MSG_TYPE_NONCOMPATIBLE_STATE = 0x62;
+    /** Unknown info element. */
+    public static final int UNKNOWN_INFO_ELEMENT = 0x63;
+    /** Conditional Information Element (IE) error. */
+    public static final int CONDITIONAL_IE_ERROR = 0x64;
+    /** Message and protocol state uncompatible. */
+    public static final int MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE = 0x65;
+    /** Protocol errors. */
+    public static final int PROTOCOL_ERRORS = 0x6F;                  /* no retry */
+    /** APN type conflict. */
+    public static final int APN_TYPE_CONFLICT = 0x70;
+    /** Invalid Proxy-Call Session Control Function (P-CSCF) address. */
+    public static final int INVALID_PCSCF_ADDR = 0x71;
+    /** Internal data call preempt by high priority APN. */
+    public static final int INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN = 0x72;
+    /** EPS (Evolved Packet System) Mobility Management (EMM) access barred. */
+    public static final int EMM_ACCESS_BARRED = 0x73;
+    /** Emergency interface only. */
+    public static final int EMERGENCY_IFACE_ONLY = 0x74;
+    /** Interface mismatch. */
+    public static final int IFACE_MISMATCH = 0x75;
+    /** Companion interface in use. */
+    public static final int COMPANION_IFACE_IN_USE = 0x76;
+    /** IP address mismatch. */
+    public static final int IP_ADDRESS_MISMATCH = 0x77;
+    public static final int IFACE_AND_POL_FAMILY_MISMATCH = 0x78;
+    /** EPS (Evolved Packet System) Mobility Management (EMM) access barred infinity retry. **/
+    public static final int EMM_ACCESS_BARRED_INFINITE_RETRY = 0x79;
+    /** Authentication failure on emergency call. */
+    public static final int AUTH_FAILURE_ON_EMERGENCY_CALL = 0x7A;
 
     // OEM sepecific error codes. To be used by OEMs when they don't
     // want to reveal error code which would be replaced by ERROR_UNSPECIFIED
-    OEM_DCFAILCAUSE_1(0x1001),
-    OEM_DCFAILCAUSE_2(0x1002),
-    OEM_DCFAILCAUSE_3(0x1003),
-    OEM_DCFAILCAUSE_4(0x1004),
-    OEM_DCFAILCAUSE_5(0x1005),
-    OEM_DCFAILCAUSE_6(0x1006),
-    OEM_DCFAILCAUSE_7(0x1007),
-    OEM_DCFAILCAUSE_8(0x1008),
-    OEM_DCFAILCAUSE_9(0x1009),
-    OEM_DCFAILCAUSE_10(0x100A),
-    OEM_DCFAILCAUSE_11(0x100B),
-    OEM_DCFAILCAUSE_12(0x100C),
-    OEM_DCFAILCAUSE_13(0x100D),
-    OEM_DCFAILCAUSE_14(0x100E),
-    OEM_DCFAILCAUSE_15(0x100F),
+    public static final int OEM_DCFAILCAUSE_1 = 0x1001;
+    public static final int OEM_DCFAILCAUSE_2 = 0x1002;
+    public static final int OEM_DCFAILCAUSE_3 = 0x1003;
+    public static final int OEM_DCFAILCAUSE_4 = 0x1004;
+    public static final int OEM_DCFAILCAUSE_5 = 0x1005;
+    public static final int OEM_DCFAILCAUSE_6 = 0x1006;
+    public static final int OEM_DCFAILCAUSE_7 = 0x1007;
+    public static final int OEM_DCFAILCAUSE_8 = 0x1008;
+    public static final int OEM_DCFAILCAUSE_9 = 0x1009;
+    public static final int OEM_DCFAILCAUSE_10 = 0x100A;
+    public static final int OEM_DCFAILCAUSE_11 = 0x100B;
+    public static final int OEM_DCFAILCAUSE_12 = 0x100C;
+    public static final int OEM_DCFAILCAUSE_13 = 0x100D;
+    public static final int OEM_DCFAILCAUSE_14 = 0x100E;
+    public static final int OEM_DCFAILCAUSE_15 = 0x100F;
 
     // Local errors generated by Vendor RIL
     // specified in ril.h
-    REGISTRATION_FAIL(-1),
-    GPRS_REGISTRATION_FAIL(-2),
-    SIGNAL_LOST(-3),                        /* no retry */
-    PREF_RADIO_TECH_CHANGED(-4),
-    RADIO_POWER_OFF(-5),                    /* no retry */
-    TETHERED_CALL_ACTIVE(-6),               /* no retry */
-    ERROR_UNSPECIFIED(0xFFFF),
+    public static final int REGISTRATION_FAIL = -1;
+    public static final int GPRS_REGISTRATION_FAIL = -2;
+    public static final int SIGNAL_LOST = -3;                        /* no retry */
+    public static final int PREF_RADIO_TECH_CHANGED = -4;
+    public static final int RADIO_POWER_OFF = -5;                    /* no retry */
+    public static final int TETHERED_CALL_ACTIVE = -6;               /* no retry */
+    public static final int ERROR_UNSPECIFIED = 0xFFFF;
 
     // Errors generated by the Framework
     // specified here
-    UNKNOWN(0x10000),
-    RADIO_NOT_AVAILABLE(0x10001),                   /* no retry */
-    UNACCEPTABLE_NETWORK_PARAMETER(0x10002),        /* no retry */
-    CONNECTION_TO_DATACONNECTIONAC_BROKEN(0x10003),
-    LOST_CONNECTION(0x10004),
-    RESET_BY_FRAMEWORK(0x10005);
+    public static final int UNKNOWN = 0x10000;
+    public static final int RADIO_NOT_AVAILABLE = 0x10001;                   /* no retry */
+    public static final int UNACCEPTABLE_NETWORK_PARAMETER = 0x10002;        /* no retry */
+    public static final int CONNECTION_TO_DATACONNECTIONAC_BROKEN = 0x10003;
+    public static final int LOST_CONNECTION = 0x10004;
+    /** Data was reset by framework. */
+    public static final int RESET_BY_FRAMEWORK = 0x10005;
 
-    private final int mErrorCode;
-    private static final HashMap<Integer, DataFailCause> sErrorCodeToFailCauseMap;
+    /** @hide */
+    @IntDef(value = {
+            NONE,
+            OPERATOR_BARRED,
+            NAS_SIGNALLING,
+            LLC_SNDCP,
+            INSUFFICIENT_RESOURCES,
+            MISSING_UNKNOWN_APN,
+            UNKNOWN_PDP_ADDRESS_TYPE,
+            USER_AUTHENTICATION,
+            ACTIVATION_REJECT_GGSN,
+            ACTIVATION_REJECT_UNSPECIFIED,
+            SERVICE_OPTION_NOT_SUPPORTED,
+            SERVICE_OPTION_NOT_SUBSCRIBED,
+            SERVICE_OPTION_OUT_OF_ORDER,
+            NSAPI_IN_USE,
+            REGULAR_DEACTIVATION,
+            QOS_NOT_ACCEPTED,
+            NETWORK_FAILURE,
+            UMTS_REACTIVATION_REQ,
+            FEATURE_NOT_SUPP,
+            TFT_SEMANTIC_ERROR,
+            TFT_SYTAX_ERROR,
+            UNKNOWN_PDP_CONTEXT,
+            FILTER_SEMANTIC_ERROR,
+            FILTER_SYTAX_ERROR,
+            PDP_WITHOUT_ACTIVE_TFT,
+            ONLY_IPV4_ALLOWED,
+            ONLY_IPV6_ALLOWED,
+            ONLY_SINGLE_BEARER_ALLOWED,
+            ESM_INFO_NOT_RECEIVED,
+            PDN_CONN_DOES_NOT_EXIST,
+            MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED,
+            MAX_ACTIVE_PDP_CONTEXT_REACHED,
+            UNSUPPORTED_APN_IN_CURRENT_PLMN,
+            INVALID_TRANSACTION_ID,
+            MESSAGE_INCORRECT_SEMANTIC,
+            INVALID_MANDATORY_INFO,
+            MESSAGE_TYPE_UNSUPPORTED,
+            MSG_TYPE_NONCOMPATIBLE_STATE,
+            UNKNOWN_INFO_ELEMENT,
+            CONDITIONAL_IE_ERROR,
+            MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE,
+            PROTOCOL_ERRORS,                 /* no retry */
+            APN_TYPE_CONFLICT,
+            INVALID_PCSCF_ADDR,
+            INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN,
+            EMM_ACCESS_BARRED,
+            EMERGENCY_IFACE_ONLY,
+            IFACE_MISMATCH,
+            COMPANION_IFACE_IN_USE,
+            IP_ADDRESS_MISMATCH,
+            IFACE_AND_POL_FAMILY_MISMATCH,
+            EMM_ACCESS_BARRED_INFINITE_RETRY,
+            AUTH_FAILURE_ON_EMERGENCY_CALL,
+            OEM_DCFAILCAUSE_1,
+            OEM_DCFAILCAUSE_2,
+            OEM_DCFAILCAUSE_3,
+            OEM_DCFAILCAUSE_4,
+            OEM_DCFAILCAUSE_5,
+            OEM_DCFAILCAUSE_6,
+            OEM_DCFAILCAUSE_7,
+            OEM_DCFAILCAUSE_8,
+            OEM_DCFAILCAUSE_9,
+            OEM_DCFAILCAUSE_10,
+            OEM_DCFAILCAUSE_11,
+            OEM_DCFAILCAUSE_12,
+            OEM_DCFAILCAUSE_13,
+            OEM_DCFAILCAUSE_14,
+            OEM_DCFAILCAUSE_15,
+            REGISTRATION_FAIL,
+            GPRS_REGISTRATION_FAIL,
+            SIGNAL_LOST,
+            PREF_RADIO_TECH_CHANGED,
+            RADIO_POWER_OFF,
+            TETHERED_CALL_ACTIVE,
+            ERROR_UNSPECIFIED,
+            UNKNOWN,
+            RADIO_NOT_AVAILABLE,
+            UNACCEPTABLE_NETWORK_PARAMETER,
+            CONNECTION_TO_DATACONNECTIONAC_BROKEN,
+            LOST_CONNECTION,
+            RESET_BY_FRAMEWORK
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FailCause{}
+
+    private static final Map<Integer, String> sFailCauseMap;
     static {
-        sErrorCodeToFailCauseMap = new HashMap<Integer, DataFailCause>();
-        for (DataFailCause fc : values()) {
-            sErrorCodeToFailCauseMap.put(fc.getErrorCode(), fc);
-        }
+        sFailCauseMap = new HashMap<>();
+        sFailCauseMap.put(NONE, "NONE");
+        sFailCauseMap.put(OPERATOR_BARRED, "OPERATOR_BARRED");
+        sFailCauseMap.put(NAS_SIGNALLING, "NAS_SIGNALLING");
+        sFailCauseMap.put(LLC_SNDCP, "LLC_SNDCP");
+        sFailCauseMap.put(INSUFFICIENT_RESOURCES, "INSUFFICIENT_RESOURCES");
+        sFailCauseMap.put(MISSING_UNKNOWN_APN, "MISSING_UNKNOWN_APN");
+        sFailCauseMap.put(UNKNOWN_PDP_ADDRESS_TYPE, "UNKNOWN_PDP_ADDRESS_TYPE");
+        sFailCauseMap.put(USER_AUTHENTICATION, "USER_AUTHENTICATION");
+        sFailCauseMap.put(ACTIVATION_REJECT_GGSN, "ACTIVATION_REJECT_GGSN");
+        sFailCauseMap.put(ACTIVATION_REJECT_UNSPECIFIED,
+                "ACTIVATION_REJECT_UNSPECIFIED");
+        sFailCauseMap.put(SERVICE_OPTION_NOT_SUPPORTED,
+                "SERVICE_OPTION_NOT_SUPPORTED");
+        sFailCauseMap.put(SERVICE_OPTION_NOT_SUBSCRIBED,
+                "SERVICE_OPTION_NOT_SUBSCRIBED");
+        sFailCauseMap.put(SERVICE_OPTION_OUT_OF_ORDER, "SERVICE_OPTION_OUT_OF_ORDER");
+        sFailCauseMap.put(NSAPI_IN_USE, "NSAPI_IN_USE");
+        sFailCauseMap.put(REGULAR_DEACTIVATION, "REGULAR_DEACTIVATION");
+        sFailCauseMap.put(QOS_NOT_ACCEPTED, "QOS_NOT_ACCEPTED");
+        sFailCauseMap.put(NETWORK_FAILURE, "NETWORK_FAILURE");
+        sFailCauseMap.put(UMTS_REACTIVATION_REQ, "UMTS_REACTIVATION_REQ");
+        sFailCauseMap.put(FEATURE_NOT_SUPP, "FEATURE_NOT_SUPP");
+        sFailCauseMap.put(TFT_SEMANTIC_ERROR, "TFT_SEMANTIC_ERROR");
+        sFailCauseMap.put(TFT_SYTAX_ERROR, "TFT_SYTAX_ERROR");
+        sFailCauseMap.put(UNKNOWN_PDP_CONTEXT, "UNKNOWN_PDP_CONTEXT");
+        sFailCauseMap.put(FILTER_SEMANTIC_ERROR, "FILTER_SEMANTIC_ERROR");
+        sFailCauseMap.put(FILTER_SYTAX_ERROR, "FILTER_SYTAX_ERROR");
+        sFailCauseMap.put(PDP_WITHOUT_ACTIVE_TFT, "PDP_WITHOUT_ACTIVE_TFT");
+        sFailCauseMap.put(ONLY_IPV4_ALLOWED, "ONLY_IPV4_ALLOWED");
+        sFailCauseMap.put(ONLY_IPV6_ALLOWED, "ONLY_IPV6_ALLOWED");
+        sFailCauseMap.put(ONLY_SINGLE_BEARER_ALLOWED, "ONLY_SINGLE_BEARER_ALLOWED");
+        sFailCauseMap.put(ESM_INFO_NOT_RECEIVED, "ESM_INFO_NOT_RECEIVED");
+        sFailCauseMap.put(PDN_CONN_DOES_NOT_EXIST, "PDN_CONN_DOES_NOT_EXIST");
+        sFailCauseMap.put(MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED,
+                "MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED");
+        sFailCauseMap.put(MAX_ACTIVE_PDP_CONTEXT_REACHED,
+                "MAX_ACTIVE_PDP_CONTEXT_REACHED");
+        sFailCauseMap.put(UNSUPPORTED_APN_IN_CURRENT_PLMN,
+                "UNSUPPORTED_APN_IN_CURRENT_PLMN");
+        sFailCauseMap.put(INVALID_TRANSACTION_ID, "INVALID_TRANSACTION_ID");
+        sFailCauseMap.put(MESSAGE_INCORRECT_SEMANTIC, "MESSAGE_INCORRECT_SEMANTIC");
+        sFailCauseMap.put(INVALID_MANDATORY_INFO, "INVALID_MANDATORY_INFO");
+        sFailCauseMap.put(MESSAGE_TYPE_UNSUPPORTED, "MESSAGE_TYPE_UNSUPPORTED");
+        sFailCauseMap.put(MSG_TYPE_NONCOMPATIBLE_STATE, "MSG_TYPE_NONCOMPATIBLE_STATE");
+        sFailCauseMap.put(UNKNOWN_INFO_ELEMENT, "UNKNOWN_INFO_ELEMENT");
+        sFailCauseMap.put(CONDITIONAL_IE_ERROR, "CONDITIONAL_IE_ERROR");
+        sFailCauseMap.put(MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE,
+                "MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE");
+        sFailCauseMap.put(PROTOCOL_ERRORS, "PROTOCOL_ERRORS");
+        sFailCauseMap.put(APN_TYPE_CONFLICT, "APN_TYPE_CONFLICT");
+        sFailCauseMap.put(INVALID_PCSCF_ADDR, "INVALID_PCSCF_ADDR");
+        sFailCauseMap.put(INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN,
+                "INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN");
+        sFailCauseMap.put(EMM_ACCESS_BARRED, "EMM_ACCESS_BARRED");
+        sFailCauseMap.put(EMERGENCY_IFACE_ONLY, "EMERGENCY_IFACE_ONLY");
+        sFailCauseMap.put(IFACE_MISMATCH, "IFACE_MISMATCH");
+        sFailCauseMap.put(COMPANION_IFACE_IN_USE, "COMPANION_IFACE_IN_USE");
+        sFailCauseMap.put(IP_ADDRESS_MISMATCH, "IP_ADDRESS_MISMATCH");
+        sFailCauseMap.put(IFACE_AND_POL_FAMILY_MISMATCH,
+                "IFACE_AND_POL_FAMILY_MISMATCH");
+        sFailCauseMap.put(EMM_ACCESS_BARRED_INFINITE_RETRY,
+                "EMM_ACCESS_BARRED_INFINITE_RETRY");
+        sFailCauseMap.put(AUTH_FAILURE_ON_EMERGENCY_CALL,
+                "AUTH_FAILURE_ON_EMERGENCY_CALL");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_1, "OEM_DCFAILCAUSE_1");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_2, "OEM_DCFAILCAUSE_2");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_3, "OEM_DCFAILCAUSE_3");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_4, "OEM_DCFAILCAUSE_4");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_5, "OEM_DCFAILCAUSE_5");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_6, "OEM_DCFAILCAUSE_6");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_7, "OEM_DCFAILCAUSE_7");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_8, "OEM_DCFAILCAUSE_8");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_9, "OEM_DCFAILCAUSE_9");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_10, "OEM_DCFAILCAUSE_10");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_11, "OEM_DCFAILCAUSE_11");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_12, "OEM_DCFAILCAUSE_12");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_13, "OEM_DCFAILCAUSE_13");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_14, "OEM_DCFAILCAUSE_14");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_15, "OEM_DCFAILCAUSE_15");
+        sFailCauseMap.put(REGISTRATION_FAIL, "REGISTRATION_FAIL");
+        sFailCauseMap.put(GPRS_REGISTRATION_FAIL, "GPRS_REGISTRATION_FAIL");
+        sFailCauseMap.put(SIGNAL_LOST, "SIGNAL_LOST");
+        sFailCauseMap.put(PREF_RADIO_TECH_CHANGED, "PREF_RADIO_TECH_CHANGED");
+        sFailCauseMap.put(RADIO_POWER_OFF, "RADIO_POWER_OFF");
+        sFailCauseMap.put(TETHERED_CALL_ACTIVE, "TETHERED_CALL_ACTIVE");
+        sFailCauseMap.put(ERROR_UNSPECIFIED, "ERROR_UNSPECIFIED");
+        sFailCauseMap.put(UNKNOWN, "UNKNOWN");
+        sFailCauseMap.put(RADIO_NOT_AVAILABLE, "RADIO_NOT_AVAILABLE");
+        sFailCauseMap.put(UNACCEPTABLE_NETWORK_PARAMETER,
+                "UNACCEPTABLE_NETWORK_PARAMETER");
+        sFailCauseMap.put(CONNECTION_TO_DATACONNECTIONAC_BROKEN,
+                "CONNECTION_TO_DATACONNECTIONAC_BROKEN");
+        sFailCauseMap.put(LOST_CONNECTION, "LOST_CONNECTION");
+        sFailCauseMap.put(RESET_BY_FRAMEWORK, "RESET_BY_FRAMEWORK");
     }
 
     /**
      * Map of subId -> set of data call setup permanent failure for the carrier.
      */
-    private static final HashMap<Integer, HashSet<DataFailCause>> sPermanentFailureCache =
+    private static final HashMap<Integer, Set<Integer>> sPermanentFailureCache =
             new HashMap<>();
 
-    DataFailCause(int errorCode) {
-        mErrorCode = errorCode;
-    }
-
-    public int getErrorCode() {
-        return mErrorCode;
-    }
-
     /**
      * Returns whether or not the fail cause is a failure that requires a modem restart
      *
      * @param context device context
+     * @param cause data disconnect cause
      * @param subId subscription index
      * @return true if the fail cause code needs platform to trigger a modem restart.
      */
-    public boolean isRadioRestartFailure(Context context, int subId) {
+    public static boolean isRadioRestartFailure(@NonNull Context context, @FailCause int cause,
+                                                int subId) {
         CarrierConfigManager configManager = (CarrierConfigManager)
                 context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
         if (configManager != null) {
             PersistableBundle b = configManager.getConfigForSubId(subId);
 
             if (b != null) {
-                if (this == REGULAR_DEACTIVATION
+                if (cause == REGULAR_DEACTIVATION
                         && b.getBoolean(CarrierConfigManager
                         .KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL)) {
                     // This is for backward compatibility support. We need to continue support this
@@ -170,7 +402,7 @@
                 int[] causeCodes = b.getIntArray(CarrierConfigManager
                         .KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY);
                 if (causeCodes != null) {
-                    return Arrays.stream(causeCodes).anyMatch(i -> i == getErrorCode());
+                    return Arrays.stream(causeCodes).anyMatch(i -> i == cause);
                 }
             }
         }
@@ -178,11 +410,11 @@
         return false;
     }
 
-    public boolean isPermanentFailure(Context context, int subId) {
-
+    public static boolean isPermanentFailure(@NonNull Context context, @FailCause int failCause,
+                                             int subId) {
         synchronized (sPermanentFailureCache) {
 
-            HashSet<DataFailCause> permanentFailureSet = sPermanentFailureCache.get(subId);
+            Set<Integer> permanentFailureSet = sPermanentFailureCache.get(subId);
 
             // In case of cache miss, we need to look up the settings from carrier config.
             if (permanentFailureSet == null) {
@@ -194,11 +426,12 @@
                     if (b != null) {
                         String[] permanentFailureStrings = b.getStringArray(CarrierConfigManager.
                                 KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS);
-
                         if (permanentFailureStrings != null) {
                             permanentFailureSet = new HashSet<>();
-                            for (String failure : permanentFailureStrings) {
-                                permanentFailureSet.add(DataFailCause.valueOf(failure));
+                            for (Map.Entry<Integer, String> e : sFailCauseMap.entrySet()) {
+                                if (ArrayUtils.contains(permanentFailureStrings, e.getValue())) {
+                                    permanentFailureSet.add(e.getKey());
+                                }
                             }
                         }
                     }
@@ -207,7 +440,7 @@
                 // If we are not able to find the configuration from carrier config, use the default
                 // ones.
                 if (permanentFailureSet == null) {
-                    permanentFailureSet = new HashSet<DataFailCause>() {
+                    permanentFailureSet = new HashSet<Integer>() {
                         {
                             add(OPERATOR_BARRED);
                             add(MISSING_UNKNOWN_APN);
@@ -232,28 +465,39 @@
                 sPermanentFailureCache.put(subId, permanentFailureSet);
             }
 
-            return permanentFailureSet.contains(this);
+            return permanentFailureSet.contains(failCause);
         }
     }
 
-    public boolean isEventLoggable() {
-        return (this == OPERATOR_BARRED) || (this == INSUFFICIENT_RESOURCES) ||
-                (this == UNKNOWN_PDP_ADDRESS_TYPE) || (this == USER_AUTHENTICATION) ||
-                (this == ACTIVATION_REJECT_GGSN) || (this == ACTIVATION_REJECT_UNSPECIFIED) ||
-                (this == SERVICE_OPTION_NOT_SUBSCRIBED) ||
-                (this == SERVICE_OPTION_NOT_SUPPORTED) ||
-                (this == SERVICE_OPTION_OUT_OF_ORDER) || (this == NSAPI_IN_USE) ||
-                (this == ONLY_IPV4_ALLOWED) || (this == ONLY_IPV6_ALLOWED) ||
-                (this == PROTOCOL_ERRORS) || (this == SIGNAL_LOST) ||
-                (this == RADIO_POWER_OFF) || (this == TETHERED_CALL_ACTIVE) ||
-                (this == UNACCEPTABLE_NETWORK_PARAMETER);
+    public static boolean isEventLoggable(@FailCause int dataFailCause) {
+        return (dataFailCause == OPERATOR_BARRED) || (dataFailCause == INSUFFICIENT_RESOURCES)
+                || (dataFailCause == UNKNOWN_PDP_ADDRESS_TYPE)
+                || (dataFailCause == USER_AUTHENTICATION)
+                || (dataFailCause == ACTIVATION_REJECT_GGSN)
+                || (dataFailCause == ACTIVATION_REJECT_UNSPECIFIED)
+                || (dataFailCause == SERVICE_OPTION_NOT_SUBSCRIBED)
+                || (dataFailCause == SERVICE_OPTION_NOT_SUPPORTED)
+                || (dataFailCause == SERVICE_OPTION_OUT_OF_ORDER)
+                || (dataFailCause == NSAPI_IN_USE)
+                || (dataFailCause == ONLY_IPV4_ALLOWED)
+                || (dataFailCause == ONLY_IPV6_ALLOWED)
+                || (dataFailCause == PROTOCOL_ERRORS)
+                || (dataFailCause == SIGNAL_LOST)
+                || (dataFailCause == RADIO_POWER_OFF)
+                || (dataFailCause == TETHERED_CALL_ACTIVE)
+                || (dataFailCause == UNACCEPTABLE_NETWORK_PARAMETER);
     }
 
-    public static DataFailCause fromInt(int errorCode) {
-        DataFailCause fc = sErrorCodeToFailCauseMap.get(errorCode);
-        if (fc == null) {
-            fc = UNKNOWN;
+    public static String toString(@FailCause int dataFailCause) {
+        int cause = getFailCause(dataFailCause);
+        return (cause == UNKNOWN) ? "UNKNOWN(" + dataFailCause + ")" : sFailCauseMap.get(cause);
+    }
+
+    public static int getFailCause(@FailCause int failCause) {
+        if (sFailCauseMap.containsKey(failCause)) {
+            return failCause;
+        } else {
+            return UNKNOWN;
         }
-        return fc;
     }
 }
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index c6887ab..f53cb82 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -16,12 +16,16 @@
 
 package android.telephony;
 
+import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 
 /**
- * Contains disconnect call causes generated by the framework and the RIL.
+ * Describes the cause of a disconnected call. Those disconnect causes can be converted into a more
+ * generic {@link android.telecom.DisconnectCause} object.
+ *
  * @hide
  */
+@SystemApi
 public class DisconnectCause {
 
     /** The disconnect cause is not valid (Not received a disconnect cause) */
@@ -101,8 +105,8 @@
     /** Unknown error or not specified */
     public static final int ERROR_UNSPECIFIED              = 36;
     /**
-     * Only emergency numbers are allowed, but we tried to dial
-     * a non-emergency number.
+     * Only emergency numbers are allowed, but we tried to dial a non-emergency number.
+     * @hide
      */
     // TODO: This should be the same as NOT_EMERGENCY
     public static final int EMERGENCY_ONLY                 = 37;
@@ -115,8 +119,7 @@
      */
     public static final int DIALED_MMI                     = 39;
     /**
-     * We tried to call a voicemail: URI but the device has no
-     * voicemail number configured.
+     * We tried to call a voicemail: URI but the device has no voicemail number configured.
      */
     public static final int VOICEMAIL_NUMBER_MISSING       = 40;
     /**
@@ -129,6 +132,8 @@
      * needs to be triggered by a *disconnect* event, rather than when
      * the InCallScreen first comes to the foreground.  For now we use
      * the needToShowCallLostDialog field for this (see below.)
+     *
+     * @hide
      */
     public static final int CDMA_CALL_LOST                 = 41;
     /**
@@ -169,62 +174,52 @@
 
     /**
      * Stk Call Control modified DIAL request to USSD request.
-     * {@hide}
      */
     public static final int DIAL_MODIFIED_TO_USSD          = 46;
     /**
      * Stk Call Control modified DIAL request to SS request.
-     * {@hide}
      */
     public static final int DIAL_MODIFIED_TO_SS            = 47;
     /**
      * Stk Call Control modified DIAL request to DIAL with modified data.
-     * {@hide}
      */
     public static final int DIAL_MODIFIED_TO_DIAL          = 48;
 
     /**
      * The call was terminated because CDMA phone service and roaming have already been activated.
-     * {@hide}
      */
     public static final int CDMA_ALREADY_ACTIVATED         = 49;
 
     /**
      * The call was terminated because it is not possible to place a video call while TTY is
      * enabled.
-     * {@hide}
      */
     public static final int VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED = 50;
 
     /**
      * The call was terminated because it was pulled to another device.
-     * {@hide}
      */
     public static final int CALL_PULLED = 51;
 
     /**
      * The call was terminated because it was answered on another device.
-     * {@hide}
      */
     public static final int ANSWERED_ELSEWHERE = 52;
 
     /**
      * The call was terminated because the maximum allowable number of calls has been reached.
-     * {@hide}
      */
     public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 53;
 
     /**
      * The call was terminated because cellular data has been disabled.
      * Used when in a video call and the user disables cellular data via the settings.
-     * {@hide}
      */
     public static final int DATA_DISABLED = 54;
 
     /**
      * The call was terminated because the data policy has disabled cellular data.
      * Used when in a video call and the user has exceeded the device data limit.
-     * {@hide}
      */
     public static final int DATA_LIMIT_REACHED = 55;
 
@@ -237,7 +232,6 @@
     /**
      * The network does not accept the emergency call request because IMEI was used as
      * identification and this cability is not supported by the network.
-     * {@hide}
      */
     public static final int IMEI_NOT_ACCEPTED = 58;
 
@@ -249,7 +243,6 @@
 
     /**
      * The call has failed because of access class barring.
-     * {@hide}
      */
     public static final int IMS_ACCESS_BLOCKED = 60;
 
@@ -265,51 +258,43 @@
 
     /**
      * Emergency call failed with a temporary fail cause and can be redialed on this slot.
-     * {@hide}
      */
     public static final int EMERGENCY_TEMP_FAILURE = 63;
 
     /**
      * Emergency call failed with a permanent fail cause and should not be redialed on this
-     * slot. 
-     * {@hide}
+     * slot.
      */
     public static final int EMERGENCY_PERM_FAILURE = 64;
 
     /**
      * This cause is used to report a normal event only when no other cause in the normal class
      * applies.
-     * {@hide}
      */
     public static final int NORMAL_UNSPECIFIED = 65;
 
     /**
      * Stk Call Control modified DIAL request to video DIAL request.
-     * {@hide}
      */
     public static final int DIAL_MODIFIED_TO_DIAL_VIDEO = 66;
 
     /**
      * Stk Call Control modified Video DIAL request to SS request.
-     * {@hide}
      */
     public static final int DIAL_VIDEO_MODIFIED_TO_SS = 67;
 
     /**
      * Stk Call Control modified Video DIAL request to USSD request.
-     * {@hide}
      */
     public static final int DIAL_VIDEO_MODIFIED_TO_USSD = 68;
 
     /**
      * Stk Call Control modified Video DIAL request to DIAL request.
-     * {@hide}
      */
     public static final int DIAL_VIDEO_MODIFIED_TO_DIAL = 69;
 
     /**
      * Stk Call Control modified Video DIAL request to Video DIAL request.
-     * {@hide}
      */
     public static final int DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 70;
 
@@ -359,7 +344,10 @@
         // Do nothing.
     }
 
-    /** Returns descriptive string for the specified disconnect cause. */
+    /**
+     * Returns descriptive string for the specified disconnect cause.
+     * @hide
+     */
     @UnsupportedAppUsage
     public static String toString(int cause) {
         switch (cause) {
diff --git a/telephony/java/android/telephony/ModemInfo.java b/telephony/java/android/telephony/ModemInfo.java
index 564effe..27a5121 100644
--- a/telephony/java/android/telephony/ModemInfo.java
+++ b/telephony/java/android/telephony/ModemInfo.java
@@ -32,6 +32,11 @@
     public final boolean isVoiceSupported;
     public final boolean isDataSupported;
 
+    // TODO b/121394331: Clean up this class after V1_1.PhoneCapability cleanup.
+    public ModemInfo(int modemId) {
+        this(modemId, 0, true, true);
+    }
+
     public ModemInfo(int modemId, int rat, boolean isVoiceSupported, boolean isDataSupported) {
         this.modemId = modemId;
         this.rat = rat;
diff --git a/telephony/java/android/telephony/NeighboringCellInfo.java b/telephony/java/android/telephony/NeighboringCellInfo.java
index ac38efb..1c615ab 100644
--- a/telephony/java/android/telephony/NeighboringCellInfo.java
+++ b/telephony/java/android/telephony/NeighboringCellInfo.java
@@ -25,6 +25,7 @@
 import static android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -53,29 +54,29 @@
      * In GSM, mRssi is the Received RSSI;
      * In UMTS, mRssi is the Level index of CPICH Received Signal Code Power
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mRssi;
     /**
      * CID in 16 bits format in GSM. Return UNKNOWN_CID in UMTS and CMDA.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mCid;
     /**
      * LAC in 16 bits format in GSM. Return UNKNOWN_CID in UMTS and CMDA.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mLac;
     /**
      * Primary Scrambling Code in 9 bits format in UMTS
      * Return UNKNOWN_CID in GSM and CMDA.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mPsc;
     /**
      * Radio network type, value is one of following
      * TelephonyManager.NETWORK_TYPE_XXXXXX.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mNetworkType;
 
     /**
diff --git a/telephony/java/android/telephony/NetworkService.java b/telephony/java/android/telephony/NetworkService.java
index 4354314..4bca404 100644
--- a/telephony/java/android/telephony/NetworkService.java
+++ b/telephony/java/android/telephony/NetworkService.java
@@ -16,7 +16,6 @@
 
 package android.telephony;
 
-import android.annotation.CallSuper;
 import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Intent;
@@ -53,7 +52,6 @@
     private final String TAG = NetworkService.class.getSimpleName();
 
     public static final String NETWORK_SERVICE_INTERFACE = "android.telephony.NetworkService";
-    public static final String NETWORK_SERVICE_EXTRA_SLOT_ID = "android.telephony.extra.SLOT_ID";
 
     private static final int NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER                 = 1;
     private static final int NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER                 = 2;
@@ -81,7 +79,7 @@
      * must extend this class to support network connection. Note that each instance of network
      * service is associated with one physical SIM slot.
      */
-    public class NetworkServiceProvider {
+    public abstract class NetworkServiceProvider implements AutoCloseable {
         private final int mSlotId;
 
         private final List<INetworkServiceCallback>
@@ -137,12 +135,12 @@
         }
 
         /**
-         * Called when the instance of network service is destroyed (e.g. got unbind or binder died).
+         * Called when the instance of network service is destroyed (e.g. got unbind or binder died)
+         * or when the network service provider is removed. The extended class should implement this
+         * method to perform cleanup works.
          */
-        @CallSuper
-        protected void onDestroy() {
-            mNetworkRegistrationStateChangedCallbacks.clear();
-        }
+        @Override
+        public abstract void close();
     }
 
     private class NetworkServiceHandler extends Handler {
@@ -168,7 +166,7 @@
                 case NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER:
                     // If the service provider doesn't exist yet, we try to create it.
                     if (serviceProvider != null) {
-                        serviceProvider.onDestroy();
+                        serviceProvider.close();
                         mServiceMap.remove(slotId);
                     }
                     break;
@@ -176,7 +174,7 @@
                     for (int i = 0; i < mServiceMap.size(); i++) {
                         serviceProvider = mServiceMap.get(i);
                         if (serviceProvider != null) {
-                            serviceProvider.onDestroy();
+                            serviceProvider.close();
                         }
                     }
                     mServiceMap.clear();
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 0df0daf..9317aa7 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -173,14 +173,14 @@
     public static final int LISTEN_CELL_INFO = 0x00000400;
 
     /**
-     * Listen for precise changes and fails to the device calls (cellular).
+     * Listen for {@link PreciseCallState.State} of ringing, background and foreground calls.
      * {@more}
      * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
      * READ_PRECISE_PHONE_STATE}
      *
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public static final int LISTEN_PRECISE_CALL_STATE                       = 0x00000800;
 
     /**
@@ -320,6 +320,18 @@
      */
     public static final int LISTEN_EMERGENCY_NUMBER_LIST                   = 0x01000000;
 
+    /**
+     * Listen for call disconnect causes which contains {@link DisconnectCause} and
+     * {@link PreciseDisconnectCause}.
+     * {@more}
+     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE}
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int LISTEN_CALL_DISCONNECT_CAUSES                  = 0x02000000;
+
     /*
      * Subscription used to listen to the phone state changes
      * @hide
@@ -530,11 +542,23 @@
 
     /**
      * Callback invoked when precise device call state changes.
+     * @param callState {@link PreciseCallState}
+     * @hide
+     */
+    @SystemApi
+    public void onPreciseCallStateChanged(PreciseCallState callState) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when call disconnect cause changes.
+     * @param disconnectCause {@link DisconnectCause}.
+     * @param preciseDisconnectCause {@link PreciseDisconnectCause}.
      *
      * @hide
      */
-    @UnsupportedAppUsage
-    public void onPreciseCallStateChanged(PreciseCallState callState) {
+    @SystemApi
+    public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) {
         // default implementation empty
     }
 
@@ -799,6 +823,15 @@
                     () -> mExecutor.execute(() -> psl.onPreciseCallStateChanged(callState)));
         }
 
+        public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onCallDisconnectCauseChanged(
+                            disconnectCause, preciseDisconnectCause)));
+        }
+
         public void onPreciseDataConnectionStateChanged(
                 PreciseDataConnectionState dataConnectionState) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
diff --git a/telephony/java/android/telephony/PreciseCallState.java b/telephony/java/android/telephony/PreciseCallState.java
index ed5c26a..59f3e1f 100644
--- a/telephony/java/android/telephony/PreciseCallState.java
+++ b/telephony/java/android/telephony/PreciseCallState.java
@@ -16,29 +16,51 @@
 
 package android.telephony;
 
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.DisconnectCause;
 import android.telephony.PreciseDisconnectCause;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
 /**
- * Contains precise call state and call fail causes generated by the
- * framework and the RIL.
+ * Contains precise call states.
  *
  * The following call information is included in returned PreciseCallState:
  *
  * <ul>
- *   <li>Ringing call state.
- *   <li>Foreground call state.
- *   <li>Background call state.
- *   <li>Disconnect cause; generated by the framework.
- *   <li>Precise disconnect cause; generated by the RIL.
+ *   <li>Precise ringing call state.
+ *   <li>Precise foreground call state.
+ *   <li>Precise background call state.
  * </ul>
  *
+ * @see android.telephony.TelephonyManager.CallState which contains generic call states.
+ *
  * @hide
  */
-public class PreciseCallState implements Parcelable {
+@SystemApi
+public final class PreciseCallState implements Parcelable {
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"PRECISE_CALL_STATE_"},
+            value = {
+                    PRECISE_CALL_STATE_NOT_VALID,
+                    PRECISE_CALL_STATE_IDLE,
+                    PRECISE_CALL_STATE_ACTIVE,
+                    PRECISE_CALL_STATE_HOLDING,
+                    PRECISE_CALL_STATE_DIALING,
+                    PRECISE_CALL_STATE_ALERTING,
+                    PRECISE_CALL_STATE_INCOMING,
+                    PRECISE_CALL_STATE_WAITING,
+                    PRECISE_CALL_STATE_DISCONNECTED,
+                    PRECISE_CALL_STATE_DISCONNECTING})
+    public @interface State {}
 
     /** Call state is not valid (Not received a call state). */
     public static final int PRECISE_CALL_STATE_NOT_VALID =      -1;
@@ -61,9 +83,9 @@
     /** Call state: Disconnecting. */
     public static final int PRECISE_CALL_STATE_DISCONNECTING =  8;
 
-    private int mRingingCallState = PRECISE_CALL_STATE_NOT_VALID;
-    private int mForegroundCallState = PRECISE_CALL_STATE_NOT_VALID;
-    private int mBackgroundCallState = PRECISE_CALL_STATE_NOT_VALID;
+    private @State int mRingingCallState = PRECISE_CALL_STATE_NOT_VALID;
+    private @State int mForegroundCallState = PRECISE_CALL_STATE_NOT_VALID;
+    private @State int mBackgroundCallState = PRECISE_CALL_STATE_NOT_VALID;
     private int mDisconnectCause = DisconnectCause.NOT_VALID;
     private int mPreciseDisconnectCause = PreciseDisconnectCause.NOT_VALID;
 
@@ -73,8 +95,9 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public PreciseCallState(int ringingCall, int foregroundCall, int backgroundCall,
-            int disconnectCause, int preciseDisconnectCause) {
+    public PreciseCallState(@State int ringingCall, @State int foregroundCall,
+                            @State int backgroundCall, int disconnectCause,
+                            int preciseDisconnectCause) {
         mRingingCallState = ringingCall;
         mForegroundCallState = foregroundCall;
         mBackgroundCallState = backgroundCall;
@@ -92,6 +115,8 @@
 
     /**
      * Construct a PreciseCallState object from the given parcel.
+     *
+     * @hide
      */
     private PreciseCallState(Parcel in) {
         mRingingCallState = in.readInt();
@@ -102,59 +127,23 @@
     }
 
     /**
-     * Get precise ringing call state
-     *
-     * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
-     * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
-     * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
-     * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
-     * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
-     * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
-     * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+     * Returns the precise ringing call state.
      */
-    @UnsupportedAppUsage
-    public int getRingingCallState() {
+    public @State int getRingingCallState() {
         return mRingingCallState;
     }
 
     /**
-     * Get precise foreground call state
-     *
-     * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
-     * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
-     * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
-     * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
-     * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
-     * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
-     * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+     * Returns the precise foreground call state.
      */
-    @UnsupportedAppUsage
-    public int getForegroundCallState() {
+    public @State int getForegroundCallState() {
         return mForegroundCallState;
     }
 
     /**
-     * Get precise background call state
-     *
-     * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
-     * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
-     * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
-     * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
-     * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
-     * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
-     * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+     * Returns the precise background call state.
      */
-    @UnsupportedAppUsage
-    public int getBackgroundCallState() {
+    public @State int getBackgroundCallState() {
         return mBackgroundCallState;
     }
 
@@ -199,6 +188,11 @@
      * @see DisconnectCause#CDMA_NOT_EMERGENCY
      * @see DisconnectCause#CDMA_ACCESS_BLOCKED
      * @see DisconnectCause#ERROR_UNSPECIFIED
+     *
+     * TODO: remove disconnect cause from preciseCallState as there is no link between random
+     * connection disconnect cause with foreground, background or ringing call.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     public int getDisconnectCause() {
@@ -238,6 +232,11 @@
      * @see PreciseDisconnectCause#CDMA_NOT_EMERGENCY
      * @see PreciseDisconnectCause#CDMA_ACCESS_BLOCKED
      * @see PreciseDisconnectCause#ERROR_UNSPECIFIED
+     *
+     * TODO: remove precise disconnect cause from preciseCallState as there is no link between
+     * random connection disconnect cause with foreground, background or ringing call.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     public int getPreciseDisconnectCause() {
@@ -272,14 +271,8 @@
 
     @Override
     public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + mRingingCallState;
-        result = prime * result + mForegroundCallState;
-        result = prime * result + mBackgroundCallState;
-        result = prime * result + mDisconnectCause;
-        result = prime * result + mPreciseDisconnectCause;
-        return result;
+        return Objects.hash(mRingingCallState, mForegroundCallState, mForegroundCallState,
+                mDisconnectCause, mPreciseDisconnectCause);
     }
 
     @Override
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index b258f52..8373899 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -17,10 +17,11 @@
 package android.telephony;
 
 import android.annotation.UnsupportedAppUsage;
+import android.net.LinkProperties;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.TelephonyManager;
-import android.net.LinkProperties;
+
+import java.util.Objects;
 
 /**
  * Contains precise data connection state.
@@ -32,7 +33,6 @@
  *   <li>Network type of the connection.
  *   <li>APN type.
  *   <li>APN.
- *   <li>Data connection change reason.
  *   <li>The properties of the network link.
  *   <li>Data connection fail cause.
  * </ul>
@@ -45,7 +45,6 @@
     private int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
     private String mAPNType = "";
     private String mAPN = "";
-    private String mReason = "";
     private LinkProperties mLinkProperties = null;
     private String mFailCause = "";
 
@@ -55,14 +54,12 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public PreciseDataConnectionState(int state, int networkType,
-            String apnType, String apn, String reason,
-            LinkProperties linkProperties, String failCause) {
+    public PreciseDataConnectionState(int state, int networkType, String apnType, String apn,
+                                      LinkProperties linkProperties, String failCause) {
         mState = state;
         mNetworkType = networkType;
         mAPNType = apnType;
         mAPN = apn;
-        mReason = reason;
         mLinkProperties = linkProperties;
         mFailCause = failCause;
     }
@@ -83,7 +80,6 @@
         mNetworkType = in.readInt();
         mAPNType = in.readString();
         mAPN = in.readString();
-        mReason = in.readString();
         mLinkProperties = (LinkProperties)in.readParcelable(null);
         mFailCause = in.readString();
     }
@@ -144,14 +140,6 @@
     }
 
     /**
-     * Get data connection change reason.
-     */
-    @UnsupportedAppUsage
-    public String getDataConnectionChangeReason() {
-        return mReason;
-    }
-
-    /**
      * Get the properties of the network link.
      */
     @UnsupportedAppUsage
@@ -178,7 +166,6 @@
         out.writeInt(mNetworkType);
         out.writeString(mAPNType);
         out.writeString(mAPN);
-        out.writeString(mReason);
         out.writeParcelable(mLinkProperties, flags);
         out.writeString(mFailCause);
     }
@@ -197,16 +184,7 @@
 
     @Override
     public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + mState;
-        result = prime * result + mNetworkType;
-        result = prime * result + ((mAPNType == null) ? 0 : mAPNType.hashCode());
-        result = prime * result + ((mAPN == null) ? 0 : mAPN.hashCode());
-        result = prime * result + ((mReason == null) ? 0 : mReason.hashCode());
-        result = prime * result + ((mLinkProperties == null) ? 0 : mLinkProperties.hashCode());
-        result = prime * result + ((mFailCause == null) ? 0 : mFailCause.hashCode());
-        return result;
+        return Objects.hash(mState, mNetworkType, mAPNType, mAPN, mLinkProperties, mFailCause);
     }
 
     @Override
@@ -252,13 +230,6 @@
         if (mNetworkType != other.mNetworkType) {
             return false;
         }
-        if (mReason == null) {
-            if (other.mReason != null) {
-                return false;
-            }
-        } else if (!mReason.equals(other.mReason)) {
-            return false;
-        }
         if (mState != other.mState) {
             return false;
         }
@@ -273,7 +244,6 @@
         sb.append(", Network type: " + mNetworkType);
         sb.append(", APN type: " + mAPNType);
         sb.append(", APN: " + mAPN);
-        sb.append(", Change reason: " + mReason);
         sb.append(", Link properties: " + mLinkProperties);
         sb.append(", Fail cause: " + mFailCause);
 
diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java
index 2acaf34..af88748 100644
--- a/telephony/java/android/telephony/PreciseDisconnectCause.java
+++ b/telephony/java/android/telephony/PreciseDisconnectCause.java
@@ -16,279 +16,329 @@
 
 package android.telephony;
 
+import android.annotation.SystemApi;
+
 /**
- * Contains precise disconnect call causes generated by the
- * framework and the RIL.
- *
+ * Contains precise disconnect call causes generated by the framework and the RIL.
  * @hide
  */
+@SystemApi
 public class PreciseDisconnectCause {
 
-    /** The disconnect cause is not valid (Not received a disconnect cause)*/
+    /** The disconnect cause is not valid (Not received a disconnect cause).*/
     public static final int NOT_VALID                                        = -1;
-    /** No disconnect cause provided. Generally a local disconnect or an incoming missed call */
+    /** No disconnect cause provided. Generally a local disconnect or an incoming missed call. */
     public static final int NO_DISCONNECT_CAUSE_AVAILABLE                    = 0;
     /**
      * The destination cannot be reached because the number, although valid,
-     * is not currently assigned
+     * is not currently assigned.
      */
     public static final int UNOBTAINABLE_NUMBER                              = 1;
-    /** The user cannot be reached because the network through which the call has been
-     *  routed does not serve the destination desired
+    /**
+     * The user cannot be reached because the network through which the call has been routed does
+     * not serve the destination desired.
      */
     public static final int NO_ROUTE_TO_DESTINATION                          = 3;
-    /** The channel most recently identified is not acceptable to the sending entity for
-     *  use in this call
+    /**
+     * The channel most recently identified is not acceptable to the sending entity for use in this
+     * call.
      */
     public static final int CHANNEL_UNACCEPTABLE                             = 6;
-    /** The MS has tried to access a service that the MS's network operator or service
-     *  provider is not prepared to allow
+    /**
+     * The mobile station (MS) has tried to access a service that the MS's network operator or
+     * service provider is not prepared to allow.
      */
     public static final int OPERATOR_DETERMINED_BARRING                      = 8;
-    /** One of the users involved in the call has requested that the call is cleared */
+    /** One of the users involved in the call has requested that the call is cleared. */
     public static final int NORMAL                                           = 16;
-    /** The called user is unable to accept another call */
+    /** The called user is unable to accept another call. */
     public static final int BUSY                                             = 17;
-    /** The user does not respond to a call establishment message with either an alerting
-     *  or connect indication within the prescribed period of time allocated
+    /**
+     * The user does not respond to a call establishment message with either an alerting or connect
+     * indication within the prescribed period of time allocated.
      */
     public static final int NO_USER_RESPONDING                               = 18;
-    /** The user has provided an alerting indication but has not provided a connect
-     *  indication within a prescribed period of time
+    /**
+     * The user has provided an alerting indication but has not provided a connect indication
+     * within a prescribed period of time.
      */
     public static final int NO_ANSWER_FROM_USER                              = 19;
-    /** The equipment sending this cause does not wish to accept this call */
+    /** The equipment sending this cause does not wish to accept this call. */
     public static final int CALL_REJECTED                                    = 21;
-    /** The called number is no longer assigned */
+    /** The called number is no longer assigned. */
     public static final int NUMBER_CHANGED                                   = 22;
-    /** This cause is returned to the network when a mobile station clears an active
-     *  call which is being pre-empted by another call with higher precedence
+    /**
+     * This cause is returned to the network when a mobile station clears an active call which is
+     * being pre-empted by another call with higher precedence.
      */
     public static final int PREEMPTION                                       = 25;
-    /** The destination indicated by the mobile station cannot be reached because
-     *  the interface to the destination is not functioning correctly
+    /**
+     * The destination indicated by the mobile station cannot be reached because the interface to
+     * the destination is not functioning correctly.
      */
     public static final int DESTINATION_OUT_OF_ORDER                         = 27;
-    /** The called party number is not a valid format or is not complete */
+    /** The called party number is not a valid format or is not complete. */
     public static final int INVALID_NUMBER_FORMAT                            = 28;
-    /** The facility requested by user can not be provided by the network */
+    /** The facility requested by user can not be provided by the network. */
     public static final int FACILITY_REJECTED                                = 29;
-    /** Provided in response to a STATUS ENQUIRY message */
+    /** Provided in response to a STATUS ENQUIRY message. */
     public static final int STATUS_ENQUIRY                                   = 30;
-    /** Reports a normal disconnect only when no other normal cause applies */
+    /** Reports a normal disconnect only when no other normal cause applies. */
     public static final int NORMAL_UNSPECIFIED                               = 31;
-    /** There is no channel presently available to handle the call */
+    /** There is no channel presently available to handle the call. */
     public static final int NO_CIRCUIT_AVAIL                                 = 34;
-    /** The network is not functioning correctly and that the condition is likely
-     *  to last a relatively long period of time
+    /**
+     * The network is not functioning correctly and that the condition is likely to last a
+     * relatively long period of time.
      */
     public static final int NETWORK_OUT_OF_ORDER                             = 38;
     /**
-     * The network is not functioning correctly and the condition is not likely to last
-     * a long period of time
+     * The network is not functioning correctly and the condition is not likely to last a long
+     * period of time.
      */
     public static final int TEMPORARY_FAILURE                                = 41;
-    /** The switching equipment is experiencing a period of high traffic */
+    /** The switching equipment is experiencing a period of high traffic. */
     public static final int SWITCHING_CONGESTION                             = 42;
-    /** The network could not deliver access information to the remote user as requested */
+    /** The network could not deliver access information to the remote user as requested. */
     public static final int ACCESS_INFORMATION_DISCARDED                     = 43;
-    /** The channel cannot be provided */
+    /** The channel cannot be provided. */
     public static final int CHANNEL_NOT_AVAIL                                = 44;
-    /** This cause is used to report a resource unavailable event only when no other
-     *  cause in the resource unavailable class applies
+    /**
+     * This cause is used to report a resource unavailable event only when no other cause in the
+     * resource unavailable class applies.
      */
     public static final int RESOURCES_UNAVAILABLE_OR_UNSPECIFIED             = 47;
-    /** The requested quality of service (ITU-T X.213) cannot be provided */
+    /** The requested quality of service (ITU-T X.213) cannot be provided. */
     public static final int QOS_NOT_AVAIL                                    = 49;
-    /** The facility could not be provided by the network because the user has no
-     *  complete subscription
+    /**
+     * The facility could not be provided by the network because the user has no complete
+     * subscription.
      */
     public static final int REQUESTED_FACILITY_NOT_SUBSCRIBED                = 50;
-    /** Incoming calls are not allowed within this CUG */
+    /** Incoming calls are not allowed within this calling user group (CUG). */
     public static final int INCOMING_CALLS_BARRED_WITHIN_CUG                 = 55;
-    /** The mobile station is not authorized to use bearer capability requested */
+    /** The mobile station is not authorized to use bearer capability requested. */
     public static final int BEARER_CAPABILITY_NOT_AUTHORIZED                 = 57;
-    /** The requested bearer capability is not available at this time */
+    /** The requested bearer capability is not available at this time. */
     public static final int BEARER_NOT_AVAIL                                 = 58;
-    /** The service option is not availble at this time */
+    /** The service option is not availble at this time. */
     public static final int SERVICE_OPTION_NOT_AVAILABLE                     = 63;
-    /** The equipment sending this cause does not support the bearer capability requested */
+    /** The equipment sending this cause does not support the bearer capability requested. */
     public static final int BEARER_SERVICE_NOT_IMPLEMENTED                   = 65;
-    /** The call clearing is due to ACM being greater than or equal to ACMmax */
+    /** The call clearing is due to ACM being greater than or equal to ACMmax. */
     public static final int ACM_LIMIT_EXCEEDED                               = 68;
-    /** The equipment sending this cause does not support the requested facility */
+    /** The equipment sending this cause does not support the requested facility. */
     public static final int REQUESTED_FACILITY_NOT_IMPLEMENTED               = 69;
-    /** The equipment sending this cause only supports the restricted version of
-     *  the requested bearer capability
+    /**
+     * The equipment sending this cause only supports the restricted version of the requested bearer
+     * capability.
      */
     public static final int ONLY_DIGITAL_INFORMATION_BEARER_AVAILABLE        = 70;
-    /** The service requested is not implemented at network */
+    /** The service requested is not implemented at network. */
     public static final int SERVICE_OR_OPTION_NOT_IMPLEMENTED                = 79;
-    /** The equipment sending this cause has received a message with a transaction identifier
-     *  which is not currently in use on the MS-network interface
+    /**
+     * The equipment sending this cause has received a message with a transaction identifier
+     * which is not currently in use on the mobile station network interface.
      */
     public static final int INVALID_TRANSACTION_IDENTIFIER                   = 81;
-    /** The called user for the incoming CUG call is not a member of the specified CUG */
+    /**
+     * The called user for the incoming CUG call is not a member of the specified calling user
+     * group (CUG).
+     */
     public static final int USER_NOT_MEMBER_OF_CUG                           = 87;
-    /** The equipment sending this cause has received a request which can't be accomodated */
+    /** The equipment sending this cause has received a request which can't be accomodated. */
     public static final int INCOMPATIBLE_DESTINATION                         = 88;
-    /** This cause is used to report receipt of a message with semantically incorrect contents */
+    /** This cause is used to report receipt of a message with semantically incorrect contents. */
     public static final int SEMANTICALLY_INCORRECT_MESSAGE                   = 95;
-    /** The equipment sending this cause has received a message with a non-semantical
-     *  mandatory IE error
+    /**
+     * The equipment sending this cause has received a message with a non-semantical mandatory
+     * information element (IE) error.
      */
     public static final int INVALID_MANDATORY_INFORMATION                    = 96;
-    /** This is sent in response to a message which is not defined, or defined but not
-     *  implemented by the equipment sending this cause
+    /**
+     * This is sent in response to a message which is not defined, or defined but not implemented
+     * by the equipment sending this cause.
      */
     public static final int MESSAGE_TYPE_NON_IMPLEMENTED                     = 97;
-    /** The equipment sending this cause has received a message not compatible with the
-     *  protocol state
+    /**
+     * The equipment sending this cause has received a message not compatible with the protocol
+     * state.
      */
     public static final int MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE  = 98;
-    /** The equipment sending this cause has received a message which includes information
-     *  elements not recognized because its identifier is not defined or it is defined but not
-     *  implemented by the equipment sending the cause
+    /**
+     * The equipment sending this cause has received a message which includes information
+     * elements not recognized because its identifier is not defined or it is defined but not
+     * implemented by the equipment sending the cause.
      */
     public static final int INFORMATION_ELEMENT_NON_EXISTENT                 = 99;
-    /** The equipment sending this cause has received a message with conditional IE errors */
+    /** The equipment sending this cause has received a message with conditional IE errors. */
     public static final int CONDITIONAL_IE_ERROR                             = 100;
-    /** The message has been received which is incompatible with the protocol state */
+    /** The message has been received which is incompatible with the protocol state. */
     public static final int MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE       = 101;
-    /** The  procedure has been initiated by the expiry of a timer in association with
-     *  3GPP TS 24.008 error handling procedures
+    /**
+     * The procedure has been initiated by the expiry of a timer in association with
+     * 3GPP TS 24.008 error handling procedures.
      */
     public static final int RECOVERY_ON_TIMER_EXPIRED                        = 102;
-    /** This protocol error event is reported only when no other cause in the protocol
-     *  error class applies
+    /**
+     * This protocol error event is reported only when no other cause in the protocol error class
+     * applies.
      */
     public static final int PROTOCOL_ERROR_UNSPECIFIED                       = 111;
-    /** interworking with a network which does not provide causes for actions it takes
-     *  thus, the precise cause for a message which is being sent cannot be ascertained
+    /**
+     * Interworking with a network which does not provide causes for actions it takes thus, the
+     * precise cause for a message which is being sent cannot be ascertained.
      */
     public static final int INTERWORKING_UNSPECIFIED                         = 127;
-    /** The call is restricted */
+    /** The call is restricted. */
     public static final int CALL_BARRED                                      = 240;
-    /** The call is blocked by the Fixed Dialing Number list */
+    /** The call is blocked by the Fixed Dialing Number list. */
     public static final int FDN_BLOCKED                                      = 241;
-    /** The given IMSI is not known at the VLR */
-    /** TS 24.008 cause 4 */
+    /** The given IMSI is not known at the Visitor Location Register (VLR) TS 24.008 cause . */
     public static final int IMSI_UNKNOWN_IN_VLR                              = 242;
     /**
      * The network does not accept emergency call establishment using an IMEI or not accept attach
-     * procedure for emergency services using an IMEI
+     * procedure for emergency services using an IMEI.
      */
     public static final int IMEI_NOT_ACCEPTED                                = 243;
-    /** The call cannot be established because RADIO is OFF */
+    /** The call cannot be established because RADIO is OFF. */
     public static final int RADIO_OFF                                        = 247;
-    /** The call cannot be established because of no cell coverage */
+    /** The call cannot be established because of no cell coverage. */
     public static final int OUT_OF_SRV                                       = 248;
-    /** The call cannot be established because of no valid SIM */
+    /** The call cannot be established because of no valid SIM. */
     public static final int NO_VALID_SIM                                     = 249;
-    /** The call is dropped or failed internally by modem */
+    /** The call is dropped or failed internally by modem. */
     public static final int RADIO_INTERNAL_ERROR                             = 250;
-    /** Call failed because of UE timer expired while waiting for a response from network */
+    /** Call failed because of UE timer expired while waiting for a response from network. */
     public static final int NETWORK_RESP_TIMEOUT                             = 251;
-    /** Call failed because of a network reject */
+    /** Call failed because of a network reject. */
     public static final int NETWORK_REJECT                                   = 252;
-    /** Call failed because of radio access failure. ex. RACH failure */
+    /** Call failed because of radio access failure. ex. RACH failure. */
     public static final int RADIO_ACCESS_FAILURE                             = 253;
-    /** Call failed/dropped because of a RLF */
+    /** Call failed/dropped because of a Radio Link Failure (RLF). */
     public static final int RADIO_LINK_FAILURE                               = 254;
-    /** Call failed/dropped because of radio link lost */
+    /** Call failed/dropped because of radio link lost. */
     public static final int RADIO_LINK_LOST                                  = 255;
-    /** Call failed because of a radio uplink issue */
+    /** Call failed because of a radio uplink issue. */
     public static final int RADIO_UPLINK_FAILURE                             = 256;
-    /** Call failed because of a RRC connection setup failure */
+    /** Call failed because of a RRC (Radio Resource Control) connection setup failure. */
     public static final int RADIO_SETUP_FAILURE                              = 257;
-    /** Call failed/dropped because of RRC connection release from NW */
+    /** Call failed/dropped because of RRC (Radio Resource Control) connection release from NW. */
     public static final int RADIO_RELEASE_NORMAL                             = 258;
-    /** Call failed/dropped because of RRC abnormally released by modem/network */
+    /**
+     * Call failed/dropped because of RRC (Radio Resource Control) abnormally released by
+     * modem/network.
+     */
     public static final int RADIO_RELEASE_ABNORMAL                           = 259;
-    /** Call setup failed because of access class barring */
+    /** Call setup failed because of access class barring. */
     public static final int ACCESS_CLASS_BLOCKED                             = 260;
-    /** Call failed/dropped because of a network detach */
+    /** Call failed/dropped because of a network detach. */
     public static final int NETWORK_DETACH                                   = 261;
 
-    /** MS is locked until next power cycle */
+    /** Mobile station (MS) is locked until next power cycle. */
     public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE                    = 1000;
-    /** Drop call*/
+    /** Drop call. */
     public static final int CDMA_DROP                                        = 1001;
-    /** INTERCEPT order received, MS state idle entered */
+    /** INTERCEPT order received, Mobile station (MS) state idle entered. */
     public static final int CDMA_INTERCEPT                                   = 1002;
-    /** MS has been redirected, call is cancelled */
+    /** Mobile station (MS) has been redirected, call is cancelled. */
     public static final int CDMA_REORDER                                     = 1003;
-    /** Service option rejection */
+    /** Service option rejection. */
     public static final int CDMA_SO_REJECT                                   = 1004;
-    /** Requested service is rejected, retry delay is set */
+    /** Requested service is rejected, retry delay is set. */
     public static final int CDMA_RETRY_ORDER                                 = 1005;
-    /** Unable to obtain access to the CDMA system */
+    /** Unable to obtain access to the CDMA system. */
     public static final int CDMA_ACCESS_FAILURE                              = 1006;
-    /** Not a preempted call */
+    /** Not a preempted call. */
     public static final int CDMA_PREEMPTED                                   = 1007;
-    /** Not an emergency call */
+    /** Not an emergency call. */
     public static final int CDMA_NOT_EMERGENCY                               = 1008;
-    /** Access Blocked by CDMA network */
+    /** Access Blocked by CDMA network. */
     public static final int CDMA_ACCESS_BLOCKED                              = 1009;
 
     /** Mapped from ImsReasonInfo */
+    // TODO: remove ImsReasonInfo from preciseDisconnectCause
     /* The passed argument is an invalid */
+    /** @hide */
     public static final int LOCAL_ILLEGAL_ARGUMENT                           = 1200;
     // The operation is invoked in invalid call state
+    /** @hide */
     public static final int LOCAL_ILLEGAL_STATE                              = 1201;
     // IMS service internal error
+    /** @hide */
     public static final int LOCAL_INTERNAL_ERROR                             = 1202;
     // IMS service goes down (service connection is lost)
+    /** @hide */
     public static final int LOCAL_IMS_SERVICE_DOWN                           = 1203;
     // No pending incoming call exists
+    /** @hide */
     public static final int LOCAL_NO_PENDING_CALL                            = 1204;
     // Service unavailable; by power off
+    /** @hide */
     public static final int LOCAL_POWER_OFF                                  = 1205;
     // Service unavailable; by low battery
+    /** @hide */
     public static final int LOCAL_LOW_BATTERY                                = 1206;
     // Service unavailable; by out of service (data service state)
+    /** @hide */
     public static final int LOCAL_NETWORK_NO_SERVICE                         = 1207;
     /* Service unavailable; by no LTE coverage
      * (VoLTE is not supported even though IMS is registered)
      */
+    /** @hide */
     public static final int LOCAL_NETWORK_NO_LTE_COVERAGE                    = 1208;
     /** Service unavailable; by located in roaming area */
+    /** @hide */
     public static final int LOCAL_NETWORK_ROAMING                            = 1209;
     /** Service unavailable; by IP changed */
+    /** @hide */
     public static final int LOCAL_NETWORK_IP_CHANGED                         = 1210;
     /** Service unavailable; other */
+    /** @hide */
     public static final int LOCAL_SERVICE_UNAVAILABLE                        = 1211;
     /* Service unavailable; IMS connection is lost (IMS is not registered) */
+    /** @hide */
     public static final int LOCAL_NOT_REGISTERED                             = 1212;
     /** Max call exceeded */
+    /** @hide */
     public static final int LOCAL_MAX_CALL_EXCEEDED                          = 1213;
     /** Call decline */
+    /** @hide */
     public static final int LOCAL_CALL_DECLINE                               = 1214;
     /** SRVCC is in progress */
+    /** @hide */
     public static final int LOCAL_CALL_VCC_ON_PROGRESSING                    = 1215;
     /** Resource reservation is failed (QoS precondition) */
+    /** @hide */
     public static final int LOCAL_CALL_RESOURCE_RESERVATION_FAILED           = 1216;
     /** Retry CS call; VoLTE service can't be provided by the network or remote end
      *  Resolve the extra code(EXTRA_CODE_CALL_RETRY_*) if the below code is set
+     *  @hide
      */
     public static final int LOCAL_CALL_CS_RETRY_REQUIRED                     = 1217;
     /** Retry VoLTE call; VoLTE service can't be provided by the network temporarily */
+    /** @hide */
     public static final int LOCAL_CALL_VOLTE_RETRY_REQUIRED                  = 1218;
     /** IMS call is already terminated (in TERMINATED state) */
+    /** @hide */
     public static final int LOCAL_CALL_TERMINATED                            = 1219;
     /** Handover not feasible */
+    /** @hide */
     public static final int LOCAL_HO_NOT_FEASIBLE                            = 1220;
 
     /** 1xx waiting timer is expired after sending INVITE request (MO only) */
+    /** @hide */
     public static final int TIMEOUT_1XX_WAITING                              = 1221;
     /** User no answer during call setup operation (MO/MT)
      *  MO : 200 OK to INVITE request is not received,
      *  MT : No action from user after alerting the call
+     *  @hide
      */
     public static final int TIMEOUT_NO_ANSWER                                = 1222;
     /** User no answer during call update operation (MO/MT)
      *  MO : 200 OK to re-INVITE request is not received,
      *  MT : No action from user after alerting the call
+     *  @hide
      */
     public static final int TIMEOUT_NO_ANSWER_CALL_UPDATE                    = 1223;
 
@@ -296,102 +346,142 @@
      * STATUSCODE (SIP response code) (IMS -> Telephony)
      */
     /** SIP request is redirected */
+    /** @hide */
     public static final int SIP_REDIRECTED                                   = 1300;
     /** 4xx responses */
     /** 400 : Bad Request */
+    /** @hide */
     public static final int SIP_BAD_REQUEST                                  = 1310;
     /** 403 : Forbidden */
+    /** @hide */
     public static final int SIP_FORBIDDEN                                    = 1311;
     /** 404 : Not Found */
+    /** @hide */
     public static final int SIP_NOT_FOUND                                    = 1312;
     /** 415 : Unsupported Media Type
      *  416 : Unsupported URI Scheme
      *  420 : Bad Extension
      */
+    /** @hide */
     public static final int SIP_NOT_SUPPORTED                                = 1313;
     /** 408 : Request Timeout */
+    /** @hide */
     public static final int SIP_REQUEST_TIMEOUT                              = 1314;
     /** 480 : Temporarily Unavailable */
+    /** @hide */
     public static final int SIP_TEMPRARILY_UNAVAILABLE                       = 1315;
     /** 484 : Address Incomplete */
+    /** @hide */
     public static final int SIP_BAD_ADDRESS                                  = 1316;
     /** 486 : Busy Here
      *  600 : Busy Everywhere
      */
+    /** @hide */
     public static final int SIP_BUSY                                         = 1317;
     /** 487 : Request Terminated */
+    /** @hide */
     public static final int SIP_REQUEST_CANCELLED                            = 1318;
     /** 406 : Not Acceptable
      *  488 : Not Acceptable Here
      *  606 : Not Acceptable
      */
+    /** @hide */
     public static final int SIP_NOT_ACCEPTABLE                               = 1319;
     /** 410 : Gone
      *  604 : Does Not Exist Anywhere
      */
+    /** @hide */
     public static final int SIP_NOT_REACHABLE                                = 1320;
     /** Others */
+    /** @hide */
     public static final int SIP_CLIENT_ERROR                                 = 1321;
     /** 481 : Transaction Does Not Exist */
+    /** @hide */
     public static final int SIP_TRANSACTION_DOES_NOT_EXIST                   = 1322;
     /** 5xx responses
      *  501 : Server Internal Error
      */
+    /** @hide */
     public static final int SIP_SERVER_INTERNAL_ERROR                        = 1330;
     /** 503 : Service Unavailable */
+    /** @hide */
     public static final int SIP_SERVICE_UNAVAILABLE                          = 1331;
     /** 504 : Server Time-out */
+    /** @hide */
     public static final int SIP_SERVER_TIMEOUT                               = 1332;
     /** Others */
+    /** @hide */
     public static final int SIP_SERVER_ERROR                                 = 1333;
     /** 6xx responses
      *  603 : Decline
      */
+    /** @hide */
     public static final int SIP_USER_REJECTED                                = 1340;
     /** Others */
+    /** @hide */
     public static final int SIP_GLOBAL_ERROR                                 = 1341;
     /** Emergency failure */
+    /** @hide */
     public static final int EMERGENCY_TEMP_FAILURE                           = 1342;
+    /** @hide */
     public static final int EMERGENCY_PERM_FAILURE                           = 1343;
     /** Media resource initialization failed */
+    /** @hide */
     public static final int MEDIA_INIT_FAILED                                = 1400;
     /** RTP timeout (no audio / video traffic in the session) */
+    /** @hide */
     public static final int MEDIA_NO_DATA                                    = 1401;
     /** Media is not supported; so dropped the call */
+    /** @hide */
     public static final int MEDIA_NOT_ACCEPTABLE                             = 1402;
     /** Unknown media related errors */
+    /** @hide */
     public static final int MEDIA_UNSPECIFIED                                = 1403;
     /** User triggers the call end */
+    /** @hide */
     public static final int USER_TERMINATED                                  = 1500;
     /** No action while an incoming call is ringing */
+    /** @hide */
     public static final int USER_NOANSWER                                    = 1501;
     /** User ignores an incoming call */
+    /** @hide */
     public static final int USER_IGNORE                                      = 1502;
     /** User declines an incoming call */
+    /** @hide */
     public static final int USER_DECLINE                                     = 1503;
     /** Device declines/ends a call due to low battery */
+    /** @hide */
     public static final int LOW_BATTERY                                      = 1504;
     /** Device declines call due to blacklisted call ID */
+    /** @hide */
     public static final int BLACKLISTED_CALL_ID                              = 1505;
     /** The call is terminated by the network or remote user */
+    /** @hide */
     public static final int USER_TERMINATED_BY_REMOTE                        = 1510;
 
     /**
      * UT
      */
+    /** @hide */
     public static final int UT_NOT_SUPPORTED                                 = 1800;
+    /** @hide */
     public static final int UT_SERVICE_UNAVAILABLE                           = 1801;
+    /** @hide */
     public static final int UT_OPERATION_NOT_ALLOWED                         = 1802;
+    /** @hide */
     public static final int UT_NETWORK_ERROR                                 = 1803;
+    /** @hide */
     public static final int UT_CB_PASSWORD_MISMATCH                          = 1804;
 
     /**
      * ECBM
+     * @hide
      */
     public static final int ECBM_NOT_SUPPORTED                               = 1900;
 
     /**
      * Fail code used to indicate that Multi-endpoint is not supported by the Ims framework.
+     * @hide
      */
     public static final int MULTIENDPOINT_NOT_SUPPORTED                      = 1901;
 
@@ -405,56 +495,68 @@
      * active wifi call and at the edge of coverage and there is no qualified LTE network available
      * to handover the call to. We get a handover NOT_TRIGERRED message from the modem. This error
      * code is received as part of the handover message.
+     * @hide
      */
     public static final int CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE               = 2000;
 
     /**
      * MT call has ended due to a release from the network
      * because the call was answered elsewhere
+     * @hide
      */
     public static final int ANSWERED_ELSEWHERE                               = 2100;
 
     /**
      * For MultiEndpoint - Call Pull request has failed
+     * @hide
      */
     public static final int CALL_PULL_OUT_OF_SYNC                            = 2101;
 
     /**
      * For MultiEndpoint - Call has been pulled from primary to secondary
+     * @hide
      */
     public static final int CALL_PULLED                                      = 2102;
 
     /**
      * Supplementary services (HOLD/RESUME) failure error codes.
      * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision.
+     * @hide
      */
     public static final int SUPP_SVC_FAILED                                  = 2300;
+    /** @hide */
     public static final int SUPP_SVC_CANCELLED                               = 2301;
+    /** @hide */
     public static final int SUPP_SVC_REINVITE_COLLISION                      = 2302;
 
     /**
      * DPD Procedure received no response or send failed
+     * @hide
      */
     public static final int IWLAN_DPD_FAILURE                                = 2400;
 
     /**
      * Establishment of the ePDG Tunnel Failed
+     * @hide
      */
     public static final int EPDG_TUNNEL_ESTABLISH_FAILURE                    = 2500;
 
     /**
      * Re-keying of the ePDG Tunnel Failed; may not always result in teardown
+     * @hide
      */
     public static final int EPDG_TUNNEL_REKEY_FAILURE                        = 2501;
 
     /**
      * Connection to the packet gateway is lost
+     * @hide
      */
     public static final int EPDG_TUNNEL_LOST_CONNECTION                      = 2502;
 
     /**
      * The maximum number of calls allowed has been reached.  Used in a multi-endpoint scenario
      * where the number of calls across all connected devices has reached the maximum.
+     * @hide
      */
     public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED                  = 2503;
 
@@ -462,21 +564,25 @@
      * Similar to {@link #CODE_LOCAL_CALL_DECLINE}, except indicates that a remote device has
      * declined the call.  Used in a multi-endpoint scenario where a remote device declined an
      * incoming call.
+     * @hide
      */
     public static final int REMOTE_CALL_DECLINE                              = 2504;
 
     /**
      * Indicates the call was disconnected due to the user reaching their data limit.
+     * @hide
      */
     public static final int DATA_LIMIT_REACHED                               = 2505;
 
     /**
      * Indicates the call was disconnected due to the user disabling cellular data.
+     * @hide
      */
     public static final int DATA_DISABLED                                    = 2506;
 
     /**
      * Indicates a call was disconnected due to loss of wifi signal.
+     * @hide
      */
     public static final int WIFI_LOST                                        = 2507;
 
@@ -499,7 +605,7 @@
     public static final int OEM_CAUSE_14                                     = 0xf00e;
     public static final int OEM_CAUSE_15                                     = 0xf00f;
 
-    /** Disconnected due to unspecified reasons */
+    /** Disconnected due to unspecified reasons. */
     public static final int ERROR_UNSPECIFIED                                = 0xffff;
 
     /** Private constructor to avoid class instantiation. */
diff --git a/telephony/java/android/telephony/RadioAccessFamily.java b/telephony/java/android/telephony/RadioAccessFamily.java
index 4482074..0d94c4d 100644
--- a/telephony/java/android/telephony/RadioAccessFamily.java
+++ b/telephony/java/android/telephony/RadioAccessFamily.java
@@ -17,6 +17,7 @@
 package android.telephony;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -56,6 +57,9 @@
     public static final int RAF_LTE = TelephonyManager.NETWORK_TYPE_BITMASK_LTE;
     public static final int RAF_LTE_CA = TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA;
 
+    // 5G
+    public static final int RAF_NR = TelephonyManager.NETWORK_TYPE_BITMASK_NR;
+
     // Grouping of RAFs
     // 2G
     private static final int GSM = RAF_GSM | RAF_GPRS | RAF_EDGE;
@@ -67,6 +71,9 @@
     // 4G
     private static final int LTE = RAF_LTE | RAF_LTE_CA;
 
+    // 5G
+    private static final int NR = RAF_NR;
+
     /* Phone ID of phone */
     private int mPhoneId;
 
@@ -159,84 +166,78 @@
 
     @UnsupportedAppUsage
     public static int getRafFromNetworkType(int type) {
-        int raf;
-
         switch (type) {
             case RILConstants.NETWORK_MODE_WCDMA_PREF:
-                raf = GSM | WCDMA;
-                break;
+                return GSM | WCDMA;
             case RILConstants.NETWORK_MODE_GSM_ONLY:
-                raf = GSM;
-                break;
+                return GSM;
             case RILConstants.NETWORK_MODE_WCDMA_ONLY:
-                raf = WCDMA;
-                break;
+                return WCDMA;
             case RILConstants.NETWORK_MODE_GSM_UMTS:
-                raf = GSM | WCDMA;
-                break;
+                return GSM | WCDMA;
             case RILConstants.NETWORK_MODE_CDMA:
-                raf = CDMA | EVDO;
-                break;
+                return CDMA | EVDO;
             case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO:
-                raf = LTE | CDMA | EVDO;
-                break;
+                return LTE | CDMA | EVDO;
             case RILConstants.NETWORK_MODE_LTE_GSM_WCDMA:
-                raf = LTE | GSM | WCDMA;
-                break;
+                return LTE | GSM | WCDMA;
             case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA:
-                raf = LTE | CDMA | EVDO | GSM | WCDMA;
-                break;
+                return LTE | CDMA | EVDO | GSM | WCDMA;
             case RILConstants.NETWORK_MODE_LTE_ONLY:
-                raf = LTE;
-                break;
+                return LTE;
             case RILConstants.NETWORK_MODE_LTE_WCDMA:
-                raf = LTE | WCDMA;
-                break;
+                return LTE | WCDMA;
             case RILConstants.NETWORK_MODE_CDMA_NO_EVDO:
-                raf = CDMA;
-                break;
+                return CDMA;
             case RILConstants.NETWORK_MODE_EVDO_NO_CDMA:
-                raf = EVDO;
-                break;
+                return EVDO;
             case RILConstants.NETWORK_MODE_GLOBAL:
-                raf = GSM | WCDMA | CDMA | EVDO;
-                break;
+                return GSM | WCDMA | CDMA | EVDO;
             case RILConstants.NETWORK_MODE_TDSCDMA_ONLY:
-                raf = RAF_TD_SCDMA;
-                break;
+                return RAF_TD_SCDMA;
             case RILConstants.NETWORK_MODE_TDSCDMA_WCDMA:
-                raf = RAF_TD_SCDMA | WCDMA;
-                break;
+                return RAF_TD_SCDMA | WCDMA;
             case RILConstants.NETWORK_MODE_LTE_TDSCDMA:
-                raf = LTE | RAF_TD_SCDMA;
-                break;
+                return LTE | RAF_TD_SCDMA;
             case RILConstants.NETWORK_MODE_TDSCDMA_GSM:
-                raf = RAF_TD_SCDMA | GSM;
-                break;
+                return RAF_TD_SCDMA | GSM;
             case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM:
-                raf = LTE | RAF_TD_SCDMA | GSM;
-                break;
+                return LTE | RAF_TD_SCDMA | GSM;
             case RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA:
-                raf = RAF_TD_SCDMA | GSM | WCDMA;
-                break;
+                return RAF_TD_SCDMA | GSM | WCDMA;
             case RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA:
-                raf = LTE | RAF_TD_SCDMA | WCDMA;
-                break;
+                return LTE | RAF_TD_SCDMA | WCDMA;
             case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA:
-                raf = LTE | RAF_TD_SCDMA | GSM | WCDMA;
-                break;
+                return LTE | RAF_TD_SCDMA | GSM | WCDMA;
             case RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
-                raf = RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
-                break;
+                return RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
             case RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
-                raf = LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
-                break;
+                return LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
+            case (RILConstants.NETWORK_MODE_NR_ONLY):
+                return NR;
+            case (RILConstants.NETWORK_MODE_NR_LTE):
+                return NR | LTE;
+            case (RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO):
+                return NR | LTE | CDMA | EVDO;
+            case (RILConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA):
+                return NR | LTE | GSM | WCDMA;
+            case (RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA):
+                return NR | LTE | CDMA | EVDO | GSM | WCDMA;
+            case (RILConstants.NETWORK_MODE_NR_LTE_WCDMA):
+                return NR | LTE | WCDMA;
+            case (RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA):
+                return NR | LTE | RAF_TD_SCDMA;
+            case (RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM):
+                return NR | LTE | RAF_TD_SCDMA | GSM;
+            case (RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA):
+                return NR | LTE | RAF_TD_SCDMA | WCDMA;
+            case (RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA):
+                return NR | LTE | RAF_TD_SCDMA | GSM | WCDMA;
+            case (RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA):
+                return NR | LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
             default:
-                raf = RAF_UNKNOWN;
-                break;
+                return RAF_UNKNOWN;
         }
-
-        return raf;
     }
 
     /**
@@ -249,6 +250,7 @@
         raf = ((CDMA & raf) > 0) ? (CDMA | raf) : raf;
         raf = ((EVDO & raf) > 0) ? (EVDO | raf) : raf;
         raf = ((LTE & raf) > 0) ? (LTE | raf) : raf;
+        raf = ((NR & raf) > 0) ? (NR | raf) : raf;
 
         return raf;
     }
@@ -271,85 +273,80 @@
         return TelephonyManager.NETWORK_CLASS_UNKNOWN;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static int getNetworkTypeFromRaf(int raf) {
-        int type;
-
         raf = getAdjustedRaf(raf);
 
         switch (raf) {
             case (GSM | WCDMA):
-                type = RILConstants.NETWORK_MODE_WCDMA_PREF;
-                break;
+                return RILConstants.NETWORK_MODE_WCDMA_PREF;
             case GSM:
-                type = RILConstants.NETWORK_MODE_GSM_ONLY;
-                break;
+                return RILConstants.NETWORK_MODE_GSM_ONLY;
             case WCDMA:
-                type = RILConstants.NETWORK_MODE_WCDMA_ONLY;
-                break;
+                return RILConstants.NETWORK_MODE_WCDMA_ONLY;
             case (CDMA | EVDO):
-                type = RILConstants.NETWORK_MODE_CDMA;
-                break;
+                return RILConstants.NETWORK_MODE_CDMA;
             case (LTE | CDMA | EVDO):
-                type = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO;
-                break;
+                return RILConstants.NETWORK_MODE_LTE_CDMA_EVDO;
             case (LTE | GSM | WCDMA):
-                type = RILConstants.NETWORK_MODE_LTE_GSM_WCDMA;
-                break;
+                return RILConstants.NETWORK_MODE_LTE_GSM_WCDMA;
             case (LTE | CDMA | EVDO | GSM | WCDMA):
-                type = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
-                break;
+                return RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
             case LTE:
-                type = RILConstants.NETWORK_MODE_LTE_ONLY;
-                break;
+                return RILConstants.NETWORK_MODE_LTE_ONLY;
             case (LTE | WCDMA):
-                type = RILConstants.NETWORK_MODE_LTE_WCDMA;
-                break;
+                return RILConstants.NETWORK_MODE_LTE_WCDMA;
             case CDMA:
-                type = RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
-                break;
+                return RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
             case EVDO:
-                type = RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
-                break;
+                return RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
             case (GSM | WCDMA | CDMA | EVDO):
-                type = RILConstants.NETWORK_MODE_GLOBAL;
-                break;
+                return RILConstants.NETWORK_MODE_GLOBAL;
             case RAF_TD_SCDMA:
-                type = RILConstants.NETWORK_MODE_TDSCDMA_ONLY;
-                break;
+                return RILConstants.NETWORK_MODE_TDSCDMA_ONLY;
             case (RAF_TD_SCDMA | WCDMA):
-                type = RILConstants.NETWORK_MODE_TDSCDMA_WCDMA;
-                break;
+                return RILConstants.NETWORK_MODE_TDSCDMA_WCDMA;
             case (LTE | RAF_TD_SCDMA):
-                type = RILConstants.NETWORK_MODE_LTE_TDSCDMA;
-                break;
+                return RILConstants.NETWORK_MODE_LTE_TDSCDMA;
             case (RAF_TD_SCDMA | GSM):
-                type = RILConstants.NETWORK_MODE_TDSCDMA_GSM;
-                break;
+                return RILConstants.NETWORK_MODE_TDSCDMA_GSM;
             case (LTE | RAF_TD_SCDMA | GSM):
-                type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM;
-                break;
+                return RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM;
             case (RAF_TD_SCDMA | GSM | WCDMA):
-                type = RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA;
-                break;
+                return RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA;
             case (LTE | RAF_TD_SCDMA | WCDMA):
-                type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA;
-                break;
+                return RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA;
             case (LTE | RAF_TD_SCDMA | GSM | WCDMA):
-                type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA;
-                break;
+                return RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA;
             case (RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA):
-                type = RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
-                break;
+                return RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
             case (LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA):
-                type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
-                break;
+                return RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+            case (NR):
+                return RILConstants.NETWORK_MODE_NR_ONLY;
+            case (NR | LTE):
+                return RILConstants.NETWORK_MODE_NR_LTE;
+            case (NR | LTE | CDMA | EVDO):
+                return RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO;
+            case (NR | LTE | GSM | WCDMA):
+                return RILConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA;
+            case (NR | LTE | CDMA | EVDO | GSM | WCDMA):
+                return RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA;
+            case (NR | LTE | WCDMA):
+                return RILConstants.NETWORK_MODE_NR_LTE_WCDMA;
+            case (NR | LTE | RAF_TD_SCDMA):
+                return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA;
+            case (NR | LTE | RAF_TD_SCDMA | GSM):
+                return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM;
+            case (NR | LTE | RAF_TD_SCDMA | WCDMA):
+                return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA;
+            case (NR | LTE | RAF_TD_SCDMA | GSM | WCDMA):
+                return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA;
+            case (NR | LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA):
+                return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
             default:
-                type = RILConstants.PREFERRED_NETWORK_MODE ;
-                break;
+                return RILConstants.PREFERRED_NETWORK_MODE;
         }
-
-        return type;
     }
 
     public static int singleRafTypeFromString(String rafString) {
@@ -376,6 +373,7 @@
             case "EVDO":    return EVDO;
             case "WCDMA":   return WCDMA;
             case "LTE_CA":  return RAF_LTE_CA;
+            case "NR":      return RAF_NR;
             default:        return RAF_UNKNOWN;
         }
     }
diff --git a/telephony/java/android/telephony/Rlog.java b/telephony/java/android/telephony/Rlog.java
index 5f2fa33..cdab2dc 100644
--- a/telephony/java/android/telephony/Rlog.java
+++ b/telephony/java/android/telephony/Rlog.java
@@ -65,7 +65,7 @@
         return Log.println_native(Log.LOG_ID_RADIO, Log.INFO, tag, msg);
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static int i(String tag, String msg, Throwable tr) {
         return Log.println_native(Log.LOG_ID_RADIO, Log.INFO, tag,
                 msg + '\n' + Log.getStackTraceString(tr));
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 78f05168..9fc1b6f 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -317,19 +317,19 @@
     @RilRadioTechnology
     private int mRilDataRadioTechnology;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private boolean mCssIndicator;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private int mNetworkId;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private int mSystemId;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mCdmaRoamingIndicator;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mCdmaDefaultRoamingIndicator;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mCdmaEriIconIndex;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mCdmaEriIconMode;
 
     @UnsupportedAppUsage
@@ -1071,7 +1071,7 @@
     }
 
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void setDataRegState(int state) {
         mDataRegState = state;
         if (VDBG) Rlog.d(LOG_TAG, "[ServiceState] setDataRegState=" + mDataRegState);
@@ -1101,7 +1101,7 @@
     }
 
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void setVoiceRoamingType(@RoamingType int type) {
         NetworkRegistrationState regState = getNetworkRegistrationState(
                 NetworkRegistrationState.DOMAIN_CS, AccessNetworkConstants.TransportType.WWAN);
@@ -1122,7 +1122,7 @@
     }
 
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void setDataRoamingType(@RoamingType int type) {
         NetworkRegistrationState regState = getNetworkRegistrationState(
                 NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WWAN);
@@ -1139,7 +1139,7 @@
     /**
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void setEmergencyOnly(boolean emergencyOnly) {
         mIsEmergencyOnly = emergencyOnly;
     }
@@ -1147,7 +1147,7 @@
     /**
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void setCdmaRoamingIndicator(int roaming) {
         this.mCdmaRoamingIndicator = roaming;
     }
@@ -1155,7 +1155,7 @@
     /**
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void setCdmaDefaultRoamingIndicator (int roaming) {
         this.mCdmaDefaultRoamingIndicator = roaming;
     }
@@ -1163,7 +1163,7 @@
     /**
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void setCdmaEriIconIndex(int index) {
         this.mCdmaEriIconIndex = index;
     }
@@ -1171,7 +1171,7 @@
     /**
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void setCdmaEriIconMode(int mode) {
         this.mCdmaEriIconMode = mode;
     }
@@ -1232,7 +1232,7 @@
      * @param b second obj
      * @return true if two objects equal or both are null
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static boolean equalsHandlesNulls (Object a, Object b) {
         return (a == null) ? (b == null) : a.equals (b);
     }
@@ -1389,7 +1389,7 @@
     }
 
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void setCssIndicator(int css) {
         this.mCssIndicator = (css != 0);
     }
@@ -1569,7 +1569,7 @@
     }
 
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public int getCssIndicator() {
         return this.mCssIndicator ? 1 : 0;
     }
@@ -1629,7 +1629,7 @@
     }
 
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static boolean bearerBitmapHasCdma(int radioTechnologyBitmap) {
         return (RIL_RADIO_CDMA_TECHNOLOGY_BITMASK & radioTechnologyBitmap) != 0;
     }
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 240b8a9..ef185c5 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -16,16 +16,14 @@
 
 package android.telephony;
 
+import android.annotation.NonNull;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.CarrierConfigManager;
-import android.util.Log;
+import android.os.PersistableBundle;
 
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Objects;
 
 /**
@@ -59,6 +57,8 @@
     /** @hide */
     @UnsupportedAppUsage
     public static final int NUM_SIGNAL_STRENGTH_BINS = 5;
+
+    /** SIGNAL_STRENGTH_NAMES is currently used by BatteryStats, but to-be-removed soon. */
     /** @hide */
     public static final String[] SIGNAL_STRENGTH_NAMES = {
         "none", "poor", "moderate", "good", "great"
@@ -72,66 +72,22 @@
     public static final int INVALID = Integer.MAX_VALUE;
 
     private static final int LTE_RSRP_THRESHOLDS_NUM = 4;
-    private static final int MAX_LTE_RSRP = -44;
-    private static final int MIN_LTE_RSRP = -140;
 
     private static final int WCDMA_RSCP_THRESHOLDS_NUM = 4;
-    private static final int MAX_WCDMA_RSCP = -24;
-    private static final int MIN_WCDMA_RSCP = -120;
 
     /* The type of signal measurement */
-    private static final String MEASUMENT_TYPE_RSCP = "rscp";
+    private static final String MEASUREMENT_TYPE_RSCP = "rscp";
 
-    /** Parameters reported by the Radio */
-    @UnsupportedAppUsage
-    private int mGsmSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
-    @UnsupportedAppUsage
-    private int mGsmBitErrorRate;   // bit error rate (0-7, 99) as defined in TS 27.007 8.5
-    @UnsupportedAppUsage
-    private int mCdmaDbm;   // This value is the RSSI value
-    @UnsupportedAppUsage
-    private int mCdmaEcio;  // This value is the Ec/Io
-    @UnsupportedAppUsage
-    private int mEvdoDbm;   // This value is the EVDO RSSI value
-    @UnsupportedAppUsage
-    private int mEvdoEcio;  // This value is the EVDO Ec/Io
-    @UnsupportedAppUsage
-    private int mEvdoSnr;   // Valid values are 0-8.  8 is the highest signal to noise ratio
-    @UnsupportedAppUsage
-    private int mLteSignalStrength;
-    @UnsupportedAppUsage
-    private int mLteRsrp;
-    @UnsupportedAppUsage
-    private int mLteRsrq;
-    @UnsupportedAppUsage
-    private int mLteRssnr;
-    @UnsupportedAppUsage
-    private int mLteCqi;
-    @UnsupportedAppUsage
-    private int mTdScdmaRscp; // Valid values are -24...-120dBm or INVALID if unknown
-    private int mWcdmaSignalStrength;
-    private int mWcdmaRscpAsu;  // the WCDMA RSCP in ASU as reported from the HAL
-    @UnsupportedAppUsage
-    private int mWcdmaRscp;     // the WCDMA RSCP in dBm
+    CellSignalStrengthCdma mCdma;
+    CellSignalStrengthGsm mGsm;
+    CellSignalStrengthWcdma mWcdma;
+    CellSignalStrengthTdscdma mTdscdma;
+    CellSignalStrengthLte mLte;
 
     /** Parameters from the framework */
     @UnsupportedAppUsage
     private int mLteRsrpBoost; // offset to be reduced from the rsrp threshold while calculating
                                 // signal strength level
-    private boolean mIsGsm; // This value is set by the ServiceStateTracker
-                            // onSignalStrengthResult.
-    private boolean mUseOnlyRsrpForLteLevel; // Use only RSRP for the number of LTE signal bar.
-
-    // The threshold of LTE RSRP for determining the display level of LTE signal bar. Note that the
-    // min and max are fixed at MIN_LTE_RSRP (-140) and MAX_LTE_RSRP (-44).
-    private int mLteRsrpThresholds[] = new int[LTE_RSRP_THRESHOLDS_NUM];
-
-    // The type of default measurement for determining the display level of WCDMA signal bar.
-    private String mWcdmaDefaultSignalMeasurement;
-
-    // The threshold of WCDMA RSCP for determining the display level of WCDMA signal bar. Note that
-    // the min and max are fixed at MIN_WCDMA_RSCP (-120) and MAX_WCDMA_RSCP (-24).
-    private int mWcdmaRscpThresholds[] = new int[WCDMA_RSCP_THRESHOLDS_NUM];
 
     /**
      * Create a new SignalStrength from a intent notifier Bundle
@@ -153,47 +109,17 @@
     }
 
     /**
-     * Empty constructor
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public SignalStrength() {
-        this(true);
-    }
-
-    /**
      * This constructor is used to create SignalStrength with default
-     * values and set the gsmFlag with the value passed in the input
+     * values.
      *
-     * @param gsmFlag true if Gsm Phone,false if Cdma phone
      * @return newly created SignalStrength
      * @hide
      */
     @UnsupportedAppUsage
-    public SignalStrength(boolean gsmFlag) {
-        mGsmSignalStrength = 99;
-        mGsmBitErrorRate = -1;
-        mCdmaDbm = INVALID;
-        mCdmaEcio = -1;
-        mEvdoDbm = INVALID;
-        mEvdoEcio = -1;
-        mEvdoSnr = -1;
-        mLteSignalStrength = 99;
-        mLteRsrp = INVALID;
-        mLteRsrq = INVALID;
-        mLteRssnr = INVALID;
-        mLteCqi = INVALID;
-        mTdScdmaRscp = INVALID;
-        mWcdmaSignalStrength = 99;
-        mWcdmaRscp = INVALID;
-        mWcdmaRscpAsu = 255;
-        mLteRsrpBoost = 0;
-        mIsGsm = gsmFlag;
-        mUseOnlyRsrpForLteLevel = false;
-        mWcdmaDefaultSignalMeasurement = "";
-        setLteRsrpThresholds(getDefaultLteRsrpThresholds());
-        setWcdmaRscpThresholds(getDefaultWcdmaRscpThresholds());
+    public SignalStrength() {
+        this(new CellSignalStrengthCdma(), new CellSignalStrengthGsm(),
+                new CellSignalStrengthWcdma(), new CellSignalStrengthTdscdma(),
+                new CellSignalStrengthLte());
     }
 
     /**
@@ -202,68 +128,64 @@
      * @hide
      */
     public SignalStrength(
-            int gsmSignalStrength, int gsmBitErrorRate,
-            int cdmaDbm, int cdmaEcio,
-            int evdoDbm, int evdoEcio, int evdoSnr,
-            int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
-            int tdScdmaRscp, int wcdmaSignalStrength, int wcdmaRscpAsu,
-            // values Added by config
-            int lteRsrpBoost, boolean gsmFlag, boolean lteLevelBaseOnRsrp,
-            String wcdmaDefaultMeasurement) {
-        mGsmSignalStrength = gsmSignalStrength;
-        mGsmBitErrorRate = gsmBitErrorRate;
-        mCdmaDbm = cdmaDbm;
-        mCdmaEcio = cdmaEcio;
-        mEvdoDbm = evdoDbm;
-        mEvdoEcio = evdoEcio;
-        mEvdoSnr = evdoSnr;
-        mLteSignalStrength = lteSignalStrength;
-        mLteRsrp = lteRsrp;
-        mLteRsrq = lteRsrq;
-        mLteRssnr = lteRssnr;
-        mLteCqi = lteCqi;
-        mTdScdmaRscp = INVALID;
-        mWcdmaSignalStrength = wcdmaSignalStrength;
-        mWcdmaRscpAsu = wcdmaRscpAsu;
-        mWcdmaRscp = wcdmaRscpAsu - 120;
-        mLteRsrpBoost = lteRsrpBoost;
-        mIsGsm = gsmFlag;
-        mUseOnlyRsrpForLteLevel = lteLevelBaseOnRsrp;
-        mWcdmaDefaultSignalMeasurement = wcdmaDefaultMeasurement;
-        setLteRsrpThresholds(getDefaultLteRsrpThresholds());
-        setWcdmaRscpThresholds(getDefaultWcdmaRscpThresholds());
-        if (DBG) log("initialize: " + toString());
+            @NonNull CellSignalStrengthCdma cdma,
+            @NonNull CellSignalStrengthGsm gsm,
+            @NonNull CellSignalStrengthWcdma wcdma,
+            @NonNull CellSignalStrengthTdscdma tdscdma,
+            @NonNull CellSignalStrengthLte lte) {
+        mCdma = cdma;
+        mGsm = gsm;
+        mWcdma = wcdma;
+        mTdscdma = tdscdma;
+        mLte = lte;
+        mLteRsrpBoost = 0;
     }
 
     /**
-     * Constructor for only values provided by Radio HAL V1.0
+     * Constructor for Radio HAL V1.0
      *
      * @hide
      */
-    public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
-            int cdmaDbm, int cdmaEcio,
-            int evdoDbm, int evdoEcio, int evdoSnr,
-            int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
-            int tdScdmaRscp) {
-        this(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
-                evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
-                lteRsrq, lteRssnr, lteCqi, tdScdmaRscp, 99, INVALID, 0, true, false, "");
+    public SignalStrength(android.hardware.radio.V1_0.SignalStrength signalStrength) {
+        this(new CellSignalStrengthCdma(signalStrength.cdma, signalStrength.evdo),
+                new CellSignalStrengthGsm(signalStrength.gw),
+                new CellSignalStrengthWcdma(),
+                new CellSignalStrengthTdscdma(signalStrength.tdScdma),
+                new CellSignalStrengthLte(signalStrength.lte));
     }
 
     /**
-     * Constructor for only values provided by Radio HAL V1.2
+     * Constructor for Radio HAL V1.2
      *
      * @hide
      */
-    public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
-            int cdmaDbm, int cdmaEcio,
-            int evdoDbm, int evdoEcio, int evdoSnr,
-            int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
-            int tdScdmaRscp, int wcdmaSignalStrength, int wcdmaRscp) {
-        this(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
-                evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
-                lteRsrq, lteRssnr, lteCqi, tdScdmaRscp, wcdmaSignalStrength, wcdmaRscp, 0, true,
-                false, "");
+    public SignalStrength(android.hardware.radio.V1_2.SignalStrength signalStrength) {
+        this(new CellSignalStrengthCdma(signalStrength.cdma, signalStrength.evdo),
+                new CellSignalStrengthGsm(signalStrength.gsm),
+                new CellSignalStrengthWcdma(signalStrength.wcdma),
+                new CellSignalStrengthTdscdma(signalStrength.tdScdma),
+                new CellSignalStrengthLte(signalStrength.lte));
+    }
+
+    private CellSignalStrength getPrimary() {
+        // This behavior is intended to replicate the legacy behavior of getLevel() by prioritizing
+        // newer faster RATs for default/for display purposes.
+        if (mLte.isValid()) return mLte;
+        if (mCdma.isValid()) return mCdma;
+        if (mTdscdma.isValid()) return mTdscdma;
+        if (mWcdma.isValid()) return mWcdma;
+        if (mGsm.isValid()) return mGsm;
+        return mLte;
+    }
+
+    /** @hide */
+    public void updateLevel(PersistableBundle cc, ServiceState ss) {
+        mLteRsrpBoost = ss.getLteEarfcnRsrpBoost();
+        mCdma.updateLevel(cc, ss);
+        mGsm.updateLevel(cc, ss);
+        mWcdma.updateLevel(cc, ss);
+        mTdscdma.updateLevel(cc, ss);
+        mLte.updateLevel(cc, ss);
     }
 
     /**
@@ -283,28 +205,12 @@
      */
     @UnsupportedAppUsage
     protected void copyFrom(SignalStrength s) {
-        mGsmSignalStrength = s.mGsmSignalStrength;
-        mGsmBitErrorRate = s.mGsmBitErrorRate;
-        mCdmaDbm = s.mCdmaDbm;
-        mCdmaEcio = s.mCdmaEcio;
-        mEvdoDbm = s.mEvdoDbm;
-        mEvdoEcio = s.mEvdoEcio;
-        mEvdoSnr = s.mEvdoSnr;
-        mLteSignalStrength = s.mLteSignalStrength;
-        mLteRsrp = s.mLteRsrp;
-        mLteRsrq = s.mLteRsrq;
-        mLteRssnr = s.mLteRssnr;
-        mLteCqi = s.mLteCqi;
-        mTdScdmaRscp = s.mTdScdmaRscp;
-        mWcdmaSignalStrength = s.mWcdmaSignalStrength;
-        mWcdmaRscpAsu = s.mWcdmaRscpAsu;
-        mWcdmaRscp = s.mWcdmaRscp;
+        mCdma = new CellSignalStrengthCdma(s.mCdma);
+        mGsm = new CellSignalStrengthGsm(s.mGsm);
+        mWcdma = new CellSignalStrengthWcdma(s.mWcdma);
+        mTdscdma = new CellSignalStrengthTdscdma(s.mTdscdma);
+        mLte = new CellSignalStrengthLte(s.mLte);
         mLteRsrpBoost = s.mLteRsrpBoost;
-        mIsGsm = s.mIsGsm;
-        mUseOnlyRsrpForLteLevel = s.mUseOnlyRsrpForLteLevel;
-        mWcdmaDefaultSignalMeasurement = s.mWcdmaDefaultSignalMeasurement;
-        setLteRsrpThresholds(s.mLteRsrpThresholds);
-        setWcdmaRscpThresholds(s.mWcdmaRscpThresholds);
     }
 
     /**
@@ -316,56 +222,25 @@
     public SignalStrength(Parcel in) {
         if (DBG) log("Size of signalstrength parcel:" + in.dataSize());
 
-        mGsmSignalStrength = in.readInt();
-        mGsmBitErrorRate = in.readInt();
-        mCdmaDbm = in.readInt();
-        mCdmaEcio = in.readInt();
-        mEvdoDbm = in.readInt();
-        mEvdoEcio = in.readInt();
-        mEvdoSnr = in.readInt();
-        mLteSignalStrength = in.readInt();
-        mLteRsrp = in.readInt();
-        mLteRsrq = in.readInt();
-        mLteRssnr = in.readInt();
-        mLteCqi = in.readInt();
-        mTdScdmaRscp = in.readInt();
-        mWcdmaSignalStrength = in.readInt();
-        mWcdmaRscpAsu = in.readInt();
-        mWcdmaRscp = in.readInt();
+        mCdma = in.readParcelable(CellSignalStrengthCdma.class.getClassLoader());
+        mGsm = in.readParcelable(CellSignalStrengthGsm.class.getClassLoader());
+        mWcdma = in.readParcelable(CellSignalStrengthWcdma.class.getClassLoader());
+        mTdscdma = in.readParcelable(CellSignalStrengthTdscdma.class.getClassLoader());
+        mLte = in.readParcelable(CellSignalStrengthLte.class.getClassLoader());
         mLteRsrpBoost = in.readInt();
-        mIsGsm = in.readBoolean();
-        mUseOnlyRsrpForLteLevel = in.readBoolean();
-        mWcdmaDefaultSignalMeasurement = in.readString();
-        in.readIntArray(mLteRsrpThresholds);
-        in.readIntArray(mWcdmaRscpThresholds);
     }
 
     /**
      * {@link Parcelable#writeToParcel}
      */
     public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(mGsmSignalStrength);
-        out.writeInt(mGsmBitErrorRate);
-        out.writeInt(mCdmaDbm);
-        out.writeInt(mCdmaEcio);
-        out.writeInt(mEvdoDbm);
-        out.writeInt(mEvdoEcio);
-        out.writeInt(mEvdoSnr);
-        out.writeInt(mLteSignalStrength);
-        out.writeInt(mLteRsrp);
-        out.writeInt(mLteRsrq);
-        out.writeInt(mLteRssnr);
-        out.writeInt(mLteCqi);
-        out.writeInt(mTdScdmaRscp);
-        out.writeInt(mWcdmaSignalStrength);
-        out.writeInt(mWcdmaRscpAsu);
-        out.writeInt(mWcdmaRscp);
+        out.writeParcelable(mCdma, flags);
+        out.writeParcelable(mGsm, flags);
+        out.writeParcelable(mWcdma, flags);
+        out.writeParcelable(mTdscdma, flags);
+        out.writeParcelable(mLte, flags);
+
         out.writeInt(mLteRsrpBoost);
-        out.writeBoolean(mIsGsm);
-        out.writeBoolean(mUseOnlyRsrpForLteLevel);
-        out.writeString(mWcdmaDefaultSignalMeasurement);
-        out.writeIntArray(mLteRsrpThresholds);
-        out.writeIntArray(mWcdmaRscpThresholds);
     }
 
     /**
@@ -392,153 +267,21 @@
     };
 
     /**
-     * Validate the individual signal strength fields as per the range
-     * specified in ril.h
-     * Set to invalid any field that is not in the valid range
-     * Cdma, evdo, lte rsrp & rsrq values are sign converted
-     * when received from ril interface
+     * Get the GSM RSSI in ASU.
      *
-     * @return
-     *      Valid values for all signalstrength fields
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public void validateInput() {
-        if (DBG) log("Signal before validate=" + this);
-        // TS 27.007 8.5
-        mGsmSignalStrength = mGsmSignalStrength >= 0 ? mGsmSignalStrength : 99;
-        mWcdmaSignalStrength = (mWcdmaSignalStrength >= 0) ? mWcdmaSignalStrength : 99;
-        mLteSignalStrength = (mLteSignalStrength >= 0) ? mLteSignalStrength : 99;
-        // BER no change;
-
-        // WCDMA RSCP valid values are -120 through -24 as defined in TS 27.007 8.69
-        // but are reported in ASU which is 0 through 96, so we do the conversion here
-        mWcdmaRscpAsu =
-                ((mWcdmaRscpAsu - 120 >= MIN_WCDMA_RSCP) && (mWcdmaRscpAsu - 120 <= MAX_WCDMA_RSCP))
-                ? mWcdmaRscpAsu : 255;
-        mWcdmaRscp = ((mWcdmaRscp >= MIN_WCDMA_RSCP) && (mWcdmaRscp <= MAX_WCDMA_RSCP))
-                ? mWcdmaRscp : INVALID;
-
-        mCdmaDbm = mCdmaDbm > 0 ? -mCdmaDbm : -120;
-        mCdmaEcio = (mCdmaEcio >= 0) ? -mCdmaEcio : -160;
-
-        mEvdoDbm = (mEvdoDbm > 0) ? -mEvdoDbm : -120;
-        mEvdoEcio = (mEvdoEcio >= 0) ? -mEvdoEcio : -160;
-        mEvdoSnr = ((mEvdoSnr >= 0) && (mEvdoSnr <= 8)) ? mEvdoSnr : -1;
-
-        // TS 36.214 Physical Layer Section 5.1.3, TS 36.331 RRC
-        mLteRsrp = ((-mLteRsrp >= MIN_LTE_RSRP) && (-mLteRsrp <= MAX_LTE_RSRP)) ? -mLteRsrp
-                                : SignalStrength.INVALID;
-        mLteRsrq = ((mLteRsrq >= 3) && (mLteRsrq <= 20)) ? -mLteRsrq : SignalStrength.INVALID;
-        mLteRssnr = ((mLteRssnr >= -200) && (mLteRssnr <= 300)) ? mLteRssnr
-                : SignalStrength.INVALID;
-
-        mTdScdmaRscp = ((mTdScdmaRscp >= 0) && (mTdScdmaRscp <= 96))
-                ? (mTdScdmaRscp - 120) : SignalStrength.INVALID;
-        // Cqi no change
-        if (DBG) log("Signal after validate=" + this);
-    }
-
-    /**
-     * Fix {@link #mIsGsm} based on the signal strength data.
+     * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
      *
-     * @hide
-     */
-    public void fixType() {
-        mIsGsm = getCdmaRelatedSignalStrength() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-    }
-
-    /**
-     * @param true - Gsm, Lte phones
-     *        false - Cdma phones
-     *
-     * Used by voice phone to set the mIsGsm
-     *        flag
-     * @hide
-     */
-    public void setGsm(boolean gsmFlag) {
-        mIsGsm = gsmFlag;
-    }
-
-    /**
-     * @param useOnlyRsrpForLteLevel true if it uses only RSRP for the number of LTE signal bar,
-     * otherwise false.
-     *
-     * Used by phone to use only RSRP or not for the number of LTE signal bar.
-     * @hide
-     */
-    public void setUseOnlyRsrpForLteLevel(boolean useOnlyRsrpForLteLevel) {
-        mUseOnlyRsrpForLteLevel = useOnlyRsrpForLteLevel;
-    }
-
-    /**
-     * @param defaultMeasurement sets the type of WCDMA default signal measurement
-     *
-     * Used by phone to determine default measurement type for calculation WCDMA signal level.
-     * @hide
-     */
-    public void setWcdmaDefaultSignalMeasurement(String defaultMeasurement) {
-        mWcdmaDefaultSignalMeasurement = defaultMeasurement;
-    }
-
-    /**
-     * @param lteRsrpBoost - signal strength offset
-     *
-     * Used by phone to set the lte signal strength offset which will be
-     * reduced from rsrp threshold while calculating signal strength level
-     *
-     * @hide
-     */
-    public void setLteRsrpBoost(int lteRsrpBoost) {
-        mLteRsrpBoost = lteRsrpBoost;
-    }
-
-    /**
-     * Sets the threshold array for determining the display level of LTE signal bar.
-     *
-     * @param lteRsrpThresholds int array for determining the display level.
-     *
-     * @hide
-     */
-    public void setLteRsrpThresholds(int[] lteRsrpThresholds) {
-        if ((lteRsrpThresholds == null)
-                || (lteRsrpThresholds.length != LTE_RSRP_THRESHOLDS_NUM)) {
-            Log.wtf(LOG_TAG, "setLteRsrpThresholds - lteRsrpThresholds is invalid.");
-            return;
-        }
-        System.arraycopy(lteRsrpThresholds, 0, mLteRsrpThresholds, 0, LTE_RSRP_THRESHOLDS_NUM);
-    }
-
-    /**
-     * Get the GSM Signal Strength, valid values are (0-31, 99) as defined in TS
-     * 27.007 8.5
+     * @return RSSI in ASU 0..31, 99, or UNAVAILABLE
      */
     public int getGsmSignalStrength() {
-        return this.mGsmSignalStrength;
+        return mGsm.getAsuLevel();
     }
 
     /**
      * Get the GSM bit error rate (0-7, 99) as defined in TS 27.007 8.5
      */
     public int getGsmBitErrorRate() {
-        return this.mGsmBitErrorRate;
-    }
-
-    /**
-     * Sets the threshold array for determining the display level of WCDMA signal bar.
-     *
-     * @param wcdmaRscpThresholds int array for determining the display level.
-     *
-     * @hide
-     */
-    public void setWcdmaRscpThresholds(int[] wcdmaRscpThresholds) {
-        if ((wcdmaRscpThresholds == null)
-                || (wcdmaRscpThresholds.length != WCDMA_RSCP_THRESHOLDS_NUM)) {
-            Log.wtf(LOG_TAG, "setWcdmaRscpThresholds - wcdmaRscpThresholds is invalid.");
-            return;
-        }
-        System.arraycopy(wcdmaRscpThresholds, 0, mWcdmaRscpThresholds, 0,
-                WCDMA_RSCP_THRESHOLDS_NUM);
+        return mGsm.getBitErrorRate();
     }
 
     /**
@@ -547,14 +290,14 @@
      * @return the CDMA RSSI value or {@link #INVALID} if invalid
      */
     public int getCdmaDbm() {
-        return this.mCdmaDbm;
+        return mCdma.getCdmaDbm();
     }
 
     /**
      * Get the CDMA Ec/Io value in dB*10
      */
     public int getCdmaEcio() {
-        return this.mCdmaEcio;
+        return mCdma.getCdmaEcio();
     }
 
     /**
@@ -563,51 +306,51 @@
      * @return the EVDO RSSI value or {@link #INVALID} if invalid
      */
     public int getEvdoDbm() {
-        return this.mEvdoDbm;
+        return mCdma.getEvdoDbm();
     }
 
     /**
      * Get the EVDO Ec/Io value in dB*10
      */
     public int getEvdoEcio() {
-        return this.mEvdoEcio;
+        return mCdma.getEvdoEcio();
     }
 
     /**
      * Get the signal to noise ratio. Valid values are 0-8. 8 is the highest.
      */
     public int getEvdoSnr() {
-        return this.mEvdoSnr;
+        return mCdma.getEvdoSnr();
     }
 
     /** @hide */
     @UnsupportedAppUsage
     public int getLteSignalStrength() {
-        return mLteSignalStrength;
+        return mLte.getRssi();
     }
 
     /** @hide */
     @UnsupportedAppUsage
     public int getLteRsrp() {
-        return mLteRsrp;
+        return mLte.getRsrp();
     }
 
     /** @hide */
     @UnsupportedAppUsage
     public int getLteRsrq() {
-        return mLteRsrq;
+        return mLte.getRsrq();
     }
 
     /** @hide */
     @UnsupportedAppUsage
     public int getLteRssnr() {
-        return mLteRssnr;
+        return mLte.getRssnr();
     }
 
     /** @hide */
     @UnsupportedAppUsage
     public int getLteCqi() {
-        return mLteCqi;
+        return mLte.getCqi();
     }
 
     /** @hide */
@@ -624,9 +367,12 @@
      *     while 4 represents a very strong signal strength.
      */
     public int getLevel() {
-        int level = mIsGsm ? getGsmRelatedSignalStrength() : getCdmaRelatedSignalStrength();
-        if (DBG) log("getLevel=" + level);
-        return level;
+        int level = getPrimary().getLevel();
+        if (level < SIGNAL_STRENGTH_NONE_OR_UNKNOWN || level > SIGNAL_STRENGTH_GREAT) {
+            log("Invalid Level " + level + ", this=" + this);
+            return SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        }
+        return getPrimary().getLevel();
     }
 
     /**
@@ -636,33 +382,7 @@
      */
     @UnsupportedAppUsage
     public int getAsuLevel() {
-        int asuLevel = 0;
-        if (mIsGsm) {
-            if (mLteRsrp != SignalStrength.INVALID) {
-                asuLevel = getLteAsuLevel();
-            } else if (mTdScdmaRscp != SignalStrength.INVALID) {
-                asuLevel = getTdScdmaAsuLevel();
-            } else if (mWcdmaRscp != SignalStrength.INVALID) {
-                asuLevel = getWcdmaAsuLevel();
-            } else {
-                asuLevel = getGsmAsuLevel();
-            }
-        } else {
-            int cdmaAsuLevel = getCdmaAsuLevel();
-            int evdoAsuLevel = getEvdoAsuLevel();
-            if (evdoAsuLevel == 0) {
-                /* We don't know evdo use, cdma */
-                asuLevel = cdmaAsuLevel;
-            } else if (cdmaAsuLevel == 0) {
-                /* We don't know cdma use, evdo */
-                asuLevel = evdoAsuLevel;
-            } else {
-                /* We know both, use the lowest level */
-                asuLevel = cdmaAsuLevel < evdoAsuLevel ? cdmaAsuLevel : evdoAsuLevel;
-            }
-        }
-        if (DBG) log("getAsuLevel=" + asuLevel);
-        return asuLevel;
+        return getPrimary().getAsuLevel();
     }
 
     /**
@@ -672,30 +392,7 @@
      */
     @UnsupportedAppUsage
     public int getDbm() {
-        int dBm = INVALID;
-
-        if(isGsm()) {
-            dBm = getLteDbm();
-            if (dBm == INVALID) {
-                if (getTdScdmaLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
-                    if (getWcdmaDbm() == INVALID) {
-                        dBm = getGsmDbm();
-                    } else {
-                        dBm = getWcdmaDbm();
-                    }
-                } else {
-                    dBm = getTdScdmaDbm();
-                }
-            }
-        } else {
-            int cdmaDbm = getCdmaDbm();
-            int evdoDbm = getEvdoDbm();
-
-            return (evdoDbm == -120) ? cdmaDbm : ((cdmaDbm == -120) ? evdoDbm
-                    : (cdmaDbm < evdoDbm ? cdmaDbm : evdoDbm));
-        }
-        if (DBG) log("getDbm=" + dBm);
-        return dBm;
+        return getPrimary().getDbm();
     }
 
     /**
@@ -705,17 +402,7 @@
      */
     @UnsupportedAppUsage
     public int getGsmDbm() {
-        int dBm;
-
-        int gsmSignalStrength = getGsmSignalStrength();
-        int asu = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength);
-        if (asu != -1) {
-            dBm = -113 + (2 * asu);
-        } else {
-            dBm = -1;
-        }
-        if (DBG) log("getGsmDbm=" + dBm);
-        return dBm;
+        return mGsm.getDbm();
     }
 
     /**
@@ -725,20 +412,7 @@
      */
     @UnsupportedAppUsage
     public int getGsmLevel() {
-        int level;
-
-        // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
-        // asu = 0 (-113dB or less) is very weak
-        // signal, its better to show 0 bars to the user in such cases.
-        // asu = 99 is a special case, where the signal strength is unknown.
-        int asu = getGsmSignalStrength();
-        if (asu <= 2 || asu == 99) level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-        else if (asu >= 12) level = SIGNAL_STRENGTH_GREAT;
-        else if (asu >= 8)  level = SIGNAL_STRENGTH_GOOD;
-        else if (asu >= 5)  level = SIGNAL_STRENGTH_MODERATE;
-        else level = SIGNAL_STRENGTH_POOR;
-        if (DBG) log("getGsmLevel=" + level);
-        return level;
+        return mGsm.getLevel();
     }
 
     /**
@@ -748,13 +422,7 @@
      */
     @UnsupportedAppUsage
     public int getGsmAsuLevel() {
-        // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
-        // asu = 0 (-113dB or less) is very weak
-        // signal, its better to show 0 bars to the user in such cases.
-        // asu = 99 is a special case, where the signal strength is unknown.
-        int level = getGsmSignalStrength();
-        if (DBG) log("getGsmAsuLevel=" + level);
-        return level;
+        return mGsm.getAsuLevel();
     }
 
     /**
@@ -764,27 +432,7 @@
      */
     @UnsupportedAppUsage
     public int getCdmaLevel() {
-        final int cdmaDbm = getCdmaDbm();
-        final int cdmaEcio = getCdmaEcio();
-        int levelDbm;
-        int levelEcio;
-
-        if (cdmaDbm >= -75) levelDbm = SIGNAL_STRENGTH_GREAT;
-        else if (cdmaDbm >= -85) levelDbm = SIGNAL_STRENGTH_GOOD;
-        else if (cdmaDbm >= -95) levelDbm = SIGNAL_STRENGTH_MODERATE;
-        else if (cdmaDbm >= -100) levelDbm = SIGNAL_STRENGTH_POOR;
-        else levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-
-        // Ec/Io are in dB*10
-        if (cdmaEcio >= -90) levelEcio = SIGNAL_STRENGTH_GREAT;
-        else if (cdmaEcio >= -110) levelEcio = SIGNAL_STRENGTH_GOOD;
-        else if (cdmaEcio >= -130) levelEcio = SIGNAL_STRENGTH_MODERATE;
-        else if (cdmaEcio >= -150) levelEcio = SIGNAL_STRENGTH_POOR;
-        else levelEcio = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-
-        int level = (levelDbm < levelEcio) ? levelDbm : levelEcio;
-        if (DBG) log("getCdmaLevel=" + level);
-        return level;
+        return mCdma.getLevel();
     }
 
     /**
@@ -794,29 +442,7 @@
      */
     @UnsupportedAppUsage
     public int getCdmaAsuLevel() {
-        final int cdmaDbm = getCdmaDbm();
-        final int cdmaEcio = getCdmaEcio();
-        int cdmaAsuLevel;
-        int ecioAsuLevel;
-
-        if (cdmaDbm >= -75) cdmaAsuLevel = 16;
-        else if (cdmaDbm >= -82) cdmaAsuLevel = 8;
-        else if (cdmaDbm >= -90) cdmaAsuLevel = 4;
-        else if (cdmaDbm >= -95) cdmaAsuLevel = 2;
-        else if (cdmaDbm >= -100) cdmaAsuLevel = 1;
-        else cdmaAsuLevel = 99;
-
-        // Ec/Io are in dB*10
-        if (cdmaEcio >= -90) ecioAsuLevel = 16;
-        else if (cdmaEcio >= -100) ecioAsuLevel = 8;
-        else if (cdmaEcio >= -115) ecioAsuLevel = 4;
-        else if (cdmaEcio >= -130) ecioAsuLevel = 2;
-        else if (cdmaEcio >= -150) ecioAsuLevel = 1;
-        else ecioAsuLevel = 99;
-
-        int level = (cdmaAsuLevel < ecioAsuLevel) ? cdmaAsuLevel : ecioAsuLevel;
-        if (DBG) log("getCdmaAsuLevel=" + level);
-        return level;
+        return mCdma.getAsuLevel();
     }
 
     /**
@@ -826,26 +452,7 @@
      */
     @UnsupportedAppUsage
     public int getEvdoLevel() {
-        int evdoDbm = getEvdoDbm();
-        int evdoSnr = getEvdoSnr();
-        int levelEvdoDbm;
-        int levelEvdoSnr;
-
-        if (evdoDbm >= -65) levelEvdoDbm = SIGNAL_STRENGTH_GREAT;
-        else if (evdoDbm >= -75) levelEvdoDbm = SIGNAL_STRENGTH_GOOD;
-        else if (evdoDbm >= -90) levelEvdoDbm = SIGNAL_STRENGTH_MODERATE;
-        else if (evdoDbm >= -105) levelEvdoDbm = SIGNAL_STRENGTH_POOR;
-        else levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-
-        if (evdoSnr >= 7) levelEvdoSnr = SIGNAL_STRENGTH_GREAT;
-        else if (evdoSnr >= 5) levelEvdoSnr = SIGNAL_STRENGTH_GOOD;
-        else if (evdoSnr >= 3) levelEvdoSnr = SIGNAL_STRENGTH_MODERATE;
-        else if (evdoSnr >= 1) levelEvdoSnr = SIGNAL_STRENGTH_POOR;
-        else levelEvdoSnr = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-
-        int level = (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
-        if (DBG) log("getEvdoLevel=" + level);
-        return level;
+        return mCdma.getEvdoLevel();
     }
 
     /**
@@ -855,28 +462,7 @@
      */
     @UnsupportedAppUsage
     public int getEvdoAsuLevel() {
-        int evdoDbm = getEvdoDbm();
-        int evdoSnr = getEvdoSnr();
-        int levelEvdoDbm;
-        int levelEvdoSnr;
-
-        if (evdoDbm >= -65) levelEvdoDbm = 16;
-        else if (evdoDbm >= -75) levelEvdoDbm = 8;
-        else if (evdoDbm >= -85) levelEvdoDbm = 4;
-        else if (evdoDbm >= -95) levelEvdoDbm = 2;
-        else if (evdoDbm >= -105) levelEvdoDbm = 1;
-        else levelEvdoDbm = 99;
-
-        if (evdoSnr >= 7) levelEvdoSnr = 16;
-        else if (evdoSnr >= 6) levelEvdoSnr = 8;
-        else if (evdoSnr >= 5) levelEvdoSnr = 4;
-        else if (evdoSnr >= 3) levelEvdoSnr = 2;
-        else if (evdoSnr >= 1) levelEvdoSnr = 1;
-        else levelEvdoSnr = 99;
-
-        int level = (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
-        if (DBG) log("getEvdoAsuLevel=" + level);
-        return level;
+        return mCdma.getEvdoAsuLevel();
     }
 
     /**
@@ -886,7 +472,7 @@
      */
     @UnsupportedAppUsage
     public int getLteDbm() {
-        return mLteRsrp;
+        return mLte.getRsrp();
     }
 
     /**
@@ -896,83 +482,7 @@
      */
     @UnsupportedAppUsage
     public int getLteLevel() {
-        /*
-         * TS 36.214 Physical Layer Section 5.1.3
-         * TS 36.331 RRC
-         *
-         * RSSI = received signal + noise
-         * RSRP = reference signal dBm
-         * RSRQ = quality of signal dB = Number of Resource blocks*RSRP/RSSI
-         * SNR = gain = signal/noise ratio = -10log P1/P2 dB
-         */
-        int rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, rsrpIconLevel = -1, snrIconLevel = -1;
-
-        if (mLteRsrp > MAX_LTE_RSRP || mLteRsrp < MIN_LTE_RSRP) {
-            if (mLteRsrp != INVALID) {
-                Log.wtf(LOG_TAG, "getLteLevel - invalid lte rsrp: mLteRsrp=" + mLteRsrp);
-            }
-        } else if (mLteRsrp >= (mLteRsrpThresholds[3] - mLteRsrpBoost)) {
-            rsrpIconLevel = SIGNAL_STRENGTH_GREAT;
-        } else if (mLteRsrp >= (mLteRsrpThresholds[2] - mLteRsrpBoost)) {
-            rsrpIconLevel = SIGNAL_STRENGTH_GOOD;
-        } else if (mLteRsrp >= (mLteRsrpThresholds[1] - mLteRsrpBoost)) {
-            rsrpIconLevel = SIGNAL_STRENGTH_MODERATE;
-        } else if (mLteRsrp >= (mLteRsrpThresholds[0] - mLteRsrpBoost)) {
-            rsrpIconLevel = SIGNAL_STRENGTH_POOR;
-        } else {
-            rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-        }
-
-        if (useOnlyRsrpForLteLevel()) {
-            log("getLTELevel - rsrp = " + rsrpIconLevel);
-            if (rsrpIconLevel != -1) {
-                return rsrpIconLevel;
-            }
-        }
-
-        /*
-         * Values are -200 dB to +300 (SNR*10dB) RS_SNR >= 13.0 dB =>4 bars 4.5
-         * dB <= RS_SNR < 13.0 dB => 3 bars 1.0 dB <= RS_SNR < 4.5 dB => 2 bars
-         * -3.0 dB <= RS_SNR < 1.0 dB 1 bar RS_SNR < -3.0 dB/No Service Antenna
-         * Icon Only
-         */
-        if (mLteRssnr > 300) snrIconLevel = -1;
-        else if (mLteRssnr >= 130) snrIconLevel = SIGNAL_STRENGTH_GREAT;
-        else if (mLteRssnr >= 45) snrIconLevel = SIGNAL_STRENGTH_GOOD;
-        else if (mLteRssnr >= 10) snrIconLevel = SIGNAL_STRENGTH_MODERATE;
-        else if (mLteRssnr >= -30) snrIconLevel = SIGNAL_STRENGTH_POOR;
-        else if (mLteRssnr >= -200)
-            snrIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-
-        if (DBG) log("getLTELevel - rsrp:" + mLteRsrp + " snr:" + mLteRssnr + " rsrpIconLevel:"
-                + rsrpIconLevel + " snrIconLevel:" + snrIconLevel
-                + " lteRsrpBoost:" + mLteRsrpBoost);
-
-        /* Choose a measurement type to use for notification */
-        if (snrIconLevel != -1 && rsrpIconLevel != -1) {
-            /*
-             * The number of bars displayed shall be the smaller of the bars
-             * associated with LTE RSRP and the bars associated with the LTE
-             * RS_SNR
-             */
-            return (rsrpIconLevel < snrIconLevel ? rsrpIconLevel : snrIconLevel);
-        }
-
-        if (snrIconLevel != -1) return snrIconLevel;
-
-        if (rsrpIconLevel != -1) return rsrpIconLevel;
-
-        /* Valid values are (0-31, 99) as defined in TS 27.007 8.5 */
-        if (mLteSignalStrength > 31) rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-        else if (mLteSignalStrength >= 12) rssiIconLevel = SIGNAL_STRENGTH_GREAT;
-        else if (mLteSignalStrength >= 8) rssiIconLevel = SIGNAL_STRENGTH_GOOD;
-        else if (mLteSignalStrength >= 5) rssiIconLevel = SIGNAL_STRENGTH_MODERATE;
-        else if (mLteSignalStrength >= 0) rssiIconLevel = SIGNAL_STRENGTH_POOR;
-
-        if (DBG) log("getLteLevel - rssi:" + mLteSignalStrength + " rssiIconLevel:"
-                + rssiIconLevel);
-        return rssiIconLevel;
-
+        return mLte.getLevel();
     }
 
     /**
@@ -983,41 +493,14 @@
      */
     @UnsupportedAppUsage
     public int getLteAsuLevel() {
-        int lteAsuLevel = 99;
-        int lteDbm = getLteDbm();
-        /*
-         * 3GPP 27.007 (Ver 10.3.0) Sec 8.69
-         * 0   -140 dBm or less
-         * 1   -139 dBm
-         * 2...96  -138... -44 dBm
-         * 97  -43 dBm or greater
-         * 255 not known or not detectable
-         */
-        /*
-         * validateInput will always give a valid range between -140 t0 -44 as
-         * per ril.h. so RSRP >= -43 & <-140 will fall under asu level 255
-         * and not 97 or 0
-         */
-        if (lteDbm == SignalStrength.INVALID) lteAsuLevel = 255;
-        else lteAsuLevel = lteDbm + 140;
-        if (DBG) log("Lte Asu level: "+lteAsuLevel);
-        return lteAsuLevel;
+        return mLte.getAsuLevel();
     }
 
     /**
      * @return true if this is for GSM
      */
     public boolean isGsm() {
-        return this.mIsGsm;
-    }
-
-    /**
-     * @return true if it uses only RSRP for the number of LTE signal bar, otherwise false.
-     *
-     * @hide
-     */
-    public boolean useOnlyRsrpForLteLevel() {
-        return this.mUseOnlyRsrpForLteLevel;
+        return !(getPrimary() instanceof CellSignalStrengthCdma);
     }
 
     /**
@@ -1027,7 +510,7 @@
      */
     @UnsupportedAppUsage
     public int getTdScdmaDbm() {
-        return this.mTdScdmaRscp;
+        return mTdscdma.getRscp();
     }
 
     /**
@@ -1040,19 +523,7 @@
      */
     @UnsupportedAppUsage
     public int getTdScdmaLevel() {
-        final int tdScdmaDbm = getTdScdmaDbm();
-        int level;
-
-        if ((tdScdmaDbm > -25) || (tdScdmaDbm == SignalStrength.INVALID))
-                level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-        else if (tdScdmaDbm >= -49) level = SIGNAL_STRENGTH_GREAT;
-        else if (tdScdmaDbm >= -73) level = SIGNAL_STRENGTH_GOOD;
-        else if (tdScdmaDbm >= -97) level = SIGNAL_STRENGTH_MODERATE;
-        else if (tdScdmaDbm >= -110) level = SIGNAL_STRENGTH_POOR;
-        else level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-
-        if (DBG) log("getTdScdmaLevel = " + level);
-        return level;
+        return mTdscdma.getLevel();
      }
 
     /**
@@ -1062,13 +533,7 @@
      */
     @UnsupportedAppUsage
     public int getTdScdmaAsuLevel() {
-        final int tdScdmaDbm = getTdScdmaDbm();
-        int tdScdmaAsuLevel;
-
-        if (tdScdmaDbm == INVALID) tdScdmaAsuLevel = 255;
-        else tdScdmaAsuLevel = tdScdmaDbm + 120;
-        if (DBG) log("TD-SCDMA Asu level: " + tdScdmaAsuLevel);
-        return tdScdmaAsuLevel;
+        return mTdscdma.getAsuLevel();
     }
 
     /**
@@ -1077,7 +542,7 @@
      * @hide
      */
     public int getWcdmaRscp() {
-        return mWcdmaRscp;
+        return mWcdma.getRscp();
     }
 
     /**
@@ -1094,14 +559,7 @@
          * 96     -24 dBm or greater
          * 255    not known or not detectable
          */
-        final int wcdmaDbm = getWcdmaDbm();
-        int wcdmaAsuLevel = 255;
-        // validateInput will always give a valid range between -120 to -24 as per ril.h. so RSCP
-        // outside range is already set to INVALID
-        if (wcdmaDbm == SignalStrength.INVALID) wcdmaAsuLevel =  255;
-        else wcdmaAsuLevel = wcdmaDbm + 120;
-        if (DBG) log("Wcdma Asu level: " + wcdmaAsuLevel);
-        return wcdmaAsuLevel;
+        return mWcdma.getAsuLevel();
     }
 
     /**
@@ -1110,7 +568,7 @@
      * @hide
      */
     public int getWcdmaDbm() {
-        return mWcdmaRscp;
+        return mWcdma.getDbm();
     }
 
     /**
@@ -1119,55 +577,7 @@
      * @hide
      */
     public int getWcdmaLevel() {
-        int level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-
-        if (mWcdmaDefaultSignalMeasurement == null) {
-            Log.wtf(LOG_TAG, "getWcdmaLevel - WCDMA default signal measurement is invalid.");
-            return level;
-        }
-
-        switch (mWcdmaDefaultSignalMeasurement) {
-            case MEASUMENT_TYPE_RSCP:
-                // RSCP valid values are (-120 through -24) as defined in TS 27.007 8.69
-                if (mWcdmaRscp < MIN_WCDMA_RSCP || mWcdmaRscp > MAX_WCDMA_RSCP) {
-                    if (mWcdmaRscp != INVALID) {
-                        Log.wtf(LOG_TAG, "getWcdmaLevel - invalid WCDMA RSCP: mWcdmaRscp="
-                                + mWcdmaRscp);
-                    }
-                } else if (mWcdmaRscp >= mWcdmaRscpThresholds[3]) {
-                    level = SIGNAL_STRENGTH_GREAT;
-                } else if (mWcdmaRscp >= mWcdmaRscpThresholds[2]) {
-                    level = SIGNAL_STRENGTH_GOOD;
-                } else if (mWcdmaRscp >= mWcdmaRscpThresholds[1]) {
-                    level = SIGNAL_STRENGTH_MODERATE;
-                } else if (mWcdmaRscp >= mWcdmaRscpThresholds[0]) {
-                    level = SIGNAL_STRENGTH_POOR;
-                }
-                if (DBG) log("getWcdmaLevel=" + level + " WcdmaRscp=" + mWcdmaRscp);
-                break;
-
-            default:
-                // RSSI valid values are (0..31) as defined in TS 27.007 8.5
-                if (mWcdmaSignalStrength < 0 || mWcdmaSignalStrength > 31) {
-                    if (mWcdmaSignalStrength != 99) {
-                        Log.wtf(LOG_TAG, "getWcdmaLevel - invalid WCDMA RSSI: mWcdmaSignalStrength="
-                                + mWcdmaSignalStrength);
-                    }
-                } else if (mWcdmaSignalStrength >= 18) {
-                    level = SIGNAL_STRENGTH_GREAT;
-                } else if (mWcdmaSignalStrength >= 13) {
-                    level = SIGNAL_STRENGTH_GOOD;
-                } else if (mWcdmaSignalStrength >= 8) {
-                    level = SIGNAL_STRENGTH_MODERATE;
-                } else if (mWcdmaSignalStrength >= 3) {
-                    level = SIGNAL_STRENGTH_POOR;
-                }
-                if (DBG) log("getWcdmaLevel=" + level + " WcdmaSignalStrength=" +
-                        mWcdmaSignalStrength);
-                break;
-
-        }
-        return level;
+        return mWcdma.getLevel();
     }
 
    /**
@@ -1175,18 +585,7 @@
      */
     @Override
     public int hashCode() {
-        int primeNum = 31;
-        return ((mGsmSignalStrength * primeNum)
-                + (mGsmBitErrorRate * primeNum)
-                + (mCdmaDbm * primeNum) + (mCdmaEcio * primeNum)
-                + (mEvdoDbm * primeNum) + (mEvdoEcio * primeNum) + (mEvdoSnr * primeNum)
-                + (mLteSignalStrength * primeNum) + (mLteRsrp * primeNum)
-                + (mLteRsrq * primeNum) + (mLteRssnr * primeNum) + (mLteCqi * primeNum)
-                + (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum)
-                + (mWcdmaSignalStrength * primeNum) + (mWcdmaRscpAsu * primeNum)
-                + (mWcdmaRscp * primeNum) + (mIsGsm ? 1 : 0) + (mUseOnlyRsrpForLteLevel ? 1 : 0)
-                + (Objects.hashCode(mWcdmaDefaultSignalMeasurement))
-                + (Arrays.hashCode(mLteRsrpThresholds)) + (Arrays.hashCode(mWcdmaRscpThresholds)));
+        return Objects.hash(mCdma, mGsm, mWcdma, mTdscdma, mLte, mLteRsrpBoost);
     }
 
     /**
@@ -1194,40 +593,16 @@
      */
     @Override
     public boolean equals (Object o) {
-        SignalStrength s;
+        if (!(o instanceof SignalStrength)) return false;
 
-        try {
-            s = (SignalStrength) o;
-        } catch (ClassCastException ex) {
-            return false;
-        }
+        SignalStrength s = (SignalStrength) o;
 
-        if (o == null) {
-            return false;
-        }
-
-        return (mGsmSignalStrength == s.mGsmSignalStrength
-                && mGsmBitErrorRate == s.mGsmBitErrorRate
-                && mCdmaDbm == s.mCdmaDbm
-                && mCdmaEcio == s.mCdmaEcio
-                && mEvdoDbm == s.mEvdoDbm
-                && mEvdoEcio == s.mEvdoEcio
-                && mEvdoSnr == s.mEvdoSnr
-                && mLteSignalStrength == s.mLteSignalStrength
-                && mLteRsrp == s.mLteRsrp
-                && mLteRsrq == s.mLteRsrq
-                && mLteRssnr == s.mLteRssnr
-                && mLteCqi == s.mLteCqi
-                && mLteRsrpBoost == s.mLteRsrpBoost
-                && mTdScdmaRscp == s.mTdScdmaRscp
-                && mWcdmaSignalStrength == s.mWcdmaSignalStrength
-                && mWcdmaRscpAsu == s.mWcdmaRscpAsu
-                && mWcdmaRscp == s.mWcdmaRscp
-                && mIsGsm == s.mIsGsm
-                && mUseOnlyRsrpForLteLevel == s.mUseOnlyRsrpForLteLevel
-                && Objects.equals(mWcdmaDefaultSignalMeasurement, s.mWcdmaDefaultSignalMeasurement)
-                && Arrays.equals(mLteRsrpThresholds, s.mLteRsrpThresholds)
-                && Arrays.equals(mWcdmaRscpThresholds, s.mWcdmaRscpThresholds));
+        return mCdma.equals(s.mCdma)
+            && mGsm.equals(s.mGsm)
+            && mWcdma.equals(s.mWcdma)
+            && mTdscdma.equals(s.mTdscdma)
+            && mLte.equals(s.mLte)
+            && mLteRsrpBoost == s.mLteRsrpBoost;
     }
 
     /**
@@ -1235,63 +610,16 @@
      */
     @Override
     public String toString() {
-        return ("SignalStrength:"
-                + " " + mGsmSignalStrength
-                + " " + mGsmBitErrorRate
-                + " " + mCdmaDbm
-                + " " + mCdmaEcio
-                + " " + mEvdoDbm
-                + " " + mEvdoEcio
-                + " " + mEvdoSnr
-                + " " + mLteSignalStrength
-                + " " + mLteRsrp
-                + " " + mLteRsrq
-                + " " + mLteRssnr
-                + " " + mLteCqi
-                + " " + mLteRsrpBoost
-                + " " + mTdScdmaRscp
-                + " " + mWcdmaSignalStrength
-                + " " + mWcdmaRscpAsu
-                + " " + mWcdmaRscp
-                + " " + (mIsGsm ? "gsm|lte" : "cdma")
-                + " " + (mUseOnlyRsrpForLteLevel ? "use_only_rsrp_for_lte_level" :
-                         "use_rsrp_and_rssnr_for_lte_level")
-                + " " + mWcdmaDefaultSignalMeasurement
-                + " " + (Arrays.toString(mLteRsrpThresholds))
-                + " " + (Arrays.toString(mWcdmaRscpThresholds)));
-    }
-
-    /** Returns the signal strength related to GSM. */
-    private int getGsmRelatedSignalStrength() {
-        int level = getLteLevel();
-        if (level == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
-            level = getTdScdmaLevel();
-            if (level == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
-                level = getWcdmaLevel();
-                if (level == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
-                    level = getGsmLevel();
-                }
-            }
-        }
-        return level;
-    }
-
-    /** Returns the signal strength related to CDMA. */
-    private int getCdmaRelatedSignalStrength() {
-        int level;
-        int cdmaLevel = getCdmaLevel();
-        int evdoLevel = getEvdoLevel();
-        if (evdoLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
-            /* We don't know evdo, use cdma */
-            level = cdmaLevel;
-        } else if (cdmaLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
-            /* We don't know cdma, use evdo */
-            level = evdoLevel;
-        } else {
-            /* We know both, use the lowest level */
-            level = cdmaLevel < evdoLevel ? cdmaLevel : evdoLevel;
-        }
-        return level;
+        return new StringBuilder().append("SignalStrength:{")
+            .append("mCdma=").append(mCdma)
+            .append(",mGsm=").append(mGsm)
+            .append(",mWcdma=").append(mWcdma)
+            .append(",mTdscdma=").append(mTdscdma)
+            .append(",mLte=").append(mLte)
+            .append(",mLteRsrpBoost=").append(mLteRsrpBoost)
+            .append(",primary=").append(getPrimary().getClass().getSimpleName())
+            .append("}")
+            .toString();
     }
 
     /**
@@ -1302,34 +630,13 @@
      */
     @UnsupportedAppUsage
     private void setFromNotifierBundle(Bundle m) {
-        mGsmSignalStrength = m.getInt("GsmSignalStrength");
-        mGsmBitErrorRate = m.getInt("GsmBitErrorRate");
-        mCdmaDbm = m.getInt("CdmaDbm");
-        mCdmaEcio = m.getInt("CdmaEcio");
-        mEvdoDbm = m.getInt("EvdoDbm");
-        mEvdoEcio = m.getInt("EvdoEcio");
-        mEvdoSnr = m.getInt("EvdoSnr");
-        mLteSignalStrength = m.getInt("LteSignalStrength");
-        mLteRsrp = m.getInt("LteRsrp");
-        mLteRsrq = m.getInt("LteRsrq");
-        mLteRssnr = m.getInt("LteRssnr");
-        mLteCqi = m.getInt("LteCqi");
+        mCdma = m.getParcelable("Cdma");
+        mGsm = m.getParcelable("Gsm");
+        mWcdma = m.getParcelable("Wcdma");
+        mTdscdma = m.getParcelable("Tdscdma");
+        mLte = m.getParcelable("Lte");
+
         mLteRsrpBoost = m.getInt("LteRsrpBoost");
-        mTdScdmaRscp = m.getInt("TdScdma");
-        mWcdmaSignalStrength = m.getInt("WcdmaSignalStrength");
-        mWcdmaRscpAsu = m.getInt("WcdmaRscpAsu");
-        mWcdmaRscp = m.getInt("WcdmaRscp");
-        mIsGsm = m.getBoolean("IsGsm");
-        mUseOnlyRsrpForLteLevel = m.getBoolean("UseOnlyRsrpForLteLevel");
-        mWcdmaDefaultSignalMeasurement = m.getString("WcdmaDefaultSignalMeasurement");
-        ArrayList<Integer> lteRsrpThresholds = m.getIntegerArrayList("lteRsrpThresholds");
-        for (int i = 0; i < lteRsrpThresholds.size(); i++) {
-            mLteRsrpThresholds[i] = lteRsrpThresholds.get(i);
-        }
-        ArrayList<Integer> wcdmaRscpThresholds = m.getIntegerArrayList("wcdmaRscpThresholds");
-        for (int i = 0; i < wcdmaRscpThresholds.size(); i++) {
-            mWcdmaRscpThresholds[i] = wcdmaRscpThresholds.get(i);
-        }
     }
 
     /**
@@ -1340,56 +647,13 @@
      */
     @UnsupportedAppUsage
     public void fillInNotifierBundle(Bundle m) {
-        m.putInt("GsmSignalStrength", mGsmSignalStrength);
-        m.putInt("GsmBitErrorRate", mGsmBitErrorRate);
-        m.putInt("CdmaDbm", mCdmaDbm);
-        m.putInt("CdmaEcio", mCdmaEcio);
-        m.putInt("EvdoDbm", mEvdoDbm);
-        m.putInt("EvdoEcio", mEvdoEcio);
-        m.putInt("EvdoSnr", mEvdoSnr);
-        m.putInt("LteSignalStrength", mLteSignalStrength);
-        m.putInt("LteRsrp", mLteRsrp);
-        m.putInt("LteRsrq", mLteRsrq);
-        m.putInt("LteRssnr", mLteRssnr);
-        m.putInt("LteCqi", mLteCqi);
+        m.putParcelable("Cdma", mCdma);
+        m.putParcelable("Gsm", mGsm);
+        m.putParcelable("Wcdma", mWcdma);
+        m.putParcelable("Tdscdma", mTdscdma);
+        m.putParcelable("Lte", mLte);
+
         m.putInt("LteRsrpBoost", mLteRsrpBoost);
-        m.putInt("TdScdma", mTdScdmaRscp);
-        m.putInt("WcdmaSignalStrength", mWcdmaSignalStrength);
-        m.putInt("WcdmaRscpAsu", mWcdmaRscpAsu);
-        m.putInt("WcdmaRscp", mWcdmaRscp);
-        m.putBoolean("IsGsm", mIsGsm);
-        m.putBoolean("UseOnlyRsrpForLteLevel", mUseOnlyRsrpForLteLevel);
-        m.putString("WcdmaDefaultSignalMeasurement", mWcdmaDefaultSignalMeasurement);
-        ArrayList<Integer> lteRsrpThresholds = new ArrayList<Integer>();
-        for (int value : mLteRsrpThresholds) {
-            lteRsrpThresholds.add(value);
-        }
-        m.putIntegerArrayList("lteRsrpThresholds", lteRsrpThresholds);
-        ArrayList<Integer> wcdmaRscpThresholds = new ArrayList<Integer>();
-        for (int value : mWcdmaRscpThresholds) {
-            wcdmaRscpThresholds.add(value);
-        }
-        m.putIntegerArrayList("wcdmaRscpThresholds", wcdmaRscpThresholds);
-    }
-
-    /**
-     * Gets the default threshold array for determining the display level of LTE signal bar.
-     *
-     * @return int array for determining the display level.
-     */
-    private int[] getDefaultLteRsrpThresholds() {
-        return CarrierConfigManager.getDefaultConfig().getIntArray(
-                CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY);
-    }
-
-    /**
-     * Gets the default threshold array for determining the display level of WCDMA signal bar.
-     *
-     * @return int array for determining the display level.
-     */
-    private int[] getDefaultWcdmaRscpThresholds() {
-        return CarrierConfigManager.getDefaultConfig().getIntArray(
-                CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY);
     }
 
     /**
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 1b37bad..1378bb0 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -28,6 +28,7 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.BaseBundle;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -89,7 +90,7 @@
             new ArrayMap<Integer, SmsManager>();
 
     /** A concrete subscription id, or the pseudo DEFAULT_SUBSCRIPTION_ID */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mSubId;
 
     /*
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index dacc5d8..a1e8b19 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -80,6 +80,12 @@
     private CharSequence mCarrierName;
 
     /**
+     * The subscription carrier id.
+     * @see TelephonyManager#getSimCarrierId()
+     */
+    private int mCarrierId;
+
+    /**
      * The source of the name, NAME_SOURCE_UNDEFINED, NAME_SOURCE_DEFAULT_SOURCE,
      * NAME_SOURCE_SIM_SOURCE or NAME_SOURCE_USER_INPUT.
      */
@@ -132,10 +138,15 @@
     private UiccAccessRule[] mAccessRules;
 
     /**
-     * The ID of the SIM card. It is the ICCID of the active profile for a UICC card and the EID
-     * for an eUICC card.
+     * The string ID of the SIM card. It is the ICCID of the active profile for a UICC card and the
+     * EID for an eUICC card.
      */
-    private String mCardId;
+    private String mCardString;
+
+    /**
+     * The card ID of the SIM card. This maps uniquely to the card string.
+     */
+    private int mCardId;
 
     /**
      * Whether the subscription is opportunistic.
@@ -168,10 +179,10 @@
     public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
             CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
             Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
-            @Nullable UiccAccessRule[] accessRules, String cardId) {
+            @Nullable UiccAccessRule[] accessRules, String cardString) {
         this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
-                roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardId,
-                false, null, true);
+                roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString,
+                false, null, true, TelephonyManager.UNKNOWN_CARRIER_ID);
     }
 
     /**
@@ -180,20 +191,22 @@
     public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
             CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
             Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
-            @Nullable UiccAccessRule[] accessRules, String cardId, boolean isOpportunistic,
-            @Nullable String groupUUID, boolean isMetered) {
+            @Nullable UiccAccessRule[] accessRules, String cardString, boolean isOpportunistic,
+            @Nullable String groupUUID, boolean isMetered, int carrierId) {
         this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
-                roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardId,
-                isOpportunistic, groupUUID, isMetered, false);
+                roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString, -1,
+                isOpportunistic, groupUUID, isMetered, false, carrierId);
     }
+
     /**
      * @hide
      */
     public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
             CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
             Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
-            @Nullable UiccAccessRule[] accessRules, String cardId, boolean isOpportunistic,
-            @Nullable String groupUUID, boolean isMetered, boolean isGroupDisabled) {
+            @Nullable UiccAccessRule[] accessRules, String cardString, int cardId,
+            boolean isOpportunistic, @Nullable String groupUUID, boolean isMetered,
+            boolean isGroupDisabled, int carrierid) {
         this.mId = id;
         this.mIccId = iccId;
         this.mSimSlotIndex = simSlotIndex;
@@ -209,11 +222,13 @@
         this.mCountryIso = countryIso;
         this.mIsEmbedded = isEmbedded;
         this.mAccessRules = accessRules;
+        this.mCardString = cardString;
         this.mCardId = cardId;
         this.mIsOpportunistic = isOpportunistic;
         this.mGroupUUID = groupUUID;
         this.mIsMetered = isMetered;
         this.mIsGroupDisabled = isGroupDisabled;
+        this.mCarrierId = carrierid;
     }
 
 
@@ -239,6 +254,14 @@
     }
 
     /**
+     * @return the carrier id of this Subscription carrier.
+     * @see TelephonyManager#getSimCarrierId()
+     */
+    public int getCarrierId() {
+        return this.mCarrierId;
+    }
+
+    /**
      * @return the name displayed to the user that identifies this subscription
      */
     public CharSequence getDisplayName() {
@@ -508,10 +531,21 @@
     }
 
     /**
-     * @return the ID of the SIM card which contains the subscription.
+     * @return the card string of the SIM card which contains the subscription. The card string is
+     * the ICCID for UICCs or the EID for eUICCs.
+     * @hide
+     * //TODO rename usages in LPA: UiccSlotUtil.java, UiccSlotsManager.java, UiccSlotInfoTest.java
+     */
+    public String getCardString() {
+        return this.mCardString;
+    }
+
+    /**
+     * @return the cardId of the SIM card which contains the subscription.
      * @hide
      */
-    public String getCardId() {
+    @SystemApi
+    public int getCardId() {
         return this.mCardId;
     }
 
@@ -549,16 +583,18 @@
             Bitmap iconBitmap = Bitmap.CREATOR.createFromParcel(source);
             boolean isEmbedded = source.readBoolean();
             UiccAccessRule[] accessRules = source.createTypedArray(UiccAccessRule.CREATOR);
-            String cardId = source.readString();
+            String cardString = source.readString();
+            int cardId = source.readInt();
             boolean isOpportunistic = source.readBoolean();
             String groupUUID = source.readString();
             boolean isMetered = source.readBoolean();
             boolean isGroupDisabled = source.readBoolean();
+            int carrierid = source.readInt();
 
             return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
                     nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso,
-                    isEmbedded, accessRules, cardId, isOpportunistic, groupUUID, isMetered,
-                    isGroupDisabled);
+                    isEmbedded, accessRules, cardString, cardId, isOpportunistic, groupUUID,
+                    isMetered, isGroupDisabled, carrierid);
         }
 
         @Override
@@ -584,11 +620,13 @@
         mIconBitmap.writeToParcel(dest, flags);
         dest.writeBoolean(mIsEmbedded);
         dest.writeTypedArray(mAccessRules, flags);
-        dest.writeString(mCardId);
+        dest.writeString(mCardString);
+        dest.writeInt(mCardId);
         dest.writeBoolean(mIsOpportunistic);
         dest.writeString(mGroupUUID);
         dest.writeBoolean(mIsMetered);
         dest.writeBoolean(mIsGroupDisabled);
+        dest.writeInt(mCarrierId);
     }
 
     @Override
@@ -614,23 +652,25 @@
     @Override
     public String toString() {
         String iccIdToPrint = givePrintableIccid(mIccId);
-        String cardIdToPrint = givePrintableIccid(mCardId);
+        String cardStringToPrint = givePrintableIccid(mCardString);
         return "{id=" + mId + ", iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex
-                + " displayName=" + mDisplayName + " carrierName=" + mCarrierName
-                + " nameSource=" + mNameSource + " iconTint=" + mIconTint + " mNumber=" + mNumber
+                + " carrierId=" + mCarrierId + " displayName=" + mDisplayName
+                + " carrierName=" + mCarrierName + " nameSource=" + mNameSource
+                + " iconTint=" + mIconTint + " mNumber=" + mNumber
                 + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
                 + " mnc " + mMnc + "mCountryIso=" + mCountryIso + " isEmbedded " + mIsEmbedded
                 + " accessRules " + Arrays.toString(mAccessRules)
-                + " cardId=" + cardIdToPrint + " isOpportunistic " + mIsOpportunistic
-                + " mGroupUUID=" + mGroupUUID + " isMetered=" + mIsMetered
-                + " mIsGroupDisabled=" + mIsGroupDisabled + "}";
+                + " cardString=" + cardStringToPrint + " cardId=" + mCardId
+                + " isOpportunistic " + mIsOpportunistic + " mGroupUUID=" + mGroupUUID
+                + " isMetered=" + mIsMetered + " mIsGroupDisabled=" + mIsGroupDisabled + "}";
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
                 mIsOpportunistic, mGroupUUID, mIsMetered, mIccId, mNumber, mMcc, mMnc,
-                mCountryIso, mCardId, mDisplayName, mCarrierName, mAccessRules, mIsGroupDisabled);
+                mCountryIso, mCardString, mCardId, mDisplayName, mCarrierName, mAccessRules,
+                mIsGroupDisabled, mCarrierId);
     }
 
     @Override
@@ -653,13 +693,15 @@
                 && mIsEmbedded == toCompare.mIsEmbedded
                 && mIsOpportunistic == toCompare.mIsOpportunistic
                 && mIsGroupDisabled == toCompare.mIsGroupDisabled
-                && Objects.equals(mGroupUUID, toCompare.mGroupUUID)
+                && mCarrierId == toCompare.mCarrierId
                 && mIsMetered == toCompare.mIsMetered
+                && Objects.equals(mGroupUUID, toCompare.mGroupUUID)
                 && Objects.equals(mIccId, toCompare.mIccId)
                 && Objects.equals(mNumber, toCompare.mNumber)
                 && Objects.equals(mMcc, toCompare.mMcc)
                 && Objects.equals(mMnc, toCompare.mMnc)
                 && Objects.equals(mCountryIso, toCompare.mCountryIso)
+                && Objects.equals(mCardString, toCompare.mCardString)
                 && Objects.equals(mCardId, toCompare.mCardId)
                 && TextUtils.equals(mDisplayName, toCompare.mDisplayName)
                 && TextUtils.equals(mCarrierName, toCompare.mCarrierName)
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 387453f..2c712a1 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -382,6 +382,14 @@
     public static final int SIM_PROVISIONED = 0;
 
     /**
+     * TelephonyProvider column name for subscription carrier id.
+     * @see TelephonyManager#getSimCarrierId()
+     * <p>Type: INTEGER (int) </p>
+     * @hide
+     */
+    public static final String CARRIER_ID = "carrier_id";
+
+    /**
      * TelephonyProvider column name for the MCC associated with a SIM, stored as a string.
      * <P>Type: TEXT (String)</P>
      * @hide
@@ -572,7 +580,6 @@
      * TelephonyProvider column name for whether a subscription is opportunistic, that is,
      * whether the network it connects to is limited in functionality or coverage.
      * For example, CBRS.
-     * IS_EMBEDDED should always be true.
      * <p>Type: INTEGER (int), 1 for opportunistic or 0 for non-opportunistic.
      * @hide
      */
@@ -1191,7 +1198,8 @@
     }
 
     /**
-     * Request a refresh of the platform cache of profile information.
+     * Request a refresh of the platform cache of profile information for the eUICC which
+     * corresponds to the card ID returned by {@link TelephonyManager#getCardIdForDefaultEuicc()}.
      *
      * <p>Should be called by the EuiccService implementation whenever this information changes due
      * to an operation done outside the scope of a request initiated by the platform to the
@@ -1199,17 +1207,50 @@
      * were made through the EuiccService.
      *
      * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+     *
+     * @see {@link TelephonyManager#getCardIdForDefaultEuicc()} for more information on the card ID.
+     *
      * @hide
      */
     @SystemApi
     public void requestEmbeddedSubscriptionInfoListRefresh() {
+        int cardId = TelephonyManager.from(mContext).getCardIdForDefaultEuicc();
         try {
             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
             if (iSub != null) {
-                iSub.requestEmbeddedSubscriptionInfoListRefresh();
+                iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId);
             }
         } catch (RemoteException ex) {
-            // ignore it
+            logd("requestEmbeddedSubscriptionInfoListFresh for card = " + cardId + " failed.");
+        }
+    }
+
+    /**
+     * Request a refresh of the platform cache of profile information for the eUICC with the given
+     * {@code cardId}.
+     *
+     * <p>Should be called by the EuiccService implementation whenever this information changes due
+     * to an operation done outside the scope of a request initiated by the platform to the
+     * EuiccService. There is no need to refresh for downloads, deletes, or other operations that
+     * were made through the EuiccService.
+     *
+     * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+     *
+     * @param cardId the card ID of the eUICC.
+     *
+     * @see {@link TelephonyManager#getCardIdForDefaultEuicc()} for more information on the card ID.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void requestEmbeddedSubscriptionInfoListRefresh(int cardId) {
+        try {
+            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            if (iSub != null) {
+                iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId);
+            }
+        } catch (RemoteException ex) {
+            logd("requestEmbeddedSubscriptionInfoListFresh for card = " + cardId + " failed.");
         }
     }
 
@@ -2379,18 +2420,32 @@
     }
 
     /**
-     * Set opportunistic by simInfo index
+     * Set whether a subscription is opportunistic, that is, whether the network it connects
+     * to has limited coverage. For example, CBRS. Setting a subscription opportunistic has
+     * following impacts:
+     *  1) Even if it's active, it will be dormant most of the time. The modem will not try
+     *     to scan or camp until it knows an available network is nearby to save power.
+     *  2) Telephony relies on system app or carrier input to notify nearby available networks.
+     *     See {@link TelephonyManager#updateAvailableNetworks(List)} for more information.
+     *  3) In multi-SIM devices, when the network is nearby and camped, system may automatically
+     *     switch internet data between it and default data subscription, based on carrier
+     *     recommendation and its signal strength and metered-ness, etc.
+     *
+     *
+     * Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier
+     * privilege permission of the subscription.
      *
      * @param opportunistic whether it’s opportunistic subscription.
      * @param subId the unique SubscriptionInfo index in database
-     * @return the number of records updated
-     * @hide
+     * @return {@code true} if the operation is succeed, {@code false} otherwise.
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public int setOpportunistic(boolean opportunistic, int subId) {
+    public boolean setOpportunistic(boolean opportunistic, int subId) {
         if (VDBG) logd("[setOpportunistic]+ opportunistic:" + opportunistic + " subId:" + subId);
         return setSubscriptionPropertyHelper(subId, "setOpportunistic",
-                (iSub)-> iSub.setOpportunistic(opportunistic, subId));
+                (iSub)-> iSub.setOpportunistic(
+                        opportunistic, subId, mContext.getOpPackageName())) == 1;
     }
 
     /**
@@ -2510,18 +2565,26 @@
     }
 
     /**
-     * Set metered by simInfo index
+     * Set if a subscription is metered or not. Similar to Wi-Fi, metered means
+     * user may be charged more if more data is used.
+     *
+     * By default all Cellular networks are considered metered. System or carrier privileged apps
+     * can set a subscription un-metered which will be considered when system switches data between
+     * primary subscription and opportunistic subscription.
+     *
+     * Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier
+     * privilege permission of the subscription.
      *
      * @param isMetered whether it’s a metered subscription.
      * @param subId the unique SubscriptionInfo index in database
-     * @return the number of records updated
-     * @hide
+     * @return {@code true} if the operation is succeed, {@code false} otherwise.
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public int setMetered(boolean isMetered, int subId) {
+    public boolean setMetered(boolean isMetered, int subId) {
         if (VDBG) logd("[setIsMetered]+ isMetered:" + isMetered + " subId:" + subId);
         return setSubscriptionPropertyHelper(subId, "setIsMetered",
-                (iSub)-> iSub.setMetered(isMetered, subId));
+                (iSub)-> iSub.setMetered(isMetered, subId, mContext.getOpPackageName())) == 1;
     }
 
     /**
@@ -2554,8 +2617,7 @@
         if (availableList == null) {
             return null;
         } else {
-            return getAvailableSubscriptionInfoList().stream()
-                    .filter(subInfo -> !shouldHideSubscription(subInfo))
+            return availableList.stream().filter(subInfo -> !shouldHideSubscription(subInfo))
                     .collect(Collectors.toList());
         }
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 11b6674..8dfdb2f 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -625,8 +625,6 @@
      * The {@link #EXTRA_RINGING_CALL_STATE} extra indicates the ringing call state.
      * The {@link #EXTRA_FOREGROUND_CALL_STATE} extra indicates the foreground call state.
      * The {@link #EXTRA_BACKGROUND_CALL_STATE} extra indicates the background call state.
-     * The {@link #EXTRA_DISCONNECT_CAUSE} extra indicates the disconnect cause.
-     * The {@link #EXTRA_PRECISE_DISCONNECT_CAUSE} extra indicates the precise disconnect cause.
      *
      * <p class="note">
      * Requires the READ_PRECISE_PHONE_STATE permission.
@@ -634,12 +632,10 @@
      * @see #EXTRA_RINGING_CALL_STATE
      * @see #EXTRA_FOREGROUND_CALL_STATE
      * @see #EXTRA_BACKGROUND_CALL_STATE
-     * @see #EXTRA_DISCONNECT_CAUSE
-     * @see #EXTRA_PRECISE_DISCONNECT_CAUSE
      *
      * <p class="note">
      * Requires the READ_PRECISE_PHONE_STATE permission.
-     *
+     * @deprecated use {@link PhoneStateListener#LISTEN_PRECISE_CALL_STATE} instead
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@@ -647,8 +643,28 @@
             "android.intent.action.PRECISE_CALL_STATE";
 
     /**
-     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
-     * for an integer containing the state of the current ringing call.
+     * Broadcast intent action indicating that call disconnect cause has changed.
+     *
+     * <p>
+     * The {@link #EXTRA_DISCONNECT_CAUSE} extra indicates the disconnect cause.
+     * The {@link #EXTRA_PRECISE_DISCONNECT_CAUSE} extra indicates the precise disconnect cause.
+     *
+     * <p class="note">
+     * Requires the READ_PRECISE_PHONE_STATE permission.
+     *
+     * @see #EXTRA_DISCONNECT_CAUSE
+     * @see #EXTRA_PRECISE_DISCONNECT_CAUSE
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CALL_DISCONNECT_CAUSE_CHANGED =
+            "android.intent.action.CALL_DISCONNECT_CAUSE";
+
+    /**
+     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
+     * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
+     * containing the state of the current ringing call.
      *
      * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
      * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
@@ -670,8 +686,9 @@
     public static final String EXTRA_RINGING_CALL_STATE = "ringing_state";
 
     /**
-     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
-     * for an integer containing the state of the current foreground call.
+     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
+     * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
+     * containing the state of the current foreground call.
      *
      * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
      * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
@@ -693,8 +710,9 @@
     public static final String EXTRA_FOREGROUND_CALL_STATE = "foreground_state";
 
     /**
-     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
-     * for an integer containing the state of the current background call.
+     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
+     * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
+     * containing the state of the current background call.
      *
      * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
      * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
@@ -716,8 +734,9 @@
     public static final String EXTRA_BACKGROUND_CALL_STATE = "background_state";
 
     /**
-     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
-     * for an integer containing the disconnect cause.
+     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
+     * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
+     * containing the disconnect cause.
      *
      * @see DisconnectCause
      *
@@ -730,8 +749,9 @@
     public static final String EXTRA_DISCONNECT_CAUSE = "disconnect_cause";
 
     /**
-     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
-     * for an integer containing the disconnect cause provided by the RIL.
+     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
+     * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
+     * containing the disconnect cause provided by the RIL.
      *
      * @see PreciseDisconnectCause
      *
@@ -752,7 +772,6 @@
      * The {@link #EXTRA_DATA_NETWORK_TYPE} extra indicates the connection network type.
      * The {@link #EXTRA_DATA_APN_TYPE} extra indicates the APN type.
      * The {@link #EXTRA_DATA_APN} extra indicates the APN.
-     * The {@link #EXTRA_DATA_CHANGE_REASON} extra indicates the connection change reason.
      * The {@link #EXTRA_DATA_IFACE_PROPERTIES} extra indicates the connection interface.
      * The {@link #EXTRA_DATA_FAILURE_CAUSE} extra indicates the connection fail cause.
      *
@@ -763,7 +782,6 @@
      * @see #EXTRA_DATA_NETWORK_TYPE
      * @see #EXTRA_DATA_APN_TYPE
      * @see #EXTRA_DATA_APN
-     * @see #EXTRA_DATA_CHANGE_REASON
      * @see #EXTRA_DATA_IFACE
      * @see #EXTRA_DATA_FAILURE_CAUSE
      * @hide
@@ -853,18 +871,6 @@
 
     /**
      * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
-     * for an String representation of the change reason.
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getStringExtra(String name)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_DATA_CHANGE_REASON = PhoneConstants.STATE_CHANGE_REASON_KEY;
-
-    /**
-     * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
      * for an String representation of the data interface.
      *
      * <p class="note">
@@ -3176,6 +3182,29 @@
     }
 
     /**
+     * Gets information about currently inserted UICCs and eUICCs. See {@link UiccCardInfo} for more
+     * details on the kind of information available.
+     *
+     * @return UiccCardInfo an array of UiccCardInfo objects, representing information on the
+     * currently inserted UICCs and eUICCs.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public UiccCardInfo[] getUiccCardsInfo() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                return null;
+            }
+            return telephony.getUiccCardsInfo();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
      * Gets all the UICC slots. The objects in the array can be null if the slot info is not
      * available, which is possible between phone process starting and getting slot info from modem.
      *
@@ -4888,7 +4917,7 @@
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
     public void requestCellInfoUpdate(
-            @NonNull Executor executor, @NonNull CellInfoCallback callback) {
+            @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony == null) return;
@@ -6174,196 +6203,258 @@
             NETWORK_MODE_LTE_TDSCDMA_WCDMA,
             NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA,
             NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA,
-            NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA
+            NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA,
+            NETWORK_MODE_NR_ONLY,
+            NETWORK_MODE_NR_LTE,
+            NETWORK_MODE_NR_LTE_CDMA_EVDO,
+            NETWORK_MODE_NR_LTE_GSM_WCDMA,
+            NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA,
+            NETWORK_MODE_NR_LTE_WCDMA,
+            NETWORK_MODE_NR_LTE_TDSCDMA,
+            NETWORK_MODE_NR_LTE_TDSCDMA_GSM,
+            NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA,
+            NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA,
+            NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface PrefNetworkMode{}
 
     /**
-     * preferred network mode is GSM/WCDMA (WCDMA preferred).
+     * Preferred network mode is GSM/WCDMA (WCDMA preferred).
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_WCDMA_PREF = RILConstants.NETWORK_MODE_WCDMA_PREF;
 
     /**
-     * preferred network mode is GSM only.
+     * Preferred network mode is GSM only.
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_GSM_ONLY = RILConstants.NETWORK_MODE_GSM_ONLY;
 
     /**
-     * preferred network mode is WCDMA only.
+     * Preferred network mode is WCDMA only.
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_WCDMA_ONLY = RILConstants.NETWORK_MODE_WCDMA_ONLY;
 
     /**
-     * preferred network mode is GSM/WCDMA (auto mode, according to PRL).
+     * Preferred network mode is GSM/WCDMA (auto mode, according to PRL).
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_GSM_UMTS = RILConstants.NETWORK_MODE_GSM_UMTS;
 
     /**
-     * preferred network mode is CDMA and EvDo (auto mode, according to PRL).
+     * Preferred network mode is CDMA and EvDo (auto mode, according to PRL).
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_CDMA_EVDO = RILConstants.NETWORK_MODE_CDMA;
 
     /**
-     * preferred network mode is CDMA only.
+     * Preferred network mode is CDMA only.
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_CDMA_NO_EVDO = RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
 
     /**
-     * preferred network mode is EvDo only.
+     * Preferred network mode is EvDo only.
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_EVDO_NO_CDMA = RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
 
     /**
-     * preferred network mode is GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL).
+     * Preferred network mode is GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL).
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_GLOBAL = RILConstants.NETWORK_MODE_GLOBAL;
 
     /**
-     * preferred network mode is LTE, CDMA and EvDo.
+     * Preferred network mode is LTE, CDMA and EvDo.
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_LTE_CDMA_EVDO = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO;
 
     /**
-     * preferred network mode is LTE, GSM/WCDMA.
+     * Preferred network mode is LTE, GSM/WCDMA.
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_LTE_GSM_WCDMA = RILConstants.NETWORK_MODE_LTE_GSM_WCDMA;
 
     /**
-     * preferred network mode is LTE, CDMA, EvDo, GSM/WCDMA.
+     * Preferred network mode is LTE, CDMA, EvDo, GSM/WCDMA.
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA =
             RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
 
     /**
-     * preferred network mode is LTE Only.
+     * Preferred network mode is LTE Only.
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_LTE_ONLY = RILConstants.NETWORK_MODE_LTE_ONLY;
 
     /**
-     * preferred network mode is LTE/WCDMA.
+     * Preferred network mode is LTE/WCDMA.
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_LTE_WCDMA = RILConstants.NETWORK_MODE_LTE_WCDMA;
 
     /**
-     * preferred network mode is TD-SCDMA only.
+     * Preferred network mode is TD-SCDMA only.
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_TDSCDMA_ONLY = RILConstants.NETWORK_MODE_TDSCDMA_ONLY;
 
     /**
-     * preferred network mode is TD-SCDMA and WCDMA.
+     * Preferred network mode is TD-SCDMA and WCDMA.
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_TDSCDMA_WCDMA = RILConstants.NETWORK_MODE_TDSCDMA_WCDMA;
 
     /**
-     * preferred network mode is TD-SCDMA and LTE.
+     * Preferred network mode is TD-SCDMA and LTE.
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_LTE_TDSCDMA = RILConstants.NETWORK_MODE_LTE_TDSCDMA;
 
     /**
-     * preferred network mode is TD-SCDMA and GSM.
+     * Preferred network mode is TD-SCDMA and GSM.
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_TDSCDMA_GSM = RILConstants.NETWORK_MODE_TDSCDMA_GSM;
 
     /**
-     * preferred network mode is TD-SCDMA,GSM and LTE.
+     * Preferred network mode is TD-SCDMA,GSM and LTE.
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_LTE_TDSCDMA_GSM =
             RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM;
 
     /**
-     * preferred network mode is TD-SCDMA, GSM/WCDMA.
+     * Preferred network mode is TD-SCDMA, GSM/WCDMA.
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_TDSCDMA_GSM_WCDMA =
             RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA;
 
     /**
-     * preferred network mode is TD-SCDMA, WCDMA and LTE.
+     * Preferred network mode is TD-SCDMA, WCDMA and LTE.
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_LTE_TDSCDMA_WCDMA =
             RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA;
 
     /**
-     * preferred network mode is TD-SCDMA, GSM/WCDMA and LTE.
+     * Preferred network mode is TD-SCDMA, GSM/WCDMA and LTE.
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA =
             RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA;
 
     /**
-     * preferred network mode is TD-SCDMA,EvDo,CDMA,GSM/WCDMA.
+     * Preferred network mode is TD-SCDMA,EvDo,CDMA,GSM/WCDMA.
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
             RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
     /**
-     * preferred network mode is TD-SCDMA/LTE/GSM/WCDMA, CDMA, and EvDo.
+     * Preferred network mode is TD-SCDMA/LTE/GSM/WCDMA, CDMA, and EvDo.
      * @hide
      */
-    @SystemApi
     public static final int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
             RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
 
     /**
+     * Preferred network mode is NR 5G only.
+     * @hide
+     */
+    public static final int NETWORK_MODE_NR_ONLY = RILConstants.NETWORK_MODE_NR_ONLY;
+
+    /**
+     * Preferred network mode is NR 5G, LTE.
+     * @hide
+     */
+    public static final int NETWORK_MODE_NR_LTE = RILConstants.NETWORK_MODE_NR_LTE;
+
+    /**
+     * Preferred network mode is NR 5G, LTE, CDMA and EvDo.
+     * @hide
+     */
+    public static final int NETWORK_MODE_NR_LTE_CDMA_EVDO =
+            RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO;
+
+    /**
+     * Preferred network mode is NR 5G, LTE, GSM and WCDMA.
+     * @hide
+     */
+    public static final int NETWORK_MODE_NR_LTE_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA;
+
+    /**
+     * Preferred network mode is NR 5G, LTE, CDMA, EvDo, GSM and WCDMA.
+     * @hide
+     */
+    public static final int NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA;
+
+    /**
+     * Preferred network mode is NR 5G, LTE and WCDMA.
+     * @hide
+     */
+    public static final int NETWORK_MODE_NR_LTE_WCDMA = RILConstants.NETWORK_MODE_NR_LTE_WCDMA;
+
+    /**
+     * Preferred network mode is NR 5G, LTE and TDSCDMA.
+     * @hide
+     */
+    public static final int NETWORK_MODE_NR_LTE_TDSCDMA = RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA;
+
+    /**
+     * Preferred network mode is NR 5G, LTE, TD-SCDMA and GSM.
+     * @hide
+     */
+    public static final int NETWORK_MODE_NR_LTE_TDSCDMA_GSM =
+            RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM;
+
+    /**
+     * Preferred network mode is NR 5G, LTE, TD-SCDMA, WCDMA.
+     * @hide
+     */
+    public static final int NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA =
+            RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA;
+
+    /**
+     * Preferred network mode is NR 5G, LTE, TD-SCDMA, GSM and WCDMA.
+     * @hide
+     */
+    public static final int NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA;
+
+    /**
+     * Preferred network mode is NR 5G, LTE, TD-SCDMA, CDMA, EVDO, GSM and WCDMA.
+     * @hide
+     */
+    public static final int NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+
+    /**
      * Get the preferred network type.
      * Used for device configuration by some CDMA operators.
      *
      * <p>Requires Permission:
-     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return the preferred network type.
      * @hide
      */
-    @RequiresPermission((android.Manifest.permission.MODIFY_PHONE_STATE))
-    @SystemApi
+    @RequiresPermission((android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE))
+    @UnsupportedAppUsage
     public @PrefNetworkMode int getPreferredNetworkType(int subId) {
         try {
             ITelephony telephony = getITelephony();
-            if (telephony != null)
+            if (telephony != null) {
                 return telephony.getPreferredNetworkType(subId);
+            }
         } catch (RemoteException ex) {
             Rlog.e(TAG, "getPreferredNetworkType RemoteException", ex);
         } catch (NullPointerException ex) {
@@ -6373,6 +6464,37 @@
     }
 
     /**
+     * Get the preferred network type bitmap.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return a 32-bit bitmap.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @SystemApi
+    public @NetworkTypeBitMask int getPreferredNetworkTypeBitmap() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return RadioAccessFamily.getRafFromNetworkType(
+                        telephony.getPreferredNetworkType(getSubId()));
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getPreferredNetworkTypeBitmap RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "getPreferredNetworkTypeBitmap NPE", ex);
+        }
+        return 0;
+    }
+
+    /**
      * Sets the network selection mode to automatic.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
@@ -6587,6 +6709,37 @@
     }
 
     /**
+     * Set the preferred network type bitmap.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param networkTypeBitmap a 32-bit bitmap.
+     * @return true on success; false on any failure.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @SystemApi
+    public boolean setPreferredNetworkTypeBitmap(@NetworkTypeBitMask int networkTypeBitmap) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.setPreferredNetworkType(
+                        getSubId(), RadioAccessFamily.getNetworkTypeFromRaf(networkTypeBitmap));
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setPreferredNetworkType RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "setPreferredNetworkType NPE", ex);
+        }
+        return false;
+    }
+
+    /**
      * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
      *
      * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
@@ -6671,8 +6824,8 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.getCarrierPrivilegeStatus(subId) ==
-                    CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+                return telephony.getCarrierPrivilegeStatus(subId)
+                        == CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
             }
         } catch (RemoteException ex) {
             Rlog.e(TAG, "hasCarrierPrivileges RemoteException", ex);
@@ -7912,7 +8065,7 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void setSimOperatorNameForPhone(int phoneId, String name) {
         setTelephonyProperty(phoneId,
                 TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, name);
@@ -7933,7 +8086,7 @@
     *
     * @hide
     */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void setSimCountryIsoForPhone(int phoneId, String iso) {
         setTelephonyProperty(phoneId,
                 TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY, iso);
@@ -7954,7 +8107,7 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void setSimStateForPhone(int phoneId, String state) {
         setTelephonyProperty(phoneId,
                 TelephonyProperties.PROPERTY_SIM_STATE, state);
@@ -8060,7 +8213,7 @@
      * @param version baseband version
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void setBasebandVersionForPhone(int phoneId, String version) {
         setTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_BASEBAND_VERSION, version);
     }
@@ -8126,7 +8279,7 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void setPhoneType(int phoneId, int type) {
         if (SubscriptionManager.isValidPhoneId(phoneId)) {
             TelephonyManager.setTelephonyProperty(phoneId,
@@ -8156,7 +8309,7 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public String getOtaSpNumberSchemaForPhone(int phoneId, String defaultValue) {
         if (SubscriptionManager.isValidPhoneId(phoneId)) {
             return TelephonyManager.getTelephonyProperty(phoneId,
@@ -8956,6 +9109,23 @@
     }
 
     /**
+     * Action set from carrier signalling broadcast receivers to reset all carrier actions
+     * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+     * @param subId the subscription ID that this action applies to.
+     * @hide
+     */
+    public void carrierActionResetAll(int subId) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                service.carrierActionResetAll(subId);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#carrierActionResetAll", e);
+        }
+    }
+
+    /**
      * Get aggregated video call data usage since boot.
      * Permissions android.Manifest.permission.READ_NETWORK_USAGE_HISTORY is required.
      *
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/telephony/java/android/telephony/UiccCardInfo.aidl
similarity index 76%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to telephony/java/android/telephony/UiccCardInfo.aidl
index b7a7920..882c233 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/telephony/java/android/telephony/UiccCardInfo.aidl
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.telephony;
 
-parcelable UsbPort;
+parcelable UiccCardInfo;
diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java
new file mode 100644
index 0000000..45e4704
--- /dev/null
+++ b/telephony/java/android/telephony/UiccCardInfo.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * The UiccCardInfo represents information about a currently inserted UICC or embedded eUICC.
+ * @hide
+ */
+@SystemApi
+public class UiccCardInfo implements Parcelable {
+
+    private final boolean mIsEuicc;
+    private final int mCardId;
+    private final String mEid;
+    private final String mIccId;
+    private final int mSlotIndex;
+
+    public static final Creator<UiccCardInfo> CREATOR = new Creator<UiccCardInfo>() {
+        @Override
+        public UiccCardInfo createFromParcel(Parcel in) {
+            return new UiccCardInfo(in);
+        }
+
+        @Override
+        public UiccCardInfo[] newArray(int size) {
+            return new UiccCardInfo[size];
+        }
+    };
+
+    private UiccCardInfo(Parcel in) {
+        mIsEuicc = in.readByte() != 0;
+        mCardId = in.readInt();
+        mEid = in.readString();
+        mIccId = in.readString();
+        mSlotIndex = in.readInt();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeByte((byte) (mIsEuicc ? 1 : 0));
+        dest.writeInt(mCardId);
+        dest.writeString(mEid);
+        dest.writeString(mIccId);
+        dest.writeInt(mSlotIndex);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public UiccCardInfo(boolean isEuicc, int cardId, String eid, String iccId, int slotIndex) {
+        this.mIsEuicc = isEuicc;
+        this.mCardId = cardId;
+        this.mEid = eid;
+        this.mIccId = iccId;
+        this.mSlotIndex = slotIndex;
+    }
+
+    /**
+     * Return whether the UiccCardInfo is an eUICC.
+     * @return true if the UICC is an eUICC.
+     */
+    public boolean isEuicc() {
+        return mIsEuicc;
+    }
+
+    /**
+     * Get the card ID of the UICC. See {@link TelephonyManager#getCardIdForDefaultEuicc()} for more
+     * details on card ID.
+     */
+    public int getCardId() {
+        return mCardId;
+    }
+
+    /**
+     * Get the embedded ID (EID) of the eUICC. If the UiccCardInfo is not an eUICC
+     * (see {@link #isEuicc()}), returns null.
+     */
+    public String getEid() {
+        if (!mIsEuicc) {
+            return null;
+        }
+        return mEid;
+    }
+
+    /**
+     * Get the ICCID of the UICC.
+     */
+    public String getIccId() {
+        return mIccId;
+    }
+
+    /**
+     * Gets the slot index for the slot that the UICC is currently inserted in.
+     */
+    public int getSlotIndex() {
+        return mSlotIndex;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        UiccCardInfo that = (UiccCardInfo) obj;
+        return ((mIsEuicc == that.mIsEuicc)
+                && (mCardId == that.mCardId)
+                && (Objects.equals(mEid, that.mEid))
+                && (Objects.equals(mIccId, that.mIccId))
+                && (mSlotIndex == that.mSlotIndex));
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mIsEuicc, mCardId, mEid, mIccId, mSlotIndex);
+    }
+
+    @Override
+    public String toString() {
+        return "UiccCardInfo (mIsEuicc="
+                + mIsEuicc
+                + ", mCardId="
+                + mCardId
+                + ", mEid="
+                + mEid
+                + ", mIccId="
+                + mIccId
+                + ", mSlotIndex="
+                + mSlotIndex
+                + ")";
+    }
+}
diff --git a/telephony/java/android/telephony/VoLteServiceState.java b/telephony/java/android/telephony/VoLteServiceState.java
index cf961d0..d0b7519 100644
--- a/telephony/java/android/telephony/VoLteServiceState.java
+++ b/telephony/java/android/telephony/VoLteServiceState.java
@@ -17,6 +17,7 @@
 package android.telephony;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -80,7 +81,7 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public VoLteServiceState(int srvccState) {
         initialize();
 
diff --git a/telephony/java/android/telephony/cdma/CdmaCellLocation.java b/telephony/java/android/telephony/cdma/CdmaCellLocation.java
index ee602c0..45b1e47 100644
--- a/telephony/java/android/telephony/cdma/CdmaCellLocation.java
+++ b/telephony/java/android/telephony/cdma/CdmaCellLocation.java
@@ -17,6 +17,7 @@
 package android.telephony.cdma;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Bundle;
 import android.telephony.CellLocation;
 
@@ -24,7 +25,7 @@
  * Represents the cell location on a CDMA phone.
  */
 public class CdmaCellLocation extends CellLocation {
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mBaseStationId = -1;
 
     /**
@@ -38,7 +39,7 @@
      * to 1296000, both values inclusive (corresponding to a range of -90
      * to +90 degrees). Integer.MAX_VALUE is considered invalid value.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mBaseStationLatitude = INVALID_LAT_LONG;
 
     /**
@@ -47,12 +48,12 @@
      * to 2592000, both values inclusive (corresponding to a range of -180
      * to +180 degrees). Integer.MAX_VALUE is considered invalid value.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mBaseStationLongitude = INVALID_LAT_LONG;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mSystemId = -1;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mNetworkId = -1;
 
     /**
@@ -206,7 +207,7 @@
      * @param b second obj
      * @return true if two objects equal or both are null
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static boolean equalsHandlesNulls(Object a, Object b) {
         return (a == null) ? (b == null) : a.equals (b);
     }
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 1db5850..74d1e83 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -16,7 +16,6 @@
 
 package android.telephony.data;
 
-import android.annotation.CallSuper;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -60,7 +59,6 @@
     private static final String TAG = DataService.class.getSimpleName();
 
     public static final String DATA_SERVICE_INTERFACE = "android.telephony.data.DataService";
-    public static final String DATA_SERVICE_EXTRA_SLOT_ID = "android.telephony.data.extra.SLOT_ID";
 
     /** {@hide} */
     @IntDef(prefix = "REQUEST_REASON_", value = {
@@ -116,7 +114,7 @@
      * must extend this class to support data connection. Note that each instance of data service
      * provider is associated with one physical SIM slot.
      */
-    public class DataServiceProvider {
+    public abstract class DataServiceProvider implements AutoCloseable {
 
         private final int mSlotId;
 
@@ -250,12 +248,12 @@
         }
 
         /**
-         * Called when the instance of data service is destroyed (e.g. got unbind or binder died).
+         * Called when the instance of data service is destroyed (e.g. got unbind or binder died)
+         * or when the data service provider is removed. The extended class should implement this
+         * method to perform cleanup works.
          */
-        @CallSuper
-        protected void onDestroy() {
-            mDataCallListChangedCallbacks.clear();
-        }
+        @Override
+        public abstract void close();
     }
 
     private static final class SetupDataCallRequest {
@@ -345,7 +343,7 @@
                     break;
                 case DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER:
                     if (serviceProvider != null) {
-                        serviceProvider.onDestroy();
+                        serviceProvider.close();
                         mServiceMap.remove(slotId);
                     }
                     break;
@@ -353,7 +351,7 @@
                     for (int i = 0; i < mServiceMap.size(); i++) {
                         serviceProvider = mServiceMap.get(i);
                         if (serviceProvider != null) {
-                            serviceProvider.onDestroy();
+                            serviceProvider.close();
                         }
                     }
                     mServiceMap.clear();
diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java
index 57d9cce..45b4849 100644
--- a/telephony/java/android/telephony/data/QualifiedNetworksService.java
+++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java
@@ -151,7 +151,7 @@
 
         /**
          * Called when the qualified networks updater is removed. The extended class should
-         * implement this method to perform clean up works.
+         * implement this method to perform cleanup works.
          */
         @Override
         public abstract void close();
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index ebf1987..6326cc6 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -61,7 +61,6 @@
     public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS =
             "android.telephony.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
 
-
     /**
      * Broadcast Action: The eUICC OTA status is changed.
      * <p class="note">
@@ -87,6 +86,20 @@
             "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP_INCOMPLETE";
 
     /**
+     * Intent action to select a profile to enable before download a new eSIM profile.
+     *
+     * May be called during device provisioning when there are multiple slots having profiles on
+     * them. This Intent launches a screen for all the current existing profiles and let users to
+     * choose which one they want to enable. In this case, the slot contains the profile will be
+     * activated.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_PROFILE_SELECTION =
+            "android.telephony.euicc.action.PROFILE_SELECTION";
+
+    /**
      * Intent action to provision an embedded subscription.
      *
      * <p>May be called during device provisioning to launch a screen to perform embedded SIM
@@ -132,6 +145,16 @@
     public static final int EMBEDDED_SUBSCRIPTION_RESULT_ERROR = 2;
 
     /**
+     * Key for an extra set on the {@link #ACTION_PROVISION_EMBEDDED_SUBSCRIPTION} intent for which
+     * kind of activation flow will be evolved. (see {@code EUICC_ACTIVATION_})
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_ACTIVATION_TYPE =
+            "android.telephony.euicc.extra.ACTIVATION_TYPE";
+
+    /**
      * Key for an extra set on {@link PendingIntent} result callbacks providing a detailed result
      * code.
      *
@@ -197,6 +220,52 @@
     public static final String META_DATA_CARRIER_ICON = "android.telephony.euicc.carriericon";
 
     /**
+     * Euicc activation type which will be included in {@link #EXTRA_ACTIVATION_TYPE} and used to
+     * decide which kind of activation flow should be lauched.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"EUICC_ACTIVATION_"}, value = {
+            EUICC_ACTIVATION_TYPE_DEFAULT,
+            EUICC_ACTIVATION_TYPE_BACKUP,
+            EUICC_ACTIVATION_TYPE_TRANSFER
+
+    })
+    public @interface EuiccActivationType{}
+
+
+    /**
+     * The default euicc activation type which includes checking server side and downloading the
+     * profile based on carrier's download configuration.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int EUICC_ACTIVATION_TYPE_DEFAULT = 1;
+
+    /**
+     * The euicc activation type used when the default download process failed. LPA will start the
+     * backup flow and try to download the profile for the carrier.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int EUICC_ACTIVATION_TYPE_BACKUP = 2;
+
+    /**
+     * The activation flow of eSIM seamless transfer will be used. LPA will start normal eSIM
+     * activation flow and if it's failed, the name of the carrier selected will be recorded. After
+     * the future device pairing, LPA will contact this carrier to transfer it from the other device
+     * to this device.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int EUICC_ACTIVATION_TYPE_TRANSFER = 3;
+
+
+    /**
      * Euicc OTA update status which can be got by {@link #getOtaStatus}
      * @hide
      */
@@ -336,7 +405,7 @@
         }
         try {
             getIEuiccController().downloadSubscription(subscription, switchAfterDownload,
-                    mContext.getOpPackageName(), callbackIntent);
+                    mContext.getOpPackageName(), null /* resolvedBundle */, callbackIntent);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/telephony/java/android/telephony/gsm/GsmCellLocation.java b/telephony/java/android/telephony/gsm/GsmCellLocation.java
index 98ce333..d6780ce 100644
--- a/telephony/java/android/telephony/gsm/GsmCellLocation.java
+++ b/telephony/java/android/telephony/gsm/GsmCellLocation.java
@@ -17,6 +17,7 @@
 package android.telephony.gsm;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Bundle;
 import android.telephony.CellLocation;
 
@@ -92,7 +93,7 @@
      * Set the primary scrambling code.
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void setPsc(int psc) {
         mPsc = psc;
     }
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index a6c24bf..cb6fcd7 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -262,6 +262,8 @@
     public static final String EXTRA_DISPLAY_TEXT = "DisplayText";
     public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
     public static final String EXTRA_IS_CALL_PULL = "CallPull";
+    public static final String EXTRA_ADDITIONAL_SIP_INVITE_FIELDS =
+                                  "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS";
 
     /**
      * Extra key which the RIL can use to indicate the radio technology used for a call.
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/telephony/java/android/telephony/ims/Rcs1To1Thread.aidl
similarity index 82%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to telephony/java/android/telephony/ims/Rcs1To1Thread.aidl
index b7a7920..9fdc41d 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/telephony/java/android/telephony/ims/Rcs1To1Thread.aidl
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ *
+ * Copyright 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.telephony.ims;
 
-parcelable UsbPort;
+parcelable Rcs1To1Thread;
diff --git a/telephony/java/android/telephony/ims/Rcs1To1Thread.java b/telephony/java/android/telephony/ims/Rcs1To1Thread.java
new file mode 100644
index 0000000..709b3aa
--- /dev/null
+++ b/telephony/java/android/telephony/ims/Rcs1To1Thread.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * Rcs1To1Thread represents a single RCS conversation thread with a total of two
+ * {@link RcsParticipant}s.
+ * @hide - TODO(sahinc) make this public
+ */
+public class Rcs1To1Thread extends RcsThread {
+    public Rcs1To1Thread(int threadId) {
+        super(threadId);
+    }
+
+    public static final Creator<Rcs1To1Thread> CREATOR = new Creator<Rcs1To1Thread>() {
+        @Override
+        public Rcs1To1Thread createFromParcel(Parcel in) {
+            return new Rcs1To1Thread(in);
+        }
+
+        @Override
+        public Rcs1To1Thread[] newArray(int size) {
+            return new Rcs1To1Thread[size];
+        }
+    };
+
+    protected Rcs1To1Thread(Parcel in) {
+        super(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(RCS_1_TO_1_TYPE);
+        super.writeToParcel(dest, flags);
+    }
+}
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/telephony/java/android/telephony/ims/RcsFileTransferPart.aidl
similarity index 82%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to telephony/java/android/telephony/ims/RcsFileTransferPart.aidl
index b7a7920..eaf3128 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/telephony/java/android/telephony/ims/RcsFileTransferPart.aidl
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ *
+ * Copyright 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.telephony.ims;
 
-parcelable UsbPort;
+parcelable RcsFileTransferPart;
diff --git a/telephony/java/android/telephony/ims/RcsFileTransferPart.java b/telephony/java/android/telephony/ims/RcsFileTransferPart.java
new file mode 100644
index 0000000..39c58dd
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsFileTransferPart.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * A part of a composite {@link RcsMessage} that holds a file transfer.
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsFileTransferPart extends RcsPart {
+    public static final Creator<RcsFileTransferPart> CREATOR = new Creator<RcsFileTransferPart>() {
+        @Override
+        public RcsFileTransferPart createFromParcel(Parcel in) {
+            return new RcsFileTransferPart(in);
+        }
+
+        @Override
+        public RcsFileTransferPart[] newArray(int size) {
+            return new RcsFileTransferPart[size];
+        }
+    };
+
+    protected RcsFileTransferPart(Parcel in) {
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+    }
+}
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/telephony/java/android/telephony/ims/RcsGroupThread.aidl
similarity index 82%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to telephony/java/android/telephony/ims/RcsGroupThread.aidl
index b7a7920..c4ce529 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/telephony/java/android/telephony/ims/RcsGroupThread.aidl
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ *
+ * Copyright 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.telephony.ims;
 
-parcelable UsbPort;
+parcelable RcsGroupThread;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThread.java b/telephony/java/android/telephony/ims/RcsGroupThread.java
new file mode 100644
index 0000000..d954b2d
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsGroupThread.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * RcsGroupThread represents a single RCS conversation thread where {@link RcsParticipant}s can join
+ * or leave.
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsGroupThread extends RcsThread {
+    public static final Creator<RcsGroupThread> CREATOR = new Creator<RcsGroupThread>() {
+        @Override
+        public RcsGroupThread createFromParcel(Parcel in) {
+            return new RcsGroupThread(in);
+        }
+
+        @Override
+        public RcsGroupThread[] newArray(int size) {
+            return new RcsGroupThread[size];
+        }
+    };
+
+    protected RcsGroupThread(Parcel in) {
+        super(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(RCS_GROUP_TYPE);
+        super.writeToParcel(dest, flags);
+    }
+}
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/telephony/java/android/telephony/ims/RcsIncomingMessage.aidl
similarity index 82%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to telephony/java/android/telephony/ims/RcsIncomingMessage.aidl
index b7a7920..6552a82 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/telephony/java/android/telephony/ims/RcsIncomingMessage.aidl
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ *
+ * Copyright 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.telephony.ims;
 
-parcelable UsbPort;
+parcelable RcsIncomingMessage;
diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessage.java b/telephony/java/android/telephony/ims/RcsIncomingMessage.java
new file mode 100644
index 0000000..f39e06d
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsIncomingMessage.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * This is a single instance of a message received over RCS.
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsIncomingMessage extends RcsMessage {
+    public static final Creator<RcsIncomingMessage> CREATOR = new Creator<RcsIncomingMessage>() {
+        @Override
+        public RcsIncomingMessage createFromParcel(Parcel in) {
+            return new RcsIncomingMessage(in);
+        }
+
+        @Override
+        public RcsIncomingMessage[] newArray(int size) {
+            return new RcsIncomingMessage[size];
+        }
+    };
+
+    protected RcsIncomingMessage(Parcel in) {
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+    }
+}
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/telephony/java/android/telephony/ims/RcsLocationPart.aidl
similarity index 82%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to telephony/java/android/telephony/ims/RcsLocationPart.aidl
index b7a7920..4fe5ca9 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/telephony/java/android/telephony/ims/RcsLocationPart.aidl
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ *
+ * Copyright 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.telephony.ims;
 
-parcelable UsbPort;
+parcelable RcsLocationPart;
diff --git a/telephony/java/android/telephony/ims/RcsLocationPart.java b/telephony/java/android/telephony/ims/RcsLocationPart.java
new file mode 100644
index 0000000..19be4ce
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsLocationPart.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * A part of a composite {@link RcsMessage} that holds a location
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsLocationPart extends RcsPart {
+    public static final Creator<RcsLocationPart> CREATOR = new Creator<RcsLocationPart>() {
+        @Override
+        public RcsLocationPart createFromParcel(Parcel in) {
+            return new RcsLocationPart(in);
+        }
+
+        @Override
+        public RcsLocationPart[] newArray(int size) {
+            return new RcsLocationPart[size];
+        }
+    };
+
+    protected RcsLocationPart(Parcel in) {
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+    }
+}
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/telephony/java/android/telephony/ims/RcsManager.aidl
similarity index 83%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to telephony/java/android/telephony/ims/RcsManager.aidl
index b7a7920..63bc71c 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/telephony/java/android/telephony/ims/RcsManager.aidl
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ *
+ * Copyright 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.telephony.ims;
 
-parcelable UsbPort;
+parcelable RcsManager;
diff --git a/telephony/java/android/telephony/ims/RcsManager.java b/telephony/java/android/telephony/ims/RcsManager.java
index d50b516..df108c8 100644
--- a/telephony/java/android/telephony/ims/RcsManager.java
+++ b/telephony/java/android/telephony/ims/RcsManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/telephony/java/android/telephony/ims/RcsMessage.aidl
similarity index 83%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to telephony/java/android/telephony/ims/RcsMessage.aidl
index b7a7920..b32cd12 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/telephony/java/android/telephony/ims/RcsMessage.aidl
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ *
+ * Copyright 2018, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.telephony.ims;
 
-parcelable UsbPort;
+parcelable RcsMessage;
diff --git a/telephony/java/android/telephony/ims/RcsMessage.java b/telephony/java/android/telephony/ims/RcsMessage.java
new file mode 100644
index 0000000..d46685c
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsMessage.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.os.Parcelable;
+
+/**
+ * This is a single instance of a message sent or received over RCS.
+ * @hide - TODO(sahinc) make this public
+ */
+public abstract class RcsMessage implements Parcelable {
+}
diff --git a/telephony/java/android/telephony/ims/RcsMessageStore.java b/telephony/java/android/telephony/ims/RcsMessageStore.java
index c89c0be..1bf6ffd 100644
--- a/telephony/java/android/telephony/ims/RcsMessageStore.java
+++ b/telephony/java/android/telephony/ims/RcsMessageStore.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -16,6 +16,8 @@
 
 package android.telephony.ims;
 
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.telephony.Rlog;
@@ -27,26 +29,93 @@
  * @hide - TODO make this public
  */
 public class RcsMessageStore {
-    private static final String TAG = "RcsMessageStore";
-    private static final boolean VDBG = false;
+    static final String TAG = "RcsMessageStore";
 
     /**
-     * Delete the RcsThread identified by the given threadId.
+     * Returns the first chunk of existing {@link RcsThread}s in the common storage.
+     * @param queryParameters Parameters to specify to return a subset of all RcsThreads.
+     *                        Passing a value of null will return all threads.
+     */
+    @WorkerThread
+    public RcsThreadQueryResult getRcsThreads(@Nullable RcsThreadQueryParameters queryParameters) {
+        try {
+            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
+            if (iRcs != null) {
+                return iRcs.getRcsThreads(queryParameters);
+            }
+        } catch (RemoteException re) {
+            Rlog.e(TAG, "RcsMessageStore: Exception happened during getRcsThreads", re);
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the next chunk of {@link RcsThread}s in the common storage.
+     * @param continuationToken A token to continue the query to get the next chunk. This is
+     *                          obtained through {@link RcsThreadQueryResult#nextChunkToken}.
+     */
+    @WorkerThread
+    public RcsThreadQueryResult getRcsThreads(RcsThreadQueryContinuationToken continuationToken) {
+        try {
+            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
+            if (iRcs != null) {
+                return iRcs.getRcsThreadsWithToken(continuationToken);
+            }
+        } catch (RemoteException re) {
+            Rlog.e(TAG, "RcsMessageStore: Exception happened during getRcsThreads", re);
+        }
+
+        return null;
+    }
+
+    /**
+     * Creates a new 1 to 1 thread with the given participant and persists it in the storage.
+     */
+    @WorkerThread
+    public Rcs1To1Thread createRcs1To1Thread(RcsParticipant recipient) {
+        try {
+            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
+            if (iRcs != null) {
+                return iRcs.createRcs1To1Thread(recipient);
+            }
+        } catch (RemoteException re) {
+            Rlog.e(TAG, "RcsMessageStore: Exception happened during createRcs1To1Thread", re);
+        }
+
+        return null;
+    }
+
+    /**
+     * Delete the {@link RcsThread} identified by the given threadId.
      * @param threadId threadId of the thread to be deleted.
      */
+    @WorkerThread
     public void deleteThread(int threadId) {
-        if (VDBG) logd("deleteThread: threadId: " + threadId);
         try {
             IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
             if (iRcs != null) {
                 iRcs.deleteThread(threadId);
             }
         } catch (RemoteException re) {
-
+            Rlog.e(TAG, "RcsMessageStore: Exception happened during deleteThread", re);
         }
     }
 
-    private static void logd(String msg) {
-        Rlog.d(TAG, msg);
+    /**
+     * Creates a new participant and persists it in the storage.
+     * @param canonicalAddress The defining address (e.g. phone number) of the participant.
+     */
+    public RcsParticipant createRcsParticipant(String canonicalAddress) {
+        try {
+            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
+            if (iRcs != null) {
+                return iRcs.createRcsParticipant(canonicalAddress);
+            }
+        } catch (RemoteException re) {
+            Rlog.e(TAG, "RcsMessageStore: Exception happened during createRcsParticipant", re);
+        }
+
+        return null;
     }
 }
diff --git a/telephony/java/android/telephony/ims/RcsMultiMediaPart.java b/telephony/java/android/telephony/ims/RcsMultiMediaPart.java
new file mode 100644
index 0000000..d295fba
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsMultiMediaPart.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * A part of a composite {@link RcsMessage} that holds a media that is rendered on the screen
+ * (i.e. image, video etc)
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsMultiMediaPart extends RcsFileTransferPart {
+    public static final Creator<RcsMultiMediaPart> CREATOR = new Creator<RcsMultiMediaPart>() {
+        @Override
+        public RcsMultiMediaPart createFromParcel(Parcel in) {
+            return new RcsMultiMediaPart(in);
+        }
+
+        @Override
+        public RcsMultiMediaPart[] newArray(int size) {
+            return new RcsMultiMediaPart[size];
+        }
+    };
+
+    protected RcsMultiMediaPart(Parcel in) {
+        super(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+    }
+}
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/telephony/java/android/telephony/ims/RcsMultimediaPart.aidl
similarity index 82%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to telephony/java/android/telephony/ims/RcsMultimediaPart.aidl
index b7a7920..5992d95 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/telephony/java/android/telephony/ims/RcsMultimediaPart.aidl
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ *
+ * Copyright 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.telephony.ims;
 
-parcelable UsbPort;
+parcelable RcsMultimediaPart;
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/telephony/java/android/telephony/ims/RcsOutgoingMessage.aidl
similarity index 82%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to telephony/java/android/telephony/ims/RcsOutgoingMessage.aidl
index b7a7920..6e0c80f 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/telephony/java/android/telephony/ims/RcsOutgoingMessage.aidl
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ *
+ * Copyright 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.telephony.ims;
 
-parcelable UsbPort;
+parcelable RcsOutgoingMessage;
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
new file mode 100644
index 0000000..bfb1611
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * This is a single instance of a message sent over RCS.
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsOutgoingMessage extends RcsMessage {
+    public static final Creator<RcsOutgoingMessage> CREATOR = new Creator<RcsOutgoingMessage>() {
+        @Override
+        public RcsOutgoingMessage createFromParcel(Parcel in) {
+            return new RcsOutgoingMessage(in);
+        }
+
+        @Override
+        public RcsOutgoingMessage[] newArray(int size) {
+            return new RcsOutgoingMessage[size];
+        }
+    };
+
+    protected RcsOutgoingMessage(Parcel in) {
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+    }
+}
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/telephony/java/android/telephony/ims/RcsPart.aidl
similarity index 83%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to telephony/java/android/telephony/ims/RcsPart.aidl
index b7a7920..8b8077d 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/telephony/java/android/telephony/ims/RcsPart.aidl
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ *
+ * Copyright 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.telephony.ims;
 
-parcelable UsbPort;
+parcelable RcsPart;
diff --git a/core/java/android/service/contentcapture/InteractionContext.aidl b/telephony/java/android/telephony/ims/RcsPart.java
similarity index 61%
copy from core/java/android/service/contentcapture/InteractionContext.aidl
copy to telephony/java/android/telephony/ims/RcsPart.java
index 982e095..da50173 100644
--- a/core/java/android/service/contentcapture/InteractionContext.aidl
+++ b/telephony/java/android/telephony/ims/RcsPart.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
+/*
+ * 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
+ *      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,
@@ -13,7 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package android.telephony.ims;
 
-package android.service.contentcapture;
+import android.os.Parcelable;
 
-parcelable InteractionContext;
+/**
+ * A part of a composite {@link RcsMessage}.
+ * @hide - TODO(sahinc) make this public
+ */
+public abstract class RcsPart implements Parcelable {
+}
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/telephony/java/android/telephony/ims/RcsParticipant.aidl
similarity index 82%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to telephony/java/android/telephony/ims/RcsParticipant.aidl
index b7a7920..1c44363 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/telephony/java/android/telephony/ims/RcsParticipant.aidl
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ *
+ * Copyright 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.telephony.ims;
 
-parcelable UsbPort;
+parcelable RcsParticipant;
diff --git a/telephony/java/android/telephony/ims/RcsParticipant.java b/telephony/java/android/telephony/ims/RcsParticipant.java
new file mode 100644
index 0000000..70500aa
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsParticipant.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * RcsParticipant is an RCS capable contact that can participate in {@link RcsThread}s.
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsParticipant implements Parcelable {
+    /**
+     * Returns the row id of this participant.
+     *
+     * TODO(sahinc) implement
+     * @hide
+     */
+    public int getId() {
+        return 12345;
+    }
+
+    public static final Creator<RcsParticipant> CREATOR = new Creator<RcsParticipant>() {
+        @Override
+        public RcsParticipant createFromParcel(Parcel in) {
+            return new RcsParticipant(in);
+        }
+
+        @Override
+        public RcsParticipant[] newArray(int size) {
+            return new RcsParticipant[size];
+        }
+    };
+
+    protected RcsParticipant(Parcel in) {
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+
+    }
+}
diff --git a/core/java/android/service/contentcapture/InteractionContext.aidl b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.aidl
similarity index 80%
copy from core/java/android/service/contentcapture/InteractionContext.aidl
copy to telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.aidl
index 982e095..b9d8190 100644
--- a/core/java/android/service/contentcapture/InteractionContext.aidl
+++ b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.aidl
@@ -1,5 +1,6 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
+/*
+ *
+ * Copyright 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.service.contentcapture;
+package android.telephony.ims;
 
-parcelable InteractionContext;
+parcelable RcsParticipantAliasChangedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
new file mode 100644
index 0000000..b9ca5a8
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * An event that indicates an {@link RcsParticipant}'s alias was changed.
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsParticipantAliasChangedEvent extends RcsParticipantEvent {
+    public static final Creator<RcsParticipantAliasChangedEvent> CREATOR =
+            new Creator<RcsParticipantAliasChangedEvent>() {
+        @Override
+        public RcsParticipantAliasChangedEvent createFromParcel(Parcel in) {
+            return new RcsParticipantAliasChangedEvent(in);
+        }
+
+        @Override
+        public RcsParticipantAliasChangedEvent[] newArray(int size) {
+            return new RcsParticipantAliasChangedEvent[size];
+        }
+    };
+
+    protected RcsParticipantAliasChangedEvent(Parcel in) {
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+    }
+}
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/telephony/java/android/telephony/ims/RcsParticipantEvent.aidl
similarity index 82%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to telephony/java/android/telephony/ims/RcsParticipantEvent.aidl
index b7a7920..c0a7789 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/telephony/java/android/telephony/ims/RcsParticipantEvent.aidl
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ *
+ * Copyright 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.telephony.ims;
 
-parcelable UsbPort;
+parcelable RcsParticipantEvent;
diff --git a/telephony/java/android/telephony/ims/RcsParticipantEvent.java b/telephony/java/android/telephony/ims/RcsParticipantEvent.java
new file mode 100644
index 0000000..371b8b7
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsParticipantEvent.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.os.Parcelable;
+
+/**
+ * An event that is associated with an {@link RcsParticipant}
+ * @hide - TODO(sahinc) make this public
+ */
+public abstract class RcsParticipantEvent implements Parcelable {
+}
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/telephony/java/android/telephony/ims/RcsTextPart.aidl
similarity index 83%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to telephony/java/android/telephony/ims/RcsTextPart.aidl
index b7a7920..4f9fe1f 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/telephony/java/android/telephony/ims/RcsTextPart.aidl
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ *
+ * Copyright 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.telephony.ims;
 
-parcelable UsbPort;
+parcelable RcsTextPart;
diff --git a/telephony/java/android/telephony/ims/RcsTextPart.java b/telephony/java/android/telephony/ims/RcsTextPart.java
new file mode 100644
index 0000000..2a72df1
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsTextPart.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * A part of a composite {@link RcsMessage} that holds a string
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsTextPart extends RcsPart {
+    public static final Creator<RcsTextPart> CREATOR = new Creator<RcsTextPart>() {
+        @Override
+        public RcsTextPart createFromParcel(Parcel in) {
+            return new RcsTextPart(in);
+        }
+
+        @Override
+        public RcsTextPart[] newArray(int size) {
+            return new RcsTextPart[size];
+        }
+    };
+
+    protected RcsTextPart(Parcel in) {
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsThread.aidl b/telephony/java/android/telephony/ims/RcsThread.aidl
index 79d4732..d9cf6db 100644
--- a/telephony/java/android/telephony/ims/RcsThread.aidl
+++ b/telephony/java/android/telephony/ims/RcsThread.aidl
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2018, The Android Open Source Project
+ * Copyright 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/telephony/java/android/telephony/ims/RcsThread.java b/telephony/java/android/telephony/ims/RcsThread.java
index b7f440d..c0a0d94 100644
--- a/telephony/java/android/telephony/ims/RcsThread.java
+++ b/telephony/java/android/telephony/ims/RcsThread.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -18,51 +18,51 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.telephony.ims.aidl.IRcs;
+import android.util.Log;
 
 /**
  * RcsThread represents a single RCS conversation thread. It holds messages that were sent and
- * received and events that occured on that thread.
+ * received and events that occurred on that thread.
  * @hide - TODO(sahinc) make this public
  */
-public class RcsThread implements Parcelable {
+public abstract class RcsThread implements Parcelable {
+    // Since this is an abstract class that gets parcelled, the sub-classes need to write these
+    // magic values into the parcel so that we know which type to unparcel into.
+    protected static final int RCS_1_TO_1_TYPE = 998;
+    protected static final int RCS_GROUP_TYPE = 999;
+
+    protected int mThreadId;
+
+    protected RcsThread(int threadId) {
+        mThreadId = threadId;
+    }
+
+    protected RcsThread(Parcel in) {
+        mThreadId = in.readInt();
+    }
+
     public static final Creator<RcsThread> CREATOR = new Creator<RcsThread>() {
         @Override
         public RcsThread createFromParcel(Parcel in) {
-            return new RcsThread(in);
+            int type = in.readInt();
+
+            switch (type) {
+                case RCS_1_TO_1_TYPE:
+                    return new Rcs1To1Thread(in);
+                case RCS_GROUP_TYPE:
+                    return new RcsGroupThread(in);
+                default:
+                    Log.e(RcsMessageStore.TAG, "Cannot unparcel RcsThread, wrong type: " + type);
+            }
+            return null;
         }
 
         @Override
         public RcsThread[] newArray(int size) {
-            return new RcsThread[size];
+            return new RcsThread[0];
         }
     };
 
-    protected RcsThread(Parcel in) {
-    }
-
-    /**
-     * Returns the number of messages in this RCS thread.
-     *
-     * @hide
-     */
-    public int getMessageCount() {
-        try {
-            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
-            if (iRcs != null) {
-                // TODO(sahinc): substitute to the regular thread id once we have database
-                // TODO(sahinc): connection in place
-                return iRcs.getMessageCount(/* rcsThreadId= */ 123);
-            }
-        } catch (RemoteException re) {
-            // TODO(sahinc): Log something meaningful
-        }
-        return 0;
-    }
-
-    /** Implement the Parcelable interface */
     @Override
     public int describeContents() {
         return 0;
@@ -70,5 +70,6 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mThreadId);
     }
 }
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/telephony/java/android/telephony/ims/RcsThreadEvent.aidl
similarity index 82%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to telephony/java/android/telephony/ims/RcsThreadEvent.aidl
index b7a7920..4a40d89 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/telephony/java/android/telephony/ims/RcsThreadEvent.aidl
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ *
+ * Copyright 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.telephony.ims;
 
-parcelable UsbPort;
+parcelable RcsThreadEvent;
diff --git a/core/java/android/service/contentcapture/InteractionContext.aidl b/telephony/java/android/telephony/ims/RcsThreadEvent.java
similarity index 60%
copy from core/java/android/service/contentcapture/InteractionContext.aidl
copy to telephony/java/android/telephony/ims/RcsThreadEvent.java
index 982e095..e10baab 100644
--- a/core/java/android/service/contentcapture/InteractionContext.aidl
+++ b/telephony/java/android/telephony/ims/RcsThreadEvent.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
+/*
+ * 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
+ *      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,
@@ -13,7 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package android.telephony.ims;
 
-package android.service.contentcapture;
+import android.os.Parcelable;
 
-parcelable InteractionContext;
+/**
+ * An event that happened on an {@link RcsThread}.
+ * @hide - TODO(sahinc) make this public
+ */
+public abstract class RcsThreadEvent implements Parcelable {
+}
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
similarity index 81%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
index b7a7920..82d985d 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ *
+ * Copyright 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.telephony.ims;
 
-parcelable UsbPort;
+parcelable RcsThreadIconChangedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.java b/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.java
new file mode 100644
index 0000000..b308fef
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * An event that indicates an {@link RcsGroupThread}'s icon was changed.
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsThreadIconChangedEvent extends RcsThreadEvent {
+    public static final Creator<RcsThreadIconChangedEvent> CREATOR =
+            new Creator<RcsThreadIconChangedEvent>() {
+        @Override
+        public RcsThreadIconChangedEvent createFromParcel(Parcel in) {
+            return new RcsThreadIconChangedEvent(in);
+        }
+
+        @Override
+        public RcsThreadIconChangedEvent[] newArray(int size) {
+            return new RcsThreadIconChangedEvent[size];
+        }
+    };
+
+    protected RcsThreadIconChangedEvent(Parcel in) {
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+    }
+}
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.aidl
similarity index 81%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.aidl
index b7a7920..54a311d 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.aidl
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ *
+ * Copyright 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.telephony.ims;
 
-parcelable UsbPort;
+parcelable RcsThreadNameChangedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.java b/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.java
new file mode 100644
index 0000000..6f5cfdf
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * An event that indicates an {@link RcsGroupThread}'s name was changed.
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsThreadNameChangedEvent extends RcsThreadEvent {
+    public static final Creator<RcsThreadNameChangedEvent> CREATOR =
+            new Creator<RcsThreadNameChangedEvent>() {
+        @Override
+        public RcsThreadNameChangedEvent createFromParcel(Parcel in) {
+            return new RcsThreadNameChangedEvent(in);
+        }
+
+        @Override
+        public RcsThreadNameChangedEvent[] newArray(int size) {
+            return new RcsThreadNameChangedEvent[size];
+        }
+    };
+
+    protected RcsThreadNameChangedEvent(Parcel in) {
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+    }
+}
diff --git a/core/java/android/service/contentcapture/InteractionContext.aidl b/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.aidl
similarity index 80%
copy from core/java/android/service/contentcapture/InteractionContext.aidl
copy to telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.aidl
index 982e095..047a424 100644
--- a/core/java/android/service/contentcapture/InteractionContext.aidl
+++ b/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.aidl
@@ -1,5 +1,6 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
+/*
+ *
+ * Copyright 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.service.contentcapture;
+package android.telephony.ims;
 
-parcelable InteractionContext;
+parcelable RcsThreadParticipantJoinedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.java b/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.java
new file mode 100644
index 0000000..5c4073c
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * An event that indicates an RCS participant has joined an {@link RcsGroupThread}.
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsThreadParticipantJoinedEvent extends RcsThreadEvent {
+    public static final Creator<RcsThreadParticipantJoinedEvent> CREATOR =
+            new Creator<RcsThreadParticipantJoinedEvent>() {
+        @Override
+        public RcsThreadParticipantJoinedEvent createFromParcel(Parcel in) {
+            return new RcsThreadParticipantJoinedEvent(in);
+        }
+
+        @Override
+        public RcsThreadParticipantJoinedEvent[] newArray(int size) {
+            return new RcsThreadParticipantJoinedEvent[size];
+        }
+    };
+
+    protected RcsThreadParticipantJoinedEvent(Parcel in) {
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+    }
+}
diff --git a/core/java/android/service/contentcapture/InteractionContext.aidl b/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.aidl
similarity index 80%
copy from core/java/android/service/contentcapture/InteractionContext.aidl
copy to telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.aidl
index 982e095..52f9bbd 100644
--- a/core/java/android/service/contentcapture/InteractionContext.aidl
+++ b/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.aidl
@@ -1,5 +1,6 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
+/*
+ *
+ * Copyright 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.service.contentcapture;
+package android.telephony.ims;
 
-parcelable InteractionContext;
+parcelable RcsThreadParticipantLeftEvent;
diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.java b/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.java
new file mode 100644
index 0000000..4bf86b9
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * An event that indicates an RCS participant has left an {@link RcsGroupThread}.
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsThreadParticipantLeftEvent extends RcsThreadEvent {
+    public static final Creator<RcsThreadParticipantLeftEvent> CREATOR =
+            new Creator<RcsThreadParticipantLeftEvent>() {
+        @Override
+        public RcsThreadParticipantLeftEvent createFromParcel(Parcel in) {
+            return new RcsThreadParticipantLeftEvent(in);
+        }
+
+        @Override
+        public RcsThreadParticipantLeftEvent[] newArray(int size) {
+            return new RcsThreadParticipantLeftEvent[size];
+        }
+    };
+
+    protected RcsThreadParticipantLeftEvent(Parcel in) {
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+    }
+}
diff --git a/packages/SystemUI/res/drawable/qs_tile_background.xml b/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl
similarity index 62%
copy from packages/SystemUI/res/drawable/qs_tile_background.xml
copy to telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl
index 96891c1..7bcebfa 100644
--- a/packages/SystemUI/res/drawable/qs_tile_background.xml
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl
@@ -1,7 +1,6 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
 /*
-** Copyright 2012, The Android Open Source Project
+**
+** Copyright 2018, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -15,12 +14,7 @@
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true">
-	<color android:color="#212121" />
-    </item>
-    <item>
-        <color android:color="#161616" />
-    </item>
-</selector>
+
+package android.telephony.ims;
+
+parcelable RcsThreadQueryContinuationToken;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java b/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java
new file mode 100644
index 0000000..931e93d
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A continuation token to provide for {@link RcsMessageStore#getRcsThreads}. Use this token to
+ * break large queries into manageable chunks
+ * @hide - TODO make this public
+ */
+public class RcsThreadQueryContinuationToken implements Parcelable {
+    protected RcsThreadQueryContinuationToken(Parcel in) {
+    }
+
+    public static final Creator<RcsThreadQueryContinuationToken> CREATOR =
+            new Creator<RcsThreadQueryContinuationToken>() {
+                @Override
+                public RcsThreadQueryContinuationToken createFromParcel(Parcel in) {
+                    return new RcsThreadQueryContinuationToken(in);
+                }
+
+                @Override
+                public RcsThreadQueryContinuationToken[] newArray(int size) {
+                    return new RcsThreadQueryContinuationToken[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+    }
+}
diff --git a/packages/SystemUI/res/drawable/qs_tile_background.xml b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl
similarity index 62%
rename from packages/SystemUI/res/drawable/qs_tile_background.xml
rename to telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl
index 96891c1..feb2d4d 100644
--- a/packages/SystemUI/res/drawable/qs_tile_background.xml
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl
@@ -1,7 +1,6 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
 /*
-** Copyright 2012, The Android Open Source Project
+**
+** Copyright 2018, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -15,12 +14,7 @@
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true">
-	<color android:color="#212121" />
-    </item>
-    <item>
-        <color android:color="#161616" />
-    </item>
-</selector>
+
+package android.telephony.ims;
+
+parcelable RcsThreadQueryParameters;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java
new file mode 100644
index 0000000..f2c4ab1
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.CheckResult;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.security.InvalidParameterException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * The parameters to pass into {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParameters)} in
+ * order to select a subset of {@link RcsThread}s present in the message store.
+ * @hide TODO - make the Builder and builder() public. The rest should stay internal only.
+ */
+public class RcsThreadQueryParameters implements Parcelable {
+    private final boolean mIsGroup;
+    private final Set<RcsParticipant> mRcsParticipants;
+    private final int mLimit;
+    private final boolean mIsAscending;
+
+    RcsThreadQueryParameters(boolean isGroup, Set<RcsParticipant> participants, int limit,
+            boolean isAscending) {
+        mIsGroup = isGroup;
+        mRcsParticipants = participants;
+        mLimit = limit;
+        mIsAscending = isAscending;
+    }
+
+    /**
+     * Returns a new builder to build a query with.
+     * TODO - make public
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
+     * the list of participants.
+     * @hide
+     */
+    public Set<RcsParticipant> getRcsParticipants() {
+        return mRcsParticipants;
+    }
+
+    /**
+     * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
+     * whether group threads should be queried
+     * @hide
+     */
+    public boolean isGroupThread() {
+        return mIsGroup;
+    }
+
+    /**
+     * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
+     * the number of tuples the result query should be limited to.
+     */
+    public int getLimit() {
+        return mLimit;
+    }
+
+    /**
+     * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to
+     * determine the sort order.
+     */
+    public boolean isAscending() {
+        return mIsAscending;
+    }
+
+    /**
+     * A helper class to build the {@link RcsThreadQueryParameters}.
+     */
+    public static class Builder {
+        private boolean mIsGroupThread;
+        private Set<RcsParticipant> mParticipants;
+        private int mLimit = 100;
+        private boolean mIsAscending;
+
+        /**
+         * Package private constructor for {@link RcsThreadQueryParameters.Builder}. To obtain this,
+         * {@link RcsThreadQueryParameters#builder()} needs to be called.
+         */
+        Builder() {
+            mParticipants = new HashSet<>();
+        }
+
+        /**
+         * Limits the query to only return group threads.
+         * @param isGroupThread Whether to limit the query result to group threads.
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder isGroupThread(boolean isGroupThread) {
+            mIsGroupThread = isGroupThread;
+            return this;
+        }
+
+        /**
+         * Limits the query to only return threads that contain the given participant.
+         * @param participant The participant that must be included in all of the returned threads.
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder withParticipant(RcsParticipant participant) {
+            mParticipants.add(participant);
+            return this;
+        }
+
+        /**
+         * Limits the query to only return threads that contain the given list of participants.
+         * @param participants An iterable list of participants that must be included in all of the
+         *                     returned threads.
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder withParticipants(Iterable<RcsParticipant> participants) {
+            for (RcsParticipant participant : participants) {
+                mParticipants.add(participant);
+            }
+            return this;
+        }
+
+        /**
+         * Desired number of threads to be returned from the query. Passing in 0 will return all
+         * existing threads at once. The limit defaults to 100.
+         * @param limit The number to limit the query result to.
+         * @return The same instance of the builder to chain parameters.
+         * @throws InvalidParameterException If the given limit is negative.
+         */
+        @CheckResult
+        public Builder limitResultsTo(int limit) throws InvalidParameterException {
+            if (limit < 0) {
+                throw new InvalidParameterException("The query limit must be non-negative");
+            }
+
+            mLimit = limit;
+            return this;
+        }
+
+        /**
+         * Sorts the results returned from the query via thread IDs.
+         *
+         * TODO - add sorting support for other fields
+         *
+         * @param isAscending whether to sort in ascending order or not
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder sort(boolean isAscending) {
+            mIsAscending = isAscending;
+            return this;
+        }
+
+        /**
+         * Builds the {@link RcsThreadQueryParameters} to use in
+         * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParameters)}
+         *
+         * @return An instance of {@link RcsThreadQueryParameters} to use with the thread query.
+         */
+        public RcsThreadQueryParameters build() {
+            return new RcsThreadQueryParameters(
+                    mIsGroupThread, mParticipants, mLimit, mIsAscending);
+        }
+    }
+
+    /**
+     * Parcelable boilerplate below.
+     */
+    protected RcsThreadQueryParameters(Parcel in) {
+        mIsGroup = in.readBoolean();
+
+        ArrayList<RcsParticipant> participantArrayList = new ArrayList<>();
+        in.readTypedList(participantArrayList, RcsParticipant.CREATOR);
+        mRcsParticipants = new HashSet<>(participantArrayList);
+
+        mLimit = in.readInt();
+        mIsAscending = in.readBoolean();
+    }
+
+    public static final Creator<RcsThreadQueryParameters> CREATOR =
+            new Creator<RcsThreadQueryParameters>() {
+                @Override
+                public RcsThreadQueryParameters createFromParcel(Parcel in) {
+                    return new RcsThreadQueryParameters(in);
+                }
+
+                @Override
+                public RcsThreadQueryParameters[] newArray(int size) {
+                    return new RcsThreadQueryParameters[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeBoolean(mIsGroup);
+        dest.writeTypedList(new ArrayList<>(mRcsParticipants));
+        dest.writeInt(mLimit);
+        dest.writeBoolean(mIsAscending);
+    }
+
+}
diff --git a/packages/SystemUI/res/drawable/qs_tile_background.xml b/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl
similarity index 62%
copy from packages/SystemUI/res/drawable/qs_tile_background.xml
copy to telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl
index 96891c1..4b06529 100644
--- a/packages/SystemUI/res/drawable/qs_tile_background.xml
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl
@@ -1,7 +1,6 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
 /*
-** Copyright 2012, The Android Open Source Project
+**
+** Copyright 2018, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -15,12 +14,7 @@
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true">
-	<color android:color="#212121" />
-    </item>
-    <item>
-        <color android:color="#161616" />
-    </item>
-</selector>
+
+package android.telephony.ims;
+
+parcelable RcsThreadQueryResult;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
new file mode 100644
index 0000000..47715f8
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.List;
+
+/**
+ * The result of a {@link RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken,
+ * RcsThreadQueryParameters)}
+ * call. This class allows getting the token for querying the next batch of threads in order to
+ * prevent handling large amounts of data at once.
+ *
+ * @hide
+ */
+public class RcsThreadQueryResult implements Parcelable {
+    private RcsThreadQueryContinuationToken mContinuationToken;
+    private List<RcsThread> mRcsThreads;
+
+    /**
+     * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
+     * to create query results
+     *
+     * @hide
+     */
+    public RcsThreadQueryResult(
+            RcsThreadQueryContinuationToken continuationToken, List<RcsThread> rcsThreads) {
+        mContinuationToken = continuationToken;
+        mRcsThreads = rcsThreads;
+    }
+
+    /**
+     * Returns a token to call
+     * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken)}
+     * to get the next batch of {@link RcsThread}s.
+     */
+    public RcsThreadQueryContinuationToken nextChunkToken() {
+        return mContinuationToken;
+    }
+
+    /**
+     * Returns all the RcsThreads in the current query result. Call {@link
+     * RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken)} to get the next batch of
+     * {@link RcsThread}s.
+     */
+    public List<RcsThread> getThreads() {
+        return mRcsThreads;
+    }
+
+    protected RcsThreadQueryResult(Parcel in) {
+        // TODO - implement
+    }
+
+    public static final Creator<RcsThreadQueryResult> CREATOR =
+            new Creator<RcsThreadQueryResult>() {
+                @Override
+                public RcsThreadQueryResult createFromParcel(Parcel in) {
+                    return new RcsThreadQueryResult(in);
+                }
+
+                @Override
+                public RcsThreadQueryResult[] newArray(int size) {
+                    return new RcsThreadQueryResult[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        // TODO - implement
+    }
+}
diff --git a/telephony/java/android/telephony/ims/aidl/IRcs.aidl b/telephony/java/android/telephony/ims/aidl/IRcs.aidl
index b2e2fad..9badac5 100644
--- a/telephony/java/android/telephony/ims/aidl/IRcs.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IRcs.aidl
@@ -16,14 +16,29 @@
 
 package android.telephony.ims.aidl;
 
+import android.telephony.ims.RcsParticipant;
+import android.telephony.ims.Rcs1To1Thread;
+import android.telephony.ims.RcsThreadQueryContinuationToken;
+import android.telephony.ims.RcsThreadQueryParameters;
+import android.telephony.ims.RcsThreadQueryResult;
+
 /**
  * RPC definition between RCS storage APIs and phone process.
  * {@hide}
  */
 interface IRcs {
     // RcsMessageStore APIs
+    RcsThreadQueryResult getRcsThreads(in RcsThreadQueryParameters queryParameters);
+
+    RcsThreadQueryResult getRcsThreadsWithToken(
+        in RcsThreadQueryContinuationToken continuationToken);
+
     void deleteThread(int threadId);
 
+    Rcs1To1Thread createRcs1To1Thread(in RcsParticipant participant);
+
+    RcsParticipant createRcsParticipant(String canonicalAddress);
+
     // RcsThread APIs
     int getMessageCount(int rcsThreadId);
 }
\ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index b0c875e..96f7a1b 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -24,7 +24,7 @@
     /**
      * IDLE: ready to start data connection setup, default state
      * CONNECTING: state of issued startPppd() but not finish yet
-     * SCANNING: data connection fails with one apn but other apns are available
+     * RETRYING: data connection fails with one apn but other apns are available
      *           ready to start data connection on other apns (before INITING)
      * CONNECTED: IP connection is setup
      * DISCONNECTING: Connection.disconnect() has been called, but PDP
@@ -34,19 +34,16 @@
      *
      * getDataConnectionState() maps State to DataState
      *      FAILED or IDLE : DISCONNECTED
-     *      RETRYING or CONNECTING or SCANNING: CONNECTING
+     *      RETRYING or CONNECTING: CONNECTING
      *      CONNECTED : CONNECTED or DISCONNECTING
      */
     public enum State {
         IDLE,
         CONNECTING,
-        SCANNING,
+        RETRYING,
         CONNECTED,
         DISCONNECTING,
         FAILED,
-        RETRYING        // After moving retry manager to ApnContext, we'll never enter this state!
-                        // Todo: Remove this state and other places that use this state and then
-                        // rename SCANNING to RETRYING.
     }
 
     public enum Activity {
@@ -79,12 +76,8 @@
     public static final int EVENT_PS_RESTRICT_DISABLED = BASE + 23;
     public static final int EVENT_CLEAN_UP_CONNECTION = BASE + 24;
     public static final int EVENT_RESTART_RADIO = BASE + 26;
-    public static final int EVENT_SET_INTERNAL_DATA_ENABLE = BASE + 27;
     public static final int EVENT_CLEAN_UP_ALL_CONNECTIONS = BASE + 29;
-    public static final int CMD_SET_USER_DATA_ENABLE = BASE + 30;
-    public static final int CMD_SET_POLICY_DATA_ENABLE = BASE + 32;
     public static final int EVENT_ICC_CHANGED = BASE + 33;
-    public static final int EVENT_DISCONNECT_DC_RETRYING = BASE + 34;
     public static final int EVENT_DATA_SETUP_COMPLETE_ERROR = BASE + 35;
     public static final int CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA = BASE + 36;
     public static final int CMD_ENABLE_MOBILE_PROVISIONING = BASE + 37;
@@ -93,14 +86,12 @@
     public static final int CMD_NET_STAT_POLL = BASE + 40;
     public static final int EVENT_DATA_RAT_CHANGED = BASE + 41;
     public static final int CMD_CLEAR_PROVISIONING_SPINNER = BASE + 42;
-    public static final int EVENT_DEVICE_PROVISIONED_CHANGE = BASE + 43;
     public static final int EVENT_REDIRECTION_DETECTED = BASE + 44;
     public static final int EVENT_PCO_DATA_RECEIVED = BASE + 45;
-    public static final int EVENT_SET_CARRIER_DATA_ENABLED = BASE + 46;
+    public static final int EVENT_DATA_ENABLED_CHANGED = BASE + 46;
     public static final int EVENT_DATA_RECONNECT = BASE + 47;
     public static final int EVENT_ROAMING_SETTING_CHANGE = BASE + 48;
     public static final int EVENT_DATA_SERVICE_BINDING_CHANGED = BASE + 49;
-    public static final int EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE = BASE + 50;
 
     /***** Constants *****/
 
diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
index 69ff329..84c0e64 100644
--- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
@@ -21,6 +21,7 @@
 import android.util.SparseIntArray;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.telephony.Rlog;
 
 import java.nio.ByteBuffer;
@@ -587,7 +588,7 @@
      * Additionally, in some country(ex. Korea), there are non-ASCII or MBCS characters.
      * If a character set is given, characters in data are treat as MBCS.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static String
     gsm8BitUnpackedToString(byte[] data, int offset, int length, String characterset) {
         boolean isMbcs = false;
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 78fc0bc4..00cf9c3 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -55,5 +55,6 @@
     void onPreferredDataSubIdChanged(in int subId);
     void onRadioPowerStateChanged(in int state);
     void onEmergencyNumberListChanged(in Map emergencyNumberList);
+    void onCallDisconnectCauseChanged(in int disconnectCause, in int preciseDisconnectCause);
 }
 
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 65d1a920..d169b7d 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -104,7 +104,7 @@
     /**
      * @see android.telephony.SubscriptionManager#requestEmbeddedSubscriptionInfoListRefresh
      */
-    oneway void requestEmbeddedSubscriptionInfoListRefresh();
+    oneway void requestEmbeddedSubscriptionInfoListRefresh(int cardId);
 
     /**
      * Add a new SubscriptionInfo to subinfo database if needed
@@ -162,7 +162,7 @@
      * @param subId the unique SubscriptionInfo index in database
      * @return the number of records updated
      */
-    int setOpportunistic(boolean opportunistic, int subId);
+    int setOpportunistic(boolean opportunistic, int subId, String callingPackage);
 
     /**
      * Inform SubscriptionManager that subscriptions in the list are bundled
@@ -190,7 +190,7 @@
      * @param subId the unique SubscriptionInfo index in database
      * @return the number of records updated
      */
-    int setMetered(boolean isMetered, int subId);
+    int setMetered(boolean isMetered, int subId, String callingPackage);
 
     /**
      * Set which subscription is preferred for cellular data. It's
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 46366d6..c5d82c5 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -56,6 +56,7 @@
 import java.util.List;
 import java.util.Map;
 
+import android.telephony.UiccCardInfo;
 import android.telephony.UiccSlotInfo;
 
 /**
@@ -1408,6 +1409,14 @@
     void carrierActionReportDefaultNetworkStatus(int subId, boolean report);
 
     /**
+     * Action set from carrier signalling broadcast receivers to reset all carrier actions.
+     * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+     * @param subId the subscription ID that this action applies to.
+     * @hide
+     */
+    void carrierActionResetAll(int subId);
+
+    /**
      * Get aggregated video call data usage since boot.
      * Permissions android.Manifest.permission.READ_NETWORK_USAGE_HISTORY is required.
      *
@@ -1486,6 +1495,17 @@
     int getCardIdForDefaultEuicc(int subId, String callingPackage); 
 
     /**
+     * Gets information about currently inserted UICCs and eUICCs. See {@link UiccCardInfo} for more
+     * details on the kind of information available.
+     *
+     * @return UiccCardInfo an array of UiccCardInfo objects, representing information on the
+     * currently inserted UICCs and eUICCs.
+     *
+     * @hide
+     */
+    UiccCardInfo[] getUiccCardsInfo();
+
+    /**
      * Get slot info for all the UICC slots.
      * @return UiccSlotInfo array.
      * @hide
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index d9f5c3f..02a6f31 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -50,13 +50,13 @@
     void notifyDataActivity(int state);
     void notifyDataActivityForSubscriber(in int subId, int state);
     void notifyDataConnection(int state, boolean isDataConnectivityPossible,
-            String reason, String apn, String apnType, in LinkProperties linkProperties,
+            String apn, String apnType, in LinkProperties linkProperties,
             in NetworkCapabilities networkCapabilities, int networkType, boolean roaming);
     void notifyDataConnectionForSubscriber(int subId, int state, boolean isDataConnectivityPossible,
-            String reason, String apn, String apnType, in LinkProperties linkProperties,
+            String apn, String apnType, in LinkProperties linkProperties,
             in NetworkCapabilities networkCapabilities, int networkType, boolean roaming);
-    void notifyDataConnectionFailed(String reason, String apnType);
-    void notifyDataConnectionFailedForSubscriber(int subId, String reason, String apnType);
+    void notifyDataConnectionFailed(String apnType);
+    void notifyDataConnectionFailedForSubscriber(int subId, String apnType);
     void notifyCellLocation(in Bundle cellLocation);
     void notifyCellLocationForSubscriber(in int subId, in Bundle cellLocation);
     void notifyOtaspChanged(in int otaspMode);
@@ -67,7 +67,7 @@
     void notifyPreciseCallState(int ringingCallState, int foregroundCallState,
             int backgroundCallState);
     void notifyDisconnectCause(int disconnectCause, int preciseDisconnectCause);
-    void notifyPreciseDataConnectionFailed(String reason, String apnType, String apn,
+    void notifyPreciseDataConnectionFailed(String apnType, String apn,
             String failCause);
     void notifyCellInfoForSubscriber(in int subId, in List<CellInfo> cellInfo);
     void notifySrvccStateChanged(in int subId, in int lteState);
diff --git a/telephony/java/com/android/internal/telephony/OperatorInfo.java b/telephony/java/com/android/internal/telephony/OperatorInfo.java
index a47e2b0..59c39b1 100644
--- a/telephony/java/com/android/internal/telephony/OperatorInfo.java
+++ b/telephony/java/com/android/internal/telephony/OperatorInfo.java
@@ -17,6 +17,7 @@
 package com.android.internal.telephony;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -91,7 +92,7 @@
                 operatorNumeric, rilStateToState(stateString));
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public OperatorInfo(String operatorAlphaLong,
             String operatorAlphaShort,
             String operatorNumeric) {
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index 21f3b92..e87d28c 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -79,8 +79,6 @@
     public static final int SIM_ACTIVATION_TYPE_DATA = 1;
 
     public static final String PHONE_NAME_KEY = "phoneName";
-    public static final String FAILURE_REASON_KEY = "reason";
-    public static final String STATE_CHANGE_REASON_KEY = "reason";
     public static final String DATA_NETWORK_TYPE_KEY = "networkType";
     public static final String DATA_FAILURE_CAUSE_KEY = "failCause";
     public static final String DATA_APN_TYPE_KEY = "apnType";
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 2ebe870..1c103a9 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -128,32 +128,108 @@
     int OEM_ERROR_25 = 525;
 
     /* NETWORK_MODE_* See ril.h RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE */
-    int NETWORK_MODE_WCDMA_PREF     = 0; /* GSM/WCDMA (WCDMA preferred) */
-    int NETWORK_MODE_GSM_ONLY       = 1; /* GSM only */
-    int NETWORK_MODE_WCDMA_ONLY     = 2; /* WCDMA only */
-    int NETWORK_MODE_GSM_UMTS       = 3; /* GSM/WCDMA (auto mode, according to PRL)
-                                            AVAILABLE Application Settings menu*/
-    int NETWORK_MODE_CDMA           = 4; /* CDMA and EvDo (auto mode, according to PRL)
-                                            AVAILABLE Application Settings menu*/
-    int NETWORK_MODE_CDMA_NO_EVDO   = 5; /* CDMA only */
-    int NETWORK_MODE_EVDO_NO_CDMA   = 6; /* EvDo only */
-    int NETWORK_MODE_GLOBAL         = 7; /* GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL)
-                                            AVAILABLE Application Settings menu*/
-    int NETWORK_MODE_LTE_CDMA_EVDO  = 8; /* LTE, CDMA and EvDo */
-    int NETWORK_MODE_LTE_GSM_WCDMA  = 9; /* LTE, GSM/WCDMA */
-    int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA = 10; /* LTE, CDMA, EvDo, GSM/WCDMA */
-    int NETWORK_MODE_LTE_ONLY       = 11; /* LTE Only mode. */
-    int NETWORK_MODE_LTE_WCDMA      = 12; /* LTE/WCDMA */
-    int NETWORK_MODE_TDSCDMA_ONLY            = 13; /* TD-SCDMA only */
-    int NETWORK_MODE_TDSCDMA_WCDMA           = 14; /* TD-SCDMA and WCDMA */
-    int NETWORK_MODE_LTE_TDSCDMA             = 15; /* TD-SCDMA and LTE */
-    int NETWORK_MODE_TDSCDMA_GSM             = 16; /* TD-SCDMA and GSM */
-    int NETWORK_MODE_LTE_TDSCDMA_GSM         = 17; /* TD-SCDMA,GSM and LTE */
-    int NETWORK_MODE_TDSCDMA_GSM_WCDMA       = 18; /* TD-SCDMA, GSM/WCDMA */
-    int NETWORK_MODE_LTE_TDSCDMA_WCDMA       = 19; /* TD-SCDMA, WCDMA and LTE */
-    int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA   = 20; /* TD-SCDMA, GSM/WCDMA and LTE */
-    int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA  = 21; /*TD-SCDMA,EvDo,CDMA,GSM/WCDMA*/
-    int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 22; /* TD-SCDMA/LTE/GSM/WCDMA, CDMA, and EvDo */
+    /** GSM, WCDMA (WCDMA preferred) */
+    int NETWORK_MODE_WCDMA_PREF = 0;
+
+    /** GSM only */
+    int NETWORK_MODE_GSM_ONLY = 1;
+
+    /** WCDMA only */
+    int NETWORK_MODE_WCDMA_ONLY = 2;
+
+    /** GSM, WCDMA (auto mode, according to PRL) */
+    int NETWORK_MODE_GSM_UMTS = 3;
+
+    /** CDMA and EvDo (auto mode, according to PRL) */
+    int NETWORK_MODE_CDMA = 4;
+
+    /** CDMA only */
+    int NETWORK_MODE_CDMA_NO_EVDO = 5;
+
+    /** EvDo only */
+    int NETWORK_MODE_EVDO_NO_CDMA = 6;
+
+    /** GSM, WCDMA, CDMA, and EvDo (auto mode, according to PRL) */
+    int NETWORK_MODE_GLOBAL = 7;
+
+    /** LTE, CDMA and EvDo */
+    int NETWORK_MODE_LTE_CDMA_EVDO = 8;
+
+    /** LTE, GSM and WCDMA */
+    int NETWORK_MODE_LTE_GSM_WCDMA = 9;
+
+    /** LTE, CDMA, EvDo, GSM, and WCDMA */
+    int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA = 10;
+
+    /** LTE only mode. */
+    int NETWORK_MODE_LTE_ONLY = 11;
+
+    /** LTE and WCDMA */
+    int NETWORK_MODE_LTE_WCDMA = 12;
+
+    /** TD-SCDMA only */
+    int NETWORK_MODE_TDSCDMA_ONLY = 13;
+
+    /** TD-SCDMA and WCDMA */
+    int NETWORK_MODE_TDSCDMA_WCDMA = 14;
+
+    /** LTE and TD-SCDMA*/
+    int NETWORK_MODE_LTE_TDSCDMA = 15;
+
+    /** TD-SCDMA and GSM */
+    int NETWORK_MODE_TDSCDMA_GSM = 16;
+
+    /** TD-SCDMA, GSM and LTE */
+    int NETWORK_MODE_LTE_TDSCDMA_GSM = 17;
+
+    /** TD-SCDMA, GSM and WCDMA */
+    int NETWORK_MODE_TDSCDMA_GSM_WCDMA = 18;
+
+    /** LTE, TD-SCDMA and WCDMA */
+    int NETWORK_MODE_LTE_TDSCDMA_WCDMA = 19;
+
+    /** LTE, TD-SCDMA, GSM, and WCDMA */
+    int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA = 20;
+
+    /** TD-SCDMA, CDMA, EVDO, GSM and WCDMA */
+    int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 21;
+
+    /** LTE, TDCSDMA, CDMA, EVDO, GSM and WCDMA */
+    int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 22;
+
+    /** NR 5G only mode */
+    int NETWORK_MODE_NR_ONLY = 23;
+
+    /** NR 5G, LTE */
+    int NETWORK_MODE_NR_LTE = 24;
+
+    /** NR 5G, LTE, CDMA and EvDo */
+    int NETWORK_MODE_NR_LTE_CDMA_EVDO = 25;
+
+    /** NR 5G, LTE, GSM and WCDMA */
+    int NETWORK_MODE_NR_LTE_GSM_WCDMA = 26;
+
+    /** NR 5G, LTE, CDMA, EvDo, GSM and WCDMA */
+    int NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA = 27;
+
+    /** NR 5G, LTE and WCDMA */
+    int NETWORK_MODE_NR_LTE_WCDMA = 28;
+
+    /** NR 5G, LTE and TDSCDMA */
+    int NETWORK_MODE_NR_LTE_TDSCDMA = 29;
+
+    /** NR 5G, LTE, TD-SCDMA and GSM */
+    int NETWORK_MODE_NR_LTE_TDSCDMA_GSM = 30;
+
+    /** NR 5G, LTE, TD-SCDMA, WCDMA */
+    int NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA = 31;
+
+    /** NR 5G, LTE, TD-SCDMA, GSM and WCDMA */
+    int NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA = 32;
+
+    /** NR 5G, LTE, TD-SCDMA, CDMA, EVDO, GSM and WCDMA */
+    int NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 33;
+
     int PREFERRED_NETWORK_MODE = Integer.parseInt(TelephonyManager.getTelephonyProperty(0,
             "ro.telephony.default_network", Integer.toString(NETWORK_MODE_WCDMA_PREF)));
 
@@ -422,6 +498,7 @@
     int RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA = 203;
     int RIL_REQUEST_SET_PREFERRED_DATA_MODEM = 204;
     int RIL_REQUEST_EMERGENCY_DIAL = 205;
+    int RIL_REQUEST_GET_PHONE_CAPABILITY = 206;
 
     /* Responses begin */
     int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
diff --git a/telephony/java/com/android/internal/telephony/SmsMessageBase.java b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
index 7b1ead9..190eac4 100644
--- a/telephony/java/com/android/internal/telephony/SmsMessageBase.java
+++ b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
@@ -23,6 +23,7 @@
 import java.util.Arrays;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.provider.Telephony;
 import android.telephony.SmsMessage;
 import android.text.Emoji;
@@ -118,7 +119,7 @@
      * Returns the address of the SMS service center that relayed this message
      * or null if there is none.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public String getServiceCenterAddress() {
         return mScAddress;
     }
@@ -223,14 +224,14 @@
     /**
      * Get protocol identifier.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public abstract int getProtocolIdentifier();
 
     /**
      * See TS 23.040 9.2.3.9 returns true if this is a "replace short message"
      * SMS
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public abstract boolean isReplace();
 
     /**
@@ -302,7 +303,7 @@
      *         See TS 23.040, 9.9.2.3.15 for a description of other possible
      *         values.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public abstract int getStatus();
 
     /**
@@ -315,7 +316,7 @@
      * Returns true iff the <code>TP-Reply-Path</code> bit is set in
      * this message.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public abstract boolean isReplyPathPresent();
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index 553e3fb..0edc002 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -284,8 +284,6 @@
      */
     private static boolean reportAccessDeniedToReadIdentifiers(Context context, int subId, int pid,
             int uid, String callingPackage, String message) {
-        Log.wtf(LOG_TAG,
-                "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message);
         // If the device identifier check is enabled then enforce the new access requirements for
         // both 1P and 3P apps.
         boolean enableDeviceIdentifierCheck = Settings.Global.getInt(context.getContentResolver(),
@@ -295,17 +293,40 @@
         boolean relax3PDeviceIdentifierCheck = Settings.Global.getInt(context.getContentResolver(),
                 Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED, 0) == 1;
         boolean is3PApp = true;
+        // Also check if the application is a preloaded non-privileged app; if so there is a
+        // separate setting to relax the check for these apps to ensure users can relax the check
+        // for 3P or non-priv apps as needed while continuing to test the other.
+        boolean relaxNonPrivDeviceIdentifierCheck = Settings.Global.getInt(
+                context.getContentResolver(),
+                Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED, 0) == 1;
+        boolean isNonPrivApp = false;
         ApplicationInfo callingPackageInfo = null;
         try {
             callingPackageInfo = context.getPackageManager().getApplicationInfo(callingPackage, 0);
-            if (callingPackageInfo.isSystemApp()) {
+            if (callingPackageInfo.isPrivilegedApp()) {
                 is3PApp = false;
+            } else if (callingPackageInfo.isSystemApp()) {
+                is3PApp = false;
+                isNonPrivApp = true;
             }
         } catch (PackageManager.NameNotFoundException e) {
             // If the application info for the calling package could not be found then assume the
             // calling app is a 3P app to detect any issues with the check
+            Log.e(LOG_TAG, "Exception caught obtaining package info for package " + callingPackage,
+                    e);
         }
-        if (enableDeviceIdentifierCheck || (is3PApp && !relax3PDeviceIdentifierCheck)) {
+        Log.wtf(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message
+                + ":is3PApp=" + is3PApp + ":isNonPrivApp=" + isNonPrivApp);
+        // The new Q restrictions for device identifier access will be enforced if any of the
+        // following are true:
+        // - The PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED setting has been set.
+        // - The app requesting a device identifier is not a preloaded app (3P), and the
+        //   PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED setting has not been set.
+        // - The app requesting a device identifier is a preloaded app but is not a privileged app,
+        //   and the PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED setting has not been set.
+        if (enableDeviceIdentifierCheck
+                || (is3PApp && !relax3PDeviceIdentifierCheck)
+                || (isNonPrivApp && !relaxNonPrivDeviceIdentifierCheck)) {
             boolean targetQBehaviorDisabled = Settings.Global.getInt(context.getContentResolver(),
                     Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED, 0) == 0;
             if (callingPackage != null) {
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index 0a0ad90..870a689 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -32,7 +32,7 @@
     String getEid();
     int getOtaStatus();
     oneway void downloadSubscription(in DownloadableSubscription subscription,
-        boolean switchAfterDownload, String callingPackage, in PendingIntent callbackIntent);
+        boolean switchAfterDownload, String callingPackage, in Bundle resolvedBundle, in PendingIntent callbackIntent);
     EuiccInfo getEuiccInfo();
     oneway void deleteSubscription(int subscriptionId, String callingPackage,
         in PendingIntent callbackIntent);
diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java
index 0ac35bc..e9a5ff7 100644
--- a/test-mock/src/android/test/mock/MockContentProvider.java
+++ b/test-mock/src/android/test/mock/MockContentProvider.java
@@ -119,7 +119,7 @@
 
         @Override
         public IBinder asBinder() {
-            throw new UnsupportedOperationException();
+            return MockContentProvider.this.getIContentProviderBinder();
         }
 
         @Override
@@ -279,6 +279,13 @@
     }
 
     /**
+     * @hide
+     */
+    public IBinder getIContentProviderBinder() {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    /**
      * Like {@link #attachInfo(Context, android.content.pm.ProviderInfo)}, but for use
      * when directly instantiating the provider for testing.
      *
diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java
index 89734e3..226c0b8 100644
--- a/test-mock/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -18,8 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.app.PackageInstallObserver;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -52,7 +50,6 @@
 import android.content.res.XmlResourceParser;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.net.Uri;
 import android.os.Handler;
 import android.os.PersistableBundle;
 import android.os.UserHandle;
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewActivity.java
index 1548d6e..f7c60fc 100644
--- a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewActivity.java
+++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewActivity.java
@@ -20,6 +20,7 @@
 import android.app.ActivityView;
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.Parcelable;
 import android.widget.Button;
 
 public class ActivityViewActivity extends Activity {
@@ -41,6 +42,10 @@
             final Intent intent = Intent.makeMainActivity(null);
             final Intent chooser = Intent.createChooser(intent,
                     "Pick an app to launch in ActivityView");
+            chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Parcelable[] {
+                    new Intent(Intent.ACTION_MAIN)
+                            .addCategory("com.android.internal.category.PLATLOGO")
+            });
             if (intent.resolveActivity(getPackageManager()) != null) {
                 chooser.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                         | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
diff --git a/tests/RcsTests/Android.mk b/tests/RcsTests/Android.mk
index adc7cab..7b348d7 100644
--- a/tests/RcsTests/Android.mk
+++ b/tests/RcsTests/Android.mk
@@ -11,7 +11,7 @@
 
 LOCAL_CERTIFICATE := platform
 LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-LOCAL_STATIC_JAVA_LIBRARIES := junit android-support-test mockito-target-minus-junit4
+LOCAL_STATIC_JAVA_LIBRARIES := junit android-support-test mockito-target-minus-junit4 truth-prebuilt
 
 include $(BUILD_PACKAGE)
 
diff --git a/tests/RcsTests/src/com/android/tests/rcs/RcsMessageStoreTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java
similarity index 97%
rename from tests/RcsTests/src/com/android/tests/rcs/RcsMessageStoreTest.java
rename to tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java
index 290e04c..44277ed 100644
--- a/tests/RcsTests/src/com/android/tests/rcs/RcsMessageStoreTest.java
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.tests.rcs;
+package com.android.tests.ims;
 
 import android.support.test.runner.AndroidJUnit4;
 import android.telephony.ims.RcsMessageStore;
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java
new file mode 100644
index 0000000..a890a38
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsParticipant;
+import android.telephony.ims.RcsThreadQueryParameters;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsThreadQueryParametersTest {
+    private RcsThreadQueryParameters mRcsThreadQueryParameters;
+    @Mock RcsParticipant mMockParticipant;
+
+    @Test
+    public void testUnparceling() {
+        String key = "some key";
+        mRcsThreadQueryParameters = RcsThreadQueryParameters.builder()
+                .isGroupThread(true)
+                .withParticipant(mMockParticipant)
+                .limitResultsTo(50)
+                .sort(true)
+                .build();
+
+        Bundle bundle = new Bundle();
+        bundle.putParcelable(key, mRcsThreadQueryParameters);
+        mRcsThreadQueryParameters = bundle.getParcelable(key);
+
+        assertThat(mRcsThreadQueryParameters.isGroupThread()).isTrue();
+        assertThat(mRcsThreadQueryParameters.getRcsParticipants()).contains(mMockParticipant);
+        assertThat(mRcsThreadQueryParameters.getLimit()).isEqualTo(50);
+        assertThat(mRcsThreadQueryParameters.isAscending()).isTrue();
+    }
+}
diff --git a/tests/RollbackTest/Android.mk b/tests/RollbackTest/Android.mk
new file mode 100644
index 0000000..34aa258
--- /dev/null
+++ b/tests/RollbackTest/Android.mk
@@ -0,0 +1,81 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+# RollbackTestAppAv1.apk
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
+LOCAL_MANIFEST_FILE := TestApp/Av1.xml
+LOCAL_PACKAGE_NAME := RollbackTestAppAv1
+include $(BUILD_PACKAGE)
+ROLLBACK_TEST_APP_AV1 := $(LOCAL_INSTALLED_MODULE)
+
+# RollbackTestAppAv2.apk
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
+LOCAL_MANIFEST_FILE := TestApp/Av2.xml
+LOCAL_PACKAGE_NAME := RollbackTestAppAv2
+include $(BUILD_PACKAGE)
+ROLLBACK_TEST_APP_AV2 := $(LOCAL_INSTALLED_MODULE)
+
+# RollbackTestAppBv1.apk
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
+LOCAL_MANIFEST_FILE := TestApp/Bv1.xml
+LOCAL_PACKAGE_NAME := RollbackTestAppBv1
+include $(BUILD_PACKAGE)
+ROLLBACK_TEST_APP_BV1 := $(LOCAL_INSTALLED_MODULE)
+
+# RollbackTestAppBv2.apk
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
+LOCAL_MANIFEST_FILE := TestApp/Bv2.xml
+LOCAL_PACKAGE_NAME := RollbackTestAppBv2
+include $(BUILD_PACKAGE)
+ROLLBACK_TEST_APP_BV2 := $(LOCAL_INSTALLED_MODULE)
+
+# RollbackTest
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_PACKAGE_NAME := RollbackTest
+LOCAL_MODULE_TAGS := tests
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_COMPATIBILITY_SUITE := general-tests
+LOCAL_JAVA_RESOURCE_FILES := \
+  $(ROLLBACK_TEST_APP_AV1) \
+  $(ROLLBACK_TEST_APP_AV2) \
+  $(ROLLBACK_TEST_APP_BV1) \
+  $(ROLLBACK_TEST_APP_BV2)
+LOCAL_SDK_VERSION := system_current
+LOCAL_TEST_CONFIG := RollbackTest.xml
+include $(BUILD_PACKAGE)
+
+# Clean up local variables
+ROLLBACK_TEST_APP_AV1 :=
+ROLLBACK_TEST_APP_AV2 :=
+ROLLBACK_TEST_APP_BV1 :=
+ROLLBACK_TEST_APP_BV2 :=
diff --git a/tests/RollbackTest/AndroidManifest.xml b/tests/RollbackTest/AndroidManifest.xml
new file mode 100644
index 0000000..e57a768
--- /dev/null
+++ b/tests/RollbackTest/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.tests.rollback" >
+
+    <application>
+        <receiver android:name="com.android.tests.rollback.LocalIntentSender"
+                  android:exported="true" />
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.tests.rollback"
+                     android:label="Rollback Test"/>
+
+</manifest>
diff --git a/tests/RollbackTest/RollbackTest.xml b/tests/RollbackTest/RollbackTest.xml
new file mode 100644
index 0000000..adbad56
--- /dev/null
+++ b/tests/RollbackTest/RollbackTest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs the rollback tests">
+    <option name="test-suite-tag" value="RollbackTest" />
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="RollbackTest.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.tests.rollback" />
+        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+    </test>
+</configuration>
diff --git a/tests/RollbackTest/TEST_MAPPING b/tests/RollbackTest/TEST_MAPPING
new file mode 100644
index 0000000..c1d95ac
--- /dev/null
+++ b/tests/RollbackTest/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "RollbackTest"
+    }
+  ]
+}
diff --git a/tests/RollbackTest/TestApp/Av1.xml b/tests/RollbackTest/TestApp/Av1.xml
new file mode 100644
index 0000000..996d831
--- /dev/null
+++ b/tests/RollbackTest/TestApp/Av1.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.tests.rollback.testapp.A"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+
+    <uses-sdk android:minSdkVersion="19" />
+
+    <application android:label="Rollback Test App A v1">
+        <meta-data android:name="version" android:value="1" />
+        <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
+                  android:exported="true" />
+        <activity android:name="com.android.tests.rollback.testapp.MainActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/RollbackTest/TestApp/Av2.xml b/tests/RollbackTest/TestApp/Av2.xml
new file mode 100644
index 0000000..21c7260
--- /dev/null
+++ b/tests/RollbackTest/TestApp/Av2.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.tests.rollback.testapp.A"
+    android:versionCode="2"
+    android:versionName="2.0" >
+
+
+    <uses-sdk android:minSdkVersion="19" />
+
+    <application android:label="Rollback Test App A v2">
+        <meta-data android:name="version" android:value="2" />
+        <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
+                  android:exported="true" />
+        <activity android:name="com.android.tests.rollback.testapp.MainActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/RollbackTest/TestApp/Bv1.xml b/tests/RollbackTest/TestApp/Bv1.xml
new file mode 100644
index 0000000..de0fd0d
--- /dev/null
+++ b/tests/RollbackTest/TestApp/Bv1.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.tests.rollback.testapp.B"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+
+    <uses-sdk android:minSdkVersion="19" />
+
+    <application android:label="Rollback Test App B v1">
+        <meta-data android:name="version" android:value="1" />
+        <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
+                  android:exported="true" />
+        <activity android:name="com.android.tests.rollback.testapp.MainActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/RollbackTest/TestApp/Bv2.xml b/tests/RollbackTest/TestApp/Bv2.xml
new file mode 100644
index 0000000..6c2e66a
--- /dev/null
+++ b/tests/RollbackTest/TestApp/Bv2.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.tests.rollback.testapp.B"
+    android:versionCode="2"
+    android:versionName="2.0" >
+
+
+    <uses-sdk android:minSdkVersion="19" />
+
+    <application android:label="Rollback Test App B v2">
+        <meta-data android:name="version" android:value="2" />
+        <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
+                  android:exported="true" />
+        <activity android:name="com.android.tests.rollback.testapp.MainActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/MainActivity.java b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/MainActivity.java
new file mode 100644
index 0000000..9f1a060
--- /dev/null
+++ b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/MainActivity.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.rollback.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * A test app for testing apk rollback support.
+ */
+public class MainActivity extends Activity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        try {
+            new ProcessUserData().processUserData(this);
+        } catch (ProcessUserData.UserDataException e) {
+            throw new AssertionError("Failed to process app user data", e);
+        }
+    }
+}
diff --git a/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java
new file mode 100644
index 0000000..fde6a83
--- /dev/null
+++ b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.rollback.testapp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Scanner;
+
+/**
+ * A broadcast reciever to check for and update user app data version
+ * compatibility.
+ */
+public class ProcessUserData extends BroadcastReceiver {
+
+    /**
+     * Exception thrown in case of issue with user data.
+     */
+    public static class UserDataException extends Exception {
+        public UserDataException(String message) {
+            super(message);
+        }
+
+        public UserDataException(String message, Throwable cause) {
+           super(message, cause);
+        }
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        try {
+            processUserData(context);
+            setResultCode(1);
+        } catch (UserDataException e) {
+            setResultCode(0);
+            setResultData(e.getMessage());
+        }
+    }
+
+    /**
+     * Update the app's user data version to match the app version.
+     *
+     * @param context The application context.
+     * @throws UserDataException in case of problems with app user data.
+     */
+    public void processUserData(Context context) throws UserDataException {
+        int appVersion = 0;
+        try {
+            ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
+                    context.getPackageName(), PackageManager.GET_META_DATA);
+            Bundle bundle = appInfo.metaData;
+            appVersion = bundle.getInt("version");
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new UserDataException("Unable to get app version info", e);
+        }
+
+        // Read the version of the app's user data and ensure it is compatible
+        // with our version of the application.
+        File versionFile = new File(context.getFilesDir(), "version.txt");
+        try {
+            Scanner s = new Scanner(versionFile);
+            int userDataVersion = s.nextInt();
+            s.close();
+
+            if (userDataVersion > appVersion) {
+                throw new UserDataException("User data is from version " + userDataVersion
+                        + ", which is not compatible with this version " + appVersion
+                        + " of the RollbackTestApp");
+            }
+        } catch (FileNotFoundException e) {
+            // No problem. This is a fresh install of the app or the user data
+            // has been wiped.
+        }
+
+        // Record the current version of the app in the user data.
+        try {
+            PrintWriter pw = new PrintWriter(versionFile);
+            pw.println(appVersion);
+            pw.close();
+        } catch (IOException e) {
+            throw new UserDataException("Unable to write user data.", e);
+        }
+    }
+}
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/LocalIntentSender.java b/tests/RollbackTest/src/com/android/tests/rollback/LocalIntentSender.java
new file mode 100644
index 0000000..ddcf1da
--- /dev/null
+++ b/tests/RollbackTest/src/com/android/tests/rollback/LocalIntentSender.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.rollback;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.support.test.InstrumentationRegistry;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * Make IntentSender that sends intent locally.
+ */
+public class LocalIntentSender extends BroadcastReceiver {
+
+    private static final String TAG = "RollbackTest";
+
+    private static final BlockingQueue<Intent> sIntentSenderResults = new LinkedBlockingQueue<>();
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        sIntentSenderResults.add(intent);
+    }
+
+    /**
+     * Get a LocalIntentSender.
+     */
+    static IntentSender getIntentSender() {
+        Context context = InstrumentationRegistry.getContext();
+        Intent intent = new Intent(context, LocalIntentSender.class);
+        PendingIntent pending = PendingIntent.getBroadcast(context, 0, intent, 0);
+        return pending.getIntentSender();
+    }
+
+    /**
+     * Returns the most recent Intent sent by a LocalIntentSender.
+     */
+    static Intent getIntentSenderResult() throws InterruptedException {
+        return sIntentSenderResults.take();
+    }
+}
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackBroadcastReceiver.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackBroadcastReceiver.java
new file mode 100644
index 0000000..d3c39f0
--- /dev/null
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackBroadcastReceiver.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.rollback;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A broadcast receiver that can be used to get
+ * ACTION_PACKAGE_ROLLBACK_EXECUTED broadcasts.
+ */
+class RollbackBroadcastReceiver extends BroadcastReceiver {
+
+    private static final String TAG = "RollbackTest";
+
+    private final BlockingQueue<Intent> mRollbackBroadcasts = new LinkedBlockingQueue<>();
+
+    /**
+     * Creates a RollbackBroadcastReceiver and registers it with the given
+     * context.
+     */
+    RollbackBroadcastReceiver() {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED);
+        filter.addDataScheme("package");
+        InstrumentationRegistry.getContext().registerReceiver(this, filter);
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Log.i(TAG, "Received rollback broadcast intent");
+        mRollbackBroadcasts.add(intent);
+    }
+
+    /**
+     * Polls for at most the given amount of time for the next rollback
+     * broadcast.
+     */
+    Intent poll(long timeout, TimeUnit unit) throws InterruptedException {
+        return mRollbackBroadcasts.poll(timeout, unit);
+    }
+
+    /**
+     * Unregisters this broadcast receiver.
+     */
+    void unregister() {
+        InstrumentationRegistry.getContext().unregisterReceiver(this);
+    }
+}
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
new file mode 100644
index 0000000..0ccfb19
--- /dev/null
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -0,0 +1,541 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.rollback;
+
+import android.Manifest;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.rollback.RollbackInfo;
+import android.content.rollback.RollbackManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test system Rollback APIs.
+ * TODO: Should this be a cts test instead? Where should it live?
+ */
+@RunWith(JUnit4.class)
+public class RollbackTest {
+
+    private static final String TAG = "RollbackTest";
+
+    private static final String TEST_APP_A = "com.android.tests.rollback.testapp.A";
+    private static final String TEST_APP_B = "com.android.tests.rollback.testapp.B";
+
+    /**
+     * Test basic rollbacks.
+     */
+    @Test
+    public void testBasic() throws Exception {
+        // Make sure an app can't listen to or disturb the internal
+        // ACTION_PACKAGE_ENABLE_ROLLBACK broadcast.
+        Context context = InstrumentationRegistry.getContext();
+        IntentFilter enableRollbackFilter = new IntentFilter();
+        enableRollbackFilter.addAction("android.intent.action.PACKAGE_ENABLE_ROLLBACK");
+        enableRollbackFilter.addDataType("application/vnd.android.package-archive");
+        enableRollbackFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+        BroadcastReceiver enableRollbackReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                abortBroadcast();
+            }
+        };
+        context.registerReceiver(enableRollbackReceiver, enableRollbackFilter);
+
+        try {
+            RollbackTestUtils.adoptShellPermissionIdentity(
+                    Manifest.permission.INSTALL_PACKAGES,
+                    Manifest.permission.DELETE_PACKAGES,
+                    Manifest.permission.MANAGE_ROLLBACKS);
+
+            // Register a broadcast receiver for notification when the rollback is
+            // done executing.
+            RollbackBroadcastReceiver broadcastReceiver = new RollbackBroadcastReceiver();
+            RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
+            // Uninstall TEST_APP_A
+            RollbackTestUtils.uninstall(TEST_APP_A);
+            assertEquals(-1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+            // TODO: There is currently a race condition between when the app is
+            // uninstalled and when rollback manager deletes the rollback. Fix it
+            // so that's not the case!
+            for (int i = 0; i < 5; ++i) {
+                for (RollbackInfo info : rm.getRecentlyExecutedRollbacks()) {
+                    if (TEST_APP_A.equals(info.targetPackage.packageName)) {
+                        Log.i(TAG, "Sleeping 1 second to wait for uninstall to take effect.");
+                        Thread.sleep(1000);
+                        break;
+                    }
+                }
+            }
+
+            // The app should not be available for rollback.
+            assertNull(rm.getAvailableRollback(TEST_APP_A));
+            assertFalse(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
+
+            // There should be no recently executed rollbacks for this package.
+            for (RollbackInfo info : rm.getRecentlyExecutedRollbacks()) {
+                assertNotEquals(TEST_APP_A, info.targetPackage.packageName);
+            }
+
+            // Install v1 of the app (without rollbacks enabled).
+            RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+            assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+            // Upgrade from v1 to v2, with rollbacks enabled.
+            RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+            // The app should now be available for rollback.
+            assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
+            RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
+            assertNotNull(rollback);
+            assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
+            assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+            assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+
+            // We should not have received any rollback requests yet.
+            // TODO: Possibly flaky if, by chance, some other app on device
+            // happens to be rolled back at the same time?
+            assertNull(broadcastReceiver.poll(0, TimeUnit.SECONDS));
+
+            // Roll back the app.
+            RollbackTestUtils.rollback(rollback);
+            assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+            // Verify we received a broadcast for the rollback.
+            // TODO: Race condition between the timeout and when the broadcast is
+            // received could lead to test flakiness.
+            Intent broadcast = broadcastReceiver.poll(5, TimeUnit.SECONDS);
+            assertNotNull(broadcast);
+            assertEquals(TEST_APP_A, broadcast.getData().getSchemeSpecificPart());
+            assertNull(broadcastReceiver.poll(0, TimeUnit.SECONDS));
+
+            // Verify the recent rollback has been recorded.
+            rollback = null;
+            for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
+                if (TEST_APP_A.equals(r.targetPackage.packageName)) {
+                    assertNull(rollback);
+                    rollback = r;
+                }
+            }
+            assertNotNull(rollback);
+            assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
+            assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+            assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+
+            broadcastReceiver.unregister();
+            context.unregisterReceiver(enableRollbackReceiver);
+        } finally {
+            RollbackTestUtils.dropShellPermissionIdentity();
+        }
+    }
+
+    /**
+     * Test that rollback data is properly persisted.
+     */
+    @Test
+    public void testRollbackDataPersistence() throws Exception {
+        try {
+            RollbackTestUtils.adoptShellPermissionIdentity(
+                    Manifest.permission.INSTALL_PACKAGES,
+                    Manifest.permission.DELETE_PACKAGES,
+                    Manifest.permission.MANAGE_ROLLBACKS);
+
+            RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
+            // Prep installation of TEST_APP_A
+            RollbackTestUtils.uninstall(TEST_APP_A);
+            RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+            RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+            // The app should now be available for rollback.
+            assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
+            RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
+            assertNotNull(rollback);
+            assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
+            assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+            assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+
+            // Reload the persisted data.
+            rm.reloadPersistedData();
+
+            // The app should still be available for rollback.
+            assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
+            rollback = rm.getAvailableRollback(TEST_APP_A);
+            assertNotNull(rollback);
+            assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
+            assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+            assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+
+            // Roll back the app.
+            RollbackTestUtils.rollback(rollback);
+            assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+            // Verify the recent rollback has been recorded.
+            rollback = null;
+            for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
+                if (TEST_APP_A.equals(r.targetPackage.packageName)) {
+                    assertNull(rollback);
+                    rollback = r;
+                }
+            }
+            assertNotNull(rollback);
+            assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
+            assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+            assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+
+            // Reload the persisted data.
+            rm.reloadPersistedData();
+
+            // Verify the recent rollback is still recorded.
+            rollback = null;
+            for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
+                if (TEST_APP_A.equals(r.targetPackage.packageName)) {
+                    assertNull(rollback);
+                    rollback = r;
+                }
+            }
+            assertNotNull(rollback);
+            assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
+            assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+            assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+        } finally {
+            RollbackTestUtils.dropShellPermissionIdentity();
+        }
+    }
+
+    /**
+     * Test explicit expiration of rollbacks.
+     * Does not test the scheduling aspects of rollback expiration.
+     */
+    @Test
+    public void testRollbackExpiration() throws Exception {
+        try {
+            RollbackTestUtils.adoptShellPermissionIdentity(
+                    Manifest.permission.INSTALL_PACKAGES,
+                    Manifest.permission.DELETE_PACKAGES,
+                    Manifest.permission.MANAGE_ROLLBACKS);
+
+            RollbackManager rm = RollbackTestUtils.getRollbackManager();
+            RollbackTestUtils.uninstall(TEST_APP_A);
+            RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+            RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+            // The app should now be available for rollback.
+            assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
+            RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
+            assertNotNull(rollback);
+            assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
+            assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+            assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+
+            // Expire the rollback.
+            rm.expireRollbackForPackage(TEST_APP_A);
+
+            // The rollback should no longer be available.
+            assertNull(rm.getAvailableRollback(TEST_APP_A));
+            assertFalse(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
+        } finally {
+            RollbackTestUtils.dropShellPermissionIdentity();
+        }
+    }
+
+    private static final String NO_RESPONSE = "NO RESPONSE";
+
+    // Calls into the test app to process user data.
+    // Asserts if the user data could not be processed or was version
+    // incompatible with the previously processed user data.
+    private void processUserData(String packageName) throws Exception {
+        Intent intent = new Intent();
+        intent.setComponent(new ComponentName(packageName,
+                    "com.android.tests.rollback.testapp.ProcessUserData"));
+        Context context = InstrumentationRegistry.getContext();
+
+        HandlerThread handlerThread = new HandlerThread("RollbackTestHandlerThread");
+        handlerThread.start();
+
+        // It can sometimes take a while after rollback before the app will
+        // receive this broadcast, so try a few times in a loop.
+        String result = NO_RESPONSE;
+        for (int i = 0; result.equals(NO_RESPONSE) && i < 5; ++i) {
+            BlockingQueue<String> resultQueue = new LinkedBlockingQueue<>();
+            context.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    if (getResultCode() == 1) {
+                        resultQueue.add("OK");
+                    } else {
+                        // If the test app doesn't receive the broadcast or
+                        // fails to set the result data, then getResultData
+                        // here returns the initial NO_RESPONSE data passed to
+                        // the sendOrderedBroadcast call.
+                        resultQueue.add(getResultData());
+                    }
+                }
+            }, new Handler(handlerThread.getLooper()), 0, NO_RESPONSE, null);
+
+            result = resultQueue.poll(10, TimeUnit.SECONDS);
+            if (result == null) {
+                result = "ProcessUserData broadcast timed out";
+            }
+        }
+
+        handlerThread.quit();
+        if (!"OK".equals(result)) {
+            fail(result);
+        }
+    }
+
+    /**
+     * Test that app user data is rolled back.
+     * TODO: Stop ignoring this test once user data rollback is supported.
+     */
+    @Ignore @Test
+    public void testUserDataRollback() throws Exception {
+        try {
+            RollbackTestUtils.adoptShellPermissionIdentity(
+                    Manifest.permission.INSTALL_PACKAGES,
+                    Manifest.permission.DELETE_PACKAGES,
+                    Manifest.permission.MANAGE_ROLLBACKS);
+
+            RollbackTestUtils.uninstall(TEST_APP_A);
+            RollbackTestUtils.install("RollbackTestAppV1.apk", false);
+            processUserData(TEST_APP_A);
+            RollbackTestUtils.install("RollbackTestAppV2.apk", true);
+            processUserData(TEST_APP_A);
+
+            RollbackManager rm = RollbackTestUtils.getRollbackManager();
+            RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
+            RollbackTestUtils.rollback(rollback);
+            processUserData(TEST_APP_A);
+        } finally {
+            RollbackTestUtils.dropShellPermissionIdentity();
+        }
+    }
+
+    /**
+     * Test restrictions on rollback broadcast sender.
+     * A random app should not be able to send a PACKAGE_ROLLBACK_EXECUTED broadcast.
+     */
+    @Test
+    public void testRollbackBroadcastRestrictions() throws Exception {
+        RollbackBroadcastReceiver broadcastReceiver = new RollbackBroadcastReceiver();
+        Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED,
+                Uri.fromParts("package", "com.android.tests.rollback.bogus", null));
+        try {
+            InstrumentationRegistry.getContext().sendBroadcast(broadcast);
+            fail("Succeeded in sending restricted broadcast from app context.");
+        } catch (SecurityException se) {
+            // Expected behavior.
+        }
+
+        // Confirm that we really haven't received the broadcast.
+        // TODO: How long to wait for the expected timeout?
+        assertNull(broadcastReceiver.poll(5, TimeUnit.SECONDS));
+
+        // TODO: Do we need to do this? Do we need to ensure this is always
+        // called, even when the test fails?
+        broadcastReceiver.unregister();
+    }
+
+    /**
+     * Regression test for rollback in the case when multiple apps are
+     * available for rollback at the same time.
+     */
+    @Test
+    public void testMultipleRollbackAvailable() throws Exception {
+        try {
+            RollbackTestUtils.adoptShellPermissionIdentity(
+                    Manifest.permission.INSTALL_PACKAGES,
+                    Manifest.permission.DELETE_PACKAGES,
+                    Manifest.permission.MANAGE_ROLLBACKS);
+            RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
+            // Prep installation of the test apps.
+            RollbackTestUtils.uninstall(TEST_APP_A);
+            RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+            RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+            RollbackTestUtils.uninstall(TEST_APP_B);
+            RollbackTestUtils.install("RollbackTestAppBv1.apk", false);
+            RollbackTestUtils.install("RollbackTestAppBv2.apk", true);
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+
+            // Both test apps should now be available for rollback, and the
+            // targetPackage returned for rollback should be correct.
+            RollbackInfo rollbackA = rm.getAvailableRollback(TEST_APP_A);
+            assertNotNull(rollbackA);
+            assertEquals(TEST_APP_A, rollbackA.targetPackage.packageName);
+
+            RollbackInfo rollbackB = rm.getAvailableRollback(TEST_APP_B);
+            assertNotNull(rollbackB);
+            assertEquals(TEST_APP_B, rollbackB.targetPackage.packageName);
+
+            // Executing rollback should roll back the correct package.
+            RollbackTestUtils.rollback(rollbackA);
+            assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+
+            RollbackTestUtils.uninstall(TEST_APP_A);
+            RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+            RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+            RollbackTestUtils.rollback(rollbackB);
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+            assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+        } finally {
+            RollbackTestUtils.dropShellPermissionIdentity();
+        }
+    }
+
+    /**
+     * Test that the MANAGE_ROLLBACKS permission is required to call
+     * RollbackManager APIs.
+     */
+    @Test
+    public void testManageRollbacksPermission() throws Exception {
+        // We shouldn't be allowed to call any of the RollbackManager APIs
+        // without the MANAGE_ROLLBACKS permission.
+        RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
+        try {
+            rm.getAvailableRollback(TEST_APP_A);
+            fail("expected SecurityException");
+        } catch (SecurityException e) {
+            // Expected.
+        }
+
+        try {
+            rm.getPackagesWithAvailableRollbacks();
+            fail("expected SecurityException");
+        } catch (SecurityException e) {
+            // Expected.
+        }
+
+        try {
+            rm.getRecentlyExecutedRollbacks();
+            fail("expected SecurityException");
+        } catch (SecurityException e) {
+            // Expected.
+        }
+
+        try {
+            // TODO: What if the implementation checks arguments for non-null
+            // first? Then this test isn't valid.
+            rm.executeRollback(null, null);
+            fail("expected SecurityException");
+        } catch (SecurityException e) {
+            // Expected.
+        }
+
+        try {
+            rm.reloadPersistedData();
+            fail("expected SecurityException");
+        } catch (SecurityException e) {
+            // Expected.
+        }
+
+        try {
+            rm.expireRollbackForPackage(TEST_APP_A);
+            fail("expected SecurityException");
+        } catch (SecurityException e) {
+            // Expected.
+        }
+    }
+
+    /**
+     * Test rollback of multi-package installs.
+     * TODO: Stop ignoring this test once support for multi-package rollback
+     * is implemented.
+     */
+    @Ignore @Test
+    public void testMultiPackage() throws Exception {
+        try {
+            RollbackTestUtils.adoptShellPermissionIdentity(
+                    Manifest.permission.INSTALL_PACKAGES,
+                    Manifest.permission.DELETE_PACKAGES,
+                    Manifest.permission.MANAGE_ROLLBACKS);
+            RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
+            // Prep installation of the test apps.
+            RollbackTestUtils.uninstall(TEST_APP_A);
+            RollbackTestUtils.uninstall(TEST_APP_B);
+            RollbackTestUtils.installMultiPackage(false,
+                    "RollbackTestAppAv1.apk",
+                    "RollbackTestAppBv1.apk");
+            RollbackTestUtils.installMultiPackage(true,
+                    "RollbackTestAppAv2.apk",
+                    "RollbackTestAppBv2.apk");
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+
+            // TEST_APP_A should now be available for rollback.
+            assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
+            RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
+            assertNotNull(rollback);
+
+            // TODO: Test the dependent apps for rollback are correct once we
+            // support that in the RollbackInfo API.
+
+            // Rollback the app. It should cause both test apps to be rolled
+            // back.
+            RollbackTestUtils.rollback(rollback);
+            assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+            assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+
+            // We should not see a recent rollback listed for TEST_APP_B
+            for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
+                assertNotEquals(TEST_APP_B, r.targetPackage.packageName);
+            }
+
+            // TODO: Test the listed dependent apps for the recently executed
+            // rollback are correct once we support that in the RollbackInfo
+            // API.
+        } finally {
+            RollbackTestUtils.dropShellPermissionIdentity();
+        }
+    }
+}
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
new file mode 100644
index 0000000..fbc3d8f
--- /dev/null
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.rollback;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.rollback.RollbackInfo;
+import android.content.rollback.RollbackManager;
+import android.support.test.InstrumentationRegistry;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Utilities to facilitate testing rollbacks.
+ */
+class RollbackTestUtils {
+
+    private static final String TAG = "RollbackTest";
+
+    static RollbackManager getRollbackManager() {
+        Context context = InstrumentationRegistry.getContext();
+        RollbackManager rm = (RollbackManager) context.getSystemService(Context.ROLLBACK_SERVICE);
+        if (rm == null) {
+            throw new AssertionError("Failed to get RollbackManager");
+        }
+        return rm;
+    }
+
+    /**
+     * Returns the version of the given package installed on device.
+     * Returns -1 if the package is not currently installed.
+     */
+    static long getInstalledVersion(String packageName) {
+        Context context = InstrumentationRegistry.getContext();
+        PackageManager pm = context.getPackageManager();
+        try {
+            PackageInfo info = pm.getPackageInfo(packageName, 0);
+            return info.getLongVersionCode();
+        } catch (PackageManager.NameNotFoundException e) {
+            return -1;
+        }
+    }
+
+    private static void assertStatusSuccess(Intent result) {
+        int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                PackageInstaller.STATUS_FAILURE);
+        if (status == -1) {
+            throw new AssertionError("PENDING USER ACTION");
+        } else if (status > 0) {
+            String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
+            throw new AssertionError(message == null ? "UNKNOWN FAILURE" : message);
+        }
+    }
+
+    /**
+     * Uninstalls the given package.
+     * Does nothing if the package is not installed.
+     * @throws AssertionError if package can't be uninstalled.
+     */
+    static void uninstall(String packageName) throws InterruptedException, IOException {
+        // No need to uninstall if the package isn't installed.
+        if (getInstalledVersion(packageName) == -1) {
+            return;
+        }
+
+        Context context = InstrumentationRegistry.getContext();
+        PackageManager packageManager = context.getPackageManager();
+        PackageInstaller packageInstaller = packageManager.getPackageInstaller();
+        packageInstaller.uninstall(packageName, LocalIntentSender.getIntentSender());
+        assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
+    }
+
+    /**
+     * Execute the given rollback.
+     * @throws AssertionError if the rollback fails.
+     */
+    static void rollback(RollbackInfo rollback) throws InterruptedException {
+        RollbackManager rm = getRollbackManager();
+        rm.executeRollback(rollback, LocalIntentSender.getIntentSender());
+        assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
+    }
+
+    /**
+     * Installs the apk with the given name.
+     *
+     * @param resourceName name of class loader resource for the apk to
+     *        install.
+     * @param enableRollback if rollback should be enabled.
+     * @throws AssertionError if the installation fails.
+     */
+    static void install(String resourceName, boolean enableRollback)
+            throws InterruptedException, IOException {
+        Context context = InstrumentationRegistry.getContext();
+        PackageInstaller.Session session = null;
+        PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
+        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+        if (enableRollback) {
+            params.setEnableRollback();
+        }
+        int sessionId = packageInstaller.createSession(params);
+        session = packageInstaller.openSession(sessionId);
+
+        ClassLoader loader = RollbackTest.class.getClassLoader();
+        try (OutputStream packageInSession = session.openWrite("package", 0, -1);
+             InputStream is = loader.getResourceAsStream(resourceName);) {
+            byte[] buffer = new byte[4096];
+            int n;
+            while ((n = is.read(buffer)) >= 0) {
+                packageInSession.write(buffer, 0, n);
+            }
+        }
+
+        // Commit the session (this will start the installation workflow).
+        session.commit(LocalIntentSender.getIntentSender());
+        assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
+    }
+
+    /**
+     * Installs the apks with the given resource names as an atomic set.
+     *
+     * @param enableRollback if rollback should be enabled.
+     * @param resourceNames names of the class loader resource for the apks to
+     *        install.
+     * @throws AssertionError if the installation fails.
+     */
+    static void installMultiPackage(boolean enableRollback, String... resourceNames)
+            throws InterruptedException, IOException {
+        Context context = InstrumentationRegistry.getContext();
+        PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
+
+        PackageInstaller.SessionParams multiPackageParams = new PackageInstaller.SessionParams(
+                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+        multiPackageParams.setMultiPackage();
+        if (enableRollback) {
+            // TODO: Do we set this on the parent params, the child params, or
+            // both?
+            multiPackageParams.setEnableRollback();
+        }
+        int multiPackageId = packageInstaller.createSession(multiPackageParams);
+        PackageInstaller.Session multiPackage = packageInstaller.openSession(multiPackageId);
+
+        for (String resourceName : resourceNames) {
+            PackageInstaller.Session session = null;
+            PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+                    PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+            if (enableRollback) {
+                params.setEnableRollback();
+            }
+            int sessionId = packageInstaller.createSession(params);
+            session = packageInstaller.openSession(sessionId);
+
+            ClassLoader loader = RollbackTest.class.getClassLoader();
+            try (OutputStream packageInSession = session.openWrite("package", 0, -1);
+                 InputStream is = loader.getResourceAsStream(resourceName);) {
+                byte[] buffer = new byte[4096];
+                int n;
+                while ((n = is.read(buffer)) >= 0) {
+                    packageInSession.write(buffer, 0, n);
+                }
+            }
+            multiPackage.addChildSessionId(sessionId);
+        }
+
+        // Commit the session (this will start the installation workflow).
+        multiPackage.commit(LocalIntentSender.getIntentSender());
+        assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
+    }
+
+    static void adoptShellPermissionIdentity(String... permissions) {
+        InstrumentationRegistry
+            .getInstrumentation()
+            .getUiAutomation()
+            .adoptShellPermissionIdentity(permissions);
+    }
+
+    static void dropShellPermissionIdentity() {
+        InstrumentationRegistry
+            .getInstrumentation()
+            .getUiAutomation()
+            .dropShellPermissionIdentity();
+    }
+}
diff --git a/tests/UsageReportingTest/Android.mk b/tests/UsageReportingTest/Android.mk
new file mode 100644
index 0000000..afb6e16b1
--- /dev/null
+++ b/tests/UsageReportingTest/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_USE_AAPT2 := true
+LOCAL_STATIC_ANDROID_LIBRARIES := androidx.legacy_legacy-support-v4
+
+LOCAL_CERTIFICATE := platform
+
+LOCAL_PACKAGE_NAME := UsageReportingTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+include $(BUILD_PACKAGE)
diff --git a/tests/UsageReportingTest/AndroidManifest.xml b/tests/UsageReportingTest/AndroidManifest.xml
new file mode 100644
index 0000000..be0b09e
--- /dev/null
+++ b/tests/UsageReportingTest/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Note: Add android:sharedUserId="android.uid.system" to the root element to simulate the system UID
+  caller case.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.tests.usagereporter"
+    >
+
+    <application android:label="@string/reporter_app">
+        <activity android:name="UsageReporterActivity"
+                  android:label="UsageReporter">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+    </application>
+</manifest>
diff --git a/tests/UsageReportingTest/res/layout/row_item.xml b/tests/UsageReportingTest/res/layout/row_item.xml
new file mode 100644
index 0000000..1eb2dab
--- /dev/null
+++ b/tests/UsageReportingTest/res/layout/row_item.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingLeft="16dp"
+    android:paddingRight="16dp"
+    android:background="@color/inactive_color">
+
+    <TextView android:id="@+id/token"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:textStyle="bold"/>
+
+    <Button android:id="@+id/start" style="@style/ActionButton"
+            android:text="@string/start" />
+
+    <Button android:id="@+id/stop" style="@style/ActionButton"
+            android:text="@string/stop" />
+</LinearLayout>
diff --git a/tests/UsageReportingTest/res/menu/main.xml b/tests/UsageReportingTest/res/menu/main.xml
new file mode 100644
index 0000000..9847c2d
--- /dev/null
+++ b/tests/UsageReportingTest/res/menu/main.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/add_token"
+          android:title="@string/add_token"/>
+    <item android:id="@+id/add_many_tokens"
+          android:title="@string/add_many_tokens"/>
+    <item android:id="@+id/stop_all"
+          android:title="@string/stop_all_tokens"/>
+    <group android:checkableBehavior="all">
+        <item android:id="@+id/restore_on_start"
+              android:title="@string/restore_tokens_on_start"/>
+    </group>
+</menu>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/tv_pip_outline.xml b/tests/UsageReportingTest/res/values/colors.xml
similarity index 72%
rename from packages/SystemUI/res/drawable/tv_pip_outline.xml
rename to tests/UsageReportingTest/res/values/colors.xml
index c84438c..03bcf8a 100644
--- a/packages/SystemUI/res/drawable/tv_pip_outline.xml
+++ b/tests/UsageReportingTest/res/values/colors.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!-- Copyright (C) 2018 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -13,8 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-    <stroke android:width="2dp" android:color="#EEEEEE" />
-</shape>
+<resources>
+    <color name="active_color">#FFF</color>
+    <color name="inactive_color">#AAA</color>
+</resources>
\ No newline at end of file
diff --git a/tests/UsageReportingTest/res/values/strings.xml b/tests/UsageReportingTest/res/values/strings.xml
new file mode 100644
index 0000000..015290e
--- /dev/null
+++ b/tests/UsageReportingTest/res/values/strings.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <!-- Do not translate -->
+    <string name="reporter_app">Usage Reporter App</string>
+    <!-- Do not translate -->
+    <string name="start">Start</string>
+    <!-- Do not translate -->
+    <string name="stop">Stop</string>
+    <!-- Do not translate -->
+    <string name="default_token">SuperSecretToken</string>
+
+    <!-- Do not translate -->
+    <string name="add_token">Add Token</string>
+    <!-- Do not translate -->
+    <string name="add_many_tokens">Add Many Tokens</string>
+    <!-- Do not translate -->
+    <string name="stop_all_tokens">Stop All</string>
+    <!-- Do not translate -->
+    <string name="restore_tokens_on_start">Readd Tokens on Start</string>
+
+
+    <!-- Do not translate -->
+    <string name="token_query">Enter token(s) (delimit tokens with commas)</string>
+    <!-- Do not translate -->
+    <string name="many_tokens_query">Generate how many tokens?</string>
+    <!-- Do not translate -->
+    <string name="stop_all_tokens_query">Stop all tokens?</string>
+    <!-- Do not translate -->
+    <string name="ok">OK</string>
+    <!-- Do not translate -->
+    <string name="cancel">Cancel</string>
+</resources>
\ No newline at end of file
diff --git a/tests/UsageReportingTest/res/values/styles.xml b/tests/UsageReportingTest/res/values/styles.xml
new file mode 100644
index 0000000..e5b86c5
--- /dev/null
+++ b/tests/UsageReportingTest/res/values/styles.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <style name="ActionButton">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:textAppearance">@style/TextAppearance.ActionButton</item>
+    </style>
+
+    <style name="TextAppearance" parent="android:TextAppearance">
+    </style>
+
+    <style name="TextAppearance.ActionButton">
+        <item name="android:textStyle">italic</item>
+    </style>
+
+</resources>
diff --git a/tests/UsageReportingTest/src/com/android/tests/usagereporter/UsageReporterActivity.java b/tests/UsageReportingTest/src/com/android/tests/usagereporter/UsageReporterActivity.java
new file mode 100644
index 0000000..946be8f
--- /dev/null
+++ b/tests/UsageReportingTest/src/com/android/tests/usagereporter/UsageReporterActivity.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.usagereporter;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.ListActivity;
+import android.app.usage.UsageStatsManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+
+public class UsageReporterActivity extends ListActivity {
+
+    private Activity mActivity;
+    private final ArrayList<String> mTokens = new ArrayList();
+    private final ArraySet<String> mActives = new ArraySet();
+    private UsageStatsManager mUsageStatsManager;
+    private Adapter mAdapter;
+    private boolean mRestoreOnStart = false;
+    private static Context sContext;
+
+    /** Called with the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        sContext = getApplicationContext();
+
+        mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
+
+        mAdapter = new Adapter();
+        setListAdapter(mAdapter);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mActivity = this;
+
+
+        if (mRestoreOnStart) {
+            ArrayList<String> removed = null;
+            for (String token : mActives) {
+                try {
+                    mUsageStatsManager.reportUsageStart(mActivity, token);
+                } catch (Exception e) {
+                    // Somthing went wrong, recover and move on
+                    if (removed == null) {
+                        removed = new ArrayList();
+                    }
+                    removed.add(token);
+                }
+            }
+            if (removed != null) {
+                for (String token : removed) {
+                    mActives.remove(token);
+                }
+            }
+        } else {
+            mActives.clear();
+        }
+    }
+
+    /**
+     * Called when the activity is about to start interacting with the user.
+     */
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mAdapter.notifyDataSetChanged();
+    }
+
+
+    /**
+     * Called when your activity's options menu needs to be created.
+     */
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.main, menu);
+        return super.onCreateOptionsMenu(menu);
+    }
+
+
+    /**
+     * Called when a menu item is selected.
+     */
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.add_token:
+                callAddToken();
+                return true;
+            case R.id.add_many_tokens:
+                callAddManyTokens();
+                return true;
+            case R.id.stop_all:
+                callStopAll();
+                return true;
+            case R.id.restore_on_start:
+                mRestoreOnStart = !mRestoreOnStart;
+                item.setChecked(mRestoreOnStart);
+                return true;
+        }
+
+        return super.onOptionsItemSelected(item);
+    }
+
+    private void callAddToken() {
+        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle(getString(R.string.token_query));
+        final EditText input = new EditText(this);
+        input.setInputType(InputType.TYPE_CLASS_TEXT);
+        input.setHint(getString(R.string.default_token));
+        builder.setView(input);
+
+        builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                String tokenNames = input.getText().toString().trim();
+                if (TextUtils.isEmpty(tokenNames)) {
+                    tokenNames = getString(R.string.default_token);
+                }
+                String[] tokens = tokenNames.split(",");
+                for (String token : tokens) {
+                    if (mTokens.contains(token)) continue;
+                    mTokens.add(token);
+                }
+                mAdapter.notifyDataSetChanged();
+
+            }
+        });
+        builder.setNegativeButton(getString(R.string.cancel),
+                                  new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                dialog.cancel();
+            }
+        });
+
+        builder.show();
+    }
+
+    private void callAddManyTokens() {
+        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle(getString(R.string.many_tokens_query));
+        final EditText input = new EditText(this);
+        input.setInputType(InputType.TYPE_CLASS_NUMBER);
+        builder.setView(input);
+
+        builder.setPositiveButton(getString(R.string.ok),
+                new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                String val = input.getText().toString().trim();
+                if (TextUtils.isEmpty(val)) return;
+                int n = Integer.parseInt(val);
+                for (int i = 0; i < n; i++) {
+                    final String token = getString(R.string.default_token) + i;
+                    if (mTokens.contains(token)) continue;
+                    mTokens.add(token);
+                }
+                mAdapter.notifyDataSetChanged();
+
+            }
+        });
+        builder.setNegativeButton(getString(R.string.cancel),
+                new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                dialog.cancel();
+            }
+        });
+
+        builder.show();
+    }
+
+    private void callStopAll() {
+        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle(getString(R.string.stop_all_tokens_query));
+
+        builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                for (String token : mActives) {
+                    mUsageStatsManager.reportUsageStop(mActivity, token);
+                }
+                mActives.clear();
+                mAdapter.notifyDataSetChanged();
+            }
+        });
+        builder.setNegativeButton(getString(R.string.cancel),
+                new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                dialog.cancel();
+            }
+        });
+        builder.show();
+    }
+
+    /**
+     * A call-back for when the user presses the back button.
+     */
+    OnClickListener mStartListener = new OnClickListener() {
+        public void onClick(View v) {
+            final View parent = (View) v.getParent();
+            final String token = ((TextView) parent.findViewById(R.id.token)).getText().toString();
+            try {
+                mUsageStatsManager.reportUsageStart(mActivity, token);
+            } catch (Exception e) {
+                Toast.makeText(sContext, e.toString(), Toast.LENGTH_LONG).show();
+            }
+            parent.setBackgroundColor(getColor(R.color.active_color));
+            mActives.add(token);
+        }
+    };
+
+    /**
+     * A call-back for when the user presses the clear button.
+     */
+    OnClickListener mStopListener = new OnClickListener() {
+        public void onClick(View v) {
+            final View parent = (View) v.getParent();
+
+            final String token = ((TextView) parent.findViewById(R.id.token)).getText().toString();
+            try {
+                mUsageStatsManager.reportUsageStop(mActivity, token);
+            } catch (Exception e) {
+                Toast.makeText(sContext, e.toString(), Toast.LENGTH_LONG).show();
+            }
+            parent.setBackgroundColor(getColor(R.color.inactive_color));
+            mActives.remove(token);
+        }
+    };
+
+
+    private class Adapter extends BaseAdapter {
+        @Override
+        public int getCount() {
+            return mTokens.size();
+        }
+
+        @Override
+        public Object getItem(int position) {
+            return mTokens.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            final ViewHolder holder;
+            if (convertView == null) {
+                convertView = LayoutInflater.from(UsageReporterActivity.this)
+                        .inflate(R.layout.row_item, parent, false);
+                holder = new ViewHolder();
+                holder.tokenName = (TextView) convertView.findViewById(R.id.token);
+
+                holder.startButton = ((Button) convertView.findViewById(R.id.start));
+                holder.startButton.setOnClickListener(mStartListener);
+                holder.stopButton = ((Button) convertView.findViewById(R.id.stop));
+                holder.stopButton.setOnClickListener(mStopListener);
+                convertView.setTag(holder);
+            } else {
+                holder = (ViewHolder) convertView.getTag();
+            }
+
+            final String token = mTokens.get(position);
+            holder.tokenName.setText(mTokens.get(position));
+            if (mActives.contains(token)) {
+                convertView.setBackgroundColor(getColor(R.color.active_color));
+            } else {
+                convertView.setBackgroundColor(getColor(R.color.inactive_color));
+            }
+            return convertView;
+        }
+    }
+
+    private static class ViewHolder {
+        public TextView tokenName;
+        public Button startButton;
+        public Button stopButton;
+    }
+}
diff --git a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
index 3d8ce21..3c628f6 100644
--- a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
+++ b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
@@ -155,7 +155,7 @@
                     intent.setPackage(getPackageName());
                     intent.putExtra(EXTRA_KEY_TIMEOUT, true);
                     mUsageStatsManager.registerAppUsageObserver(1, packages,
-                            30, TimeUnit.SECONDS, PendingIntent.getActivity(UsageStatsActivity.this,
+                            60, TimeUnit.SECONDS, PendingIntent.getActivity(UsageStatsActivity.this,
                                     1, intent, 0));
                 }
             }
diff --git a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
index d5987a5..8564698 100644
--- a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
+++ b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
@@ -28,6 +28,7 @@
 import android.view.IWindowSession;
 import android.view.InsetsState;
 import android.view.Surface;
+import android.view.SurfaceControl;
 import android.view.View;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
@@ -106,7 +107,7 @@
                                 window.mSeq, mLayoutParams, -1, -1, View.VISIBLE, 0, -1, mTmpRect,
                                 mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect,
                                 new DisplayCutout.ParcelableWrapper(), new MergedConfiguration(),
-                                new Surface(), new InsetsState());
+                                new SurfaceControl(), new InsetsState());
                     } catch (RemoteException e) {
                         e.printStackTrace();
                     }
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index 1e3a49b..2c2afd4 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -832,25 +832,23 @@
                 0 /* operations */);
 
         // Traffic measured for the root uid on the base interface if eBPF is in use.
-        // Incorrectly includes appEntry's bytes and packets, plus IPv4-IPv6 translation
-        // overhead (20 bytes per packet), only for TX traffic.
         final NetworkStats.Entry ebpfRootUidEntry = new NetworkStats.Entry(
                 baseIface, rootUid, SET_DEFAULT, TAG_NONE,
                 163577 /* rxBytes */,
                 187 /* rxPackets */,
-                1169942 /* txBytes */,
-                13902 /* txPackets */,
+                17607 /* txBytes */,
+                97 /* txPackets */,
                 0 /* operations */);
 
         // Traffic measured for the root uid on the base interface if xt_qtaguid is in use.
         // Incorrectly includes appEntry's bytes and packets, plus IPv4-IPv6 translation
-        // overhead (20 bytes per packet), in both directions.
+        // overhead (20 bytes per packet), in rx direction.
         final NetworkStats.Entry xtRootUidEntry = new NetworkStats.Entry(
                 baseIface, rootUid, SET_DEFAULT, TAG_NONE,
                 31113087 /* rxBytes */,
                 22588 /* rxPackets */,
-                1169942 /* txBytes */,
-                13902 /* txPackets */,
+                17607 /* txBytes */,
+                97 /* txPackets */,
                 0 /* operations */);
 
         final NetworkStats.Entry otherEntry = new NetworkStats.Entry(
diff --git a/tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java b/tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java
index 7f8e7b5..ba0448c 100644
--- a/tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java
+++ b/tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java
@@ -35,8 +35,8 @@
 import android.annotation.Nullable;
 import android.net.IpPrefix;
 import android.net.MacAddress;
-import android.net.util.SharedLog;
 import android.net.dhcp.DhcpServer.Clock;
+import android.net.util.SharedLog;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -126,7 +126,7 @@
         mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS);
 
         // /28 should have 16 addresses, 14 w/o the first/last, 11 w/o excluded addresses
-        requestAddresses((byte)11);
+        requestAddresses((byte) 11);
 
         try {
             mRepo.getOffer(null, TEST_MAC_2,
diff --git a/tests/net/java/android/net/dhcp/DhcpServerTest.java b/tests/net/java/android/net/dhcp/DhcpServerTest.java
index df34c73..ab9bd84 100644
--- a/tests/net/java/android/net/dhcp/DhcpServerTest.java
+++ b/tests/net/java/android/net/dhcp/DhcpServerTest.java
@@ -25,7 +25,6 @@
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -48,7 +47,6 @@
 import android.net.dhcp.DhcpLeaseRepository.OutOfAddressesException;
 import android.net.dhcp.DhcpServer.Clock;
 import android.net.dhcp.DhcpServer.Dependencies;
-import android.net.util.InterfaceParams;
 import android.net.util.SharedLog;
 import android.os.test.TestLooper;
 import android.support.test.filters.SmallTest;
@@ -74,9 +72,6 @@
 public class DhcpServerTest {
     private static final String PROP_DEXMAKER_SHARE_CLASSLOADER = "dexmaker.share_classloader";
     private static final String TEST_IFACE = "testiface";
-    private static final MacAddress TEST_IFACE_MAC = MacAddress.fromString("11:22:33:44:55:66");
-    private static final InterfaceParams TEST_IFACEPARAMS =
-            new InterfaceParams(TEST_IFACE, 1, TEST_IFACE_MAC);
 
     private static final Inet4Address TEST_SERVER_ADDR = parseAddr("192.168.0.2");
     private static final LinkAddress TEST_SERVER_LINKADDR = new LinkAddress(TEST_SERVER_ADDR, 20);
@@ -149,7 +144,7 @@
                 .build();
 
         mLooper = new TestLooper();
-        mServer = new DhcpServer(mLooper.getLooper(), TEST_IFACEPARAMS, servingParams,
+        mServer = new DhcpServer(mLooper.getLooper(), TEST_IFACE, servingParams,
                 new SharedLog(DhcpServerTest.class.getSimpleName()), mDeps);
 
         mServer.start();
diff --git a/tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java b/tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java
new file mode 100644
index 0000000..4a6f20a
--- /dev/null
+++ b/tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.dhcp;
+
+import static android.net.InetAddresses.parseNumericAddress;
+
+import static com.google.android.collect.Sets.newHashSet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.net.LinkAddress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Inet4Address;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DhcpServingParamsParcelExtTest {
+    private static final Inet4Address TEST_ADDRESS = inet4Addr("192.168.0.123");
+    private static final int TEST_ADDRESS_PARCELED = 0xc0a8007b;
+    private static final int TEST_PREFIX_LENGTH = 17;
+    private static final int TEST_LEASE_TIME_SECS = 120;
+    private static final int TEST_MTU = 1000;
+    private static final Set<Inet4Address> TEST_ADDRESS_SET =
+            newHashSet(inet4Addr("192.168.1.123"), inet4Addr("192.168.1.124"));
+    private static final Set<Integer> TEST_ADDRESS_SET_PARCELED =
+            newHashSet(0xc0a8017b, 0xc0a8017c);
+
+    private DhcpServingParamsParcelExt mParcel;
+
+    @Before
+    public void setUp() {
+        mParcel = new DhcpServingParamsParcelExt();
+    }
+
+    @Test
+    public void testSetServerAddr() {
+        mParcel.setServerAddr(new LinkAddress(TEST_ADDRESS, TEST_PREFIX_LENGTH));
+
+        assertEquals(TEST_ADDRESS_PARCELED, mParcel.serverAddr);
+        assertEquals(TEST_PREFIX_LENGTH, mParcel.serverAddrPrefixLength);
+    }
+
+    @Test
+    public void testSetDefaultRouters() {
+        mParcel.setDefaultRouters(TEST_ADDRESS_SET);
+        assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.defaultRouters));
+    }
+
+    @Test
+    public void testSetDnsServers() {
+        mParcel.setDnsServers(TEST_ADDRESS_SET);
+        assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.dnsServers));
+    }
+
+    @Test
+    public void testSetExcludedAddrs() {
+        mParcel.setExcludedAddrs(TEST_ADDRESS_SET);
+        assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.excludedAddrs));
+    }
+
+    @Test
+    public void testSetDhcpLeaseTimeSecs() {
+        mParcel.setDhcpLeaseTimeSecs(TEST_LEASE_TIME_SECS);
+        assertEquals(TEST_LEASE_TIME_SECS, mParcel.dhcpLeaseTimeSecs);
+    }
+
+    @Test
+    public void testSetLinkMtu() {
+        mParcel.setLinkMtu(TEST_MTU);
+        assertEquals(TEST_MTU, mParcel.linkMtu);
+    }
+
+    @Test
+    public void testSetMetered() {
+        mParcel.setMetered(true);
+        assertTrue(mParcel.metered);
+        mParcel.setMetered(false);
+        assertFalse(mParcel.metered);
+    }
+
+    private static Inet4Address inet4Addr(String addr) {
+        return (Inet4Address) parseNumericAddress(addr);
+    }
+
+    private static Set<Integer> asSet(int[] ints) {
+        return IntStream.of(ints).boxed().collect(Collectors.toSet());
+    }
+}
diff --git a/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java b/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java
index b6a4073..2ab2246 100644
--- a/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java
+++ b/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java
@@ -16,6 +16,7 @@
 
 package android.net.dhcp;
 
+import static android.net.NetworkUtils.inet4AddressToIntHTH;
 import static android.net.dhcp.DhcpServingParams.MTU_UNSET;
 
 import static junit.framework.Assert.assertEquals;
@@ -27,6 +28,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.net.LinkAddress;
+import android.net.NetworkUtils;
 import android.net.dhcp.DhcpServingParams.InvalidParameterException;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -35,8 +37,10 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.lang.reflect.Modifier;
 import java.net.Inet4Address;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -56,6 +60,7 @@
     private static final int TEST_MTU = 1500;
     private static final Set<Inet4Address> TEST_EXCLUDED_ADDRS = new HashSet<>(
             Arrays.asList(parseAddr("192.168.0.200"), parseAddr("192.168.0.201")));
+    private static final boolean TEST_METERED = true;
 
     @Before
     public void setUp() {
@@ -65,7 +70,8 @@
                 .setDnsServers(TEST_DNS_SERVERS)
                 .setServerAddr(TEST_LINKADDR)
                 .setLinkMtu(TEST_MTU)
-                .setExcludedAddrs(TEST_EXCLUDED_ADDRS);
+                .setExcludedAddrs(TEST_EXCLUDED_ADDRS)
+                .setMetered(TEST_METERED);
     }
 
     @Test
@@ -91,6 +97,7 @@
         assertEquals(TEST_DNS_SERVERS, params.dnsServers);
         assertEquals(TEST_LINKADDR, params.serverAddr);
         assertEquals(TEST_MTU, params.linkMtu);
+        assertEquals(TEST_METERED, params.metered);
 
         assertContains(params.excludedAddrs, TEST_EXCLUDED_ADDRS);
         assertContains(params.excludedAddrs, TEST_DEFAULT_ROUTERS);
@@ -159,6 +166,39 @@
         mBuilder.setDefaultRouters(parseAddr("192.168.254.254")).build();
     }
 
+    @Test
+    public void testFromParcelableObject() throws InvalidParameterException {
+        final DhcpServingParams params = mBuilder.build();
+        final DhcpServingParamsParcel parcel = new DhcpServingParamsParcel();
+        parcel.defaultRouters = toIntArray(TEST_DEFAULT_ROUTERS);
+        parcel.dhcpLeaseTimeSecs = TEST_LEASE_TIME_SECS;
+        parcel.dnsServers = toIntArray(TEST_DNS_SERVERS);
+        parcel.serverAddr = inet4AddressToIntHTH(TEST_SERVER_ADDR);
+        parcel.serverAddrPrefixLength = TEST_LINKADDR.getPrefixLength();
+        parcel.linkMtu = TEST_MTU;
+        parcel.excludedAddrs = toIntArray(TEST_EXCLUDED_ADDRS);
+        parcel.metered = TEST_METERED;
+        final DhcpServingParams parceled = DhcpServingParams.fromParcelableObject(parcel);
+
+        assertEquals(params.defaultRouters, parceled.defaultRouters);
+        assertEquals(params.dhcpLeaseTimeSecs, parceled.dhcpLeaseTimeSecs);
+        assertEquals(params.dnsServers, parceled.dnsServers);
+        assertEquals(params.serverAddr, parceled.serverAddr);
+        assertEquals(params.linkMtu, parceled.linkMtu);
+        assertEquals(params.excludedAddrs, parceled.excludedAddrs);
+        assertEquals(params.metered, parceled.metered);
+
+        // Ensure that we do not miss any field if added in the future
+        final long numFields = Arrays.stream(DhcpServingParams.class.getDeclaredFields())
+                .filter(f -> !Modifier.isStatic(f.getModifiers()))
+                .count();
+        assertEquals(7, numFields);
+    }
+
+    private static int[] toIntArray(Collection<Inet4Address> addrs) {
+        return addrs.stream().mapToInt(NetworkUtils::inet4AddressToIntHTH).toArray();
+    }
+
     private static <T> void assertContains(@NonNull Set<T> set, @NonNull Set<T> subset) {
         for (final T elem : subset) {
             assertContains(set, elem);
diff --git a/tests/net/java/android/net/ip/IpServerTest.java b/tests/net/java/android/net/ip/IpServerTest.java
index cff0b54..0178228 100644
--- a/tests/net/java/android/net/ip/IpServerTest.java
+++ b/tests/net/java/android/net/ip/IpServerTest.java
@@ -16,6 +16,16 @@
 
 package android.net.ip;
 
+import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
+import static android.net.ConnectivityManager.TETHERING_USB;
+import static android.net.ConnectivityManager.TETHERING_WIFI;
+import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
+import static android.net.ip.IpServer.STATE_AVAILABLE;
+import static android.net.ip.IpServer.STATE_TETHERED;
+import static android.net.ip.IpServer.STATE_UNAVAILABLE;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -31,16 +41,6 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
-import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
-import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
-import static android.net.ConnectivityManager.TETHERING_USB;
-import static android.net.ConnectivityManager.TETHERING_WIFI;
-import static android.net.ip.IpServer.STATE_AVAILABLE;
-import static android.net.ip.IpServer.STATE_TETHERED;
-import static android.net.ip.IpServer.STATE_UNAVAILABLE;
-
 import android.net.INetworkStatsService;
 import android.net.InterfaceConfiguration;
 import android.net.IpPrefix;
@@ -60,8 +60,6 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.text.TextUtils;
 
-import java.net.Inet4Address;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -71,6 +69,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.net.Inet4Address;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class IpServerTest {
@@ -404,7 +404,7 @@
 
     private void assertDhcpStarted(IpPrefix expectedPrefix) {
         verify(mDependencies, times(1)).makeDhcpServer(
-                eq(mLooper.getLooper()), eq(TEST_IFACE_PARAMS), any(), eq(mSharedLog));
+                eq(mLooper.getLooper()), eq(IFACE_NAME), any(), eq(mSharedLog));
         verify(mDhcpServer, times(1)).start();
         final DhcpServingParams params = mDhcpParamsCaptor.getValue();
         // Last address byte is random
diff --git a/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
index 788924b..90bf7b1 100644
--- a/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
+++ b/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
@@ -159,7 +159,7 @@
         assertStatsEntry(stats, "v4-wlan0", 1000, SET_DEFAULT, 0x0, 30812L, 2310L);
         assertStatsEntry(stats, "v4-wlan0", 10102, SET_DEFAULT, 0x0, 10022L, 3330L);
         assertStatsEntry(stats, "v4-wlan0", 10060, SET_DEFAULT, 0x0, 9532772L, 254112L);
-        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 15229L, 5766L);
+        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 15229L, 0L);
         assertStatsEntry(stats, "wlan0", 1000, SET_DEFAULT, 0x0, 6126L, 2013L);
         assertStatsEntry(stats, "wlan0", 10013, SET_DEFAULT, 0x0, 0L, 144L);
         assertStatsEntry(stats, "wlan0", 10018, SET_DEFAULT, 0x0, 5980263L, 167667L);
@@ -170,6 +170,8 @@
         assertStatsEntry(stats, "dummy0", 0, SET_DEFAULT, 0x0, 0L, 168L);
         assertStatsEntry(stats, "lo", 0, SET_DEFAULT, 0x0, 1288L, 1288L);
 
+        assertNoStatsEntry(stats, "wlan0", 1029, SET_DEFAULT, 0x0);
+
         NetworkStatsFactory.clearStackedIfaces();
     }
 
@@ -191,12 +193,12 @@
         // Stats snapshot before the download
         stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_100mb_download_before);
         assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesBefore, 5199872L);
-        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesBefore, 647888L);
+        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesBefore, 0L);
 
         // Stats snapshot after the download
         stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_100mb_download_after);
         assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesAfter, 7867488L);
-        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesAfter, 647587L);
+        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesAfter, 0L);
 
         NetworkStatsFactory.clearStackedIfaces();
     }
@@ -252,6 +254,15 @@
         assertEquals("unexpected txBytes", txBytes, entry.txBytes);
     }
 
+    private static void assertNoStatsEntry(NetworkStats stats, String iface, int uid, int set,
+            int tag) {
+        final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO);
+        if (i >= 0) {
+            fail("unexpected NetworkStats entry at " + i);
+        }
+    }
+
     private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set,
             int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) {
         final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO,
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index bca9be7..e6b43d2 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -240,7 +240,7 @@
                 }
 
                 @Override
-                public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface,
+                public DhcpServer makeDhcpServer(Looper looper, String ifName,
                         DhcpServingParams params, SharedLog log) {
                     return mDhcpServer;
                 }
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat b/tests/net/res/raw/xt_qtaguid_with_clat
index 77e5c7b..6cd7499 100644
--- a/tests/net/res/raw/xt_qtaguid_with_clat
+++ b/tests/net/res/raw/xt_qtaguid_with_clat
@@ -7,7 +7,7 @@
 7 v4-wlan0 0x0 10060 1 1448660 1041 31192 753 1448660 1041 0 0 0 0 31192 753 0 0 0 0
 8 v4-wlan0 0x0 10102 0 9702 16 2870 23 9702 16 0 0 0 0 2870 23 0 0 0 0
 9 v4-wlan0 0x0 10102 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-10 wlan0 0x0 0 0 11058671 7892 312046 5113 11043898 7811 13117 61 1656 20 306544 5046 3230 38 2272 29
+10 wlan0 0x0 0 0 11058671 7892 0 0 11043898 7811 13117 61 1656 20 0 0 0 0 0 0
 11 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 12 wlan0 0x0 1000 0 6126 13 2013 16 5934 11 192 2 0 0 1821 14 192 2 0 0
 13 wlan0 0x0 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
@@ -41,3 +41,5 @@
 41 dummy0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 42 lo 0x0 0 0 1288 16 1288 16 0 0 532 8 756 8 0 0 532 8 756 8
 43 lo 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+44 wlan0 0x0 1029 0 0 0 312046 5113 0 0 0 0 0 0 306544 5046 3230 38 2272 29
+45 wlan0 0x0 1029 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after b/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after
index c78f84f..9f86153 100644
--- a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after
+++ b/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after
@@ -9,7 +9,7 @@
 9 v4-wlan0 0x0 10057 1 728 7 392 7 0 0 728 7 0 0 0 0 392 7 0 0
 10 v4-wlan0 0x0 10106 0 2232 18 2232 18 0 0 2232 18 0 0 0 0 2232 18 0 0
 11 v4-wlan0 0x0 10106 1 432952718 314238 5442288 121260 432950238 314218 2480 20 0 0 5433900 121029 8388 231 0 0
-12 wlan0 0x0 0 0 440746376 329772 8524052 130894 439660007 315369 232001 1276 854368 13127 7871216 121284 108568 1325 544268 8285
+12 wlan0 0x0 0 0 440746376 329772 0 0 439660007 315369 232001 1276 854368 13127 0 0 0 0 0 0
 13 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 14 wlan0 0x0 1000 0 77113 272 56151 575 77113 272 0 0 0 0 19191 190 36960 385 0 0
 15 wlan0 0x0 1000 1 20227 80 8356 72 18539 74 1688 6 0 0 7562 66 794 6 0 0
@@ -185,3 +185,5 @@
 185 wlan0 0xffffff0900000000 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 186 dummy0 0x0 0 0 0 0 168 3 0 0 0 0 0 0 0 0 0 0 168 3
 187 dummy0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+188 wlan0 0x0 1029 0 0 0 8524052 130894 0 0 0 0 0 0 7871216 121284 108568 1325 544268 8285
+189 wlan0 0x0 1029 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_before b/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_before
index d035387..ce4bcc3 100644
--- a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_before
+++ b/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_before
@@ -9,7 +9,7 @@
 9 v4-wlan0 0x0 10057 1 728 7 392 7 0 0 728 7 0 0 0 0 392 7 0 0
 10 v4-wlan0 0x0 10106 0 1488 12 1488 12 0 0 1488 12 0 0 0 0 1488 12 0 0
 11 v4-wlan0 0x0 10106 1 323981189 235142 3509032 84542 323979453 235128 1736 14 0 0 3502676 84363 6356 179 0 0
-12 wlan0 0x0 0 0 330187296 250652 5855801 94173 329106990 236273 226202 1255 854104 13124 5208040 84634 103637 1256 544124 8283
+12 wlan0 0x0 0 0 330187296 250652 0 0 329106990 236273 226202 1255 854104 13124 0 0 0 0 0 0
 13 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 14 wlan0 0x0 1000 0 77113 272 56151 575 77113 272 0 0 0 0 19191 190 36960 385 0 0
 15 wlan0 0x0 1000 1 20227 80 8356 72 18539 74 1688 6 0 0 7562 66 794 6 0 0
@@ -183,3 +183,5 @@
 183 wlan0 0xffffff0900000000 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 184 dummy0 0x0 0 0 0 0 168 3 0 0 0 0 0 0 0 0 0 0 168 3
 185 dummy0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+186 wlan0 0x0 1029 0 0 0 5855801 94173 0 0 0 0 0 0 5208040 84634 103637 1256 544124 8283
+187 wlan0 0x0 1029 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_simple b/tests/net/res/raw/xt_qtaguid_with_clat_simple
index 7f0e56f..8c132e7 100644
--- a/tests/net/res/raw/xt_qtaguid_with_clat_simple
+++ b/tests/net/res/raw/xt_qtaguid_with_clat_simple
@@ -1,5 +1,6 @@
 idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
-2 v4-wlan0 0x0 10060 0 42600 213 4100 41 42600 213 4100 41 0 0 0 0 0 0 0 0
+2 v4-wlan0 0x0 10060 0 42600 213 4100 41 42600 213 0 0 0 0 4100 41 0 0 0 0
 3 v4-wlan0 0x0 10060 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-4 wlan0 0x0 0 0 46860 213 4920 41 46860 213 4920 41 0 0 0 0 0 0 0 0
+4 wlan0 0x0 0 0 46860 213 0 0 46860 213 0 0 0 0 0 0 0 0 0 0
 5 wlan0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+6 wlan0 0x0 1029 0 0 0 4920 41 0 0 0 0 0 0 4920 41 0 0 0 0
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 14312cf..4c2a984 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -16,15 +16,16 @@
 
 package com.android.framework.permission.tests;
 
-import android.content.res.Configuration;
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.view.IWindowManager;
-import junit.framework.TestCase;
 
-import static android.view.Display.DEFAULT_DISPLAY;
+import junit.framework.TestCase;
 
 /**
  * TODO: Remove this. This is only a placeholder, need to implement this.
@@ -72,17 +73,6 @@
         }
 
         try {
-            mWm.updateOrientationFromAppTokens(new Configuration(),
-                    null /* freezeThisOneIfNeeded */, DEFAULT_DISPLAY);
-            fail("IWindowManager.updateOrientationFromAppTokens did not throw SecurityException as"
-                    + " expected");
-        } catch (SecurityException e) {
-            // expected
-        } catch (RemoteException e) {
-            fail("Unexpected remote exception");
-        }
-
-        try {
             mWm.prepareAppTransition(0, false);
             fail("IWindowManager.prepareAppTransition did not throw SecurityException as"
                     + " expected");
@@ -107,7 +97,7 @@
     public void testDISABLE_KEYGUARD() {
         Binder token = new Binder();
         try {
-            mWm.disableKeyguard(token, "foo");
+            mWm.disableKeyguard(token, "foo", UserHandle.myUserId());
             fail("IWindowManager.disableKeyguard did not throw SecurityException as"
                     + " expected");
         } catch (SecurityException e) {
@@ -117,7 +107,7 @@
         }
 
         try {
-            mWm.reenableKeyguard(token);
+            mWm.reenableKeyguard(token, UserHandle.myUserId());
             fail("IWindowManager.reenableKeyguard did not throw SecurityException as"
                     + " expected");
         } catch (SecurityException e) {
diff --git a/tests/testables/src/android/testing/BaseFragmentTest.java b/tests/testables/src/android/testing/BaseFragmentTest.java
index 5fa065a..d18c126 100644
--- a/tests/testables/src/android/testing/BaseFragmentTest.java
+++ b/tests/testables/src/android/testing/BaseFragmentTest.java
@@ -21,7 +21,9 @@
 import android.app.FragmentController;
 import android.app.FragmentHostCallback;
 import android.app.FragmentManagerNonConfig;
+import android.content.Context;
 import android.graphics.PixelFormat;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Parcelable;
 import android.support.test.InstrumentationRegistry;
@@ -75,7 +77,7 @@
         TestableLooper.get(this).runWithLooper(() -> {
             mHandler = new Handler();
 
-            mFragment = mCls.newInstance();
+            mFragment = instantiate(mContext, mCls.getName(), null);
             mFragments = FragmentController.createController(new HostCallbacks());
             mFragments.attachHost(null);
             mFragments.getFragmentManager().beginTransaction()
@@ -187,6 +189,13 @@
         TestableLooper.get(this).processAllMessages();
     }
 
+    /**
+     * Method available for override to replace fragment instantiation.
+     */
+    protected Fragment instantiate(Context context, String className, @Nullable Bundle arguments) {
+        return Fragment.instantiate(context, className, arguments);
+    }
+
     private View findViewById(int id) {
         return mView.findViewById(id);
     }
@@ -206,6 +215,11 @@
         }
 
         @Override
+        public Fragment instantiate(Context context, String className, Bundle arguments) {
+            return BaseFragmentTest.this.instantiate(context, className, arguments);
+        }
+
+        @Override
         public boolean onShouldSaveFragmentState(Fragment fragment) {
             return true; // True for now.
         }
diff --git a/tools/aapt/ConfigDescription.h b/tools/aapt/ConfigDescription.h
index 09430f2..b4ea624 100644
--- a/tools/aapt/ConfigDescription.h
+++ b/tools/aapt/ConfigDescription.h
@@ -29,7 +29,7 @@
         size = sizeof(android::ResTable_config);
     }
 
-    ConfigDescription(const android::ResTable_config&o) {  // NOLINT(implicit)
+    ConfigDescription(const android::ResTable_config&o) {  // NOLINT(google-explicit-constructor)
         *static_cast<android::ResTable_config*>(this) = o;
         size = sizeof(android::ResTable_config);
     }
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index ba498e1..c42a888 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -170,6 +170,7 @@
     srcs: [
         "test/Builders.cpp",
         "test/Common.cpp",
+        "test/Fixture.cpp",
         "**/*_test.cpp",
     ] + toolSources,
     static_libs: [
@@ -177,7 +178,10 @@
         "libgmock",
     ],
     defaults: ["aapt2_defaults"],
-    data: ["integration-tests/CompileTest/**/*"],
+    data: [
+         "integration-tests/CompileTest/**/*",
+         "integration-tests/CommandTests/**/*"
+    ],
 }
 
 // ==========================================================
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 9460c9e..5f664f5 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -445,7 +445,7 @@
  public:
   using xml::ConstVisitor::Visit;
 
-  XmlPrinter(Printer* printer) : printer_(printer) {
+  explicit XmlPrinter(Printer* printer) : printer_(printer) {
   }
 
   void Visit(const xml::Element* el) override {
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index dd5c751..67ba895 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -113,7 +113,7 @@
   ResourceNameRef() = default;
   ResourceNameRef(const ResourceNameRef&) = default;
   ResourceNameRef(ResourceNameRef&&) = default;
-  ResourceNameRef(const ResourceName& rhs);  // NOLINT(implicit)
+  ResourceNameRef(const ResourceName& rhs);  // NOLINT(google-explicit-constructor)
   ResourceNameRef(const android::StringPiece& p, ResourceType t, const android::StringPiece& e);
   ResourceNameRef& operator=(const ResourceNameRef& rhs) = default;
   ResourceNameRef& operator=(ResourceNameRef&& rhs) = default;
@@ -144,7 +144,7 @@
 
   ResourceId();
   ResourceId(const ResourceId& rhs);
-  ResourceId(uint32_t res_id);  // NOLINT(implicit)
+  ResourceId(uint32_t res_id);  // NOLINT(google-explicit-constructor)
   ResourceId(uint8_t p, uint8_t t, uint16_t e);
 
   bool is_valid() const;
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 9587704..58702dc 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -78,7 +78,7 @@
 
 static uint32_t ParseFormatAttribute(const StringPiece& str) {
   uint32_t mask = 0;
-  for (StringPiece part : util::Tokenize(str, '|')) {
+  for (const StringPiece& part : util::Tokenize(str, '|')) {
     StringPiece trimmed_part = util::TrimWhitespace(part);
     uint32_t type = ParseFormatType(trimmed_part);
     if (type == 0) {
@@ -99,7 +99,7 @@
   ResourceId id;
   Visibility::Level visibility_level = Visibility::Level::kUndefined;
   bool allow_new = false;
-  Maybe<Overlayable> overlayable;
+  Maybe<OverlayableItem> overlayable_item;
 
   std::string comment;
   std::unique_ptr<Value> value;
@@ -133,8 +133,8 @@
     }
   }
 
-  if (res->overlayable) {
-    if (!table->SetOverlayable(res->name, res->overlayable.value(), diag)) {
+  if (res->overlayable_item) {
+    if (!table->SetOverlayable(res->name, res->overlayable_item.value(), diag)) {
       return false;
     }
   }
@@ -1059,92 +1059,119 @@
 bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource) {
   if (out_resource->config != ConfigDescription::DefaultConfig()) {
     diag_->Warn(DiagMessage(out_resource->source)
-                    << "ignoring configuration '" << out_resource->config
-                    << "' for <overlayable> tag");
+                << "ignoring configuration '" << out_resource->config
+                << "' for <overlayable> tag");
   }
 
+  Maybe<StringPiece> overlayable_name = xml::FindNonEmptyAttribute(parser, "name");
+  if (!overlayable_name) {
+    diag_->Error(DiagMessage(out_resource->source)
+                  << "<overlayable> tag must have a 'name' attribute");
+    return false;
+  }
+
+  const std::string kActorUriScheme =
+      android::base::StringPrintf("%s://", Overlayable::kActorScheme);
+  Maybe<StringPiece> overlayable_actor = xml::FindNonEmptyAttribute(parser, "actor");
+  if (overlayable_actor && !util::StartsWith(overlayable_actor.value(), kActorUriScheme)) {
+    diag_->Error(DiagMessage(out_resource->source)
+                 << "specified <overlayable> tag 'actor' attribute must use the scheme '"
+                 << Overlayable::kActorScheme << "'");
+    return false;
+  }
+
+  // Create a overlayable entry grouping that represents this <overlayable>
+  auto overlayable = std::make_shared<Overlayable>(
+      overlayable_name.value(), (overlayable_actor) ? overlayable_actor.value() : "",
+      out_resource->source);
+
   bool error = false;
   std::string comment;
-  Overlayable::PolicyFlags current_policies = Overlayable::Policy::kNone;
+  OverlayableItem::PolicyFlags current_policies = OverlayableItem::Policy::kNone;
   const size_t start_depth = parser->depth();
   while (xml::XmlPullParser::IsGoodEvent(parser->Next())) {
     xml::XmlPullParser::Event event = parser->event();
     if (event == xml::XmlPullParser::Event::kEndElement && parser->depth() == start_depth) {
-      // Break the loop when exiting the overlayable element
+      // Break the loop when exiting the <overlayable>
       break;
     } else if (event == xml::XmlPullParser::Event::kEndElement
                && parser->depth() == start_depth + 1) {
-      // Clear the current policies when exiting the policy element
-      current_policies = Overlayable::Policy::kNone;
+      // Clear the current policies when exiting the <policy> tags
+      current_policies = OverlayableItem::Policy::kNone;
       continue;
     } else if (event == xml::XmlPullParser::Event::kComment) {
-      // Get the comment of individual item elements
+      // Retrieve the comment of individual <item> tags
       comment = parser->comment();
       continue;
     } else if (event != xml::XmlPullParser::Event::kStartElement) {
-      // Skip to the next element
+      // Skip to the start of the next element
       continue;
     }
 
-    const Source item_source = source_.WithLine(parser->line_number());
+    const Source element_source = source_.WithLine(parser->line_number());
     const std::string& element_name = parser->element_name();
     const std::string& element_namespace = parser->element_namespace();
     if (element_namespace.empty() && element_name == "item") {
       // Items specify the name and type of resource that should be overlayable
-      Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
-      if (!maybe_name) {
-        diag_->Error(DiagMessage(item_source)
-                     << "<item> within an <overlayable> tag must have a 'name' attribute");
+      Maybe<StringPiece> item_name = xml::FindNonEmptyAttribute(parser, "name");
+      if (!item_name) {
+        diag_->Error(DiagMessage(element_source)
+                     << "<item> within an <overlayable> must have a 'name' attribute");
         error = true;
         continue;
       }
 
-      Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
-      if (!maybe_type) {
-        diag_->Error(DiagMessage(item_source)
-                     << "<item> within an <overlayable> tag must have a 'type' attribute");
+      Maybe<StringPiece> item_type = xml::FindNonEmptyAttribute(parser, "type");
+      if (!item_type) {
+        diag_->Error(DiagMessage(element_source)
+                     << "<item> within an <overlayable> must have a 'type' attribute");
         error = true;
         continue;
       }
 
-      const ResourceType* type = ParseResourceType(maybe_type.value());
+      const ResourceType* type = ParseResourceType(item_type.value());
       if (type == nullptr) {
-        diag_->Error(DiagMessage(item_source)
-                     << "invalid resource type '" << maybe_type.value()
+        diag_->Error(DiagMessage(element_source)
+                     << "invalid resource type '" << item_type.value()
                      << "' in <item> within an <overlayable>");
         error = true;
         continue;
       }
 
-      ParsedResource child_resource;
+      OverlayableItem overlayable_item(overlayable);
+      overlayable_item.policies = current_policies;
+      overlayable_item.comment = comment;
+      overlayable_item.source = element_source;
+
+      ParsedResource child_resource{};
       child_resource.name.type = *type;
-      child_resource.name.entry = maybe_name.value().to_string();
-      child_resource.overlayable = Overlayable{current_policies, item_source, comment};
+      child_resource.name.entry = item_name.value().to_string();
+      child_resource.overlayable_item = overlayable_item;
       out_resource->child_resources.push_back(std::move(child_resource));
 
     } else if (element_namespace.empty() && element_name == "policy") {
-      if (current_policies != Overlayable::Policy::kNone) {
+      if (current_policies != OverlayableItem::Policy::kNone) {
         // If the policy list is not empty, then we are currently inside a policy element
-        diag_->Error(DiagMessage(item_source) << "<policy> blocks cannot be recursively nested");
+        diag_->Error(DiagMessage(element_source) << "<policy> blocks cannot be recursively nested");
         error = true;
         break;
       } else if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
         // Parse the polices separated by vertical bar characters to allow for specifying multiple
-        // policies
+        // policies. Items within the policy tag will have the specified policy.
         for (StringPiece part : util::Tokenize(maybe_type.value(), '|')) {
           StringPiece trimmed_part = util::TrimWhitespace(part);
           if (trimmed_part == "public") {
-            current_policies |= Overlayable::Policy::kPublic;
+            current_policies |= OverlayableItem::Policy::kPublic;
           } else if (trimmed_part == "product") {
-            current_policies |= Overlayable::Policy::kProduct;
+            current_policies |= OverlayableItem::Policy::kProduct;
           } else if (trimmed_part == "product_services") {
-            current_policies |= Overlayable::Policy::kProductServices;
+            current_policies |= OverlayableItem::Policy::kProductServices;
           } else if (trimmed_part == "system") {
-            current_policies |= Overlayable::Policy::kSystem;
+            current_policies |= OverlayableItem::Policy::kSystem;
           } else if (trimmed_part == "vendor") {
-            current_policies |= Overlayable::Policy::kVendor;
+            current_policies |= OverlayableItem::Policy::kVendor;
           } else {
-            diag_->Error(DiagMessage(item_source)
+            diag_->Error(DiagMessage(element_source)
                          << "<policy> has unsupported type '" << trimmed_part << "'");
             error = true;
             continue;
@@ -1152,11 +1179,13 @@
         }
       }
     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
-      diag_->Error(DiagMessage(item_source) << "invalid element <" << element_name << "> "
+      diag_->Error(DiagMessage(element_source) << "invalid element <" << element_name << "> "
                                             << " in <overlayable>");
       error = true;
       break;
     }
+
+    comment.clear();
   }
 
   return !error;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 03e6197..debca9c 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -892,11 +892,8 @@
 }
 
 TEST_F(ResourceParserTest, ParseOverlayable) {
-  std::string input = R"(<overlayable />)";
-  EXPECT_TRUE(TestParse(input));
-
-  input = R"(
-      <overlayable>
+  std::string input = R"(
+      <overlayable name="Name" actor="overlay://theme">
         <item type="string" name="foo" />
         <item type="drawable" name="bar" />
       </overlayable>)";
@@ -905,24 +902,35 @@
   auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
-              Eq(Overlayable::Policy::kNone));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
 
   search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
-              Eq(Overlayable::Policy::kNone));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+}
+
+TEST_F(ResourceParserTest, ParseOverlayableRequiresName) {
+  EXPECT_FALSE(TestParse(R"(<overlayable actor="overlay://theme" />)"));
+  EXPECT_TRUE(TestParse(R"(<overlayable name="Name" />)"));
+  EXPECT_TRUE(TestParse(R"(<overlayable name="Name" actor="overlay://theme" />)"));
+}
+
+TEST_F(ResourceParserTest, ParseOverlayableBadActorFail) {
+  EXPECT_FALSE(TestParse(R"(<overlayable name="Name" actor="overley://theme" />)"));
 }
 
 TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
-  std::string input = R"(<overlayable />)";
-  EXPECT_TRUE(TestParse(input));
-
-  input = R"(
-      <overlayable>
+  std::string input = R"(
+      <overlayable name="Name">
         <item type="string" name="foo" />
         <policy type="product">
           <item type="string" name="bar" />
@@ -945,49 +953,55 @@
   auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  Overlayable& overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kNone));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProduct));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/baz"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProductServices));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProductServices));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/fiz"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kSystem));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSystem));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/fuz"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kVendor));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/faz"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kPublic));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
 }
 
 TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) {
   std::string input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="illegal_policy">
           <item type="string" name="foo" />
         </policy>
@@ -995,7 +1009,7 @@
   EXPECT_FALSE(TestParse(input));
 
   input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="product">
           <item name="foo" />
         </policy>
@@ -1003,7 +1017,7 @@
   EXPECT_FALSE(TestParse(input));
 
   input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="vendor">
           <item type="string" />
         </policy>
@@ -1013,7 +1027,7 @@
 
 TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) {
   std::string input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="vendor|product_services">
           <item type="string" name="foo" />
         </policy>
@@ -1026,39 +1040,59 @@
   auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  Overlayable& overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kVendor
-                                       | Overlayable::Policy::kProductServices));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor
+                                                   | OverlayableItem::Policy::kProductServices));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProduct
-                                       | Overlayable::Policy::kSystem));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
+                                                   | OverlayableItem::Policy::kSystem));
 }
 
 TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
   std::string input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <item type="string" name="foo" />
         <item type="string" name="foo" />
       </overlayable>)";
   EXPECT_FALSE(TestParse(input));
 
   input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <item type="string" name="foo" />
       </overlayable>
-      <overlayable>
+      <overlayable name="Name">
         <item type="string" name="foo" />
       </overlayable>)";
   EXPECT_FALSE(TestParse(input));
 
   input = R"(
-      <overlayable>
+      <overlayable name="Name">
+        <item type="string" name="foo" />
+      </overlayable>
+      <overlayable name="Other">
+        <item type="string" name="foo" />
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+
+  input = R"(
+      <overlayable name="Name" actor="overlay://my.actor.one">
+        <item type="string" name="foo" />
+      </overlayable>
+      <overlayable name="Other" actor="overlay://my.actor.two">
+        <item type="string" name="foo" />
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+
+  input = R"(
+      <overlayable name="Name">
         <policy type="product">
           <item type="string" name="foo" />
           <item type="string" name="foo" />
@@ -1067,7 +1101,7 @@
   EXPECT_FALSE(TestParse(input));
 
   input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="product">
           <item type="string" name="foo" />
         </policy>
@@ -1076,7 +1110,7 @@
   EXPECT_FALSE(TestParse(input));
 
   input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="product">
           <item type="string" name="foo" />
         </policy>
@@ -1087,13 +1121,13 @@
   EXPECT_FALSE(TestParse(input));
 
   input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="product">
           <item type="string" name="foo" />
         </policy>
       </overlayable>
 
-      <overlayable>
+      <overlayable name="Name">
         <policy type="product">
           <item type="string" name="foo" />
         </policy>
@@ -1103,7 +1137,7 @@
 
 TEST_F(ResourceParserTest, NestPolicyInOverlayableError) {
   std::string input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="vendor|product">
           <policy type="product_services">
             <item type="string" name="foo" />
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 54633ad..dbd0a0c 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -40,6 +40,8 @@
 
 namespace aapt {
 
+const char* Overlayable::kActorScheme = "overlay";
+
 static bool less_than_type_and_id(const std::unique_ptr<ResourceTableType>& lhs,
                                   const std::pair<ResourceType, Maybe<uint8_t>>& rhs) {
   return lhs->type < rhs.first || (lhs->type == rhs.first && rhs.second && lhs->id < rhs.second);
@@ -625,17 +627,18 @@
   return true;
 }
 
-bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
+bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const OverlayableItem& overlayable,
                                    IDiagnostics* diag) {
   return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
 }
 
 bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name,
-                                          const Overlayable& overlayable, IDiagnostics* diag) {
+                                          const OverlayableItem& overlayable, IDiagnostics* diag) {
   return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag);
 }
 
-bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
+bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name,
+                                       const OverlayableItem& overlayable,
                                        NameValidator name_validator, IDiagnostics *diag) {
   CHECK(diag != nullptr);
 
@@ -647,14 +650,15 @@
   ResourceTableType* type = package->FindOrCreateType(name.type);
   ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
 
-  if (entry->overlayable) {
+  if (entry->overlayable_item) {
     diag->Error(DiagMessage(overlayable.source)
-                    << "duplicate overlayable declaration for resource '" << name << "'");
-    diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here");
+                << "duplicate overlayable declaration for resource '" << name << "'");
+    diag->Error(DiagMessage(entry->overlayable_item.value().source)
+                << "previous declaration here");
     return false;
   }
 
-  entry->overlayable = overlayable;
+  entry->overlayable_item = overlayable;
   return true;
 }
 
@@ -690,7 +694,7 @@
         new_entry->id = entry->id;
         new_entry->visibility = entry->visibility;
         new_entry->allow_new = entry->allow_new;
-        new_entry->overlayable = entry->overlayable;
+        new_entry->overlayable_item = entry->overlayable_item;
 
         for (const auto& config_value : entry->values) {
           ResourceConfigValue* new_value =
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index e646f5b..eaf6a47 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -57,10 +57,27 @@
   std::string comment;
 };
 
-// Represents a declaration that a resource is overlayable at runtime.
 struct Overlayable {
+  Overlayable() = default;
+   Overlayable(const android::StringPiece& name, const android::StringPiece& actor)
+       : name(name.to_string()), actor(actor.to_string()) {}
+   Overlayable(const android::StringPiece& name, const android::StringPiece& actor,
+                    const Source& source)
+       : name(name.to_string()), actor(actor.to_string()), source(source ){}
+
+  static const char* kActorScheme;
+  std::string name;
+  std::string actor;
+  Source source;
+};
+
+// Represents a declaration that a resource is overlayable at runtime.
+struct OverlayableItem {
+  explicit OverlayableItem(const std::shared_ptr<Overlayable>& overlayable)
+      : overlayable(overlayable) {}
 
   // Represents the types overlays that are allowed to overlay the resource.
+  typedef uint32_t PolicyFlags;
   enum Policy : uint32_t {
     kNone = 0x00,
 
@@ -80,11 +97,10 @@
     kProductServices = 0x10
   };
 
-  typedef uint32_t PolicyFlags;
+  std::shared_ptr<Overlayable> overlayable;
   PolicyFlags policies = Policy::kNone;
-
-  Source source;
   std::string comment;
+  Source source;
 };
 
 class ResourceConfigValue {
@@ -121,7 +137,7 @@
   Maybe<AllowNew> allow_new;
 
   // The declarations of this resource as overlayable for RROs
-  Maybe<Overlayable> overlayable;
+  Maybe<OverlayableItem> overlayable_item;
 
   // The resource's values for each configuration.
   std::vector<std::unique_ptr<ResourceConfigValue>> values;
@@ -251,9 +267,9 @@
   bool SetVisibilityWithIdMangled(const ResourceNameRef& name, const Visibility& visibility,
                                   const ResourceId& res_id, IDiagnostics* diag);
 
-  bool SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
+  bool SetOverlayable(const ResourceNameRef& name, const OverlayableItem& overlayable,
                       IDiagnostics *diag);
-  bool SetOverlayableMangled(const ResourceNameRef& name, const Overlayable& overlayable,
+  bool SetOverlayableMangled(const ResourceNameRef& name, const OverlayableItem& overlayable,
                              IDiagnostics* diag);
 
   bool SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new, IDiagnostics* diag);
@@ -328,7 +344,7 @@
   bool SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
                        NameValidator name_validator, IDiagnostics* diag);
 
-  bool SetOverlayableImpl(const ResourceNameRef &name, const Overlayable &overlayable,
+  bool SetOverlayableImpl(const ResourceNameRef &name, const OverlayableItem& overlayable,
                           NameValidator name_validator, IDiagnostics *diag);
 
   bool SetSymbolStateImpl(const ResourceNameRef& name, const ResourceId& res_id,
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index 31095c4..a733134 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -244,48 +244,90 @@
 
 TEST(ResourceTableTest, SetOverlayable) {
   ResourceTable table;
-  Overlayable overlayable{};
-  overlayable.policies |= Overlayable::Policy::kProduct;
-  overlayable.policies |= Overlayable::Policy::kProductServices;
-  overlayable.comment = "comment";
+  auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme",
+                                                   Source("res/values/overlayable.xml", 40));
+  OverlayableItem overlayable_item(overlayable);
+  overlayable_item.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_item.policies |= OverlayableItem::Policy::kProductServices;
+  overlayable_item.comment = "comment";
+  overlayable_item.source = Source("res/values/overlayable.xml", 42);
 
   const ResourceName name = test::ParseNameOrDie("android:string/foo");
-  ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+  ASSERT_TRUE(table.SetOverlayable(name, overlayable_item, test::GetDiagnostics()));
   Maybe<ResourceTable::SearchResult> search_result = table.FindResource(name);
 
   ASSERT_TRUE(search_result);
-  ASSERT_TRUE(search_result.value().entry->overlayable);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
 
-  Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
-  ASSERT_THAT(result_overlayable.comment, StrEq("comment"));
-  EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kProduct
-                                              | Overlayable::Policy::kProductServices));
+  OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(result_overlayable_item.overlayable->source.path, Eq("res/values/overlayable.xml"));
+  EXPECT_THAT(result_overlayable_item.overlayable->source.line, 40);
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
+                                                   | OverlayableItem::Policy::kProductServices));
+  ASSERT_THAT(result_overlayable_item.comment, StrEq("comment"));
+  EXPECT_THAT(result_overlayable_item.source.path, Eq("res/values/overlayable.xml"));
+  EXPECT_THAT(result_overlayable_item.source.line, 42);
 }
 
-TEST(ResourceTableTest, AddDuplicateOverlayableSamePolicyFail) {
+TEST(ResourceTableTest, SetMultipleOverlayableResources) {
+  ResourceTable table;
+
+  const ResourceName foo = test::ParseNameOrDie("android:string/foo");
+  auto group = std::make_shared<Overlayable>("Name", "overlay://theme");
+  OverlayableItem overlayable(group);
+  overlayable.policies = OverlayableItem::Policy::kProduct;
+  ASSERT_TRUE(table.SetOverlayable(foo, overlayable, test::GetDiagnostics()));
+
+  const ResourceName bar = test::ParseNameOrDie("android:string/bar");
+  OverlayableItem overlayable2(group);
+  overlayable2.policies = OverlayableItem::Policy::kProduct;
+  ASSERT_TRUE(table.SetOverlayable(bar, overlayable2, test::GetDiagnostics()));
+
+  const ResourceName baz = test::ParseNameOrDie("android:string/baz");
+  OverlayableItem overlayable3(group);
+  overlayable3.policies = OverlayableItem::Policy::kVendor;
+  ASSERT_TRUE(table.SetOverlayable(baz, overlayable3, test::GetDiagnostics()));
+}
+
+TEST(ResourceTableTest, SetOverlayableDifferentResourcesDifferentName) {
+  ResourceTable table;
+
+  const ResourceName foo = test::ParseNameOrDie("android:string/foo");
+  OverlayableItem overlayable_item(std::make_shared<Overlayable>("Name", "overlay://theme"));
+  overlayable_item.policies = OverlayableItem::Policy::kProduct;
+  ASSERT_TRUE(table.SetOverlayable(foo, overlayable_item, test::GetDiagnostics()));
+
+  const ResourceName bar = test::ParseNameOrDie("android:string/bar");
+  OverlayableItem overlayable_item2(std::make_shared<Overlayable>("Name2",  "overlay://theme"));
+  overlayable_item2.policies = OverlayableItem::Policy::kProduct;
+  ASSERT_TRUE(table.SetOverlayable(bar, overlayable_item2, test::GetDiagnostics()));
+}
+
+TEST(ResourceTableTest, SetOverlayableSameResourcesFail) {
   ResourceTable table;
   const ResourceName name = test::ParseNameOrDie("android:string/foo");
 
-  Overlayable overlayable{};
-  overlayable.policies = Overlayable::Policy::kProduct;
-  ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+  auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme");
+  OverlayableItem overlayable_item(overlayable);
+  ASSERT_TRUE(table.SetOverlayable(name, overlayable_item, test::GetDiagnostics()));
 
-  Overlayable overlayable2{};
-  overlayable2.policies = Overlayable::Policy::kProduct;
-  ASSERT_FALSE(table.SetOverlayable(name, overlayable2, test::GetDiagnostics()));
+  OverlayableItem overlayable_item2(overlayable);
+  ASSERT_FALSE(table.SetOverlayable(name, overlayable_item2, test::GetDiagnostics()));
 }
 
-TEST(ResourceTableTest, AddDuplicateOverlayableDifferentPolicyFail) {
+TEST(ResourceTableTest,  SetOverlayableSameResourcesDifferentNameFail) {
   ResourceTable table;
   const ResourceName name = test::ParseNameOrDie("android:string/foo");
 
-  Overlayable overlayable{};
-  overlayable.policies = Overlayable::Policy::kProduct;
-  ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+  auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme");
+  OverlayableItem overlayable_item(overlayable);
+  ASSERT_TRUE(table.SetOverlayable(name, overlayable_item, test::GetDiagnostics()));
 
-  Overlayable overlayable2{};
-  overlayable2.policies = Overlayable::Policy::kVendor;
-  ASSERT_FALSE(table.SetOverlayable(name, overlayable2, test::GetDiagnostics()));
+  auto overlayable2 = std::make_shared<Overlayable>("Other", "overlay://theme");
+  OverlayableItem overlayable_item2(overlayable2);
+  ASSERT_FALSE(table.SetOverlayable(name, overlayable_item2, test::GetDiagnostics()));
 }
 
 TEST(ResourceTableTest, AllowDuplictaeResourcesNames) {
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index da22e88..c6f9152 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -362,7 +362,7 @@
     return util::make_unique<BinaryPrimitive>(flags);
   }
 
-  for (StringPiece part : util::Tokenize(str, '|')) {
+  for (const StringPiece& part : util::Tokenize(str, '|')) {
     StringPiece trimmed_part = util::TrimWhitespace(part);
 
     bool flag_set = false;
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 81a2c2e..da541be 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -49,6 +49,9 @@
 
   // Resource definitions corresponding to an Android package.
   repeated Package package = 2;
+
+  // The <overlayable> declarations within the resource table.
+  repeated Overlayable overlayable = 3;
 }
 
 // A package ID in the range [0x00, 0xff].
@@ -133,8 +136,20 @@
   string comment = 2;
 }
 
-// Represents a declaration that a resource is overayable at runtime.
+// Represents a set of overlayable resources.
 message Overlayable {
+  // The name of the <overlyabale>.
+  string name = 1;
+
+  // The location of the <overlyabale> declaration in the source.
+  Source source = 2;
+
+  // The component responsible for enabling and disabling overlays targeting this <overlayable>.
+  string actor = 3;
+}
+
+// Represents an overlayable <item> declaration within an <overlayable> tag.
+message OverlayableItem {
   enum Policy {
     PUBLIC = 0;
     SYSTEM = 1;
@@ -143,14 +158,18 @@
     PRODUCT_SERVICES = 4;
   }
 
-  // Where this declaration was defined in source.
+  // The location of the <item> declaration in source.
   Source source = 1;
 
   // Any comment associated with the declaration.
   string comment = 2;
 
-  // The policy defined in the overlayable declaration.
+  // The policy defined by the enclosing <policy> tag of this <item>.
   repeated Policy policy = 3;
+
+  // The index into overlayable list that points to the <overlayable> tag that contains
+  // this <item>.
+  uint32 overlayable_idx = 4;
 }
 
 // An entry ID in the range [0x0000, 0xffff].
@@ -180,7 +199,7 @@
   AllowNew allow_new = 4;
 
   // Whether this resource can be overlaid by a runtime resource overlay (RRO).
-  Overlayable overlayable = 5;
+  OverlayableItem overlayable_item = 5;
 
   // The set of values defined for this entry, each corresponding to a different
   // configuration/variant.
diff --git a/tools/aapt2/cmd/Command.cpp b/tools/aapt2/cmd/Command.cpp
index bdee5c9..4424a35 100644
--- a/tools/aapt2/cmd/Command.cpp
+++ b/tools/aapt2/cmd/Command.cpp
@@ -21,80 +21,97 @@
 #include <string>
 #include <vector>
 
+#include "android-base/stringprintf.h"
+#include "android-base/utf8.h"
 #include "androidfw/StringPiece.h"
 
 #include "util/Util.h"
 
+using android::base::StringPrintf;
 using android::StringPiece;
 
 namespace aapt {
 
-void Command::AddRequiredFlag(const StringPiece& name,
-    const StringPiece& description, std::string* value) {
-  auto func = [value](const StringPiece& arg) -> bool {
-    *value = arg.to_string();
+std::string GetSafePath(const StringPiece& arg) {
+#ifdef _WIN32
+  // If the path exceeds the maximum path length for Windows, encode the path using the
+  // extended-length prefix
+  std::wstring path16;
+  CHECK(android::base::UTF8PathToWindowsLongPath(arg.data(), &path16))
+      << "Failed to convert file path to UTF-16: file path " << arg.data();
+
+  std::string path8;
+  CHECK(android::base::WideToUTF8(path16, &path8))
+      << "Failed to convert file path back to UTF-8: file path " << arg.data();
+
+  return path8;
+#else
+  return arg.to_string();
+#endif
+}
+
+void Command::AddRequiredFlag(const StringPiece& name, const StringPiece& description,
+                              std::string* value, uint32_t flags) {
+  auto func = [value, flags](const StringPiece& arg) -> bool {
+    *value = (flags & Command::kPath) ? GetSafePath(arg) : arg.to_string();
     return true;
   };
 
-  flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false});
+  flags_.emplace_back(Flag(name, description, /* required */ true, /* num_args */ 1, func));
 }
 
-void Command::AddRequiredFlagList(const StringPiece& name,
-    const StringPiece& description,
-    std::vector<std::string>* value) {
-  auto func = [value](const StringPiece& arg) -> bool {
-    value->push_back(arg.to_string());
+void Command::AddRequiredFlagList(const StringPiece& name, const StringPiece& description,
+                                  std::vector<std::string>* value, uint32_t flags) {
+  auto func = [value, flags](const StringPiece& arg) -> bool {
+    value->push_back((flags & Command::kPath) ? GetSafePath(arg) : arg.to_string());
     return true;
   };
 
-  flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false});
+  flags_.emplace_back(Flag(name, description, /* required */ true, /* num_args */ 1, func));
 }
 
-void Command::AddOptionalFlag(const StringPiece& name,
-    const StringPiece& description,
-    Maybe<std::string>* value) {
-  auto func = [value](const StringPiece& arg) -> bool {
-    *value = arg.to_string();
+void Command::AddOptionalFlag(const StringPiece& name, const StringPiece& description,
+                              Maybe<std::string>* value, uint32_t flags) {
+  auto func = [value, flags](const StringPiece& arg) -> bool {
+    *value = (flags & Command::kPath) ? GetSafePath(arg) : arg.to_string();
     return true;
   };
 
-  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
+  flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 1, func));
 }
 
-void Command::AddOptionalFlagList(const StringPiece& name,
-    const StringPiece& description,
-    std::vector<std::string>* value) {
-  auto func = [value](const StringPiece& arg) -> bool {
-    value->push_back(arg.to_string());
+void Command::AddOptionalFlagList(const StringPiece& name, const StringPiece& description,
+                                  std::vector<std::string>* value, uint32_t flags) {
+  auto func = [value, flags](const StringPiece& arg) -> bool {
+    value->push_back((flags & Command::kPath) ? GetSafePath(arg) : arg.to_string());
     return true;
   };
 
-  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
+  flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 1, func));
 }
 
-void Command::AddOptionalFlagList(const StringPiece& name,
-    const StringPiece& description,
-    std::unordered_set<std::string>* value) {
+void Command::AddOptionalFlagList(const StringPiece& name, const StringPiece& description,
+                                  std::unordered_set<std::string>* value) {
   auto func = [value](const StringPiece& arg) -> bool {
     value->insert(arg.to_string());
     return true;
   };
 
-  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
+  flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 1, func));
 }
 
-void Command::AddOptionalSwitch(const StringPiece& name,
-    const StringPiece& description, bool* value) {
+void Command::AddOptionalSwitch(const StringPiece& name, const StringPiece& description,
+                                bool* value) {
   auto func = [value](const StringPiece& arg) -> bool {
     *value = true;
     return true;
   };
 
-  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 0, false});
+  flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 0, func));
 }
 
 void Command::AddOptionalSubcommand(std::unique_ptr<Command>&& subcommand, bool experimental) {
-  subcommand->fullname_ = name_ + " " + subcommand->name_;
+  subcommand->full_subcommand_name_ = StringPrintf("%s %s", name_.data(), subcommand->name_.data());
   if (experimental) {
     experimental_subcommands_.push_back(std::move(subcommand));
   } else {
@@ -102,14 +119,14 @@
   }
 }
 
-void Command::SetDescription(const android::StringPiece& description) {
+void Command::SetDescription(const StringPiece& description) {
   description_ = description.to_string();
 }
 
 void Command::Usage(std::ostream* out) {
   constexpr size_t kWidth = 50;
 
-  *out << fullname_;
+  *out << full_subcommand_name_;
 
   if (!subcommands_.empty()) {
     *out << " [subcommand]";
@@ -117,7 +134,7 @@
 
   *out << " [options]";
   for (const Flag& flag : flags_) {
-    if (flag.required) {
+    if (flag.is_required) {
       *out << " " << flag.name << " arg";
     }
   }
@@ -160,29 +177,31 @@
   out->flush();
 }
 
-int Command::Execute(const std::vector<android::StringPiece>& args, std::ostream* out_error) {
+int Command::Execute(const std::vector<StringPiece>& args, std::ostream* out_error) {
   std::vector<std::string> file_args;
 
   for (size_t i = 0; i < args.size(); i++) {
-    StringPiece arg = args[i];
+    const StringPiece& arg = args[i];
     if (*(arg.data()) != '-') {
       // Continue parsing as the subcommand if the first argument matches one of the subcommands
       if (i == 0) {
         for (auto& subcommand : subcommands_) {
-          if (arg == subcommand->name_ || arg==subcommand->short_name_) {
+          if (arg == subcommand->name_ || (!subcommand->short_name_.empty()
+                                           && arg == subcommand->short_name_)) {
             return subcommand->Execute(
-                std::vector<android::StringPiece>(args.begin() + 1, args.end()), out_error);
+                std::vector<StringPiece>(args.begin() + 1, args.end()), out_error);
           }
         }
         for (auto& subcommand : experimental_subcommands_) {
-          if (arg == subcommand->name_ || arg==subcommand->short_name_) {
+          if (arg == subcommand->name_ || (!subcommand->short_name_.empty()
+                                           && arg == subcommand->short_name_)) {
             return subcommand->Execute(
-              std::vector<android::StringPiece>(args.begin() + 1, args.end()), out_error);
+              std::vector<StringPiece>(args.begin() + 1, args.end()), out_error);
           }
         }
       }
 
-      file_args.push_back(arg.to_string());
+      file_args.push_back(GetSafePath(arg));
       continue;
     }
 
@@ -205,7 +224,7 @@
         } else {
           flag.action({});
         }
-        flag.parsed = true;
+        flag.found = true;
         match = true;
         break;
       }
@@ -219,7 +238,7 @@
   }
 
   for (const Flag& flag : flags_) {
-    if (flag.required && !flag.parsed) {
+    if (flag.is_required && !flag.found) {
       *out_error << "missing required flag " << flag.name << "\n\n";
       Usage(out_error);
       return 1;
diff --git a/tools/aapt2/cmd/Command.h b/tools/aapt2/cmd/Command.h
index 1694988..d21571d 100644
--- a/tools/aapt2/cmd/Command.h
+++ b/tools/aapt2/cmd/Command.h
@@ -32,55 +32,83 @@
 class Command {
  public:
   explicit Command(const android::StringPiece& name) : name_(name.to_string()),
-                                                       fullname_(name.to_string()) { }
+                                                       short_name_(""),
+                                                       full_subcommand_name_(name.to_string()) {}
+
   explicit Command(const android::StringPiece& name, const android::StringPiece& short_name)
-      : name_(name.to_string()), short_name_(short_name.to_string()), fullname_(name.to_string()) {}
+      : name_(name.to_string()), short_name_(short_name.to_string()),
+        full_subcommand_name_(name.to_string()) {}
+
   virtual ~Command() = default;
 
+  // Behavior flags used with the following functions that change how the command flags are parsed
+  // displayed.
+  enum : uint32_t {
+    // Indicates the arguments are file or folder paths. On Windows, paths that exceed the maximum
+    // path length will be converted to use the extended path prefix '//?/'. Without this
+    // conversion, files with long paths cannot be opened.
+    kPath = 1 << 0,
+  };
+
   void AddRequiredFlag(const android::StringPiece& name, const android::StringPiece& description,
-      std::string* value);
-  void AddRequiredFlagList(const android::StringPiece& name, const android::StringPiece&
-      description, std::vector<std::string>* value);
+                       std::string* value, uint32_t flags = 0);
+
+  void AddRequiredFlagList(const android::StringPiece& name,
+                           const android::StringPiece& description, std::vector<std::string>* value,
+                           uint32_t flags = 0);
+
   void AddOptionalFlag(const android::StringPiece& name, const android::StringPiece& description,
-      Maybe<std::string>* value);
+                       Maybe<std::string>* value, uint32_t flags = 0);
+
   void AddOptionalFlagList(const android::StringPiece& name,
-      const android::StringPiece& description, std::vector<std::string>* value);
+                           const android::StringPiece& description, std::vector<std::string>* value,
+                           uint32_t flags = 0);
+
   void AddOptionalFlagList(const android::StringPiece& name,
-      const android::StringPiece& description, std::unordered_set<std::string>* value);
+                           const android::StringPiece& description,
+                           std::unordered_set<std::string>* value);
+
   void AddOptionalSwitch(const android::StringPiece& name, const android::StringPiece& description,
-      bool* value);
+                         bool* value);
+
   void AddOptionalSubcommand(std::unique_ptr<Command>&& subcommand, bool experimental = false);
 
   void SetDescription(const android::StringPiece& name);
 
-  /** Prints the help menu of the command. */
+  // Prints the help menu of the command.
   void Usage(std::ostream* out);
 
-  /**
-   * Parses the command line arguments, sets the flag variable values, and runs the action of
-   * the command. If the arguments fail to parse to the command and its subcommands, then the action
-   * will not be run and the usage will be printed instead.
-   **/
+  // Parses the command line arguments, sets the flag variable values, and runs the action of
+  // the command. If the arguments fail to parse to the command and its subcommands, then the action
+  // will not be run and the usage will be printed instead.
   int Execute(const std::vector<android::StringPiece>& args, std::ostream* outError);
 
-  /** The action to preform when the command is executed. */
+  // The action to preform when the command is executed.
   virtual int Action(const std::vector<std::string>& args) = 0;
 
  private:
-  struct Flag {
-    std::string name;
-    std::string description;
-    std::function<bool(const android::StringPiece& value)> action;
-    bool required;
-    size_t num_args;
+  DISALLOW_COPY_AND_ASSIGN(Command);
 
-    bool parsed;
+  struct Flag {
+    explicit Flag(const android::StringPiece& name, const android::StringPiece& description,
+                  const bool is_required, const size_t num_args,
+                  std::function<bool(const android::StringPiece& value)>&& action)
+        : name(name.to_string()), description(description.to_string()), is_required(is_required),
+          num_args(num_args), action(std::move(action)) {}
+
+    const std::string name;
+    const std::string description;
+    const bool is_required;
+    const size_t num_args;
+    const std::function<bool(const android::StringPiece& value)> action;
+    bool found = false;
   };
 
-  std::string description_;
-  std::string name_;
-  std::string short_name_;
-  std::string fullname_;
+  const std::string name_;
+  const std::string short_name_;
+  std::string description_ = "";
+  std::string full_subcommand_name_;
+
   std::vector<Flag> flags_;
   std::vector<std::unique_ptr<Command>> subcommands_;
   std::vector<std::unique_ptr<Command>> experimental_subcommands_;
diff --git a/tools/aapt2/cmd/Command_test.cpp b/tools/aapt2/cmd/Command_test.cpp
new file mode 100644
index 0000000..65608fd
--- /dev/null
+++ b/tools/aapt2/cmd/Command_test.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Command.h"
+
+#include "test/Test.h"
+
+using ::testing::Eq;
+
+namespace aapt {
+
+class TestCommand : public Command {
+ public:
+  explicit TestCommand() : Command("command") {}
+  int Action(const std::vector<std::string>& args) override {
+    args_ = args;
+    return 0;
+  }
+
+  std::vector<std::string> args_;
+};
+
+#ifdef _WIN32
+TEST(CommandTest, LongFullyQualifiedPathWindows) {
+  TestCommand command;
+  std::string required_flag;
+  command.AddRequiredFlag("--rflag", "", &required_flag, Command::kPath);
+  Maybe<std::string> optional_flag;
+  command.AddOptionalFlag("--oflag", "", &optional_flag, Command::kPath);
+  std::vector<std::string> required_flag_list;
+  command.AddRequiredFlagList("--rlflag", "", &required_flag_list, Command::kPath);
+  std::vector<std::string> optional_flag_list;
+  command.AddOptionalFlagList("--olflag", "", &optional_flag_list, Command::kPath);
+  std::string non_path_flag;
+  command.AddRequiredFlag("--nflag", "", &non_path_flag);
+
+  const std::string kLongPath =
+      "C:\\Users\\jedo\\_bazel_jedo\\vcmdctjv\\execroot\\__main__\\_tmp"
+      "\\6767b4778f8798efc0f784ee74fa70ee\\tests\\testApksAr8c7560a9a65"
+      "\\1346ee7c014a089fb55d8c46cf3d9\\project\\baseModule\\build"
+      "\\intermediates\\processed_res\\minified\\processMinifiedResources"
+      "\\1346ee7c014a089fb55d8c46cf3d9\\project\\baseModule\\build"
+      "\\intermediates\\processed_res\\minified\\processMinifiedResources"
+      "\\out\\resources-minified.ap_";
+
+  const std::string kExpected =
+      "\\\\?\\C:\\Users\\jedo\\_bazel_jedo\\vcmdctjv\\execroot\\__main__\\_tmp"
+      "\\6767b4778f8798efc0f784ee74fa70ee\\tests\\testApksAr8c7560a9a65"
+      "\\1346ee7c014a089fb55d8c46cf3d9\\project\\baseModule\\build"
+      "\\intermediates\\processed_res\\minified\\processMinifiedResources"
+      "\\1346ee7c014a089fb55d8c46cf3d9\\project\\baseModule\\build"
+      "\\intermediates\\processed_res\\minified\\processMinifiedResources"
+      "\\out\\resources-minified.ap_";
+
+
+  ASSERT_THAT(command.Execute({"--rflag", kLongPath,
+                               "--oflag", kLongPath,
+                               "--rlflag", kLongPath,
+                               "--rlflag", kLongPath,
+                               "--olflag", kLongPath,
+                               "--olflag", kLongPath,
+                               "--nflag", kLongPath,
+                               kLongPath, kLongPath}, &std::cerr), Eq(0));
+
+  ASSERT_THAT(required_flag, Eq(kExpected));
+  ASSERT_THAT(optional_flag, Eq(kExpected));
+  ASSERT_THAT(required_flag_list.size(), Eq(2));
+  ASSERT_THAT(required_flag_list[0], Eq(kExpected));
+  ASSERT_THAT(required_flag_list[1], Eq(kExpected));
+  ASSERT_THAT(optional_flag_list.size(), Eq(2));
+  ASSERT_THAT(optional_flag_list[0], Eq(kExpected));
+  ASSERT_THAT(optional_flag_list[1], Eq(kExpected));
+
+  // File arguments should also be converted to use the long path prefix
+  ASSERT_THAT(command.args_.size(), Eq(2));
+  ASSERT_THAT(command.args_[0], Eq(kExpected));
+  ASSERT_THAT(command.args_[1], Eq(kExpected));
+
+  // Do not convert flags that are not marged as paths
+  ASSERT_THAT(non_path_flag, Eq(kLongPath));
+}
+#endif
+
+}  // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index fc9514a..52375a3 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -433,7 +433,7 @@
     }
 
     Printer r_txt_printer(&fout_text);
-    for (const auto res : xmlres->file.exported_symbols) {
+    for (const auto& res : xmlres->file.exported_symbols) {
       r_txt_printer.Print("default int id ");
       r_txt_printer.Println(res.name.entry);
     }
@@ -584,7 +584,7 @@
 
 class CompileContext : public IAaptContext {
  public:
-  CompileContext(IDiagnostics* diagnostics) : diagnostics_(diagnostics) {
+  explicit CompileContext(IDiagnostics* diagnostics) : diagnostics_(diagnostics) {
   }
 
   PackageType GetPackageType() override {
diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h
index c429d5f..9b32cb3 100644
--- a/tools/aapt2/cmd/Compile.h
+++ b/tools/aapt2/cmd/Compile.h
@@ -44,13 +44,13 @@
   explicit CompileCommand(IDiagnostics* diagnostic) : Command("compile", "c"),
                                                       diagnostic_(diagnostic) {
     SetDescription("Compiles resources to be linked into an apk.");
-    AddRequiredFlag("-o", "Output path", &options_.output_path);
-    AddOptionalFlag("--dir", "Directory to scan for resources", &options_.res_dir);
+    AddRequiredFlag("-o", "Output path", &options_.output_path, Command::kPath);
+    AddOptionalFlag("--dir", "Directory to scan for resources", &options_.res_dir, Command::kPath);
     AddOptionalFlag("--zip", "Zip file containing the res directory to scan for resources",
-        &options_.res_zip);
+        &options_.res_zip, Command::kPath);
     AddOptionalFlag("--output-text-symbols",
         "Generates a text file containing the resource symbols in the\n"
-            "specified file", &options_.generate_text_symbols_path);
+            "specified file", &options_.generate_text_symbols_path, Command::kPath);
     AddOptionalSwitch("--pseudo-localize", "Generate resources for pseudo-locales "
         "(en-XA and ar-XB)", &options_.pseudolocalize);
     AddOptionalSwitch("--no-crunch", "Disables PNG processing", &options_.no_png_crunch);
@@ -70,8 +70,9 @@
   Maybe<std::string> visibility_;
 };
 
-int Compile(IAaptContext* context, io::IFileCollection* inputs,
-             IArchiveWriter* output_writer, CompileOptions& options);
+int Compile(IAaptContext* context, io::IFileCollection* inputs, IArchiveWriter* output_writer,
+            CompileOptions& options);
+
 }// namespace aapt
 
 #endif //AAPT2_COMPILE_H
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 4492f6b..85f9080 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -60,16 +60,17 @@
 class BinaryApkSerializer : public IApkSerializer {
  public:
   BinaryApkSerializer(IAaptContext* context, const Source& source,
-                   const TableFlattenerOptions& options)
-      : IApkSerializer(context, source), tableFlattenerOptions_(options) {}
+                      const TableFlattenerOptions& table_flattener_options,
+                      const XmlFlattenerOptions& xml_flattener_options)
+      : IApkSerializer(context, source),
+        table_flattener_options_(table_flattener_options),
+        xml_flattener_options_(xml_flattener_options) {}
 
   bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
                     IArchiveWriter* writer, uint32_t compression_flags) override {
     BigBuffer buffer(4096);
-    XmlFlattenerOptions options = {};
-    options.use_utf16 = utf16;
-    options.keep_raw_values = true;
-    XmlFlattener flattener(&buffer, options);
+    xml_flattener_options_.use_utf16 = utf16;
+    XmlFlattener flattener(&buffer, xml_flattener_options_);
     if (!flattener.Consume(context_, xml)) {
       return false;
     }
@@ -80,7 +81,7 @@
 
   bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) override {
     BigBuffer buffer(4096);
-    TableFlattener table_flattener(tableFlattenerOptions_, &buffer);
+    TableFlattener table_flattener(table_flattener_options_, &buffer);
     if (!table_flattener.Consume(context_, table)) {
       return false;
     }
@@ -136,7 +137,8 @@
   }
 
  private:
-  TableFlattenerOptions tableFlattenerOptions_;
+  TableFlattenerOptions table_flattener_options_;
+  XmlFlattenerOptions xml_flattener_options_;
 
   DISALLOW_COPY_AND_ASSIGN(BinaryApkSerializer);
 };
@@ -252,13 +254,15 @@
 };
 
 int Convert(IAaptContext* context, LoadedApk* apk, IArchiveWriter* output_writer,
-            ApkFormat output_format, TableFlattenerOptions& options) {
+            ApkFormat output_format, TableFlattenerOptions table_flattener_options,
+            XmlFlattenerOptions xml_flattener_options) {
   // Do not change the ordering of strings in the values string pool
-  options.sort_stringpool_entries = false;
+  table_flattener_options.sort_stringpool_entries = false;
 
   unique_ptr<IApkSerializer> serializer;
   if (output_format == ApkFormat::kBinary) {
-    serializer.reset(new BinaryApkSerializer(context, apk->GetSource(), options));
+    serializer.reset(new BinaryApkSerializer(context, apk->GetSource(), table_flattener_options,
+                                             xml_flattener_options));
   } else if (output_format == ApkFormat::kProto) {
     serializer.reset(new ProtoApkSerializer(context, apk->GetSource()));
   } else {
@@ -378,7 +382,8 @@
     return 1;
   }
 
-  return Convert(&context, apk.get(), writer.get(), format, options_);
+  return Convert(&context, apk.get(), writer.get(), format, table_flattener_options_,
+                 xml_flattener_options_);
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/cmd/Convert.h b/tools/aapt2/cmd/Convert.h
index 6a6719c..7e2029d 100644
--- a/tools/aapt2/cmd/Convert.h
+++ b/tools/aapt2/cmd/Convert.h
@@ -20,6 +20,7 @@
 #include "Command.h"
 #include "LoadedApk.h"
 #include "format/binary/TableFlattener.h"
+#include "format/binary/XmlFlattener.h"
 
 namespace aapt {
 
@@ -27,14 +28,18 @@
  public:
   explicit ConvertCommand() : Command("convert") {
     SetDescription("Converts an apk between binary and proto formats.");
-    AddRequiredFlag("-o", "Output path", &output_path_);
+    AddRequiredFlag("-o", "Output path", &output_path_, Command::kPath);
     AddOptionalFlag("--output-format", android::base::StringPrintf("Format of the output. "
             "Accepted values are '%s' and '%s'. When not set, defaults to '%s'.",
         kOutputFormatProto, kOutputFormatBinary, kOutputFormatBinary), &output_format_);
     AddOptionalSwitch("--enable-sparse-encoding",
         "Enables encoding sparse entries using a binary search tree.\n"
-            "This decreases APK size at the cost of resource retrieval performance.",
-        &options_.use_sparse_entries);
+        "This decreases APK size at the cost of resource retrieval performance.",
+         &table_flattener_options_.use_sparse_entries);
+    AddOptionalSwitch("--keep-raw-values",
+        android::base::StringPrintf("Preserve raw attribute values in xml files when using the"
+            " '%s' output format", kOutputFormatBinary),
+        &xml_flattener_options_.keep_raw_values);
     AddOptionalSwitch("-v", "Enables verbose logging", &verbose_);
   }
 
@@ -44,14 +49,16 @@
   const static char* kOutputFormatProto;
   const static char* kOutputFormatBinary;
 
-  TableFlattenerOptions options_;
+  TableFlattenerOptions table_flattener_options_;
+  XmlFlattenerOptions xml_flattener_options_;
   std::string output_path_;
   Maybe<std::string> output_format_;
   bool verbose_ = false;
 };
 
 int Convert(IAaptContext* context, LoadedApk* input, IArchiveWriter* output_writer,
-            ApkFormat output_format, TableFlattenerOptions& options);
+            ApkFormat output_format,TableFlattenerOptions table_flattener_options,
+            XmlFlattenerOptions xml_flattener_options);
 
 }  // namespace aapt
 
diff --git a/tools/aapt2/cmd/Convert_test.cpp b/tools/aapt2/cmd/Convert_test.cpp
new file mode 100644
index 0000000..2e43150
--- /dev/null
+++ b/tools/aapt2/cmd/Convert_test.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Convert.h"
+
+#include "LoadedApk.h"
+#include "test/Test.h"
+
+using testing::Eq;
+using testing::Ne;
+
+namespace aapt {
+
+using ConvertTest = CommandTestFixture;
+
+TEST_F(ConvertTest, RemoveRawXmlStrings) {
+  StdErrDiagnostics diag;
+  const std::string compiled_files_dir = GetTestPath("compiled");
+  ASSERT_TRUE(CompileFile(GetTestPath("res/xml/test.xml"), R"(<Item AgentCode="007"/>)",
+                          compiled_files_dir, &diag));
+
+  const std::string out_apk = GetTestPath("out.apk");
+  std::vector<std::string> link_args = {
+      "--manifest", GetDefaultManifest(),
+      "-o", out_apk,
+      "--keep-raw-values",
+      "--proto-format"
+  };
+
+  ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
+
+  const std::string out_convert_apk = GetTestPath("out_convert.apk");
+  std::vector<android::StringPiece> convert_args = {
+      "-o", out_convert_apk,
+      "--output-format", "binary",
+      out_apk,
+  };
+  ASSERT_THAT(ConvertCommand().Execute(convert_args, &std::cerr), Eq(0));
+
+  // Load the binary xml tree
+  android::ResXMLTree tree;
+  std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_convert_apk, &diag);
+  AssertLoadXml(apk.get(), "res/xml/test.xml", &tree);
+
+  // Check that the raw string index has not been assigned
+  EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(-1));
+}
+
+TEST_F(ConvertTest, KeepRawXmlStrings) {
+  StdErrDiagnostics diag;
+  const std::string compiled_files_dir = GetTestPath("compiled");
+  ASSERT_TRUE(CompileFile(GetTestPath("res/xml/test.xml"), R"(<Item AgentCode="007"/>)",
+                          compiled_files_dir, &diag));
+
+  const std::string out_apk = GetTestPath("out.apk");
+  std::vector<std::string> link_args = {
+      "--manifest", GetDefaultManifest(),
+      "-o", out_apk,
+      "--keep-raw-values",
+      "--proto-format"
+  };
+
+  ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
+
+  const std::string out_convert_apk = GetTestPath("out_convert.apk");
+  std::vector<android::StringPiece> convert_args = {
+      "-o", out_convert_apk,
+      "--output-format", "binary",
+      "--keep-raw-values",
+      out_apk,
+  };
+  ASSERT_THAT(ConvertCommand().Execute(convert_args, &std::cerr), Eq(0));
+
+  // Load the binary xml tree
+  android::ResXMLTree tree;
+  std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_convert_apk, &diag);
+  AssertLoadXml(apk.get(), "res/xml/test.xml", &tree);
+
+  // Check that the raw string index has been set to the correct string pool entry
+  int32_t raw_index = tree.getAttributeValueStringID(0);
+  ASSERT_THAT(raw_index, Ne(-1));
+  EXPECT_THAT(util::GetString(tree.getStrings(), static_cast<size_t>(raw_index)), Eq("007"));
+}
+
+}  // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 1b5601d..729447e 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -78,7 +78,7 @@
 
 class LinkContext : public IAaptContext {
  public:
-  LinkContext(IDiagnostics* diagnostics)
+  explicit LinkContext(IDiagnostics* diagnostics)
       : diagnostics_(diagnostics), name_mangler_({}), symbols_(&name_mangler_) {
   }
 
@@ -163,7 +163,7 @@
 // See b/37498913.
 class FeatureSplitSymbolTableDelegate : public DefaultSymbolTableDelegate {
  public:
-  FeatureSplitSymbolTableDelegate(IAaptContext* context) : context_(context) {
+  explicit FeatureSplitSymbolTableDelegate(IAaptContext* context) : context_(context) {
   }
 
   virtual ~FeatureSplitSymbolTableDelegate() = default;
@@ -1545,7 +1545,8 @@
   // to the IArchiveWriter.
   bool WriteApk(IArchiveWriter* writer, proguard::KeepSet* keep_set, xml::XmlResource* manifest,
                 ResourceTable* table) {
-    const bool keep_raw_values = context_->GetPackageType() == PackageType::kStaticLib;
+    const bool keep_raw_values = (context_->GetPackageType() == PackageType::kStaticLib)
+                                 || options_.keep_raw_values;
     bool result = FlattenXml(context_, *manifest, kAndroidManifestPath, keep_raw_values,
                              true /*utf16*/, options_.output_format, writer);
     if (!result) {
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 950dac2..f70470a 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -75,6 +75,7 @@
 
   // Flattening options.
   TableFlattenerOptions table_flattener_options;
+  bool keep_raw_values = false;
 
   // Split APK options.
   TableSplitterOptions table_splitter_options;
@@ -100,24 +101,26 @@
   explicit LinkCommand(IDiagnostics* diag) : Command("link", "l"),
                                              diag_(diag) {
     SetDescription("Links resources into an apk.");
-    AddRequiredFlag("-o", "Output path.", &options_.output_path);
+    AddRequiredFlag("-o", "Output path.", &options_.output_path, Command::kPath);
     AddRequiredFlag("--manifest", "Path to the Android manifest to build.",
-        &options_.manifest_path);
-    AddOptionalFlagList("-I", "Adds an Android APK to link against.", &options_.include_paths);
+        &options_.manifest_path, Command::kPath);
+    AddOptionalFlagList("-I", "Adds an Android APK to link against.", &options_.include_paths,
+         Command::kPath);
     AddOptionalFlagList("-A", "An assets directory to include in the APK. These are unprocessed.",
-        &options_.assets_dirs);
+        &options_.assets_dirs, Command::kPath);
     AddOptionalFlagList("-R", "Compilation unit to link, using `overlay` semantics.\n"
-        "The last conflicting resource given takes precedence.", &overlay_arg_list_);
+        "The last conflicting resource given takes precedence.", &overlay_arg_list_,
+        Command::kPath);
     AddOptionalFlag("--package-id",
         "Specify the package ID to use for this app. Must be greater or equal to\n"
             "0x7f and can't be used with --static-lib or --shared-lib.", &package_id_);
     AddOptionalFlag("--java", "Directory in which to generate R.java.",
-        &options_.generate_java_class_path);
+        &options_.generate_java_class_path, Command::kPath);
     AddOptionalFlag("--proguard", "Output file for generated Proguard rules.",
-        &options_.generate_proguard_rules_path);
+        &options_.generate_proguard_rules_path, Command::kPath);
     AddOptionalFlag("--proguard-main-dex",
         "Output file for generated Proguard rules for the main dex.",
-        &options_.generate_main_dex_proguard_rules_path);
+        &options_.generate_main_dex_proguard_rules_path, Command::kPath);
     AddOptionalSwitch("--proguard-conditional-keep-rules",
         "Generate conditional Proguard keep rules.",
         &options_.generate_conditional_proguard_rules);
@@ -242,6 +245,8 @@
         &options_.extensions_to_not_compress);
     AddOptionalSwitch("--no-compress", "Do not compress any resources.",
         &options_.do_not_compress_anything);
+    AddOptionalSwitch("--keep-raw-values", "Preserve raw attribute values in xml files.",
+        &options_.keep_raw_values);
     AddOptionalSwitch("--warn-manifest-validation",
         "Treat manifest validation errors as warnings.",
         &options_.manifest_fixer_options.warn_validation);
@@ -250,7 +255,6 @@
             "Syntax: path/to/output.apk:<config>[,<config>[...]].\n"
             "On Windows, use a semicolon ';' separator instead.",
         &split_args_);
-    AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_);
     AddOptionalSwitch("--debug-mode",
         "Inserts android:debuggable=\"true\" in to the application node of the\n"
             "manifest, making the application debuggable even on production devices.",
@@ -258,6 +262,7 @@
     AddOptionalSwitch("--strict-visibility",
         "Do not allow overlays with different visibility levels.",
         &options_.strict_visibility);
+    AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_);
   }
 
   int Action(const std::vector<std::string>& args) override;
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
new file mode 100644
index 0000000..3c8b72d
--- /dev/null
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Link.h"
+
+#include "LoadedApk.h"
+#include "test/Test.h"
+
+using testing::Eq;
+using testing::Ne;
+
+namespace aapt {
+
+using LinkTest = CommandTestFixture;
+
+TEST_F(LinkTest, RemoveRawXmlStrings) {
+  StdErrDiagnostics diag;
+  const std::string compiled_files_dir = GetTestPath("compiled");
+  ASSERT_TRUE(CompileFile(GetTestPath("res/xml/test.xml"), R"(<Item AgentCode="007"/>)",
+                          compiled_files_dir, &diag));
+
+  const std::string out_apk = GetTestPath("out.apk");
+  std::vector<std::string> link_args = {
+      "--manifest", GetDefaultManifest(),
+      "-o", out_apk,
+  };
+
+  ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
+
+  // Load the binary xml tree
+  android::ResXMLTree tree;
+  std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
+  AssertLoadXml(apk.get(), "res/xml/test.xml", &tree);
+
+  // Check that the raw string index has not been assigned
+  EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(-1));
+}
+
+TEST_F(LinkTest, KeepRawXmlStrings) {
+  StdErrDiagnostics diag;
+  const std::string compiled_files_dir = GetTestPath("compiled");
+  ASSERT_TRUE(CompileFile(GetTestPath("res/xml/test.xml"), R"(<Item AgentCode="007"/>)",
+                          compiled_files_dir, &diag));
+
+  const std::string out_apk = GetTestPath("out.apk");
+  std::vector<std::string> link_args = {
+      "--manifest", GetDefaultManifest(),
+      "-o", out_apk,
+      "--keep-raw-values"
+  };
+
+  ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
+
+  // Load the binary xml tree
+  android::ResXMLTree tree;
+  std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
+  AssertLoadXml(apk.get(), "res/xml/test.xml", &tree);
+
+  // Check that the raw string index has been set to the correct string pool entry
+  int32_t raw_index = tree.getAttributeValueStringID(0);
+  ASSERT_THAT(raw_index, Ne(-1));
+  EXPECT_THAT(util::GetString(tree.getStrings(), static_cast<size_t>(raw_index)), Eq("007"));
+}
+
+}  // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/cmd/Optimize.h b/tools/aapt2/cmd/Optimize.h
index 43bc216..d07305b 100644
--- a/tools/aapt2/cmd/Optimize.h
+++ b/tools/aapt2/cmd/Optimize.h
@@ -61,9 +61,10 @@
  public:
   explicit OptimizeCommand() : Command("optimize") {
     SetDescription("Preforms resource optimizations on an apk.");
-    AddOptionalFlag("-o", "Path to the output APK.", &options_.output_path);
-    AddOptionalFlag("-d", "Path to the output directory (for splits).", &options_.output_dir);
-    AddOptionalFlag("-x", "Path to XML configuration file.", &config_path_);
+    AddOptionalFlag("-o", "Path to the output APK.", &options_.output_path, Command::kPath);
+    AddOptionalFlag("-d", "Path to the output directory (for splits).", &options_.output_dir,
+        Command::kPath);
+    AddOptionalFlag("-x", "Path to XML configuration file.", &config_path_, Command::kPath);
     AddOptionalSwitch("-p", "Print the multi APK artifacts and exit.", &print_only_);
     AddOptionalFlag(
         "--target-densities",
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 11a4074..e17fb47 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -43,8 +43,10 @@
   PERMISSION_ATTR = 0x01010006,
   EXPORTED_ATTR = 0x01010010,
   GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
+  PRIORITY_ATTR = 0x0101001c,
   RESOURCE_ATTR = 0x01010025,
   DEBUGGABLE_ATTR = 0x0101000f,
+  TARGET_PACKAGE_ATTR = 0x01010021,
   VALUE_ATTR = 0x01010024,
   VERSION_CODE_ATTR = 0x0101021b,
   VERSION_NAME_ATTR = 0x0101021c,
@@ -77,8 +79,11 @@
   ISGAME_ATTR = 0x10103f4,
   VERSION_ATTR = 0x01010519,
   CERT_DIGEST_ATTR = 0x01010548,
-  REQUIRED_FEATURE_ATTR = 0x1010557,
-  REQUIRED_NOT_FEATURE_ATTR = 0x1010558,
+  REQUIRED_FEATURE_ATTR = 0x01010557,
+  REQUIRED_NOT_FEATURE_ATTR = 0x01010558,
+  IS_STATIC_ATTR = 0x0101055a,
+  REQUIRED_SYSTEM_PROPERTY_NAME_ATTR = 0x01010565,
+  REQUIRED_SYSTEM_PROPERTY_VALUE_ATTR = 0x01010566,
   COMPILE_SDK_VERSION_ATTR = 0x01010572,
   COMPILE_SDK_VERSION_CODENAME_ATTR = 0x01010573,
   VERSION_MAJOR_ATTR = 0x01010577,
@@ -1586,6 +1591,44 @@
   }
 };
 
+
+/** Represents <overlay> elements. **/
+class Overlay : public ManifestExtractor::Element {
+ public:
+  Overlay() = default;
+  const std::string* target_package = nullptr;
+  int priority;
+  bool is_static;
+  const std::string* required_property_name = nullptr;
+  const std::string* required_property_value = nullptr;
+
+  void Extract(xml::Element* element) override {
+    target_package = GetAttributeString(FindAttribute(element, TARGET_PACKAGE_ATTR));
+    priority = GetAttributeIntegerDefault(FindAttribute(element, PRIORITY_ATTR), 0);
+    is_static = GetAttributeIntegerDefault(FindAttribute(element, IS_STATIC_ATTR), false) != 0;
+    required_property_name = GetAttributeString(
+        FindAttribute(element, REQUIRED_SYSTEM_PROPERTY_NAME_ATTR));
+    required_property_value = GetAttributeString(
+        FindAttribute(element, REQUIRED_SYSTEM_PROPERTY_VALUE_ATTR));
+  }
+
+  void Print(text::Printer* printer) override {
+    printer->Print(StringPrintf("overlay:"));
+    if (target_package) {
+      printer->Print(StringPrintf(" targetPackage='%s'", target_package->c_str()));
+    }
+    printer->Print(StringPrintf(" priority='%d'", priority));
+    printer->Print(StringPrintf(" isStatic='%s'", is_static ? "true" : "false"));
+    if (required_property_name) {
+      printer->Print(StringPrintf(" requiredPropertyName='%s'", required_property_name->c_str()));
+    }
+    if (required_property_value) {
+      printer->Print(StringPrintf(" requiredPropertyValue='%s'", required_property_value->c_str()));
+    }
+    printer->Print("\n");
+  }
+};
+
 /** * Represents <package-verifier> elements. **/
 class PackageVerifier : public ManifestExtractor::Element {
  public:
@@ -2166,6 +2209,7 @@
     {"meta-data", std::is_base_of<MetaData, T>::value},
     {"manifest", std::is_base_of<Manifest, T>::value},
     {"original-package", std::is_base_of<OriginalPackage, T>::value},
+    {"overlay", std::is_base_of<Overlay, T>::value},
     {"package-verifier", std::is_base_of<PackageVerifier, T>::value},
     {"permission", std::is_base_of<Permission, T>::value},
     {"provider", std::is_base_of<Provider, T>::value},
@@ -2215,6 +2259,7 @@
     {"manifest", &CreateType<Manifest>},
     {"meta-data", &CreateType<MetaData>},
     {"original-package", &CreateType<OriginalPackage>},
+    {"overlay", &CreateType<Overlay>},
     {"package-verifier", &CreateType<PackageVerifier>},
     {"permission", &CreateType<Permission>},
     {"provider", &CreateType<Provider>},
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 61ebd4e..c496ff0 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -434,6 +434,8 @@
     return false;
   }
 
+  auto overlayable = std::make_shared<Overlayable>();
+
   ResChunkPullParser parser(GetChunkData(chunk),
                             GetChunkDataLen(chunk));
   while (ResChunkPullParser::IsGoodEvent(parser.Next())) {
@@ -441,25 +443,25 @@
       const ResTable_overlayable_policy_header* policy_header =
           ConvertTo<ResTable_overlayable_policy_header>(parser.chunk());
 
-      Overlayable::PolicyFlags policies = Overlayable::Policy::kNone;
+      OverlayableItem::PolicyFlags policies = OverlayableItem::Policy::kNone;
       if (policy_header->policy_flags & ResTable_overlayable_policy_header::POLICY_PUBLIC) {
-        policies |= Overlayable::Policy::kPublic;
+        policies |= OverlayableItem::Policy::kPublic;
       }
       if (policy_header->policy_flags
           & ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION) {
-        policies |= Overlayable::Policy::kSystem;
+        policies |= OverlayableItem::Policy::kSystem;
       }
       if (policy_header->policy_flags
           & ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION) {
-        policies |= Overlayable::Policy::kVendor;
+        policies |= OverlayableItem::Policy::kVendor;
       }
       if (policy_header->policy_flags
           & ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION) {
-        policies |= Overlayable::Policy::kProduct;
+        policies |= OverlayableItem::Policy::kProduct;
       }
       if (policy_header->policy_flags
           & ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION) {
-        policies |= Overlayable::Policy::kProductServices;
+        policies |= OverlayableItem::Policy::kProductServices;
       }
 
       const ResTable_ref* const ref_begin = reinterpret_cast<const ResTable_ref*>(
@@ -478,10 +480,10 @@
           return false;
         }
 
-        Overlayable overlayable{};
-        overlayable.source = source_.WithLine(0);
-        overlayable.policies = policies;
-        if (!table_->SetOverlayable(iter->second, overlayable, diag_)) {
+        OverlayableItem overlayable_item(overlayable);
+        overlayable_item.source = source_.WithLine(0);
+        overlayable_item.policies = policies;
+        if (!table_->SetOverlayable(iter->second, overlayable_item, diag_)) {
           return false;
         }
       }
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 200e2d4..931d57b 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -429,29 +429,29 @@
       CHECK(bool(type->id)) << "type must have an ID set when flattening <overlayable>";
       for (auto& entry : type->entries) {
         CHECK(bool(type->id)) << "entry must have an ID set when flattening <overlayable>";
-        if (!entry->overlayable) {
+        if (!entry->overlayable_item) {
           continue;
         }
 
-        Overlayable overlayable = entry->overlayable.value();
-        uint32_t policy_flags = Overlayable::Policy::kNone;
-        if (overlayable.policies & Overlayable::Policy::kPublic) {
+        OverlayableItem& overlayable = entry->overlayable_item.value();
+        uint32_t policy_flags = OverlayableItem::Policy::kNone;
+        if (overlayable.policies & OverlayableItem::Policy::kPublic) {
           policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
         }
-        if (overlayable.policies & Overlayable::Policy::kSystem) {
+        if (overlayable.policies & OverlayableItem::Policy::kSystem) {
           policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
         }
-        if (overlayable.policies & Overlayable::Policy::kVendor) {
+        if (overlayable.policies & OverlayableItem::Policy::kVendor) {
           policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
         }
-        if (overlayable.policies & Overlayable::Policy::kProduct) {
+        if (overlayable.policies & OverlayableItem::Policy::kProduct) {
           policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
         }
-        if (overlayable.policies & Overlayable::Policy::kProductServices) {
+        if (overlayable.policies & OverlayableItem::Policy::kProductServices) {
           policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION;
         }
 
-        if (overlayable.policies == Overlayable::Policy::kNone) {
+        if (overlayable.policies == OverlayableItem::Policy::kNone) {
           // Encode overlayable entries defined without a policy as publicly overlayable
           policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
         }
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index e99ab1f..a5fb6fd 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -628,17 +628,17 @@
 }
 
 TEST_F(TableFlattenerTest, FlattenOverlayable) {
-  Overlayable overlayable{};
-  overlayable.policies |= Overlayable::Policy::kProduct;
-  overlayable.policies |= Overlayable::Policy::kSystem;
-  overlayable.policies |= Overlayable::Policy::kVendor;
+  OverlayableItem overlayable_item(std::make_shared<Overlayable>("TestName", "overlay://theme"));
+  overlayable_item.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_item.policies |= OverlayableItem::Policy::kSystem;
+  overlayable_item.policies |= OverlayableItem::Policy::kVendor;
 
   std::string name = "com.app.test:integer/overlayable";
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.test", 0x7f)
           .AddSimple(name, ResourceId(0x7f020000))
-          .SetOverlayable(name, overlayable)
+          .SetOverlayable(name, overlayable_item)
           .Build();
 
   ResourceTable output_table;
@@ -647,45 +647,46 @@
   auto search_result = output_table.FindResource(test::ParseNameOrDie(name));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kSystem
-                                         | Overlayable::Policy::kVendor
-                                         | Overlayable::Policy::kProduct);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(result_overlayable_item.policies, OverlayableItem::Policy::kSystem
+                                         | OverlayableItem::Policy::kVendor
+                                         | OverlayableItem::Policy::kProduct);
 }
 
 TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) {
-  std::string name_zero = "com.app.test:integer/overlayable_zero";
-  Overlayable overlayable_zero{};
-  overlayable_zero.policies |= Overlayable::Policy::kProduct;
-  overlayable_zero.policies |= Overlayable::Policy::kSystem;
-  overlayable_zero.policies |= Overlayable::Policy::kProductServices;
+  auto overlayable = std::make_shared<Overlayable>("TestName", "overlay://theme");
+  std::string name_zero = "com.app.test:integer/overlayable_zero_item";
+  OverlayableItem overlayable_zero_item(overlayable);
+  overlayable_zero_item.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_zero_item.policies |= OverlayableItem::Policy::kSystem;
+  overlayable_zero_item.policies |= OverlayableItem::Policy::kProductServices;
 
-  std::string name_one = "com.app.test:integer/overlayable_one";
-  Overlayable overlayable_one{};
-  overlayable_one.policies |= Overlayable::Policy::kPublic;
-  overlayable_one.policies |= Overlayable::Policy::kProductServices;
+  std::string name_one = "com.app.test:integer/overlayable_one_item";
+  OverlayableItem overlayable_one_item(overlayable);
+  overlayable_one_item.policies |= OverlayableItem::Policy::kPublic;
+  overlayable_one_item.policies |= OverlayableItem::Policy::kProductServices;
 
-  std::string name_two = "com.app.test:integer/overlayable_two";
-  Overlayable overlayable_two{};
-  overlayable_two.policies |= Overlayable::Policy::kProduct;
-  overlayable_two.policies |= Overlayable::Policy::kSystem;
-  overlayable_two.policies |= Overlayable::Policy::kVendor;
+  std::string name_two = "com.app.test:integer/overlayable_two_item";
+  OverlayableItem overlayable_two_item(overlayable);
+  overlayable_two_item.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_two_item.policies |= OverlayableItem::Policy::kSystem;
+  overlayable_two_item.policies |= OverlayableItem::Policy::kVendor;
 
-  std::string name_three = "com.app.test:integer/overlayable_three";
-  Overlayable overlayable_three{};
+  std::string name_three = "com.app.test:integer/overlayable_three_item";
+  OverlayableItem overlayable_three_item(overlayable);
 
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.test", 0x7f)
           .AddSimple(name_zero, ResourceId(0x7f020000))
-          .SetOverlayable(name_zero, overlayable_zero)
+          .SetOverlayable(name_zero, overlayable_zero_item)
           .AddSimple(name_one, ResourceId(0x7f020001))
-          .SetOverlayable(name_one, overlayable_one)
+          .SetOverlayable(name_one, overlayable_one_item)
           .AddSimple(name_two, ResourceId(0x7f020002))
-          .SetOverlayable(name_two, overlayable_two)
+          .SetOverlayable(name_two, overlayable_two_item)
           .AddSimple(name_three, ResourceId(0x7f020003))
-          .SetOverlayable(name_three, overlayable_three)
+          .SetOverlayable(name_three, overlayable_three_item)
           .Build();
 
   ResourceTable output_table;
@@ -694,35 +695,35 @@
   auto search_result = output_table.FindResource(test::ParseNameOrDie(name_zero));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kSystem
-                                         | Overlayable::Policy::kProduct
-                                         | Overlayable::Policy::kProductServices);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem& overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem
+                                       | OverlayableItem::Policy::kProduct
+                                       | OverlayableItem::Policy::kProductServices);
 
   search_result = output_table.FindResource(test::ParseNameOrDie(name_one));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kPublic
-                                         | Overlayable::Policy::kProductServices);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic
+                                       | OverlayableItem::Policy::kProductServices);
 
   search_result = output_table.FindResource(test::ParseNameOrDie(name_two));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kSystem
-                                         | Overlayable::Policy::kProduct
-                                         | Overlayable::Policy::kVendor);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem
+                                       | OverlayableItem::Policy::kProduct
+                                       | OverlayableItem::Policy::kVendor);
 
   search_result = output_table.FindResource(test::ParseNameOrDie(name_three));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kPublic);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic);
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index cf2ab0f..6b5746d 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -373,9 +373,44 @@
   return Visibility::Level::kUndefined;
 }
 
+bool DeserializeOverlayableItemFromPb(const pb::OverlayableItem& pb_overlayable,
+                                      const android::ResStringPool& src_pool,
+                                      OverlayableItem* out_overlayable, std::string* out_error) {
+  for (const int policy : pb_overlayable.policy()) {
+    switch (policy) {
+      case pb::OverlayableItem::PUBLIC:
+        out_overlayable->policies |= OverlayableItem::Policy::kPublic;
+        break;
+      case pb::OverlayableItem::SYSTEM:
+        out_overlayable->policies |= OverlayableItem::Policy::kSystem;
+        break;
+      case pb::OverlayableItem::VENDOR:
+        out_overlayable->policies |= OverlayableItem::Policy::kVendor;
+        break;
+      case pb::OverlayableItem::PRODUCT:
+        out_overlayable->policies |= OverlayableItem::Policy::kProduct;
+        break;
+      case pb::OverlayableItem::PRODUCT_SERVICES:
+        out_overlayable->policies |= OverlayableItem::Policy::kProductServices;
+        break;
+      default:
+        *out_error = "unknown overlayable policy";
+        return false;
+    }
+  }
+
+  if (pb_overlayable.has_source()) {
+    DeserializeSourceFromPb(pb_overlayable.source(), src_pool, &out_overlayable->source);
+  }
+
+  out_overlayable->comment = pb_overlayable.comment();
+  return true;
+}
+
 static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStringPool& src_pool,
-                                     io::IFileCollection* files, ResourceTable* out_table,
-                                     std::string* out_error) {
+                                     io::IFileCollection* files,
+                                     const std::vector<std::shared_ptr<Overlayable>>& overlayables,
+                                     ResourceTable* out_table, std::string* out_error) {
   Maybe<uint8_t> id;
   if (pb_package.has_package_id()) {
     id = static_cast<uint8_t>(pb_package.package_id().id());
@@ -437,39 +472,22 @@
         entry->allow_new = std::move(allow_new);
       }
 
-      if (pb_entry.has_overlayable()) {
-        Overlayable overlayable{};
-
-        const pb::Overlayable& pb_overlayable = pb_entry.overlayable();
-        for (const int policy : pb_overlayable.policy()) {
-          switch (policy) {
-            case pb::Overlayable::PUBLIC:
-              overlayable.policies |= Overlayable::Policy::kPublic;
-              break;
-            case pb::Overlayable::SYSTEM:
-              overlayable.policies |= Overlayable::Policy::kSystem;
-              break;
-            case pb::Overlayable::VENDOR:
-              overlayable.policies |= Overlayable::Policy::kVendor;
-              break;
-            case pb::Overlayable::PRODUCT:
-              overlayable.policies |= Overlayable::Policy::kProduct;
-              break;
-            case pb::Overlayable::PRODUCT_SERVICES:
-              overlayable.policies |= Overlayable::Policy::kProductServices;
-              break;
-            default:
-              *out_error = "unknown overlayable policy";
-              return false;
-          }
+      if (pb_entry.has_overlayable_item()) {
+        // Find the overlayable to which this item belongs
+        pb::OverlayableItem pb_overlayable_item = pb_entry.overlayable_item();
+        if (pb_overlayable_item.overlayable_idx() >= overlayables.size()) {
+          *out_error = android::base::StringPrintf("invalid overlayable_idx value %d",
+                                                   pb_overlayable_item.overlayable_idx());
+          return false;
         }
 
-        if (pb_overlayable.has_source()) {
-          DeserializeSourceFromPb(pb_overlayable.source(), src_pool, &overlayable.source);
+        OverlayableItem overlayable_item(overlayables[pb_overlayable_item.overlayable_idx()]);
+        if (!DeserializeOverlayableItemFromPb(pb_overlayable_item, src_pool, &overlayable_item,
+                                              out_error)) {
+          return false;
         }
 
-        overlayable.comment = pb_overlayable.comment();
-        entry->overlayable = overlayable;
+        entry->overlayable_item = std::move(overlayable_item);
       }
 
       ResourceId resid(pb_package.package_id().id(), pb_type.type_id().id(),
@@ -522,8 +540,19 @@
     }
   }
 
+  // Deserialize the overlayable groups of the table
+  std::vector<std::shared_ptr<Overlayable>> overlayables;
+  for (const pb::Overlayable& pb_overlayable : pb_table.overlayable()) {
+    auto group = std::make_shared<Overlayable>(pb_overlayable.name(), pb_overlayable.actor());
+    if (pb_overlayable.has_source()) {
+      DeserializeSourceFromPb(pb_overlayable.source(), source_pool, &group->source);
+    }
+    overlayables.push_back(group);
+  }
+
   for (const pb::Package& pb_package : pb_table.package()) {
-    if (!DeserializePackageFromPb(pb_package, source_pool, files, out_table, out_error)) {
+    if (!DeserializePackageFromPb(pb_package, source_pool, files, overlayables, out_table,
+                                  out_error)) {
       return false;
     }
   }
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 70bf868..76fbb46 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -272,9 +272,57 @@
   out_pb_config->set_sdk_version(config.sdkVersion);
 }
 
+static void SerializeOverlayableItemToPb(const OverlayableItem& overlayable_item,
+                                         std::vector<Overlayable*>& serialized_overlayables,
+                                         StringPool* source_pool, pb::Entry* pb_entry,
+                                         pb::ResourceTable* pb_table) {
+  // Retrieve the index of the overlayable in the list of groups that have already been serialized.
+  size_t i;
+  for (i = 0 ; i < serialized_overlayables.size(); i++) {
+    if (overlayable_item.overlayable.get() == serialized_overlayables[i]) {
+      break;
+    }
+  }
+
+  // Serialize the overlayable if it has not been serialized already.
+  if (i == serialized_overlayables.size()) {
+    serialized_overlayables.push_back(overlayable_item.overlayable.get());
+    pb::Overlayable* pb_overlayable = pb_table->add_overlayable();
+    pb_overlayable->set_name(overlayable_item.overlayable->name);
+    pb_overlayable->set_actor(overlayable_item.overlayable->actor);
+    SerializeSourceToPb(overlayable_item.overlayable->source, source_pool,
+                        pb_overlayable->mutable_source());
+  }
+
+  pb::OverlayableItem* pb_overlayable_item = pb_entry->mutable_overlayable_item();
+  pb_overlayable_item->set_overlayable_idx(i);
+
+  if (overlayable_item.policies & OverlayableItem::Policy::kPublic) {
+    pb_overlayable_item->add_policy(pb::OverlayableItem::PUBLIC);
+  }
+  if (overlayable_item.policies & OverlayableItem::Policy::kProduct) {
+    pb_overlayable_item->add_policy(pb::OverlayableItem::PRODUCT);
+  }
+  if (overlayable_item.policies & OverlayableItem::Policy::kProductServices) {
+    pb_overlayable_item->add_policy(pb::OverlayableItem::PRODUCT_SERVICES);
+  }
+  if (overlayable_item.policies & OverlayableItem::Policy::kSystem) {
+    pb_overlayable_item->add_policy(pb::OverlayableItem::SYSTEM);
+  }
+  if (overlayable_item.policies & OverlayableItem::Policy::kVendor) {
+    pb_overlayable_item->add_policy(pb::OverlayableItem::VENDOR);
+  }
+
+  SerializeSourceToPb(overlayable_item.source, source_pool,
+                      pb_overlayable_item->mutable_source());
+  pb_overlayable_item->set_comment(overlayable_item.comment);
+}
+
 void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table,
                         IDiagnostics* diag) {
   StringPool source_pool;
+
+  std::vector<Overlayable*> overlayables;
   for (const std::unique_ptr<ResourceTablePackage>& package : table.packages) {
     pb::Package* pb_package = out_table->add_package();
     if (package->id) {
@@ -310,29 +358,9 @@
           pb_allow_new->set_comment(entry->allow_new.value().comment);
         }
 
-        if (entry->overlayable) {
-          pb::Overlayable* pb_overlayable = pb_entry->mutable_overlayable();
-
-          Overlayable overlayable = entry->overlayable.value();
-          if (overlayable.policies & Overlayable::Policy::kPublic) {
-            pb_overlayable->add_policy(pb::Overlayable::PUBLIC);
-          }
-          if (overlayable.policies & Overlayable::Policy::kProduct) {
-            pb_overlayable->add_policy(pb::Overlayable::PRODUCT);
-          }
-          if (overlayable.policies & Overlayable::Policy::kProductServices) {
-            pb_overlayable->add_policy(pb::Overlayable::PRODUCT_SERVICES);
-          }
-          if (overlayable.policies & Overlayable::Policy::kSystem) {
-            pb_overlayable->add_policy(pb::Overlayable::SYSTEM);
-          }
-          if (overlayable.policies & Overlayable::Policy::kVendor) {
-            pb_overlayable->add_policy(pb::Overlayable::VENDOR);
-          }
-
-          SerializeSourceToPb(overlayable.source, &source_pool,
-                              pb_overlayable->mutable_source());
-          pb_overlayable->set_comment(overlayable.comment);
+        if (entry->overlayable_item) {
+          SerializeOverlayableItemToPb(entry->overlayable_item.value(), overlayables, &source_pool,
+                                       pb_entry, out_table);
         }
 
         for (const std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index fb913f40..4a3c1b8 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -93,8 +93,11 @@
       util::make_unique<Reference>(expected_ref), context->GetDiagnostics()));
 
   // Make an overlayable resource.
+  OverlayableItem overlayable_item(std::make_shared<Overlayable>(
+      "OverlayableName", "overlay://theme", Source("res/values/overlayable.xml", 40)));
+  overlayable_item.source = Source("res/values/overlayable.xml", 42);
   ASSERT_TRUE(table->SetOverlayable(test::ParseNameOrDie("com.app.a:integer/overlayable"),
-                                    Overlayable{}, test::GetDiagnostics()));
+                                    overlayable_item, test::GetDiagnostics()));
 
   pb::ResourceTable pb_table;
   SerializeTableToPb(*table, &pb_table, context->GetDiagnostics());
@@ -160,9 +163,15 @@
       new_table.FindResource(test::ParseNameOrDie("com.app.a:integer/overlayable"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
-              Eq(Overlayable::Policy::kNone));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("OverlayableName"));
+  EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(result_overlayable_item.overlayable->source.path, Eq("res/values/overlayable.xml"));
+  EXPECT_THAT(result_overlayable_item.overlayable->source.line, Eq(40));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+  EXPECT_THAT(result_overlayable_item.source.path, Eq("res/values/overlayable.xml"));
+  EXPECT_THAT(result_overlayable_item.source.line, Eq(42));
 }
 
 TEST(ProtoSerializeTest, SerializeAndDeserializeXml) {
@@ -503,26 +512,31 @@
 }
 
 TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
-  Overlayable overlayable_foo{};
-  overlayable_foo.policies |= Overlayable::Policy::kSystem;
-  overlayable_foo.policies |= Overlayable::Policy::kProduct;
+  OverlayableItem overlayable_item_foo(std::make_shared<Overlayable>(
+      "CustomizableResources", "overlay://customization"));
+  overlayable_item_foo.policies |= OverlayableItem::Policy::kSystem;
+  overlayable_item_foo.policies |= OverlayableItem::Policy::kProduct;
 
-  Overlayable overlayable_bar{};
-  overlayable_bar.policies |= Overlayable::Policy::kProductServices;
-  overlayable_bar.policies |= Overlayable::Policy::kVendor;
+  OverlayableItem overlayable_item_bar(std::make_shared<Overlayable>(
+      "TaskBar", "overlay://theme"));
+  overlayable_item_bar.policies |= OverlayableItem::Policy::kProductServices;
+  overlayable_item_bar.policies |= OverlayableItem::Policy::kVendor;
 
-  Overlayable overlayable_baz{};
-  overlayable_baz.policies |= Overlayable::Policy::kPublic;
+  OverlayableItem overlayable_item_baz(std::make_shared<Overlayable>(
+      "FontPack", "overlay://theme"));
+  overlayable_item_baz.policies |= OverlayableItem::Policy::kPublic;
 
-  Overlayable overlayable_biz{};
+  OverlayableItem overlayable_item_biz(std::make_shared<Overlayable>(
+      "Other", "overlay://customization"));
+  overlayable_item_biz.comment ="comment";
 
   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
-          .SetOverlayable("com.app.a:bool/foo", overlayable_foo)
-          .SetOverlayable("com.app.a:bool/bar", overlayable_bar)
-          .SetOverlayable("com.app.a:bool/baz", overlayable_baz)
-          .SetOverlayable("com.app.a:bool/biz", overlayable_biz)
+          .SetOverlayable("com.app.a:bool/foo", overlayable_item_foo)
+          .SetOverlayable("com.app.a:bool/bar", overlayable_item_bar)
+          .SetOverlayable("com.app.a:bool/baz", overlayable_item_baz)
+          .SetOverlayable("com.app.a:bool/biz", overlayable_item_biz)
           .AddValue("com.app.a:bool/fiz", ResourceUtils::TryParseBool("true"))
           .Build();
 
@@ -538,33 +552,41 @@
   Maybe<ResourceTable::SearchResult> search_result =
       new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
   ASSERT_TRUE(search_result);
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  Overlayable result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kSystem
-                                              | Overlayable::Policy::kProduct));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem& overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(overlayable_item.overlayable->name, Eq("CustomizableResources"));
+  EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://customization"));
+  EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kSystem
+                                              | OverlayableItem::Policy::kProduct));
 
   search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/bar"));
   ASSERT_TRUE(search_result);
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kProductServices
-                                              | Overlayable::Policy::kVendor));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(overlayable_item.overlayable->name, Eq("TaskBar"));
+  EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kProductServices
+                                              | OverlayableItem::Policy::kVendor));
 
   search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/baz"));
   ASSERT_TRUE(search_result);
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(result_overlayable.policies, Overlayable::Policy::kPublic);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(overlayable_item.overlayable->name, Eq("FontPack"));
+  EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
 
   search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/biz"));
   ASSERT_TRUE(search_result);
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(result_overlayable.policies, Overlayable::Policy::kNone);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(overlayable_item.overlayable->name, Eq("Other"));
+  EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+  EXPECT_THAT(overlayable_item.comment, Eq("comment"));
 
   search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/fiz"));
   ASSERT_TRUE(search_result);
-  ASSERT_FALSE(search_result.value().entry->overlayable);
+  ASSERT_FALSE(search_result.value().entry->overlayable_item);
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/integration-tests/CommandTests/android-28.jar b/tools/aapt2/integration-tests/CommandTests/android-28.jar
new file mode 100644
index 0000000..ef7576d
--- /dev/null
+++ b/tools/aapt2/integration-tests/CommandTests/android-28.jar
Binary files differ
diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
index 8d91b00..a4610b2 100644
--- a/tools/aapt2/java/AnnotationProcessor.cpp
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -113,7 +113,7 @@
 void AnnotationProcessor::Print(Printer* printer) const {
   if (has_comments_) {
     std::string result = comment_.str();
-    for (StringPiece line : util::Tokenize(result, '\n')) {
+    for (const StringPiece& line : util::Tokenize(result, '\n')) {
       printer->Println(line);
     }
     printer->Println(" */");
diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
index 38b4860..f9656d1 100644
--- a/tools/aapt2/java/ProguardRules.h
+++ b/tools/aapt2/java/ProguardRules.h
@@ -49,7 +49,7 @@
  public:
   KeepSet() = default;
 
-  KeepSet(bool conditional_keep_rules) : conditional_keep_rules_(conditional_keep_rules) {
+  explicit KeepSet(bool conditional_keep_rules) : conditional_keep_rules_(conditional_keep_rules) {
   }
 
   inline void AddManifestClass(const UsageLocation& file, const std::string& class_name) {
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 8cbc037..c2340ba 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -374,8 +374,8 @@
         }
 
         // Ensure that definitions for values declared as overlayable exist
-        if (entry->overlayable && entry->values.empty()) {
-          context->GetDiagnostics()->Error(DiagMessage(entry->overlayable.value().source)
+        if (entry->overlayable_item && entry->values.empty()) {
+          context->GetDiagnostics()->Error(DiagMessage(entry->overlayable_item.value().source)
                                            << "no definition for overlayable symbol '"
                                            << name << "'");
           error = true;
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 22e1723..cc9fed5 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -134,18 +134,18 @@
     dst_entry->allow_new = std::move(src_entry->allow_new);
   }
 
-  if (src_entry->overlayable) {
-    if (dst_entry->overlayable) {
+  if (src_entry->overlayable_item) {
+    if (dst_entry->overlayable_item) {
       // Do not allow a resource with an overlayable declaration to have that overlayable
       // declaration redefined
-      context->GetDiagnostics()->Error(DiagMessage(src_entry->overlayable.value().source)
+      context->GetDiagnostics()->Error(DiagMessage(src_entry->overlayable_item.value().source)
                                        << "duplicate overlayable declaration for resource '"
                                        << src_entry->name << "'");
-      context->GetDiagnostics()->Error(DiagMessage(dst_entry->overlayable.value().source)
+      context->GetDiagnostics()->Error(DiagMessage(dst_entry->overlayable_item.value().source)
                                        << "previous declaration here");
       return false;
     } else {
-      dst_entry->overlayable = std::move(src_entry->overlayable);
+      dst_entry->overlayable_item = std::move(src_entry->overlayable_item);
     }
   }
 
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index 17b2a83..921d634 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -437,14 +437,16 @@
 }
 
 TEST_F(TableMergerTest, SetOverlayable) {
-  Overlayable overlayable{};
-  overlayable.policies |= Overlayable::Policy::kProduct;
-  overlayable.policies |= Overlayable::Policy::kVendor;
+  auto overlayable = std::make_shared<Overlayable>("CustomizableResources",
+                                                  "overlay://customization");
+  OverlayableItem overlayable_item(overlayable);
+  overlayable_item.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_item.policies |= OverlayableItem::Policy::kVendor;
 
   std::unique_ptr<ResourceTable> table_a =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
-          .SetOverlayable("bool/foo", overlayable)
+          .SetOverlayable("bool/foo", overlayable_item)
           .Build();
 
   std::unique_ptr<ResourceTable> table_b =
@@ -463,26 +465,30 @@
   const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
   Maybe<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
   ASSERT_TRUE(search_result);
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kProduct
-                                              | Overlayable::Policy::kVendor));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("CustomizableResources"));
+  EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://customization"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
+                                                   | OverlayableItem::Policy::kVendor));
 }
 
 TEST_F(TableMergerTest, SetOverlayableLater) {
+  auto overlayable = std::make_shared<Overlayable>("CustomizableResources",
+                                                  "overlay://customization");
   std::unique_ptr<ResourceTable> table_a =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
           .AddSimple("bool/foo")
           .Build();
 
-  Overlayable overlayable{};
-  overlayable.policies |= Overlayable::Policy::kPublic;
-  overlayable.policies |= Overlayable::Policy::kProductServices;
+  OverlayableItem overlayable_item(overlayable);
+  overlayable_item.policies |= OverlayableItem::Policy::kPublic;
+  overlayable_item.policies |= OverlayableItem::Policy::kProductServices;
   std::unique_ptr<ResourceTable> table_b =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
-          .SetOverlayable("bool/foo", overlayable)
+          .SetOverlayable("bool/foo", overlayable_item)
           .Build();
 
   ResourceTable final_table;
@@ -495,27 +501,33 @@
   const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
   Maybe<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
   ASSERT_TRUE(search_result);
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kPublic
-                                              | Overlayable::Policy::kProductServices));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("CustomizableResources"));
+  EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://customization"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic
+                                                   | OverlayableItem::Policy::kProductServices));
 }
 
-TEST_F(TableMergerTest, SetOverlayableSamePolicesFail) {
-  Overlayable overlayable_first{};
-  overlayable_first.policies |= Overlayable::Policy::kProduct;
+TEST_F(TableMergerTest, SameResourceDifferentNameFail) {
+  auto overlayable_first = std::make_shared<Overlayable>("CustomizableResources",
+                                                         "overlay://customization");
+  OverlayableItem overlayable_item_first(overlayable_first);
+  overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
   std::unique_ptr<ResourceTable> table_a =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
-          .SetOverlayable("bool/foo", overlayable_first)
+          .SetOverlayable("bool/foo", overlayable_item_first)
           .Build();
 
-  Overlayable overlayable_second{};
-  overlayable_second.policies |= Overlayable::Policy::kProduct;
+  auto overlayable_second = std::make_shared<Overlayable>("ThemeResources",
+                                                          "overlay://theme");
+  OverlayableItem overlayable_item_second(overlayable_second);
+  overlayable_item_second.policies |= OverlayableItem::Policy::kProduct;
   std::unique_ptr<ResourceTable> table_b =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
-          .SetOverlayable("bool/foo", overlayable_second)
+          .SetOverlayable("bool/foo", overlayable_item_second)
           .Build();
 
   ResourceTable final_table;
@@ -526,21 +538,24 @@
   ASSERT_FALSE(merger.Merge({}, table_b.get(), false /*overlay*/));
 }
 
-TEST_F(TableMergerTest, SetOverlayableDifferentPolicesFail) {
-  Overlayable overlayable_first{};
-  overlayable_first.policies |= Overlayable::Policy::kVendor;
+TEST_F(TableMergerTest, SameResourceSameNameFail) {
+  auto overlayable = std::make_shared<Overlayable>("CustomizableResources",
+                                                  "overlay://customization");
+
+  OverlayableItem overlayable_item_first(overlayable);
+  overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
   std::unique_ptr<ResourceTable> table_a =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
-          .SetOverlayable("bool/foo",overlayable_first)
+          .SetOverlayable("bool/foo", overlayable_item_first)
           .Build();
 
-  Overlayable overlayable_second{};
-  overlayable_second.policies |= Overlayable::Policy::kProduct;
+  OverlayableItem overlayable_item_second(overlayable);
+  overlayable_item_second.policies |= OverlayableItem::Policy::kSystem;
   std::unique_ptr<ResourceTable> table_b =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
-          .SetOverlayable("bool/foo", overlayable_second)
+          .SetOverlayable("bool/foo", overlayable_item_second)
           .Build();
 
   ResourceTable final_table;
diff --git a/tools/aapt2/link/XmlCompatVersioner.h b/tools/aapt2/link/XmlCompatVersioner.h
index 099e23c..9980618 100644
--- a/tools/aapt2/link/XmlCompatVersioner.h
+++ b/tools/aapt2/link/XmlCompatVersioner.h
@@ -55,7 +55,7 @@
  public:
   using Rules = std::unordered_map<ResourceId, std::unique_ptr<IDegradeRule>>;
 
-  XmlCompatVersioner(const Rules* rules);
+  explicit XmlCompatVersioner(const Rules* rules);
 
   std::vector<std::unique_ptr<xml::XmlResource>> Process(IAaptContext* context,
                                                          xml::XmlResource* doc,
@@ -83,7 +83,7 @@
 
 class DegradeToManyRule : public IDegradeRule {
  public:
-  DegradeToManyRule(std::vector<ReplacementAttr> attrs);
+  explicit DegradeToManyRule(std::vector<ReplacementAttr> attrs);
   virtual ~DegradeToManyRule() = default;
 
   std::vector<DegradeResult> Degrade(const xml::Element& src_el, const xml::Attribute& src_attr,
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 51a2e37..2d8bd02 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -71,7 +71,7 @@
     bool is_dynamic = false;
   };
 
-  SymbolTable(NameMangler* mangler);
+  explicit SymbolTable(NameMangler* mangler);
 
   // Overrides the default ISymbolTableDelegate, which allows a custom defined strategy for
   // looking up resources from a set of sources.
diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp
index 9c5b5d3..24cd5ba 100644
--- a/tools/aapt2/split/TableSplitter.cpp
+++ b/tools/aapt2/split/TableSplitter.cpp
@@ -248,7 +248,7 @@
             if (!split_entry->id) {
               split_entry->id = entry->id;
               split_entry->visibility = entry->visibility;
-              split_entry->overlayable = entry->overlayable;
+              split_entry->overlayable_item = entry->overlayable_item;
             }
 
             // Copy the selected values into the new Split Entry.
diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp
index 884ec38..9a93f2a 100644
--- a/tools/aapt2/test/Builders.cpp
+++ b/tools/aapt2/test/Builders.cpp
@@ -136,7 +136,7 @@
 }
 
 ResourceTableBuilder& ResourceTableBuilder::SetOverlayable(const StringPiece& name,
-                                                           const Overlayable& overlayable) {
+                                                           const OverlayableItem& overlayable) {
 
   ResourceName res_name = ParseNameOrDie(name);
   CHECK(table_->SetOverlayable(res_name, overlayable, GetDiagnostics()));
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index a120484..c971a1b 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -74,7 +74,7 @@
   ResourceTableBuilder& SetSymbolState(const android::StringPiece& name, const ResourceId& id,
                                        Visibility::Level level, bool allow_new = false);
   ResourceTableBuilder& SetOverlayable(const android::StringPiece& name,
-                                       const Overlayable& overlayable);
+                                       const OverlayableItem& overlayable);
 
   StringPool* string_pool();
   std::unique_ptr<ResourceTable> Build();
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index 50b41f1..777ca5c 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -173,10 +173,12 @@
 template <typename TValue>
 class ValueEqMatcher {
  public:
+  // NOLINTNEXTLINE(google-explicit-constructor)
   ValueEqMatcher(TValue expected) : expected_(std::move(expected)) {
   }
 
   template <typename T>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   operator ::testing::Matcher<T>() const {
     return ::testing::Matcher<T>(new ValueEqImpl<T>(&expected_));
   }
@@ -188,10 +190,12 @@
 template <typename TValue>
 class ValueEqPointerMatcher {
  public:
+  // NOLINTNEXTLINE(google-explicit-constructor)
   ValueEqPointerMatcher(const TValue* expected) : expected_(expected) {
   }
 
   template <typename T>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   operator ::testing::Matcher<T>() const {
     return ::testing::Matcher<T>(new ValueEqImpl<T>(expected_));
   }
diff --git a/tools/aapt2/test/Fixture.cpp b/tools/aapt2/test/Fixture.cpp
new file mode 100644
index 0000000..aae79fa
--- /dev/null
+++ b/tools/aapt2/test/Fixture.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "test/Fixture.h"
+
+#include <dirent.h>
+
+#include "android-base/errors.h"
+#include "android-base/file.h"
+#include "android-base/stringprintf.h"
+#include "android-base/utf8.h"
+#include "androidfw/StringPiece.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "cmd/Compile.h"
+#include "cmd/Link.h"
+#include "io/FileStream.h"
+#include "io/Util.h"
+#include "util/Files.h"
+
+using testing::Eq;
+using testing::Ne;
+
+namespace aapt {
+
+void ClearDirectory(const android::StringPiece& path) {
+  const std::string root_dir = path.to_string();
+  std::unique_ptr<DIR, decltype(closedir)*> dir(opendir(root_dir.data()), closedir);
+  if (!dir) {
+    StdErrDiagnostics().Error(DiagMessage() << android::base::SystemErrorCodeToString(errno));
+    return;
+  }
+
+  while (struct dirent* entry = readdir(dir.get())) {
+    // Do not delete hidden files and do not recurse to the parent of this directory
+    if (util::StartsWith(entry->d_name, ".")) {
+      continue;
+    }
+
+    std::string full_path = file::BuildPath({root_dir, entry->d_name});
+    if (file::GetFileType(full_path) == file::FileType::kDirectory) {
+      ClearDirectory(full_path);
+#ifdef _WIN32
+      _rmdir(full_path.c_str());
+#else
+      rmdir(full_path.c_str());
+#endif
+    } else {
+      android::base::utf8::unlink(full_path.c_str());
+    }
+  }
+}
+
+void TestDirectoryFixture::SetUp() {
+  temp_dir_ = file::BuildPath({android::base::GetExecutableDirectory(),
+                               "_temp",
+                               testing::UnitTest::GetInstance()->current_test_case()->name(),
+                               testing::UnitTest::GetInstance()->current_test_info()->name()});
+  ASSERT_TRUE(file::mkdirs(temp_dir_));
+  ClearDirectory(temp_dir_);
+}
+
+void TestDirectoryFixture::TearDown() {
+  ClearDirectory(temp_dir_);
+}
+
+bool TestDirectoryFixture::WriteFile(const std::string& path, const std::string& contents) {
+  CHECK(util::StartsWith(path, temp_dir_))
+      << "Attempting to create a file outside of test temporary directory.";
+
+  // Create any intermediate directories specified in the path
+  auto pos = std::find(path.rbegin(), path.rend(), file::sDirSep);
+  if (pos != path.rend()) {
+    std::string dirs = path.substr(0, (&*pos - path.data()));
+    file::mkdirs(dirs);
+  }
+
+  return android::base::WriteStringToFile(contents, path);
+}
+
+bool CommandTestFixture::CompileFile(const std::string& path, const std::string& contents,
+                                     const android::StringPiece& out_dir, IDiagnostics* diag) {
+  CHECK(WriteFile(path, contents));
+  CHECK(file::mkdirs(out_dir.data()));
+  return CompileCommand(diag).Execute({path, "-o", out_dir, "-v"}, &std::cerr) == 0;
+}
+
+bool CommandTestFixture::Link(const std::vector<std::string>& args,
+                              const android::StringPiece& flat_dir, IDiagnostics* diag) {
+  std::vector<android::StringPiece> link_args;
+  for(const std::string& arg : args) {
+    link_args.emplace_back(arg);
+  }
+
+  // Link against the android SDK
+  std::string android_sdk = file::BuildPath({android::base::GetExecutableDirectory(),
+                                             "integration-tests", "CommandTests",
+                                             "android-28.jar"});
+  link_args.insert(link_args.end(), {"-I", android_sdk});
+
+  // Add the files from the compiled resources directory to the link file arguments
+  Maybe<std::vector<std::string>> compiled_files = file::FindFiles(flat_dir, diag);
+  if (compiled_files) {
+    for (std::string& compile_file : compiled_files.value()) {
+      compile_file = file::BuildPath({flat_dir, compile_file});
+      link_args.emplace_back(std::move(compile_file));
+    }
+  }
+
+  return LinkCommand(diag).Execute(link_args, &std::cerr) == 0;
+}
+
+std::string CommandTestFixture::GetDefaultManifest() {
+  const std::string manifest_file = GetTestPath("AndroidManifest.xml");
+  CHECK(WriteFile(manifest_file, R"(
+      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.aapt.command.test">
+      </manifest>)"));
+  return manifest_file;
+}
+
+void CommandTestFixture::AssertLoadXml(LoadedApk *apk, const android::StringPiece &xml_path,
+                                       android::ResXMLTree *out_tree) {
+  ASSERT_THAT(apk, Ne(nullptr));
+
+  io::IFile* file = apk->GetFileCollection()->FindFile(xml_path);
+  ASSERT_THAT(file, Ne(nullptr));
+
+  std::unique_ptr<io::IData> data = file->OpenAsData();
+  ASSERT_THAT(data, Ne(nullptr));
+
+  out_tree->setTo(data->data(), data->size());
+  ASSERT_THAT(out_tree->getError(), Eq(android::OK));
+  while (out_tree->next() != android::ResXMLTree::START_TAG) {
+    ASSERT_THAT(out_tree->getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
+    ASSERT_THAT(out_tree->getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
+  }
+}
+
+} // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/test/Fixture.h b/tools/aapt2/test/Fixture.h
new file mode 100644
index 0000000..89d3b7b
--- /dev/null
+++ b/tools/aapt2/test/Fixture.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_TEST_FIXTURE_H
+#define AAPT_TEST_FIXTURE_H
+
+#include "android-base/file.h"
+#include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "io/Util.h"
+#include "util/Files.h"
+#include "LoadedApk.h"
+
+namespace aapt {
+
+class TestDirectoryFixture : public ::testing::Test {
+ public:
+  TestDirectoryFixture() = default;
+  virtual ~TestDirectoryFixture() = default;
+
+  // Creates the test directory or clears its contents if it contains previously created files.
+  void SetUp() override;
+
+  // Clears the contents of the test directory.
+  void TearDown() override;
+
+  // Retrieve the test directory of the fixture.
+  const android::StringPiece GetTestDirectory() {
+    return temp_dir_;
+  }
+
+  // Retrieves the absolute path of the specified relative path in the test directory. Directories
+  // should be separated using forward slashes ('/'), and these slashes will be translated to
+  // backslashes when running Windows tests.
+  const std::string GetTestPath(const android::StringPiece& path) {
+    std::string base = temp_dir_;
+    for (android::StringPiece part : util::Split(path, '/')) {
+      file::AppendPath(&base, part);
+    }
+    return base;
+  }
+
+  // Creates a file with the specified contents, creates any intermediate directories in the
+  // process. The file path must be an absolute path within the test directory.
+  bool WriteFile(const std::string& path, const std::string& contents);
+
+ private:
+  std::string temp_dir_;
+  DISALLOW_COPY_AND_ASSIGN(TestDirectoryFixture);
+};
+
+class CommandTestFixture : public TestDirectoryFixture {
+ public:
+  CommandTestFixture() = default;
+  virtual ~CommandTestFixture() = default;
+
+  // Wries the contents of the file to the specified path. The file is compiled and the flattened
+  // file is written to the out directory.
+  bool CompileFile(const std::string& path, const std::string& contents,
+                   const android::StringPiece& flat_out_dir, IDiagnostics* diag);
+
+  // Executes the link command with the specified arguments. The flattened files residing in the
+  // flat directory will be added to the link command as file arguments.
+  bool Link(const std::vector<std::string>& args, const android::StringPiece& flat_dir,
+            IDiagnostics* diag);
+
+  // Creates a minimal android manifest within the test directory and returns the file path.
+  std::string GetDefaultManifest();
+
+  // Asserts that loading the tree from the specified file in the apk succeeds.
+  void AssertLoadXml(LoadedApk* apk, const android::StringPiece& xml_path,
+                     android::ResXMLTree* out_tree);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CommandTestFixture);
+};
+
+} // namespace aapt
+
+#endif  // AAPT_TEST_FIXTURE_H
\ No newline at end of file
diff --git a/tools/aapt2/test/Test.h b/tools/aapt2/test/Test.h
index a24c01c..7d96d1f 100644
--- a/tools/aapt2/test/Test.h
+++ b/tools/aapt2/test/Test.h
@@ -23,5 +23,6 @@
 #include "test/Builders.h"
 #include "test/Common.h"
 #include "test/Context.h"
+#include "test/Fixture.h"
 
 #endif  // AAPT_TEST_TEST_H
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index 73105e16..7b268bb 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -180,9 +180,20 @@
   base->append(part.data(), part.size());
 }
 
+std::string BuildPath(std::vector<const StringPiece>&& args) {
+  if (args.empty()) {
+    return "";
+  }
+  std::string out = args[0].to_string();
+  for (int i = 1; i < args.size(); i++) {
+    file::AppendPath(&out, args[i]);
+  }
+  return out;
+}
+
 std::string PackageToPath(const StringPiece& package) {
   std::string out_path;
-  for (StringPiece part : util::Tokenize(package, '.')) {
+  for (const StringPiece& part : util::Tokenize(package, '.')) {
     AppendPath(&out_path, part);
   }
   return out_path;
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index 219e1a0..5839552 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -57,6 +57,9 @@
 // Appends a path to `base`, separated by the directory separator.
 void AppendPath(std::string* base, android::StringPiece part);
 
+// Concatenates the list of paths and separates each part with the directory separator.
+std::string BuildPath(std::vector<const android::StringPiece>&& args);
+
 // Makes all the directories in `path`. The last element in the path is interpreted as a directory.
 bool mkdirs(const std::string& path);
 
diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h
index 031276c..047e1a5 100644
--- a/tools/aapt2/util/Maybe.h
+++ b/tools/aapt2/util/Maybe.h
@@ -44,12 +44,12 @@
   Maybe(const Maybe& rhs);
 
   template <typename U>
-  Maybe(const Maybe<U>& rhs);  // NOLINT(implicit)
+  Maybe(const Maybe<U>& rhs);  // NOLINT(google-explicit-constructor)
 
   Maybe(Maybe&& rhs) noexcept;
 
   template <typename U>
-  Maybe(Maybe<U>&& rhs);  // NOLINT(implicit)
+  Maybe(Maybe<U>&& rhs);  // NOLINT(google-explicit-constructor)
 
   Maybe& operator=(const Maybe& rhs);
 
@@ -64,12 +64,12 @@
   /**
    * Construct a Maybe holding a value.
    */
-  Maybe(const T& value);  // NOLINT(implicit)
+  Maybe(const T& value);  // NOLINT(google-explicit-constructor)
 
   /**
    * Construct a Maybe holding a value.
    */
-  Maybe(T&& value);  // NOLINT(implicit)
+  Maybe(T&& value);  // NOLINT(google-explicit-constructor)
 
   /**
    * True if this holds a value, false if
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index b5a990e..acf1f1e 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -26,7 +26,7 @@
 $ apilint.py /tmp/currentblame.txt previous.txt --no-color
 """
 
-import re, sys, collections, traceback, argparse
+import re, sys, collections, traceback, argparse, itertools
 
 
 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
@@ -50,45 +50,37 @@
     return "\033[%sm" % (";".join(codes))
 
 
-def ident(raw):
-    """Strips superficial signature changes, giving us a strong key that
-    can be used to identify members across API levels."""
-    raw = raw.replace(" deprecated ", " ")
-    raw = raw.replace(" synchronized ", " ")
-    raw = raw.replace(" final ", " ")
-    raw = re.sub("<.+?>", "", raw)
-    if " throws " in raw:
-        raw = raw[:raw.index(" throws ")]
-    return raw
-
-
 class Field():
-    def __init__(self, clazz, line, raw, blame):
+    def __init__(self, clazz, line, raw, blame, sig_format = 1):
         self.clazz = clazz
         self.line = line
         self.raw = raw.strip(" {;")
         self.blame = blame
 
-        # drop generics for now; may need multiple passes
-        raw = re.sub("<[^<]+?>", "", raw)
-        raw = re.sub("<[^<]+?>", "", raw)
+        if sig_format == 2:
+            V2LineParser(raw).parse_into_field(self)
+        elif sig_format == 1:
+            # drop generics for now; may need multiple passes
+            raw = re.sub("<[^<]+?>", "", raw)
+            raw = re.sub("<[^<]+?>", "", raw)
 
-        raw = raw.split()
-        self.split = list(raw)
+            raw = raw.split()
+            self.split = list(raw)
 
-        for r in ["field", "volatile", "transient", "public", "protected", "static", "final", "deprecated"]:
-            while r in raw: raw.remove(r)
+            for r in ["field", "volatile", "transient", "public", "protected", "static", "final", "deprecated"]:
+                while r in raw: raw.remove(r)
 
-        # ignore annotations for now
-        raw = [ r for r in raw if not r.startswith("@") ]
+            # ignore annotations for now
+            raw = [ r for r in raw if not r.startswith("@") ]
 
-        self.typ = raw[0]
-        self.name = raw[1].strip(";")
-        if len(raw) >= 4 and raw[2] == "=":
-            self.value = raw[3].strip(';"')
-        else:
-            self.value = None
-        self.ident = ident(self.raw)
+            self.typ = raw[0]
+            self.name = raw[1].strip(";")
+            if len(raw) >= 4 and raw[2] == "=":
+                self.value = raw[3].strip(';"')
+            else:
+                self.value = None
+
+        self.ident = "-".join((self.typ, self.name, self.value or ""))
 
     def __hash__(self):
         return hash(self.raw)
@@ -96,48 +88,55 @@
     def __repr__(self):
         return self.raw
 
-
 class Method():
-    def __init__(self, clazz, line, raw, blame):
+    def __init__(self, clazz, line, raw, blame, sig_format = 1):
         self.clazz = clazz
         self.line = line
         self.raw = raw.strip(" {;")
         self.blame = blame
 
-        # drop generics for now; may need multiple passes
-        raw = re.sub("<[^<]+?>", "", raw)
-        raw = re.sub("<[^<]+?>", "", raw)
+        if sig_format == 2:
+            V2LineParser(raw).parse_into_method(self)
+        elif sig_format == 1:
+            # drop generics for now; may need multiple passes
+            raw = re.sub("<[^<]+?>", "", raw)
+            raw = re.sub("<[^<]+?>", "", raw)
 
-        # handle each clause differently
-        raw_prefix, raw_args, _, raw_throws = re.match(r"(.*?)\((.*?)\)( throws )?(.*?);$", raw).groups()
+            # handle each clause differently
+            raw_prefix, raw_args, _, raw_throws = re.match(r"(.*?)\((.*?)\)( throws )?(.*?);$", raw).groups()
 
-        # parse prefixes
-        raw = re.split("[\s]+", raw_prefix)
-        for r in ["", ";"]:
-            while r in raw: raw.remove(r)
-        self.split = list(raw)
+            # parse prefixes
+            raw = re.split("[\s]+", raw_prefix)
+            for r in ["", ";"]:
+                while r in raw: raw.remove(r)
+            self.split = list(raw)
 
-        for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract", "default", "operator"]:
-            while r in raw: raw.remove(r)
+            for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract", "default", "operator", "synchronized"]:
+                while r in raw: raw.remove(r)
 
-        self.typ = raw[0]
-        self.name = raw[1]
+            self.typ = raw[0]
+            self.name = raw[1]
 
-        # parse args
-        self.args = []
-        for arg in re.split(",\s*", raw_args):
-            arg = re.split("\s", arg)
-            # ignore annotations for now
-            arg = [ a for a in arg if not a.startswith("@") ]
-            if len(arg[0]) > 0:
-                self.args.append(arg[0])
+            # parse args
+            self.args = []
+            for arg in re.split(",\s*", raw_args):
+                arg = re.split("\s", arg)
+                # ignore annotations for now
+                arg = [ a for a in arg if not a.startswith("@") ]
+                if len(arg[0]) > 0:
+                    self.args.append(arg[0])
 
-        # parse throws
-        self.throws = []
-        for throw in re.split(",\s*", raw_throws):
-            self.throws.append(throw)
+            # parse throws
+            self.throws = []
+            for throw in re.split(",\s*", raw_throws):
+                self.throws.append(throw)
+        else:
+            raise ValueError("Unknown signature format: " + sig_format)
 
-        self.ident = ident(self.raw)
+        self.ident = "-".join((self.typ, self.name, "-".join(self.args)))
+
+    def sig_matches(self, typ, name, args):
+        return typ == self.typ and name == self.name and args == self.args
 
     def __hash__(self):
         return hash(self.raw)
@@ -147,7 +146,7 @@
 
 
 class Class():
-    def __init__(self, pkg, line, raw, blame):
+    def __init__(self, pkg, line, raw, blame, sig_format = 1):
         self.pkg = pkg
         self.line = line
         self.raw = raw.strip(" {;")
@@ -156,31 +155,44 @@
         self.fields = []
         self.methods = []
 
-        # drop generics for now; may need multiple passes
-        raw = re.sub("<[^<]+?>", "", raw)
-        raw = re.sub("<[^<]+?>", "", raw)
+        if sig_format == 2:
+            V2LineParser(raw).parse_into_class(self)
+        elif sig_format == 1:
+            # drop generics for now; may need multiple passes
+            raw = re.sub("<[^<]+?>", "", raw)
+            raw = re.sub("<[^<]+?>", "", raw)
 
-        raw = raw.split()
-        self.split = list(raw)
-        if "class" in raw:
-            self.fullname = raw[raw.index("class")+1]
-        elif "interface" in raw:
-            self.fullname = raw[raw.index("interface")+1]
-        elif "@interface" in raw:
-            self.fullname = raw[raw.index("@interface")+1]
-        else:
-            raise ValueError("Funky class type %s" % (self.raw))
+            raw = raw.split()
+            self.split = list(raw)
+            if "class" in raw:
+                self.fullname = raw[raw.index("class")+1]
+            elif "interface" in raw:
+                self.fullname = raw[raw.index("interface")+1]
+            elif "@interface" in raw:
+                self.fullname = raw[raw.index("@interface")+1]
+            else:
+                raise ValueError("Funky class type %s" % (self.raw))
 
-        if "extends" in raw:
-            self.extends = raw[raw.index("extends")+1]
-            self.extends_path = self.extends.split(".")
+            if "extends" in raw:
+                self.extends = raw[raw.index("extends")+1]
+            else:
+                self.extends = None
+
+            if "implements" in raw:
+                self.implements = raw[raw.index("implements")+1]
+            else:
+                self.implements = None
         else:
-            self.extends = None
-            self.extends_path = []
+            raise ValueError("Unknown signature format: " + sig_format)
 
         self.fullname = self.pkg.name + "." + self.fullname
         self.fullname_path = self.fullname.split(".")
 
+        if self.extends is not None:
+            self.extends_path = self.extends.split(".")
+        else:
+            self.extends_path = []
+
         self.name = self.fullname[self.fullname.rindex(".")+1:]
 
     def merge_from(self, other):
@@ -208,25 +220,355 @@
     def __repr__(self):
         return self.raw
 
+class V2Tokenizer(object):
+    __slots__ = ["raw"]
 
-def _parse_stream(f, clazz_cb=None, base_f=None):
+    DELIMITER = re.compile(r'\s+|[()@<>;,={}/"!?]|\[\]|\.\.\.')
+    STRING_SPECIAL = re.compile(r'["\\]')
+
+    def __init__(self, raw):
+        self.raw = raw
+
+    def tokenize(self):
+        tokens = []
+        current = 0
+        raw = self.raw
+        length = len(raw)
+
+        while current < length:
+            while current < length:
+                start = current
+                match = V2Tokenizer.DELIMITER.search(raw, start)
+                if match is not None:
+                    match_start = match.start()
+                    if match_start == current:
+                        end = match.end()
+                    else:
+                        end = match_start
+                else:
+                    end = length
+
+                token = raw[start:end]
+                current = end
+
+                if token == "" or token[0] == " ":
+                    continue
+                else:
+                    break
+
+            if token == "@":
+                if raw[start:start+11] == "@interface ":
+                    current = start + 11
+                    tokens.append("@interface")
+                    continue
+            elif token == '/':
+                if raw[start:start+2] == "//":
+                    current = length
+                    continue
+            elif token == '"':
+                current, string_token = self.tokenize_string(raw, length, current)
+                tokens.append(token + string_token)
+                continue
+
+            tokens.append(token)
+
+        return tokens
+
+    def tokenize_string(self, raw, length, current):
+        start = current
+        end = length
+        while start < end:
+            match = V2Tokenizer.STRING_SPECIAL.search(raw, start)
+            if match:
+                if match.group() == '"':
+                    end = match.end()
+                    break
+                elif match.group() == '\\':
+                    # ignore whatever is after the slash
+                    start += 2
+                else:
+                    raise ValueError("Unexpected match: `%s`" % (match.group()))
+            else:
+                raise ValueError("Unexpected EOF tokenizing string: `%s`" % (raw[current - 1:],))
+
+        token = raw[current:end]
+        return end, token
+
+class V2LineParser(object):
+    __slots__ = ["tokenized", "current", "len"]
+
+    MODIFIERS = set("public protected internal private abstract default static final transient volatile synchronized".split())
+    JAVA_LANG_TYPES = set("AbstractMethodError AbstractStringBuilder Appendable ArithmeticException ArrayIndexOutOfBoundsException ArrayStoreException AssertionError AutoCloseable Boolean BootstrapMethodError Byte Character CharSequence Class ClassCastException ClassCircularityError ClassFormatError ClassLoader ClassNotFoundException Cloneable CloneNotSupportedException Comparable Compiler Deprecated Double Enum EnumConstantNotPresentException Error Exception ExceptionInInitializerError Float FunctionalInterface IllegalAccessError IllegalAccessException IllegalArgumentException IllegalMonitorStateException IllegalStateException IllegalThreadStateException IncompatibleClassChangeError IndexOutOfBoundsException InheritableThreadLocal InstantiationError InstantiationException Integer InternalError InterruptedException Iterable LinkageError Long Math NegativeArraySizeException NoClassDefFoundError NoSuchFieldError NoSuchFieldException NoSuchMethodError NoSuchMethodException NullPointerException Number NumberFormatException Object OutOfMemoryError Override Package package-info.java Process ProcessBuilder ProcessEnvironment ProcessImpl Readable ReflectiveOperationException Runnable Runtime RuntimeException RuntimePermission SafeVarargs SecurityException SecurityManager Short StackOverflowError StackTraceElement StrictMath String StringBuffer StringBuilder StringIndexOutOfBoundsException SuppressWarnings System Thread ThreadDeath ThreadGroup ThreadLocal Throwable TypeNotPresentException UNIXProcess UnknownError UnsatisfiedLinkError UnsupportedClassVersionError UnsupportedOperationException VerifyError VirtualMachineError Void".split())
+
+    def __init__(self, raw):
+        self.tokenized = V2Tokenizer(raw).tokenize()
+        self.current = 0
+        self.len = len(self.tokenized)
+
+    def parse_into_method(self, method):
+        method.split = []
+        kind = self.parse_one_of("ctor", "method")
+        method.split.append(kind)
+        annotations = self.parse_annotations()
+        method.split.extend(self.parse_modifiers())
+        self.parse_matching_paren("<", ">")
+        if "@Deprecated" in annotations:
+            method.split.append("deprecated")
+        if kind == "ctor":
+            method.typ = "ctor"
+        else:
+            method.typ = self.parse_type()
+            method.split.append(method.typ)
+        method.name = self.parse_name()
+        method.split.append(method.name)
+        self.parse_token("(")
+        method.args = self.parse_args()
+        self.parse_token(")")
+        method.throws = self.parse_throws()
+        if "@interface" in method.clazz.split:
+            self.parse_annotation_default()
+        self.parse_token(";")
+        self.parse_eof()
+
+    def parse_into_class(self, clazz):
+        clazz.split = []
+        annotations = self.parse_annotations()
+        if "@Deprecated" in annotations:
+            clazz.split.append("deprecated")
+        clazz.split.extend(self.parse_modifiers())
+        kind = self.parse_one_of("class", "interface", "@interface", "enum")
+        if kind == "enum":
+            # enums are implicitly final
+            clazz.split.append("final")
+        clazz.split.append(kind)
+        clazz.fullname = self.parse_name()
+        self.parse_matching_paren("<", ">")
+        extends = self.parse_extends()
+        clazz.extends = extends[0] if extends else None
+        implements = self.parse_implements()
+        clazz.implements = implements[0] if implements else None
+        # The checks assume that interfaces are always found in implements, which isn't true for
+        # subinterfaces.
+        if not implements and "interface" in clazz.split:
+            clazz.implements = clazz.extends
+        self.parse_token("{")
+        self.parse_eof()
+
+    def parse_into_field(self, field):
+        kind = self.parse_token("field")
+        field.split = [kind]
+        annotations = self.parse_annotations()
+        if "@Deprecated" in annotations:
+            field.split.append("deprecated")
+        field.split.extend(self.parse_modifiers())
+        field.typ = self.parse_type()
+        field.split.append(field.typ)
+        field.name = self.parse_name()
+        field.split.append(field.name)
+        if self.parse_if("="):
+            field.value = self.parse_value_stripped()
+        else:
+            field.value = None
+
+        self.parse_token(";")
+        self.parse_eof()
+
+    def lookahead(self):
+        return self.tokenized[self.current]
+
+    def parse_one_of(self, *options):
+        found = self.lookahead()
+        if found not in options:
+            raise ValueError("Parsing failed, expected one of `%s` but found `%s` in %s" % (options, found, repr(self.tokenized)))
+        return self.parse_token()
+
+    def parse_token(self, tok = None):
+        found = self.lookahead()
+        if tok is not None and found != tok:
+            raise ValueError("Parsing failed, expected `%s` but found `%s` in %s" % (tok, found, repr(self.tokenized)))
+        self.current += 1
+        return found
+
+    def eof(self):
+        return self.current == self.len
+
+    def parse_eof(self):
+        if not self.eof():
+            raise ValueError("Parsing failed, expected EOF, but %s has not been parsed in %s" % (self.tokenized[self.current:], self.tokenized))
+
+    def parse_if(self, tok):
+        if not self.eof() and self.lookahead() == tok:
+            self.parse_token()
+            return True
+        return False
+
+    def parse_annotations(self):
+        ret = []
+        while self.lookahead() == "@":
+            ret.append(self.parse_annotation())
+        return ret
+
+    def parse_annotation(self):
+        ret = self.parse_token("@") + self.parse_token()
+        self.parse_matching_paren("(", ")")
+        return ret
+
+    def parse_matching_paren(self, open, close):
+        start = self.current
+        if not self.parse_if(open):
+            return
+        length = len(self.tokenized)
+        count = 1
+        while count > 0:
+            if self.current == length:
+                raise ValueError("Unexpected EOF looking for closing paren: `%s`" % (self.tokenized[start:],))
+            t = self.parse_token()
+            if t == open:
+                count += 1
+            elif t == close:
+                count -= 1
+        return self.tokenized[start:self.current]
+
+    def parse_modifiers(self):
+        ret = []
+        while self.lookahead() in V2LineParser.MODIFIERS:
+            ret.append(self.parse_token())
+        return ret
+
+    def parse_kotlin_nullability(self):
+        t = self.lookahead()
+        if t == "?" or t == "!":
+            return self.parse_token()
+        return None
+
+    def parse_type(self):
+        type = self.parse_token()
+        if type in V2LineParser.JAVA_LANG_TYPES:
+            type = "java.lang." + type
+        self.parse_matching_paren("<", ">")
+        while True:
+            t = self.lookahead()
+            if t == "[]":
+                type += self.parse_token()
+            elif self.parse_kotlin_nullability() is not None:
+                pass  # discard nullability for now
+            else:
+                break
+        return type
+
+    def parse_arg_type(self):
+        type = self.parse_type()
+        if self.parse_if("..."):
+            type += "..."
+        self.parse_kotlin_nullability() # discard nullability for now
+        return type
+
+    def parse_name(self):
+        return self.parse_token()
+
+    def parse_args(self):
+        args = []
+        if self.lookahead() == ")":
+            return args
+
+        while True:
+            args.append(self.parse_arg())
+            if self.lookahead() == ")":
+                return args
+            self.parse_token(",")
+
+    def parse_arg(self):
+        self.parse_annotations()
+        type = self.parse_arg_type()
+        l = self.lookahead()
+        if l != "," and l != ")":
+            self.parse_token()  # kotlin argument name
+            if self.parse_if('='): # kotlin default value
+                (self.parse_matching_paren('(', ')') or
+                 self.parse_matching_paren('{', '}') or
+                 self.parse_token() and self.parse_matching_paren('(', ')'))
+        return type
+
+    def parse_throws(self):
+        ret = []
+        if self.parse_if("throws"):
+            ret.append(self.parse_type())
+            while self.parse_if(","):
+                ret.append(self.parse_type())
+        return ret
+
+    def parse_extends(self):
+        if self.parse_if("extends"):
+            return self.parse_space_delimited_type_list()
+        return []
+
+    def parse_implements(self):
+        if self.parse_if("implements"):
+            return self.parse_space_delimited_type_list()
+        return []
+
+    def parse_space_delimited_type_list(self, terminals = ["implements", "{"]):
+        types = []
+        while True:
+            types.append(self.parse_type())
+            if self.lookahead() in terminals:
+                return types
+
+    def parse_annotation_default(self):
+        if self.parse_if("default"):
+            self.parse_value()
+
+    def parse_value(self):
+        if self.lookahead() == "{":
+            return " ".join(self.parse_matching_paren("{", "}"))
+        elif self.lookahead() == "(":
+            return " ".join(self.parse_matching_paren("(", ")"))
+        else:
+            return self.parse_token()
+
+    def parse_value_stripped(self):
+        value = self.parse_value()
+        if value[0] == '"':
+            return value[1:-1]
+        return value
+
+
+def _parse_stream(f, clazz_cb=None, base_f=None, out_classes_with_base=None,
+                  in_classes_with_base=[]):
     api = {}
+    in_classes_with_base = _retry_iterator(in_classes_with_base)
 
     if base_f:
-        base_classes = _parse_stream_to_generator(base_f)
+        base_classes = _retry_iterator(_parse_stream_to_generator(base_f))
     else:
         base_classes = []
 
-    for clazz in _parse_stream_to_generator(f):
-        base_class = _parse_to_matching_class(base_classes, clazz)
-        if base_class:
-            clazz.merge_from(base_class)
-
+    def handle_class(clazz):
         if clazz_cb:
             clazz_cb(clazz)
         else: # In callback mode, don't keep track of the full API
             api[clazz.fullname] = clazz
 
+    def handle_missed_classes_with_base(clazz):
+        for c in _yield_until_matching_class(in_classes_with_base, clazz):
+            base_class = _skip_to_matching_class(base_classes, c)
+            if base_class:
+                handle_class(base_class)
+
+    for clazz in _parse_stream_to_generator(f):
+        # Before looking at clazz, let's see if there's some classes that were not present, but
+        # may have an entry in the base stream.
+        handle_missed_classes_with_base(clazz)
+
+        base_class = _skip_to_matching_class(base_classes, clazz)
+        if base_class:
+            clazz.merge_from(base_class)
+            if out_classes_with_base is not None:
+                out_classes_with_base.append(clazz)
+        handle_class(clazz)
+
+    handle_missed_classes_with_base(None)
+
     return api
 
 def _parse_stream_to_generator(f):
@@ -234,6 +576,7 @@
     pkg = None
     clazz = None
     blame = None
+    sig_format = 1
 
     re_blame = re.compile("^([a-z0-9]{7,}) \(<([^>]+)>.+?\) (.+?)$")
     for raw in f:
@@ -246,29 +589,35 @@
         else:
             blame = None
 
-        if raw.startswith("package"):
+        if line == 1 and raw == "// Signature format: 2.0":
+            sig_format = 2
+        elif raw.startswith("package"):
             pkg = Package(line, raw, blame)
         elif raw.startswith("  ") and raw.endswith("{"):
-            clazz = Class(pkg, line, raw, blame)
+            clazz = Class(pkg, line, raw, blame, sig_format=sig_format)
         elif raw.startswith("    ctor"):
-            clazz.ctors.append(Method(clazz, line, raw, blame))
+            clazz.ctors.append(Method(clazz, line, raw, blame, sig_format=sig_format))
         elif raw.startswith("    method"):
-            clazz.methods.append(Method(clazz, line, raw, blame))
+            clazz.methods.append(Method(clazz, line, raw, blame, sig_format=sig_format))
         elif raw.startswith("    field"):
-            clazz.fields.append(Field(clazz, line, raw, blame))
+            clazz.fields.append(Field(clazz, line, raw, blame, sig_format=sig_format))
         elif raw.startswith("  }") and clazz:
-            while True:
-                retry = yield clazz
-                if not retry:
-                    break
-                # send() was called, asking us to redeliver clazz on next(). Still need to yield
-                # a dummy value to the send() first though.
-                if (yield "Returning clazz on next()"):
-                        raise TypeError("send() must be followed by next(), not send()")
+            yield clazz
 
+def _retry_iterator(it):
+    """Wraps an iterator, such that calling send(True) on it will redeliver the same element"""
+    for e in it:
+        while True:
+            retry = yield e
+            if not retry:
+                break
+            # send() was called, asking us to redeliver clazz on next(). Still need to yield
+            # a dummy value to the send() first though.
+            if (yield "Returning clazz on next()"):
+                raise TypeError("send() must be followed by next(), not send()")
 
-def _parse_to_matching_class(classes, needle):
-    """Takes a classes generator and parses it until it returns the class we're looking for
+def _skip_to_matching_class(classes, needle):
+    """Takes a classes iterator and consumes entries until it returns the class we're looking for
 
     This relies on classes being sorted by package and class name."""
 
@@ -276,8 +625,8 @@
         if clazz.pkg.name < needle.pkg.name:
             # We haven't reached the right package yet
             continue
-        if clazz.name < needle.name:
-            # We haven't reached the right class yet
+        if clazz.pkg.name == needle.pkg.name and clazz.fullname < needle.fullname:
+            # We're in the right package, but not the right class yet
             continue
         if clazz.fullname == needle.fullname:
             return clazz
@@ -285,6 +634,28 @@
         classes.send(clazz)
         return None
 
+def _yield_until_matching_class(classes, needle):
+    """Takes a class iterator and yields entries it until it reaches the class we're looking for.
+
+    This relies on classes being sorted by package and class name."""
+
+    for clazz in classes:
+        if needle is None:
+            yield clazz
+        elif clazz.pkg.name < needle.pkg.name:
+            # We haven't reached the right package yet
+            yield clazz
+        elif clazz.pkg.name == needle.pkg.name and clazz.fullname < needle.fullname:
+            # We're in the right package, but not the right class yet
+            yield clazz
+        elif clazz.fullname == needle.fullname:
+            # Class found, abort.
+            return
+        else:
+            # We ran past the right class. Send it back into the iterator, then abort.
+            classes.send(clazz)
+            return
+
 class Failure():
     def __init__(self, sig, clazz, detail, error, rule, msg):
         self.sig = sig
@@ -323,7 +694,7 @@
     """Records an API failure to be processed later."""
     global failures
 
-    sig = "%s-%s-%s" % (clazz.fullname, repr(detail), msg)
+    sig = "%s-%s-%s" % (clazz.fullname, detail.ident if detail else None, msg)
     sig = sig.replace(" deprecated ", " ")
 
     failures[sig] = Failure(sig, clazz, detail, error, rule, msg)
@@ -364,7 +735,7 @@
 
 def verify_enums(clazz):
     """Enums are bad, mmkay?"""
-    if "extends java.lang.Enum" in clazz.raw:
+    if clazz.extends == "java.lang.Enum" or "enum" in clazz.split:
         error(clazz, None, "F5", "Enums are not allowed")
 
 
@@ -423,7 +794,7 @@
         interface OnFooListener { void onFoo() }"""
 
     if clazz.name.endswith("Listener"):
-        if " abstract class " in clazz.raw:
+        if "abstract" in clazz.split and "class" in clazz.split:
             error(clazz, None, "L1", "Listeners should be an interface, or otherwise renamed Callback")
 
         for m in clazz.methods:
@@ -502,16 +873,16 @@
     eq = False
     hc = False
     for m in clazz.methods:
-        if " static " in m.raw: continue
-        if "boolean equals(java.lang.Object)" in m.raw: eq = True
-        if "int hashCode()" in m.raw: hc = True
+        if "static" in m.split: continue
+        if m.sig_matches("boolean", "equals", ["java.lang.Object"]): eq = True
+        if m.sig_matches("int", "hashCode", []): hc = True
     if eq != hc:
         error(clazz, None, "M8", "Must override both equals and hashCode; missing one")
 
 
 def verify_parcelable(clazz):
     """Verify that Parcelable objects aren't hiding required bits."""
-    if "implements android.os.Parcelable" in clazz.raw:
+    if clazz.implements == "android.os.Parcelable":
         creator = [ i for i in clazz.fields if i.name == "CREATOR" ]
         write = [ i for i in clazz.methods if i.name == "writeToParcel" ]
         describe = [ i for i in clazz.methods if i.name == "describeContents" ]
@@ -519,8 +890,7 @@
         if len(creator) == 0 or len(write) == 0 or len(describe) == 0:
             error(clazz, None, "FW3", "Parcelable requires CREATOR, writeToParcel, and describeContents; missing one")
 
-        if ((" final class " not in clazz.raw) and
-            (" final deprecated class " not in clazz.raw)):
+        if "final" not in clazz.split:
             error(clazz, None, "FW8", "Parcelable classes must be final")
 
         for c in clazz.ctors:
@@ -640,7 +1010,7 @@
     """Verify that helper classes are named consistently with what they extend.
     All developer extendable methods should be named onFoo()."""
     test_methods = False
-    if "extends android.app.Service" in clazz.raw:
+    if clazz.extends == "android.app.Service":
         test_methods = True
         if not clazz.name.endswith("Service"):
             error(clazz, None, "CL4", "Inconsistent class name; should be FooService")
@@ -652,7 +1022,7 @@
                 if f.value != clazz.fullname:
                     error(clazz, f, "C4", "Inconsistent interface constant; expected '%s'" % (clazz.fullname))
 
-    if "extends android.content.ContentProvider" in clazz.raw:
+    if clazz.extends == "android.content.ContentProvider":
         test_methods = True
         if not clazz.name.endswith("Provider"):
             error(clazz, None, "CL4", "Inconsistent class name; should be FooProvider")
@@ -664,12 +1034,12 @@
                 if f.value != clazz.fullname:
                     error(clazz, f, "C4", "Inconsistent interface constant; expected '%s'" % (clazz.fullname))
 
-    if "extends android.content.BroadcastReceiver" in clazz.raw:
+    if clazz.extends == "android.content.BroadcastReceiver":
         test_methods = True
         if not clazz.name.endswith("Receiver"):
             error(clazz, None, "CL4", "Inconsistent class name; should be FooReceiver")
 
-    if "extends android.app.Activity" in clazz.raw:
+    if clazz.extends == "android.app.Activity":
         test_methods = True
         if not clazz.name.endswith("Activity"):
             error(clazz, None, "CL4", "Inconsistent class name; should be FooActivity")
@@ -687,7 +1057,7 @@
 def verify_builder(clazz):
     """Verify builder classes.
     Methods should return the builder to enable chaining."""
-    if " extends " in clazz.raw: return
+    if clazz.extends: return
     if not clazz.name.endswith("Builder"): return
 
     if clazz.name != "Builder":
@@ -715,7 +1085,7 @@
 
 def verify_aidl(clazz):
     """Catch people exposing raw AIDL."""
-    if "extends android.os.Binder" in clazz.raw or "implements android.os.IInterface" in clazz.raw:
+    if clazz.extends == "android.os.Binder" or clazz.implements == "android.os.IInterface":
         error(clazz, None, None, "Raw AIDL interfaces must not be exposed")
 
 
@@ -724,48 +1094,66 @@
     if clazz.pkg.name.startswith("com.android"):
         error(clazz, None, None, "Internal classes must not be exposed")
 
+def layering_build_ranking(ranking_list):
+    r = {}
+    for rank, ps in enumerate(ranking_list):
+        if not isinstance(ps, list):
+            ps = [ps]
+        for p in ps:
+            rs = r
+            for n in p.split('.'):
+                if n not in rs:
+                    rs[n] = {}
+                rs = rs[n]
+            rs['-rank'] = rank
+    return r
+
+LAYERING_PACKAGE_RANKING = layering_build_ranking([
+    ["android.service","android.accessibilityservice","android.inputmethodservice","android.printservice","android.appwidget","android.webkit","android.preference","android.gesture","android.print"],
+    "android.app",
+    "android.widget",
+    "android.view",
+    "android.animation",
+    "android.provider",
+    ["android.content","android.graphics.drawable"],
+    "android.database",
+    "android.text",
+    "android.graphics",
+    "android.os",
+    "android.util"
+])
 
 def verify_layering(clazz):
     """Catch package layering violations.
     For example, something in android.os depending on android.app."""
-    ranking = [
-        ["android.service","android.accessibilityservice","android.inputmethodservice","android.printservice","android.appwidget","android.webkit","android.preference","android.gesture","android.print"],
-        "android.app",
-        "android.widget",
-        "android.view",
-        "android.animation",
-        "android.provider",
-        ["android.content","android.graphics.drawable"],
-        "android.database",
-        "android.text",
-        "android.graphics",
-        "android.os",
-        "android.util"
-    ]
 
     def rank(p):
-        for i in range(len(ranking)):
-            if isinstance(ranking[i], list):
-                for j in ranking[i]:
-                    if p.startswith(j): return i
+        r = None
+        l = LAYERING_PACKAGE_RANKING
+        for n in p.split('.'):
+            if n in l:
+                l = l[n]
+                if '-rank' in l:
+                    r = l['-rank']
             else:
-                if p.startswith(ranking[i]): return i
+                break
+        return r
 
     cr = rank(clazz.pkg.name)
     if cr is None: return
 
     for f in clazz.fields:
         ir = rank(f.typ)
-        if ir and ir < cr:
+        if ir is not None and ir < cr:
             warn(clazz, f, "FW6", "Field type violates package layering")
 
-    for m in clazz.methods:
+    for m in itertools.chain(clazz.methods, clazz.ctors):
         ir = rank(m.typ)
-        if ir and ir < cr:
+        if ir is not None and ir < cr:
             warn(clazz, m, "FW6", "Method return type violates package layering")
         for arg in m.args:
             ir = rank(arg)
-            if ir and ir < cr:
+            if ir is not None and ir < cr:
                 warn(clazz, m, "FW6", "Method argument type violates package layering")
 
 
@@ -856,21 +1244,18 @@
             if len(m.args) == 0 and t in ["java.lang.IllegalArgumentException", "java.lang.NullPointerException"]:
                 warn(clazz, m, "S1", "Methods taking no arguments should throw IllegalStateException")
 
+GOOGLE_IGNORECASE = re.compile("google", re.IGNORECASE)
 
 def verify_google(clazz):
     """Verifies that APIs never reference Google."""
 
-    if re.search("google", clazz.raw, re.IGNORECASE):
+    if GOOGLE_IGNORECASE.search(clazz.raw) is not None:
         error(clazz, None, None, "Must never reference Google")
 
-    test = []
-    test.extend(clazz.ctors)
-    test.extend(clazz.fields)
-    test.extend(clazz.methods)
-
-    for t in test:
-        if re.search("google", t.raw, re.IGNORECASE):
-            error(clazz, t, None, "Must never reference Google")
+    for test in clazz.ctors, clazz.fields, clazz.methods:
+        for t in test:
+            if GOOGLE_IGNORECASE.search(t.raw) is not None:
+                error(clazz, t, None, "Must never reference Google")
 
 
 def verify_bitset(clazz):
@@ -1124,7 +1509,7 @@
     """Verifies that abstract inner classes are static."""
 
     if re.match(".+?\.[A-Z][^\.]+\.[A-Z]", clazz.fullname):
-        if " abstract " in clazz.raw and " static " not in clazz.raw:
+        if "abstract" in clazz.split and "static" not in clazz.split:
             warn(clazz, None, None, "Abstract inner classes should be static to improve testability")
 
 
@@ -1219,8 +1604,8 @@
 
 def verify_closable(clazz):
     """Verifies that classes are AutoClosable."""
-    if "implements java.lang.AutoCloseable" in clazz.raw: return
-    if "implements java.io.Closeable" in clazz.raw: return
+    if clazz.implements == "java.lang.AutoCloseable": return
+    if clazz.implements == "java.io.Closeable": return
 
     for m in clazz.methods:
         if len(m.args) > 0: continue
@@ -1306,6 +1691,9 @@
 def verify_collections_over_arrays(clazz):
     """Warn that [] should be Collections."""
 
+    if "@interface" in clazz.split:
+        return
+
     safe = ["java.lang.String[]","byte[]","short[]","int[]","long[]","float[]","double[]","boolean[]","char[]"]
     for m in clazz.methods:
         if m.typ.endswith("[]") and m.typ not in safe:
@@ -1543,12 +1931,14 @@
     verify_singleton(clazz)
 
 
-def examine_stream(stream, base_stream=None):
+def examine_stream(stream, base_stream=None, in_classes_with_base=[], out_classes_with_base=None):
     """Find all style issues in the given API stream."""
     global failures, noticed
     failures = {}
     noticed = {}
-    _parse_stream(stream, examine_clazz, base_f=base_stream)
+    _parse_stream(stream, examine_clazz, base_f=base_stream,
+                  in_classes_with_base=in_classes_with_base,
+                  out_classes_with_base=out_classes_with_base)
     return (failures, noticed)
 
 
@@ -1637,11 +2027,11 @@
             del cur[prev_clazz.fullname]
 
     for clazz in cur.values():
-        if " deprecated " in clazz.raw and not clazz.fullname in prev:
+        if "deprecated" in clazz.split and not clazz.fullname in prev:
             error(clazz, None, None, "Found API deprecation at birth")
 
         for i in clazz.ctors + clazz.methods + clazz.fields:
-            if " deprecated " in i.raw:
+            if "deprecated" in i.split:
                 error(clazz, i, None, "Found API deprecation at birth")
 
     print "%s Deprecated at birth %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True),
@@ -1734,19 +2124,24 @@
         show_stats(cur, prev)
         sys.exit()
 
+    classes_with_base = []
+
     with current_file as f:
         if base_current_file:
             with base_current_file as base_f:
-                cur_fail, cur_noticed = examine_stream(f, base_f)
+                cur_fail, cur_noticed = examine_stream(f, base_f,
+                                                       out_classes_with_base=classes_with_base)
         else:
-            cur_fail, cur_noticed = examine_stream(f)
+            cur_fail, cur_noticed = examine_stream(f, out_classes_with_base=classes_with_base)
+
     if not previous_file is None:
         with previous_file as f:
             if base_previous_file:
                 with base_previous_file as base_f:
-                    prev_fail, prev_noticed = examine_stream(f, base_f)
+                    prev_fail, prev_noticed = examine_stream(f, base_f,
+                                                             in_classes_with_base=classes_with_base)
             else:
-                prev_fail, prev_noticed = examine_stream(f)
+                prev_fail, prev_noticed = examine_stream(f, in_classes_with_base=classes_with_base)
 
         # ignore errors from previous API level
         for p in prev_fail:
diff --git a/tools/apilint/apilint_sha_system.sh b/tools/apilint/apilint_sha_system.sh
new file mode 100755
index 0000000..8538a3d
--- /dev/null
+++ b/tools/apilint/apilint_sha_system.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+if git show --name-only --pretty=format: $1 | grep api/ > /dev/null; then
+  python tools/apilint/apilint.py \
+    --base-current <(git show $1:api/current.txt) \
+    --base-previous <(git show $1^:api/current.txt) \
+    <(git show $1:api/system-current.txt) \
+    <(git show $1^:api/system-current.txt)
+fi
diff --git a/tools/apilint/apilint_test.py b/tools/apilint/apilint_test.py
new file mode 100644
index 0000000..081e98d
--- /dev/null
+++ b/tools/apilint/apilint_test.py
@@ -0,0 +1,290 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+
+import apilint
+
+def cls(pkg, name):
+    return apilint.Class(apilint.Package(999, "package %s {" % pkg, None), 999,
+                  "public final class %s {" % name, None)
+
+_ri = apilint._retry_iterator
+
+c1 = cls("android.app", "ActivityManager")
+c2 = cls("android.app", "Notification")
+c3 = cls("android.app", "Notification.Action")
+c4 = cls("android.graphics", "Bitmap")
+
+class UtilTests(unittest.TestCase):
+    def test_retry_iterator(self):
+        it = apilint._retry_iterator([1, 2, 3, 4])
+        self.assertEqual(it.next(), 1)
+        self.assertEqual(it.next(), 2)
+        self.assertEqual(it.next(), 3)
+        it.send("retry")
+        self.assertEqual(it.next(), 3)
+        self.assertEqual(it.next(), 4)
+        with self.assertRaises(StopIteration):
+            it.next()
+
+    def test_retry_iterator_one(self):
+        it = apilint._retry_iterator([1])
+        self.assertEqual(it.next(), 1)
+        it.send("retry")
+        self.assertEqual(it.next(), 1)
+        with self.assertRaises(StopIteration):
+            it.next()
+
+    def test_retry_iterator_one(self):
+        it = apilint._retry_iterator([1])
+        self.assertEqual(it.next(), 1)
+        it.send("retry")
+        self.assertEqual(it.next(), 1)
+        with self.assertRaises(StopIteration):
+            it.next()
+
+    def test_skip_to_matching_class_found(self):
+        it = _ri([c1, c2, c3, c4])
+        self.assertEquals(apilint._skip_to_matching_class(it, c3),
+                          c3)
+        self.assertEqual(it.next(), c4)
+
+    def test_skip_to_matching_class_not_found(self):
+        it = _ri([c1, c2, c3, c4])
+        self.assertEquals(apilint._skip_to_matching_class(it, cls("android.content", "ContentProvider")),
+                          None)
+        self.assertEqual(it.next(), c4)
+
+    def test_yield_until_matching_class_found(self):
+        it = _ri([c1, c2, c3, c4])
+        self.assertEquals(list(apilint._yield_until_matching_class(it, c3)),
+                          [c1, c2])
+        self.assertEqual(it.next(), c4)
+
+    def test_yield_until_matching_class_not_found(self):
+        it = _ri([c1, c2, c3, c4])
+        self.assertEquals(list(apilint._yield_until_matching_class(it, cls("android.content", "ContentProvider"))),
+                          [c1, c2, c3])
+        self.assertEqual(it.next(), c4)
+
+    def test_yield_until_matching_class_None(self):
+        it = _ri([c1, c2, c3, c4])
+        self.assertEquals(list(apilint._yield_until_matching_class(it, None)),
+                          [c1, c2, c3, c4])
+
+
+faulty_current_txt = """
+package android.app {
+  public final class Activity {
+  }
+
+  public final class WallpaperColors implements android.os.Parcelable {
+    ctor public WallpaperColors(android.os.Parcel);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
+  }
+}
+""".split('\n')
+
+ok_current_txt = """
+package android.app {
+  public final class Activity {
+  }
+
+  public final class WallpaperColors implements android.os.Parcelable {
+    ctor public WallpaperColors();
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
+  }
+}
+""".split('\n')
+
+system_current_txt = """
+package android.app {
+  public final class WallpaperColors implements android.os.Parcelable {
+    method public int getSomething();
+  }
+}
+""".split('\n')
+
+
+
+class BaseFileTests(unittest.TestCase):
+    def test_base_file_avoids_errors(self):
+        failures, _ = apilint.examine_stream(system_current_txt, ok_current_txt)
+        self.assertEquals(failures, {})
+
+    def test_class_with_base_finds_same_errors(self):
+        failures_with_classes_with_base, _ = apilint.examine_stream("", faulty_current_txt,
+                                                                    in_classes_with_base=[cls("android.app", "WallpaperColors")])
+        failures_with_system_txt, _ = apilint.examine_stream(system_current_txt, faulty_current_txt)
+
+        self.assertEquals(failures_with_classes_with_base.keys(), failures_with_system_txt.keys())
+
+    def test_classes_with_base_is_emited(self):
+        classes_with_base = []
+        _, _ = apilint.examine_stream(system_current_txt, faulty_current_txt,
+                                      out_classes_with_base=classes_with_base)
+        self.assertEquals(map(lambda x: x.fullname, classes_with_base), ["android.app.WallpaperColors"])
+
+class V2TokenizerTests(unittest.TestCase):
+    def _test(self, raw, expected):
+        self.assertEquals(apilint.V2Tokenizer(raw).tokenize(), expected)
+
+    def test_simple(self):
+        self._test("  method public some.Type someName(some.Argument arg, int arg);",
+                   ['method', 'public', 'some.Type', 'someName', '(', 'some.Argument',
+                    'arg', ',', 'int', 'arg', ')', ';'])
+        self._test("class Some.Class extends SomeOther {",
+                   ['class', 'Some.Class', 'extends', 'SomeOther', '{'])
+
+    def test_varargs(self):
+        self._test("name(String...)",
+                   ['name', '(', 'String', '...', ')'])
+
+    def test_kotlin(self):
+        self._test("String? name(String!...)",
+                   ['String', '?', 'name', '(', 'String', '!',  '...', ')'])
+
+    def test_annotation(self):
+        self._test("method @Nullable public void name();",
+                   ['method', '@', 'Nullable', 'public', 'void', 'name', '(', ')', ';'])
+
+    def test_annotation_args(self):
+        self._test("@Some(val=1, other=2) class Class {",
+                   ['@', 'Some', '(', 'val', '=', '1', ',', 'other', '=', '2', ')',
+                    'class', 'Class', '{'])
+    def test_comment(self):
+        self._test("some //comment", ['some'])
+
+    def test_strings(self):
+        self._test(r'"" "foo" "\"" "\\"', ['""', '"foo"', r'"\""', r'"\\"'])
+
+    def test_at_interface(self):
+        self._test("public @interface Annotation {",
+                   ['public', '@interface', 'Annotation', '{'])
+
+    def test_array_type(self):
+        self._test("int[][]", ['int', '[]', '[]'])
+
+    def test_generics(self):
+        self._test("<>foobar<A extends Object>",
+                   ['<', '>', 'foobar', '<', 'A', 'extends', 'Object', '>'])
+
+class V2ParserTests(unittest.TestCase):
+    def _cls(self, raw):
+        pkg = apilint.Package(999, "package pkg {", None)
+        return apilint.Class(pkg, 1, raw, '', sig_format=2)
+
+    def _method(self, raw, cls=None):
+        if not cls:
+            cls = self._cls("class Class {")
+        return apilint.Method(cls, 1, raw, '', sig_format=2)
+
+    def _field(self, raw):
+        cls = self._cls("class Class {")
+        return apilint.Field(cls, 1, raw, '', sig_format=2)
+
+    def test_class(self):
+        cls = self._cls("@Deprecated @IntRange(from=1, to=2) public static abstract class Some.Name extends Super<Class> implements Interface<Class> {")
+        self.assertTrue('deprecated' in cls.split)
+        self.assertTrue('static' in cls.split)
+        self.assertTrue('abstract' in cls.split)
+        self.assertTrue('class' in cls.split)
+        self.assertEquals('Super', cls.extends)
+        self.assertEquals('Interface', cls.implements)
+        self.assertEquals('pkg.Some.Name', cls.fullname)
+
+    def test_interface(self):
+        cls = self._cls("@Deprecated @IntRange(from=1, to=2) public interface Some.Name extends Interface<Class> {")
+        self.assertTrue('deprecated' in cls.split)
+        self.assertTrue('interface' in cls.split)
+        self.assertEquals('Interface', cls.extends)
+        self.assertEquals('Interface', cls.implements)
+        self.assertEquals('pkg.Some.Name', cls.fullname)
+
+    def test_at_interface(self):
+        cls = self._cls("@java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface SuppressLint {")
+        self.assertTrue('@interface' in cls.split)
+        self.assertEquals('pkg.SuppressLint', cls.fullname)
+
+    def test_parse_method(self):
+        m = self._method("method @Deprecated public static <T> Class<T>[][] name("
+                         + "Class<T[]>[][], Class<T[][][]>[][]...) throws Exception, T;")
+        self.assertTrue('static' in m.split)
+        self.assertTrue('public' in m.split)
+        self.assertTrue('method' in m.split)
+        self.assertTrue('deprecated' in m.split)
+        self.assertEquals('java.lang.Class[][]', m.typ)
+        self.assertEquals('name', m.name)
+        self.assertEquals(['java.lang.Class[][]', 'java.lang.Class[][]...'], m.args)
+        self.assertEquals(['java.lang.Exception', 'T'], m.throws)
+
+    def test_ctor(self):
+        m = self._method("ctor @Deprecated <T> ClassName();")
+        self.assertTrue('ctor' in m.split)
+        self.assertTrue('deprecated' in m.split)
+        self.assertEquals('ctor', m.typ)
+        self.assertEquals('ClassName', m.name)
+
+    def test_parse_annotation_method(self):
+        cls = self._cls("@interface Annotation {")
+        self._method('method abstract String category() default "";', cls=cls)
+        self._method('method abstract boolean deepExport() default false;', cls=cls)
+        self._method('method abstract ViewDebug.FlagToString[] flagMapping() default {};', cls=cls)
+
+    def test_parse_string_field(self):
+        f = self._field('field @Deprecated public final String SOME_NAME = "value";')
+        self.assertTrue('field' in f.split)
+        self.assertTrue('deprecated' in f.split)
+        self.assertTrue('final' in f.split)
+        self.assertEquals('java.lang.String', f.typ)
+        self.assertEquals('SOME_NAME', f.name)
+        self.assertEquals('value', f.value)
+
+    def test_parse_field(self):
+        f = self._field('field public Object SOME_NAME;')
+        self.assertTrue('field' in f.split)
+        self.assertEquals('java.lang.Object', f.typ)
+        self.assertEquals('SOME_NAME', f.name)
+        self.assertEquals(None, f.value)
+
+    def test_parse_int_field(self):
+        f = self._field('field public int NAME = 123;')
+        self.assertTrue('field' in f.split)
+        self.assertEquals('int', f.typ)
+        self.assertEquals('NAME', f.name)
+        self.assertEquals('123', f.value)
+
+    def test_parse_quotient_field(self):
+        f = self._field('field public int NAME = (0.0/0.0);')
+        self.assertTrue('field' in f.split)
+        self.assertEquals('int', f.typ)
+        self.assertEquals('NAME', f.name)
+        self.assertEquals('( 0.0 / 0.0 )', f.value)
+
+    def test_kotlin_types(self):
+        self._field('field public List<Integer[]?[]!>?[]![]? NAME;')
+        self._method("method <T?> Class<T!>?[]![][]? name(Type!, Type argname,"
+                         + "Class<T?>[][]?[]!...!) throws Exception, T;")
+        self._method("method <T> T name(T a = 1, T b = A(1), Lambda f = { false }, N? n = null, "
+                         + """double c = (1/0), float d = 1.0f, String s = "heyo", char c = 'a');""")
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/tools/incident_report/printer.h b/tools/incident_report/printer.h
index ed93fa1..63e276b 100644
--- a/tools/incident_report/printer.h
+++ b/tools/incident_report/printer.h
@@ -22,7 +22,7 @@
 class Out
 {
 public:
-    Out(int fd);
+    explicit Out(int fd);
     ~Out();
 
     void printf(const char* format, ...);
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index faa3547..f6c9c0e 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -453,7 +453,7 @@
     map<string, bool> variableNames;
     set<string> parents;
     vector<const FieldDescriptor*> fieldsInOrder = sortFields(descriptor);
-    bool skip[fieldsInOrder.size()];
+    vector<bool> skip(fieldsInOrder.size());
     const Destination incidentDest = getPrivacyFlags(descriptor).dest();
 
     for (size_t i=0; i<fieldsInOrder.size(); i++) {
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index 703a67b..5725f0c 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -96,6 +96,7 @@
 
 cc_library_shared {
     name: "libstatslog",
+    host_supported: true,
     generated_sources: ["statslog.cpp"],
     generated_headers: ["statslog.h"],
     cflags: [
@@ -105,8 +106,19 @@
     export_generated_headers: ["statslog.h"],
     shared_libs: [
         "liblog",
-        "libutils",
         "libcutils",
     ],
     static_libs: ["libstatssocket"],
+    target: {
+        android: {
+            shared_libs: [
+                "libutils",
+            ],
+        },
+        host: {
+            static_libs: [
+                "libutils",
+            ],
+        },
+    },
 }
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 5192a0e..11b408f 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -106,7 +106,9 @@
     fprintf(out, "#include <mutex>\n");
     fprintf(out, "#include <chrono>\n");
     fprintf(out, "#include <thread>\n");
+    fprintf(out, "#ifdef __ANDROID__\n");
     fprintf(out, "#include <cutils/properties.h>\n");
+    fprintf(out, "#endif\n");
     fprintf(out, "#include <stats_event_list.h>\n");
     fprintf(out, "#include <log/log.h>\n");
     fprintf(out, "#include <statslog.h>\n");
@@ -117,7 +119,11 @@
     fprintf(out, "namespace util {\n");
     fprintf(out, "// the single event tag id for all stats logs\n");
     fprintf(out, "const static int kStatsEventTag = 1937006964;\n");
+    fprintf(out, "#ifdef __ANDROID__\n");
     fprintf(out, "const static bool kStatsdEnabled = property_get_bool(\"ro.statsd.enable\", true);\n");
+    fprintf(out, "#else\n");
+    fprintf(out, "const static bool kStatsdEnabled = false;\n");
+    fprintf(out, "#endif\n");
 
     std::set<string> kTruncatingAtomNames = {"mobile_radio_power_state_changed",
                                              "audio_state_changed",
diff --git a/wifi/java/android/net/wifi/DppStatusCallback.java b/wifi/java/android/net/wifi/DppStatusCallback.java
new file mode 100644
index 0000000..fa2ab30
--- /dev/null
+++ b/wifi/java/android/net/wifi/DppStatusCallback.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.os.Handler;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * DPP Status Callback. Use this callback to get status updates (success, failure, progress)
+ * from the DPP operation started with {@link WifiManager#startDppAsConfiguratorInitiator(String,
+ * int, int, Handler, DppStatusCallback)} or {@link WifiManager#startDppAsEnrolleeInitiator(String,
+ * Handler, DppStatusCallback)}
+ * @hide
+ */
+@SystemApi
+public abstract class DppStatusCallback {
+    /**
+     * DPP Success event: Configuration sent (Configurator mode).
+     */
+    public static final int DPP_EVENT_SUCCESS_CONFIGURATION_SENT = 0;
+
+    /** @hide */
+    @IntDef(prefix = { "DPP_EVENT_SUCCESS_" }, value = {
+            DPP_EVENT_SUCCESS_CONFIGURATION_SENT,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DppSuccessStatusCode {}
+
+    /**
+     * DPP Progress event: Initial authentication with peer succeeded.
+     */
+    public static final int DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0;
+
+    /**
+     * DPP Progress event: Peer requires more time to process bootstrapping.
+     */
+    public static final int DPP_EVENT_PROGRESS_RESPONSE_PENDING = 1;
+
+    /** @hide */
+    @IntDef(prefix = { "DPP_EVENT_PROGRESS_" }, value = {
+            DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS,
+            DPP_EVENT_PROGRESS_RESPONSE_PENDING,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DppProgressStatusCode {}
+
+    /**
+     * DPP Failure event: Scanned QR code is either not a DPP URI, or the DPP URI has errors.
+     */
+    public static final int DPP_EVENT_FAILURE_INVALID_URI = -1;
+
+    /**
+     * DPP Failure event: Bootstrapping/Authentication initialization process failure.
+     */
+    public static final int DPP_EVENT_FAILURE_AUTHENTICATION = -2;
+
+    /**
+     * DPP Failure event: Both devices are implementing the same role and are incompatible.
+     */
+    public static final int DPP_EVENT_FAILURE_NOT_COMPATIBLE = -3;
+
+    /**
+     * DPP Failure event: Configuration process has failed due to malformed message.
+     */
+    public static final int DPP_EVENT_FAILURE_CONFIGURATION = -4;
+
+    /**
+     * DPP Failure event: DPP request while in another DPP exchange.
+     */
+    public static final int DPP_EVENT_FAILURE_BUSY = -5;
+
+    /**
+     * DPP Failure event: No response from the peer.
+     */
+    public static final int DPP_EVENT_FAILURE_TIMEOUT = -6;
+
+    /**
+     * DPP Failure event: General protocol failure.
+     */
+    public static final int DPP_EVENT_FAILURE = -7;
+
+    /**
+     * DPP Failure event: Feature or option is not supported.
+     */
+    public static final int DPP_EVENT_FAILURE_NOT_SUPPORTED = -8;
+
+    /**
+     * DPP Failure event: Invalid network provided to DPP configurator.
+     * Network must either be WPA3-Personal (SAE) or WPA2-Personal (PSK).
+     */
+    public static final int DPP_EVENT_FAILURE_INVALID_NETWORK = -9;
+
+
+    /** @hide */
+    @IntDef(prefix = {"DPP_EVENT_FAILURE_"}, value = {
+            DPP_EVENT_FAILURE_INVALID_URI,
+            DPP_EVENT_FAILURE_AUTHENTICATION,
+            DPP_EVENT_FAILURE_NOT_COMPATIBLE,
+            DPP_EVENT_FAILURE_CONFIGURATION,
+            DPP_EVENT_FAILURE_BUSY,
+            DPP_EVENT_FAILURE_TIMEOUT,
+            DPP_EVENT_FAILURE,
+            DPP_EVENT_FAILURE_NOT_SUPPORTED,
+            DPP_EVENT_FAILURE_INVALID_NETWORK,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DppFailureStatusCode {
+    }
+
+    /**
+     * Called when local DPP Enrollee successfully receives a new Wi-Fi configuration from the
+     * peer DPP configurator. This callback marks the successful end of the DPP current DPP
+     * session, and no further callbacks will be called. This callback is the successful outcome
+     * of a DPP flow starting with {@link WifiManager#startDppAsEnrolleeInitiator(String, Handler,
+     * DppStatusCallback)}.
+     *
+     * @param newNetworkId New Wi-Fi configuration with a network ID received from the configurator
+     */
+    public abstract void onEnrolleeSuccess(int newNetworkId);
+
+    /**
+     * Called when a DPP success event takes place, except for when configuration is received from
+     * an external Configurator. The callback onSuccessConfigReceived will be used in this case.
+     * This callback marks the successful end of the current DPP session, and no further
+     * callbacks will be called. This callback is the successful outcome of a DPP flow starting with
+     * {@link WifiManager#startDppAsConfiguratorInitiator(String, int, int, Handler,
+     * DppStatusCallback)}.
+     *
+     * @param code DPP success status code.
+     */
+    public abstract void onConfiguratorSuccess(@DppSuccessStatusCode int code);
+
+    /**
+     * Called when a DPP Failure event takes place. This callback marks the unsuccessful end of the
+     * current DPP session, and no further callbacks will be called.
+     *
+     * @param code DPP failure status code.
+     */
+    public abstract void onFailure(@DppFailureStatusCode int code);
+
+    /**
+     * Called when DPP events that indicate progress take place. Can be used by UI elements
+     * to show progress.
+     *
+     * @param code DPP progress status code.
+     */
+    public abstract void onProgress(@DppProgressStatusCode int code);
+}
diff --git a/wifi/java/android/net/wifi/IDppCallback.aidl b/wifi/java/android/net/wifi/IDppCallback.aidl
new file mode 100644
index 0000000..c452c76
--- /dev/null
+++ b/wifi/java/android/net/wifi/IDppCallback.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+/**
+ * Interface for DPP callback.
+ *
+ * @hide
+ */
+oneway interface IDppCallback
+{
+    /**
+     * Called when local DPP Enrollee successfully receives a new Wi-Fi configuratrion from the
+     * peer DPP configurator.
+     */
+    void onSuccessConfigReceived(int newNetworkId);
+
+    /**
+     * Called when DPP success events take place, except for when configuration is received from
+     * an external Configurator. The callback onSuccessConfigReceived will be used in this case.
+     */
+    void onSuccess(int status);
+
+    /**
+     * Called when DPP Failure events take place.
+     */
+    void onFailure(int status);
+
+    /**
+     * Called when DPP events that indicate progress take place. Can be used by UI elements
+     * to show progress.
+     */
+    void onProgress(int status);
+}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 21d6b94..fef0ed7 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -16,7 +16,6 @@
 
 package android.net.wifi;
 
-
 import android.content.pm.ParceledListSlice;
 
 import android.net.wifi.hotspot2.OsuProvider;
@@ -25,6 +24,7 @@
 
 import android.net.DhcpInfo;
 import android.net.Network;
+import android.net.wifi.IDppCallback;
 import android.net.wifi.INetworkRequestMatchCallback;
 import android.net.wifi.ISoftApCallback;
 import android.net.wifi.ITrafficStateCallback;
@@ -62,9 +62,11 @@
 
     ParceledListSlice getPrivilegedConfiguredNetworks();
 
-    List<WifiConfiguration> getAllMatchingWifiConfigs(in List<ScanResult> scanResult);
+    Map getAllMatchingFqdnsForScanResults(in List<ScanResult> scanResult);
 
-    List<OsuProvider> getMatchingOsuProviders(in List<ScanResult> scanResult);
+    Map getMatchingOsuProviders(in List<ScanResult> scanResult);
+
+    Map getMatchingPasspointConfigsForOsuProviders(in List<OsuProvider> osuProviders);
 
     int addOrUpdateNetwork(in WifiConfiguration config, String packageName);
 
@@ -74,6 +76,8 @@
 
     List<PasspointConfiguration> getPasspointConfigurations();
 
+    List<WifiConfiguration> getWifiConfigsForPasspointProfiles(in List<String> fqdnList);
+
     void queryPasspointIcon(long bssid, String fileName);
 
     int matchProviderWithCurrentNetwork(String fqdn);
@@ -195,5 +199,15 @@
     int removeNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
 
     String[] getFactoryMacAddresses();
+
+    void setDeviceMobilityState(int state);
+
+    void startDppAsConfiguratorInitiator(in IBinder binder, in String enrolleeUri,
+        int selectedNetworkId, int netRole, in IDppCallback callback);
+
+    void startDppAsEnrolleeInitiator(in IBinder binder, in String configuratorUri,
+        in IDppCallback callback);
+
+    void stopDppSession();
 }
 
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 64f8adb..a809cad 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -28,6 +28,7 @@
 import android.net.StaticIpConfiguration;
 import android.net.Uri;
 import android.net.wifi.WifiInfo;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
@@ -632,7 +633,7 @@
      *  the network we need to be before autojoin kicks in
      */
     /** @hide **/
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static int INVALID_RSSI = -127;
 
     // States for the userApproved field
@@ -708,6 +709,12 @@
     }
 
     /**
+     * This Wifi configuration is expected for OSU(Online Sign Up) of Passpoint Release 2.
+     * @hide
+     */
+    public boolean osu;
+
+    /**
      * @hide
      * Last time the system was connected to this configuration.
      */
@@ -1645,6 +1652,7 @@
         selfAdded = false;
         didSelfAdd = false;
         ephemeral = false;
+        osu = false;
         trusted = true; // Networks are considered trusted by default.
         meteredHint = false;
         meteredOverride = METERED_OVERRIDE_NONE;
@@ -1755,6 +1763,7 @@
         if (this.selfAdded) sbuf.append(" selfAdded");
         if (this.validatedInternetAccess) sbuf.append(" validatedInternetAccess");
         if (this.ephemeral) sbuf.append(" ephemeral");
+        if (this.osu) sbuf.append(" osu");
         if (this.trusted) sbuf.append(" trusted");
         if (this.meteredHint) sbuf.append(" meteredHint");
         if (this.useExternalScores) sbuf.append(" useExternalScores");
@@ -2244,6 +2253,7 @@
             validatedInternetAccess = source.validatedInternetAccess;
             isLegacyPasspointConfig = source.isLegacyPasspointConfig;
             ephemeral = source.ephemeral;
+            osu = source.osu;
             trusted = source.trusted;
             meteredHint = source.meteredHint;
             meteredOverride = source.meteredOverride;
@@ -2340,6 +2350,7 @@
         dest.writeInt(recentFailure.getAssociationStatus());
         dest.writeParcelable(mRandomizedMacAddress, flags);
         dest.writeInt(macRandomizationSetting);
+        dest.writeInt(osu ? 1 : 0);
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -2410,6 +2421,7 @@
                 config.recentFailure.setAssociationStatus(in.readInt());
                 config.mRandomizedMacAddress = in.readParcelable(null);
                 config.macRandomizationSetting = in.readInt();
+                config.osu = in.readInt() != 0;
                 return config;
             }
 
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index e5dcef0..af5ad51 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -19,6 +19,7 @@
 import android.annotation.UnsupportedAppUsage;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkUtils;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -69,7 +70,7 @@
     }
 
     private SupplicantState mSupplicantState;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private String mBSSID;
     @UnsupportedAppUsage
     private WifiSsid mWifiSsid;
@@ -113,6 +114,11 @@
     private boolean mTrusted;
 
     /**
+     * OSU (Online Sign Up) AP for Passpoint R2.
+     */
+    private boolean mOsuAp;
+
+    /**
      * Running total count of lost (not ACKed) transmitted unicast data packets.
      * @hide
      */
@@ -189,6 +195,7 @@
         setFrequency(-1);
         setMeteredHint(false);
         setEphemeral(false);
+        setOsuAp(false);
         txBad = 0;
         txSuccess = 0;
         rxSuccess = 0;
@@ -218,6 +225,7 @@
             mMeteredHint = source.mMeteredHint;
             mEphemeral = source.mEphemeral;
             mTrusted = source.mTrusted;
+            mOsuAp = source.mOsuAp;
             txBad = source.txBad;
             txRetries = source.txRetries;
             txSuccess = source.txSuccess;
@@ -410,6 +418,15 @@
         return mTrusted;
     }
 
+    /** {@hide} */
+    public void setOsuAp(boolean osuAp) {
+        mOsuAp = osuAp;
+    }
+
+    /** {@hide} */
+    public boolean isOsuAp() {
+        return mOsuAp;
+    }
 
     /** @hide */
     @UnsupportedAppUsage
@@ -564,6 +581,7 @@
         dest.writeLong(rxSuccess);
         dest.writeDouble(rxSuccessRate);
         mSupplicantState.writeToParcel(dest, flags);
+        dest.writeInt(mOsuAp ? 1 : 0);
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -599,6 +617,7 @@
                 info.rxSuccess = in.readLong();
                 info.rxSuccessRate = in.readDouble();
                 info.mSupplicantState = SupplicantState.CREATOR.createFromParcel(in);
+                info.mOsuAp = in.readInt() != 0;
                 return info;
             }
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 57c97ea..ad2ed81 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -44,6 +44,7 @@
 import android.os.RemoteException;
 import android.os.WorkSource;
 import android.util.Log;
+import android.util.Pair;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -57,8 +58,11 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
 import java.net.InetAddress;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 
 /**
@@ -1188,25 +1192,39 @@
     }
 
     /**
-     * Returns all matching WifiConfigurations for a given list of ScanResult.
+     * Returns a list of all matching WifiConfigurations for a given list of ScanResult.
      *
      * An empty list will be returned when no configurations are installed or if no configurations
      * match the ScanResult.
-
+     *
      * @param scanResults a list of scanResult that represents the BSSID
-     * @return A list of {@link WifiConfiguration} that can have duplicate entries.
+     * @return List that consists of {@link WifiConfiguration} and corresponding scanResults.
      * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
-    public List<WifiConfiguration> getAllMatchingWifiConfigs(
+    public List<Pair<WifiConfiguration, List<ScanResult>>> getAllMatchingWifiConfigs(
             @NonNull List<ScanResult> scanResults) {
+        List<Pair<WifiConfiguration, List<ScanResult>>> configs = new ArrayList<>();
         try {
-            return mService.getAllMatchingWifiConfigs(scanResults);
+            Map<String, List<ScanResult>> results = mService.getAllMatchingFqdnsForScanResults(
+                    scanResults);
+            if (results.isEmpty()) {
+                return configs;
+            }
+            List<WifiConfiguration> wifiConfigurations =
+                    mService.getWifiConfigsForPasspointProfiles(new ArrayList<>(results.keySet()));
+            for (WifiConfiguration configuration : wifiConfigurations) {
+                List<ScanResult> scanResultList = results.get(configuration.FQDN);
+                if (scanResultList != null) {
+                    configs.add(Pair.create(configuration, scanResultList));
+                }
+            }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
+
+        return configs;
     }
 
     /**
@@ -1216,12 +1234,13 @@
      * An empty list will be returned if no match is found.
      *
      * @param scanResults a list of ScanResult
-     * @return A list of {@link OsuProvider} that does not contain duplicate entries.
+     * @return Map that consists {@link OsuProvider} and a list of matching {@link ScanResult}
      * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
-    public List<OsuProvider> getMatchingOsuProviders(List<ScanResult> scanResults) {
+    public Map<OsuProvider, List<ScanResult>> getMatchingOsuProviders(
+            List<ScanResult> scanResults) {
         try {
             return mService.getMatchingOsuProviders(scanResults);
         } catch (RemoteException e) {
@@ -1230,6 +1249,30 @@
     }
 
     /**
+     * Returns the matching Passpoint R2 configurations for given OSU (Online Sign-Up) providers.
+     *
+     * Given a list of OSU providers, this only returns OSU providers that already have Passpoint R2
+     * configurations in the device.
+     * An empty map will be returned when there is no matching Passpoint R2 configuration for the
+     * given OsuProviders.
+     *
+     * @param osuProviders a set of {@link OsuProvider}
+     * @return Map that consists of {@link OsuProvider} and matching {@link PasspointConfiguration}.
+     * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public Map<OsuProvider, PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(
+            @NonNull Set<OsuProvider> osuProviders) {
+        try {
+            return mService.getMatchingPasspointConfigsForOsuProviders(
+                    new ArrayList<>(osuProviders));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Add a new network description to the set of configured networks.
      * The {@code networkId} field of the supplied configuration object
      * is ignored.
@@ -1990,6 +2033,8 @@
     public static final int WIFI_FEATURE_OWE              = 0x20000000; // Enhanced Open
     /** @hide */
     public static final int WIFI_FEATURE_LOW_LATENCY      = 0x40000000; // Low Latency modes
+    /** @hide */
+    public static final int WIFI_FEATURE_DPP              = 0x80000000; // DPP (Easy-Connect)
 
     private int getSupportedFeatures() {
         try {
@@ -4436,6 +4481,13 @@
     }
 
     /**
+     * @return true if this device supports Wi-Fi Device Provisioning Protocol (Easy-connect)
+     */
+    public boolean isDppSupported() {
+        return isFeatureSupported(WIFI_FEATURE_DPP);
+    }
+
+    /**
      * Gets the factory Wi-Fi MAC addresses.
      * @return Array of String representing Wi-Fi MAC addresses sorted lexically or an empty Array
      * if failed.
@@ -4449,4 +4501,211 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"DEVICE_MOBILITY_STATE_"}, value = {
+            DEVICE_MOBILITY_STATE_UNKNOWN,
+            DEVICE_MOBILITY_STATE_HIGH_MVMT,
+            DEVICE_MOBILITY_STATE_LOW_MVMT,
+            DEVICE_MOBILITY_STATE_STATIONARY})
+    public @interface DeviceMobilityState {}
+
+    /**
+     * Unknown device mobility state
+     *
+     * @see #setDeviceMobilityState(int)
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int DEVICE_MOBILITY_STATE_UNKNOWN = 0;
+
+    /**
+     * High movement device mobility state
+     *
+     * @see #setDeviceMobilityState(int)
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int DEVICE_MOBILITY_STATE_HIGH_MVMT = 1;
+
+    /**
+     * Low movement device mobility state
+     *
+     * @see #setDeviceMobilityState(int)
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2;
+
+    /**
+     * Stationary device mobility state
+     *
+     * @see #setDeviceMobilityState(int)
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int DEVICE_MOBILITY_STATE_STATIONARY = 3;
+
+    /**
+     * Updates the device mobility state. Wifi uses this information to adjust the interval between
+     * Wifi scans in order to balance power consumption with scan accuracy.
+     * @param state the updated device mobility state
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE)
+    public void setDeviceMobilityState(@DeviceMobilityState int state) {
+        try {
+            mService.setDeviceMobilityState(state);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /* DPP - Device Provisioning Protocol AKA "Easy Connect" */
+
+    /**
+     * DPP Network role: Station.
+     * @hide
+     */
+    @SystemApi
+    public static final int DPP_NETWORK_ROLE_STA = 0;
+
+    /**
+     * DPP Network role: Access Point.
+     * @hide
+     */
+    @SystemApi
+    public static final int DPP_NETWORK_ROLE_AP = 1;
+
+    /** @hide */
+    @IntDef(prefix = {"DPP_NETWORK_ROLE_"}, value = {
+            DPP_NETWORK_ROLE_STA,
+            DPP_NETWORK_ROLE_AP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DppNetworkRole {}
+
+    /**
+     * Start DPP in Configurator-Initiator role. The current device will initiate DPP bootstrapping
+     * with a peer, and configure the peer with the SSID and password of the specified network using
+     * the DPP protocol on an encrypted link.
+     *
+     * @param enrolleeUri URI of the Enrollee obtained separately (e.g. QR code scanning)
+     * @param selectedNetworkId Selected network ID to be sent to the peer
+     * @param enrolleeNetworkRole The network role of the enrollee
+     * @param callback Callback for status updates
+     * @param handler The handler on whose thread to execute the callbacks. Null for main thread.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD})
+    public void startDppAsConfiguratorInitiator(@NonNull String enrolleeUri,
+            int selectedNetworkId, @DppNetworkRole int enrolleeNetworkRole,
+            @Nullable Handler handler, @NonNull DppStatusCallback callback) {
+        Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper();
+        Binder binder = new Binder();
+        try {
+            mService.startDppAsConfiguratorInitiator(binder, enrolleeUri, selectedNetworkId,
+                    enrolleeNetworkRole, new DppCallbackProxy(looper, callback));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Start DPP in Enrollee-Initiator role. The current device will initiate DPP bootstrapping
+     * with a peer, and receive the SSID and password from the peer configurator.
+     *
+     * @param configuratorUri URI of the Configurator obtained separately (e.g. QR code scanning)
+     * @param callback Callback for status updates
+     * @param handler The handler on whose thread to execute the callbacks. Null for main thread.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD})
+    public void startDppAsEnrolleeInitiator(@NonNull String configuratorUri,
+            @Nullable Handler handler, @NonNull DppStatusCallback callback) {
+        Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper();
+        Binder binder = new Binder();
+        try {
+            mService.startDppAsEnrolleeInitiator(binder, configuratorUri,
+                     new DppCallbackProxy(looper, callback));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Stop or abort a current DPP session.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD})
+    public void stopDppSession() {
+        try {
+            /* Request lower layers to stop/abort and clear resources */
+            mService.stopDppSession();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Helper class to support DPP callbacks
+     * @hide
+     */
+    @SystemApi
+    private static class DppCallbackProxy extends IDppCallback.Stub {
+        private final Handler mHandler;
+        private final DppStatusCallback mDppStatusCallback;
+
+        DppCallbackProxy(Looper looper, DppStatusCallback dppStatusCallback) {
+            mHandler = new Handler(looper);
+            mDppStatusCallback = dppStatusCallback;
+        }
+
+        @Override
+        public void onSuccessConfigReceived(int newNetworkId) {
+            Log.d(TAG, "DPP onSuccessConfigReceived callback");
+            mHandler.post(() -> {
+                mDppStatusCallback.onEnrolleeSuccess(newNetworkId);
+            });
+        }
+
+        @Override
+        public void onSuccess(int status) {
+            Log.d(TAG, "DPP onSuccess callback");
+            mHandler.post(() -> {
+                mDppStatusCallback.onConfiguratorSuccess(status);
+            });
+        }
+
+        @Override
+        public void onFailure(int status) {
+            Log.d(TAG, "DPP onFailure callback");
+            mHandler.post(() -> {
+                mDppStatusCallback.onFailure(status);
+            });
+        }
+
+        @Override
+        public void onProgress(int status) {
+            Log.d(TAG, "DPP onProgress callback");
+            mHandler.post(() -> {
+                mDppStatusCallback.onProgress(status);
+            });
+        }
+    }
 }
diff --git a/wifi/java/android/net/wifi/WpsInfo.java b/wifi/java/android/net/wifi/WpsInfo.java
index d12cce1..ae3104b 100644
--- a/wifi/java/android/net/wifi/WpsInfo.java
+++ b/wifi/java/android/net/wifi/WpsInfo.java
@@ -16,63 +16,42 @@
 
 package android.net.wifi;
 
-import android.os.Parcelable;
 import android.os.Parcel;
+import android.os.Parcelable;
 
 /**
  * A class representing Wi-Fi Protected Setup
- * @deprecated This class is no longer supported.
+ *
  * {@see WifiP2pConfig}
  */
-@Deprecated
 public class WpsInfo implements Parcelable {
 
-    /** Push button configuration
-     * @deprecated This is no longer supported.*/
-    @Deprecated
+    /** Push button configuration */
     public static final int PBC     = 0;
-    /** Display pin method configuration - pin is generated and displayed on device
-     * @deprecated This is no longer supported.*/
-    @Deprecated
+    /** Display pin method configuration - pin is generated and displayed on device */
     public static final int DISPLAY = 1;
-    /** Keypad pin method configuration - pin is entered on device
-     * @deprecated This is no longer supported.*/
-    @Deprecated
+    /** Keypad pin method configuration - pin is entered on device */
     public static final int KEYPAD  = 2;
-    /** Label pin method configuration - pin is labelled on device
-     * @deprecated This is no longer supported.*/
-    @Deprecated
+    /** Label pin method configuration - pin is labelled on device */
     public static final int LABEL   = 3;
-    /** Invalid configuration
-     * @deprecated This is no longer supported.*/
-    @Deprecated
+    /** Invalid configuration */
     public static final int INVALID = 4;
 
-    /** Wi-Fi Protected Setup. www.wi-fi.org/wifi-protected-setup has details
-     * @deprecated This is no longer supported.*/
-    @Deprecated
+    /** Wi-Fi Protected Setup. www.wi-fi.org/wifi-protected-setup has details */
     public int setup;
 
-    /** Passed with pin method KEYPAD
-     * @deprecated This is no longer supported.*/
-    @Deprecated
+    /** Passed with pin method KEYPAD */
     public String BSSID;
 
-    /** Passed with pin method configuration
-     * @deprecated This is no longer supported.*/
-    @Deprecated
+    /** Passed with pin method configuration */
     public String pin;
 
-    /** @deprecated This API is no longer supported.*/
-    @Deprecated
     public WpsInfo() {
         setup = INVALID;
         BSSID = null;
         pin = null;
     }
 
-    /** @deprecated This API is no longer supported.*/
-    @Deprecated
     public String toString() {
         StringBuffer sbuf = new StringBuffer();
         sbuf.append(" setup: ").append(setup);
@@ -84,16 +63,12 @@
         return sbuf.toString();
     }
 
-    /** Implement the Parcelable interface
-     * @deprecated This API is no longer supported.*/
-    @Deprecated
+    /** Implement the Parcelable interface */
     public int describeContents() {
         return 0;
     }
 
-    /* Copy constructor
-    * @deprecated This API is no longer supported.*/
-    @Deprecated
+    /* Copy constructor */
     public WpsInfo(WpsInfo source) {
         if (source != null) {
             setup = source.setup;
@@ -102,22 +77,16 @@
         }
     }
 
-    /** Implement the Parcelable interface
-     * @deprecated This API is no longer supported. */
-    @Deprecated
+    /** Implement the Parcelable interface */
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(setup);
         dest.writeString(BSSID);
         dest.writeString(pin);
     }
 
-    /** Implement the Parcelable interface
-     * @deprecated This API is no longer supported.*/
-    @Deprecated
+    /** Implement the Parcelable interface */
     public static final Creator<WpsInfo> CREATOR =
         new Creator<WpsInfo>() {
-            /** @deprecated This API is nolonger supported.*/
-            @Deprecated
             public WpsInfo createFromParcel(Parcel in) {
                 WpsInfo config = new WpsInfo();
                 config.setup = in.readInt();
@@ -126,8 +95,6 @@
                 return config;
             }
 
-            /** @deprecated This API is nolonger supported.*/
-            @Deprecated
             public WpsInfo[] newArray(int size) {
                 return new WpsInfo[size];
             }
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySession.java b/wifi/java/android/net/wifi/aware/DiscoverySession.java
index 699f54c..a47e70b 100644
--- a/wifi/java/android/net/wifi/aware/DiscoverySession.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySession.java
@@ -34,11 +34,11 @@
  * {@link PublishDiscoverySession} and {@link SubscribeDiscoverySession}. This
  * class provides functionality common to both publish and subscribe discovery sessions:
  * <ul>
- *     <li>Sending messages: {@link #sendMessage(PeerHandle, int, byte[])} method.
- *     <li>Creating a network-specifier when requesting a Aware connection:
- *     {@link #createNetworkSpecifierOpen(PeerHandle)} or
- *     {@link #createNetworkSpecifierPassphrase(PeerHandle, String)}.
+ *      <li>Sending messages: {@link #sendMessage(PeerHandle, int, byte[])} method.
+ *      <li>Creating a network-specifier when requesting a Aware connection using
+ *      {@link WifiAwareManager.NetworkSpecifierBuilder}.
  * </ul>
+ * <p>
  * The {@link #close()} method must be called to destroy discovery sessions once they are
  * no longer needed.
  */
@@ -270,6 +270,7 @@
      * <p>
      * To set up an encrypted link use the
      * {@link #createNetworkSpecifierPassphrase(PeerHandle, String)} API.
+     * @deprecated Use the replacement {@link WifiAwareManager.NetworkSpecifierBuilder}.
      *
      * @param peerHandle The peer's handle obtained through
      * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)}
@@ -284,6 +285,7 @@
      * android.net.ConnectivityManager.NetworkCallback)}
      * [or other varieties of that API].
      */
+    @Deprecated
     public NetworkSpecifier createNetworkSpecifierOpen(@NonNull PeerHandle peerHandle) {
         if (mTerminated) {
             Log.w(TAG, "createNetworkSpecifierOpen: called on terminated session");
@@ -318,6 +320,7 @@
      * <p>
      * Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR
      * and a Publisher is a RESPONDER.
+     * @deprecated Use the replacement {@link WifiAwareManager.NetworkSpecifierBuilder}.
      *
      * @param peerHandle The peer's handle obtained through
      * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
@@ -336,6 +339,7 @@
      * android.net.ConnectivityManager.NetworkCallback)}
      * [or other varieties of that API].
      */
+    @Deprecated
     public NetworkSpecifier createNetworkSpecifierPassphrase(
             @NonNull PeerHandle peerHandle, @NonNull String passphrase) {
         if (!WifiAwareUtils.validatePassphrase(passphrase)) {
@@ -376,6 +380,7 @@
      * <p>
      * Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR
      * and a Publisher is a RESPONDER.
+     * @deprecated Use the replacement {@link WifiAwareManager.NetworkSpecifierBuilder}.
      *
      * @param peerHandle The peer's handle obtained through
      * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
@@ -397,6 +402,7 @@
      *
      * @hide
      */
+    @Deprecated
     @SystemApi
     public NetworkSpecifier createNetworkSpecifierPmk(@NonNull PeerHandle peerHandle,
             @NonNull byte[] pmk) {
diff --git a/wifi/java/android/net/wifi/aware/PeerHandle.java b/wifi/java/android/net/wifi/aware/PeerHandle.java
index 8ae4b5a..1603d00 100644
--- a/wifi/java/android/net/wifi/aware/PeerHandle.java
+++ b/wifi/java/android/net/wifi/aware/PeerHandle.java
@@ -16,6 +16,9 @@
 
 package android.net.wifi.aware;
 
+import android.os.Parcel;
+import android.os.Parcelable;
+
 /**
  * Opaque object used to represent a Wi-Fi Aware peer. Obtained from discovery sessions in
  * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)} or
@@ -33,7 +36,7 @@
  * {@link PublishConfig.Builder#setServiceSpecificInfo(byte[])}, or match filter,
  * {@link PublishConfig.Builder#setMatchFilter(java.util.List)}.
  */
-public class PeerHandle {
+public final class PeerHandle implements Parcelable {
     /** @hide */
     public PeerHandle(int peerId) {
         this.peerId = peerId;
@@ -59,4 +62,29 @@
     public int hashCode() {
         return peerId;
     }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(peerId);
+    }
+
+    public static final Creator<PeerHandle> CREATOR = new Creator<PeerHandle>() {
+        @Override
+        public PeerHandle[] newArray(int size) {
+            return new PeerHandle[size];
+        }
+
+        @Override
+        public PeerHandle createFromParcel(Parcel in) {
+            int peerHandle = in.readInt();
+
+            return new PeerHandle(peerHandle);
+        }
+    };
+
 }
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index 8529a89..26a6c08 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
 import android.net.ConnectivityManager;
@@ -57,11 +58,7 @@
  * {@link WifiAwareSession#subscribe(SubscribeConfig, DiscoverySessionCallback, Handler)}.
  * <li>Create a Aware network specifier to be used with
  * {@link ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)}
- * to set-up a Aware connection with a peer. Refer to
- * {@link DiscoverySession#createNetworkSpecifierOpen(PeerHandle)},
- * {@link DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String)},
- * {@link WifiAwareSession#createNetworkSpecifierOpen(int, byte[])}, and
- * {@link WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String)}.
+ * to set-up a Aware connection with a peer. Refer to {@link NetworkSpecifierBuilder}.
  * </ul>
  * <p>
  *     Aware may not be usable when Wi-Fi is disabled (and other conditions). To validate that
@@ -110,10 +107,7 @@
  *        <li>{@link NetworkRequest.Builder#addTransportType(int)} of
  *        {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
  *        <li>{@link NetworkRequest.Builder#setNetworkSpecifier(String)} using
- *        {@link WifiAwareSession#createNetworkSpecifierOpen(int, byte[])},
- *        {@link WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String)},
- *        {@link DiscoverySession#createNetworkSpecifierOpen(PeerHandle)}, or
- *        {@link DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String)}.
+ *        {@link NetworkSpecifierBuilder}.
  *    </ul>
  */
 @SystemService(Context.WIFI_AWARE_SERVICE)
@@ -145,8 +139,6 @@
      * Connection creation role is that of INITIATOR. Used to create a network specifier string
      * when requesting a Aware network.
      *
-     * @see DiscoverySession#createNetworkSpecifierOpen(PeerHandle)
-     * @see DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String)
      * @see WifiAwareSession#createNetworkSpecifierOpen(int, byte[])
      * @see WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String)
      */
@@ -156,8 +148,6 @@
      * Connection creation role is that of RESPONDER. Used to create a network specifier string
      * when requesting a Aware network.
      *
-     * @see DiscoverySession#createNetworkSpecifierOpen(PeerHandle)
-     * @see DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String)
      * @see WifiAwareSession#createNetworkSpecifierOpen(int, byte[])
      * @see WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String)
      */
@@ -415,6 +405,11 @@
                     + ", passphrase=" + ((passphrase == null) ? "null" : "non-null"));
         }
 
+        if (!WifiAwareUtils.isLegacyVersion(mContext, Build.VERSION_CODES.Q)) {
+            throw new UnsupportedOperationException(
+                    "API not deprecated - use WifiAwareManager.NetworkSpecifierBuilder");
+        }
+
         if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
                 && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) {
             throw new IllegalArgumentException(
@@ -813,4 +808,135 @@
             mOriginalCallback.onSessionTerminated();
         }
     }
+
+    /**
+     * A builder class for a Wi-Fi Aware network specifier to set up an Aware connection with a
+     * peer.
+     * <p>
+     * Note that all Wi-Fi Aware connection specifier objects must call the
+     * {@link NetworkSpecifierBuilder#setDiscoverySession(DiscoverySession)} to specify the context
+     * within which the connection is created, and
+     * {@link NetworkSpecifierBuilder#setPeerHandle(PeerHandle)} to specify the peer to which the
+     * connection is created.
+     */
+    public static class NetworkSpecifierBuilder {
+        private DiscoverySession mDiscoverySession;
+        private PeerHandle mPeerHandle;
+        private String mPskPassphrase;
+        private byte[] mPmk;
+
+        /**
+         * Configure the {@link PublishDiscoverySession} or {@link SubscribeDiscoverySession}
+         * discovery session in whose context the connection is created.
+         * <p>
+         * Note: this method must be called for any connection request!
+         *
+         * @param discoverySession A Wi-Fi Aware discovery session.
+         * @return the current {@link NetworkSpecifierBuilder} builder, enabling chaining of builder
+         *         methods.
+         */
+        public @NonNull NetworkSpecifierBuilder setDiscoverySession(
+                @NonNull DiscoverySession discoverySession) {
+            if (discoverySession == null) {
+                throw new IllegalArgumentException("Non-null discoverySession required");
+            }
+            mDiscoverySession = discoverySession;
+            return this;
+        }
+
+        /**
+         * Configure the {@link PeerHandle} of the peer to which the Wi-Fi Aware connection is
+         * requested. The peer is discovered through Wi-Fi Aware discovery,
+         * <p>
+         * Note: this method must be called for any connection request!
+         *
+         * @param peerHandle The peer's handle obtained through
+         * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)}
+         *                   or
+         *                   {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, byte[])}.
+         * @return the current {@link NetworkSpecifierBuilder} builder, enabling chaining of builder
+         *         methods.
+         */
+        public @NonNull NetworkSpecifierBuilder setPeerHandle(@NonNull PeerHandle peerHandle) {
+            if (peerHandle == null) {
+                throw new IllegalArgumentException("Non-null peerHandle required");
+            }
+            mPeerHandle = peerHandle;
+            return this;
+        }
+
+        /**
+         * Configure the PSK Passphrase for the Wi-Fi Aware connection being requested. This method
+         * is optional - if not called, then an Open (unencrypted) connection will be created.
+         *
+         * @param pskPassphrase The (optional) passphrase to be used to encrypt the link.
+         * @return the current {@link NetworkSpecifierBuilder} builder, enabling chaining of builder
+         *         methods.
+         */
+        public @NonNull NetworkSpecifierBuilder setPskPassphrase(@NonNull String pskPassphrase) {
+            if (!WifiAwareUtils.validatePassphrase(pskPassphrase)) {
+                throw new IllegalArgumentException("Passphrase must meet length requirements");
+            }
+            mPskPassphrase = pskPassphrase;
+            return this;
+        }
+
+        /**
+         * Configure the PMK for the Wi-Fi Aware connection being requested. This method
+         * is optional - if not called, then an Open (unencrypted) connection will be created.
+         *
+         * @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for
+         *            encrypting the data-path. Use the {@link #setPskPassphrase(String)} to
+         *            specify a Passphrase.
+         * @return the current {@link NetworkSpecifierBuilder} builder, enabling chaining of builder
+         *         methods.
+         * @hide
+         */
+        @SystemApi
+        public @NonNull NetworkSpecifierBuilder setPmk(@NonNull byte[] pmk) {
+            if (!WifiAwareUtils.validatePmk(pmk)) {
+                throw new IllegalArgumentException("PMK must 32 bytes");
+            }
+            mPmk = pmk;
+            return this;
+        }
+
+        /**
+         * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)}
+         * for a WiFi Aware connection (link) to the specified peer. The
+         * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
+         * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
+         * <p> The default builder constructor will initialize a NetworkSpecifier which requests an
+         * open (non-encrypted) link. To request an encrypted link use the
+         * {@link #setPskPassphrase(String)} builder method.
+         *
+         * @return A {@link NetworkSpecifier} to be used to construct
+         * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass
+         * to {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
+         * android.net.ConnectivityManager.NetworkCallback)}
+         * [or other varieties of that API].
+         */
+        public @NonNull NetworkSpecifier build() {
+            if (mDiscoverySession == null) {
+                throw new IllegalStateException("Null discovery session!?");
+            }
+            if (mPskPassphrase != null & mPmk != null) {
+                throw new IllegalStateException(
+                        "Can only specify a Passphrase or a PMK - not both!");
+            }
+
+            int role = mDiscoverySession instanceof SubscribeDiscoverySession
+                    ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
+                    : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
+
+            if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR && mPeerHandle == null) {
+                throw new IllegalStateException("Null peerHandle!?");
+            }
+
+            return new WifiAwareNetworkSpecifier(
+                    WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB, role,
+                    mDiscoverySession.mClientId, mDiscoverySession.mSessionId, mPeerHandle.peerId,
+                    null, mPmk, mPskPassphrase, Process.myUid());
+        }
+    }
 }
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java
new file mode 100644
index 0000000..0f29e08
--- /dev/null
+++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.aware;
+
+import android.annotation.Nullable;
+import android.net.NetworkCapabilities;
+import android.net.TransportInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.Inet6Address;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.Objects;
+
+/**
+ * Wi-Fi Aware-specific network information. The information can be extracted from the
+ * {@link android.net.NetworkCapabilities} of the network using
+ * {@link NetworkCapabilities#getTransportInfo()}.
+ * The {@link NetworkCapabilities} is provided by the connectivity service to apps, e.g. received
+ * through the
+ * {@link android.net.ConnectivityManager.NetworkCallback#onCapabilitiesChanged(android.net.Network,
+ * android.net.NetworkCapabilities)} callback.
+ * <p>
+ * The Wi-Fi Aware-specific network information include the peer's scoped link-local IPv6 address
+ * for the Wi-Fi Aware link. The scoped link-local IPv6 can then be used to create a
+ * {@link java.net.Socket} connection to the peer.
+ */
+public final class WifiAwareNetworkInfo implements TransportInfo, Parcelable {
+    private Inet6Address mIpv6Addr;
+
+    /** @hide */
+    public WifiAwareNetworkInfo(Inet6Address ipv6Addr) {
+        mIpv6Addr = ipv6Addr;
+    }
+
+    /**
+     * Get the scoped link-local IPv6 address of the Wi-Fi Aware peer (not of the local device!).
+     *
+     * @return An IPv6 address.
+     */
+    @Nullable
+    public Inet6Address getPeerIpv6Addr() {
+        return mIpv6Addr;
+    }
+
+    // parcelable methods
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeByteArray(mIpv6Addr.getAddress());
+        NetworkInterface ni = mIpv6Addr.getScopedInterface();
+        dest.writeString(ni == null ? null : ni.getName());
+    }
+
+    public static final Creator<WifiAwareNetworkInfo> CREATOR =
+            new Creator<WifiAwareNetworkInfo>() {
+                @Override
+                public WifiAwareNetworkInfo createFromParcel(Parcel in) {
+                    Inet6Address ipv6Addr;
+                    try {
+                        byte[] addr = in.createByteArray();
+                        String interfaceName = in.readString();
+                        NetworkInterface ni = null;
+                        if (interfaceName != null) {
+                            try {
+                                ni = NetworkInterface.getByName(interfaceName);
+                            } catch (SocketException e) {
+                                e.printStackTrace();
+                            }
+                        }
+                        ipv6Addr = Inet6Address.getByAddress(null, addr, ni);
+                    } catch (UnknownHostException e) {
+                        e.printStackTrace();
+                        return null;
+                    }
+
+                    return new WifiAwareNetworkInfo(ipv6Addr);
+                }
+
+                @Override
+                public WifiAwareNetworkInfo[] newArray(int size) {
+                    return new WifiAwareNetworkInfo[size];
+                }
+            };
+
+
+    // object methods
+
+    @Override
+    public String toString() {
+        return new StringBuilder("AwareNetworkInfo: IPv6=").append(mIpv6Addr).toString();
+    }
+
+    /** @hide */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (!(obj instanceof WifiAwareNetworkInfo)) {
+            return false;
+        }
+
+        WifiAwareNetworkInfo lhs = (WifiAwareNetworkInfo) obj;
+        return Objects.equals(mIpv6Addr, lhs.mIpv6Addr);
+    }
+
+    /** @hide */
+    @Override
+    public int hashCode() {
+        return Objects.hash(mIpv6Addr);
+    }
+}
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareSession.java b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
index 3219653..5f8841c 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareSession.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
@@ -213,7 +213,7 @@
      *     This API is targeted for applications which can obtain the peer MAC address using OOB
      *     (out-of-band) discovery. Aware discovery does not provide the MAC address of the peer -
      *     when using Aware discovery use the alternative network specifier method -
-     *     {@link DiscoverySession#createNetworkSpecifierOpen(PeerHandle)}.
+     *     {@link android.net.wifi.aware.WifiAwareManager.NetworkSpecifierBuilder}.
      * <p>
      * To set up an encrypted link use the
      * {@link #createNetworkSpecifierPassphrase(int, byte[], String)} API.
@@ -254,7 +254,7 @@
      *     This API is targeted for applications which can obtain the peer MAC address using OOB
      *     (out-of-band) discovery. Aware discovery does not provide the MAC address of the peer -
      *     when using Aware discovery use the alternative network specifier method -
-     *     {@link DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String)}.
+     *     {@link android.net.wifi.aware.WifiAwareManager.NetworkSpecifierBuilder}.
      *
      * @param role  The role of this device:
      *              {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or
@@ -300,7 +300,7 @@
      *     This API is targeted for applications which can obtain the peer MAC address using OOB
      *     (out-of-band) discovery. Aware discovery does not provide the MAC address of the peer -
      *     when using Aware discovery use the alternative network specifier method -
-     *     {@link DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String)}.
+     *     {@link android.net.wifi.aware.WifiAwareManager.NetworkSpecifierBuilder}.
      *
      * @param role  The role of this device:
      *              {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or
diff --git a/wifi/java/android/net/wifi/hotspot2/OsuProvider.java b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
index 893b19c..6d82ca1 100644
--- a/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
+++ b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
@@ -19,13 +19,16 @@
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.net.wifi.WifiSsid;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
 
 import java.util.ArrayList;
-import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Locale;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -52,9 +55,9 @@
     private WifiSsid mOsuSsid;
 
     /**
-     * Friendly name of the OSU provider.
+     * Map of friendly names expressed as different language for the OSU provider.
      */
-    private final String mFriendlyName;
+    private final Map<String, String> mFriendlyNames;
 
     /**
      * Description of the OSU provider.
@@ -81,10 +84,11 @@
      */
     private final Icon mIcon;
 
-    public OsuProvider(WifiSsid osuSsid, String friendlyName, String serviceDescription,
-            Uri serverUri, String nai, List<Integer> methodList, Icon icon) {
+    public OsuProvider(WifiSsid osuSsid, Map<String, String> friendlyNames,
+            String serviceDescription, Uri serverUri, String nai, List<Integer> methodList,
+            Icon icon) {
         mOsuSsid = osuSsid;
-        mFriendlyName = friendlyName;
+        mFriendlyNames = friendlyNames;
         mServiceDescription = serviceDescription;
         mServerUri = serverUri;
         mNetworkAccessIdentifier = nai;
@@ -104,7 +108,7 @@
     public OsuProvider(OsuProvider source) {
         if (source == null) {
             mOsuSsid = null;
-            mFriendlyName = null;
+            mFriendlyNames = null;
             mServiceDescription = null;
             mServerUri = null;
             mNetworkAccessIdentifier = null;
@@ -114,7 +118,7 @@
         }
 
         mOsuSsid = source.mOsuSsid;
-        mFriendlyName = source.mFriendlyName;
+        mFriendlyNames = source.mFriendlyNames;
         mServiceDescription = source.mServiceDescription;
         mServerUri = source.mServerUri;
         mNetworkAccessIdentifier = source.mNetworkAccessIdentifier;
@@ -134,8 +138,32 @@
         mOsuSsid = osuSsid;
     }
 
+    /**
+     * Return the friendly Name for current language from the list of friendly names of OSU
+     * provider.
+     *
+     * The string matching the default locale will be returned if it is found, otherwise the string
+     * in english or the first string in the list will be returned if english is not found.
+     * A null will be returned if the list is empty.
+     *
+     * @return String matching the default locale, null otherwise
+     */
     public String getFriendlyName() {
-        return mFriendlyName;
+        if (mFriendlyNames == null || mFriendlyNames.isEmpty()) return null;
+        String lang = Locale.getDefault().getLanguage();
+        String friendlyName = mFriendlyNames.get(lang);
+        if (friendlyName != null) {
+            return friendlyName;
+        }
+        friendlyName = mFriendlyNames.get("en");
+        if (friendlyName != null) {
+            return friendlyName;
+        }
+        return mFriendlyNames.get(mFriendlyNames.keySet().stream().findFirst().get());
+    }
+
+    public Map<String, String> getFriendlyNameList() {
+        return mFriendlyNames;
     }
 
     public String getServiceDescription() {
@@ -151,7 +179,7 @@
     }
 
     public List<Integer> getMethodList() {
-        return Collections.unmodifiableList(mMethodList);
+        return mMethodList;
     }
 
     public Icon getIcon() {
@@ -166,12 +194,14 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeParcelable(mOsuSsid, flags);
-        dest.writeString(mFriendlyName);
         dest.writeString(mServiceDescription);
         dest.writeParcelable(mServerUri, flags);
         dest.writeString(mNetworkAccessIdentifier);
         dest.writeList(mMethodList);
         dest.writeParcelable(mIcon, flags);
+        Bundle bundle = new Bundle();
+        bundle.putSerializable("friendlyNameMap", (HashMap<String, String>) mFriendlyNames);
+        dest.writeBundle(bundle);
     }
 
     @Override
@@ -184,7 +214,8 @@
         }
         OsuProvider that = (OsuProvider) thatObject;
         return (mOsuSsid == null ? that.mOsuSsid == null : mOsuSsid.equals(that.mOsuSsid))
-                && TextUtils.equals(mFriendlyName, that.mFriendlyName)
+                && (mFriendlyNames == null) ? that.mFriendlyNames == null
+                            : mFriendlyNames.equals(that.mFriendlyNames)
                 && TextUtils.equals(mServiceDescription, that.mServiceDescription)
                 && (mServerUri == null ? that.mServerUri == null
                             : mServerUri.equals(that.mServerUri))
@@ -196,14 +227,15 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mOsuSsid, mFriendlyName, mServiceDescription, mServerUri,
-                mNetworkAccessIdentifier, mMethodList, mIcon);
+        // mIcon is not hashable, skip the variable.
+        return Objects.hash(mOsuSsid, mServiceDescription, mFriendlyNames,
+                mServerUri, mNetworkAccessIdentifier, mMethodList);
     }
 
     @Override
     public String toString() {
         return "OsuProvider{mOsuSsid=" + mOsuSsid
-                + " mFriendlyName=" + mFriendlyName
+                + " mFriendlyNames=" + mFriendlyNames
                 + " mServiceDescription=" + mServiceDescription
                 + " mServerUri=" + mServerUri
                 + " mNetworkAccessIdentifier=" + mNetworkAccessIdentifier
@@ -212,20 +244,22 @@
     }
 
     public static final Creator<OsuProvider> CREATOR =
-        new Creator<OsuProvider>() {
-            @Override
-            public OsuProvider createFromParcel(Parcel in) {
-                WifiSsid osuSsid = (WifiSsid) in.readParcelable(null);
-                String friendlyName = in.readString();
-                String serviceDescription = in.readString();
-                Uri serverUri = (Uri) in.readParcelable(null);
-                String nai = in.readString();
-                List<Integer> methodList = new ArrayList<>();
-                in.readList(methodList, null);
-                Icon icon = (Icon) in.readParcelable(null);
-                return new OsuProvider(osuSsid, friendlyName, serviceDescription, serverUri,
-                        nai, methodList, icon);
-            }
+            new Creator<OsuProvider>() {
+                @Override
+                public OsuProvider createFromParcel(Parcel in) {
+                    WifiSsid osuSsid = in.readParcelable(null);
+                    String serviceDescription = in.readString();
+                    Uri serverUri = in.readParcelable(null);
+                    String nai = in.readString();
+                    List<Integer> methodList = new ArrayList<>();
+                    in.readList(methodList, null);
+                    Icon icon = in.readParcelable(null);
+                    Bundle bundle = in.readBundle();
+                    Map<String, String> friendlyNamesMap = (HashMap) bundle.getSerializable(
+                            "friendlyNameMap");
+                    return new OsuProvider(osuSsid, friendlyNamesMap, serviceDescription,
+                            serverUri, nai, methodList, icon);
+                }
 
             @Override
             public OsuProvider[] newArray(int size) {
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index 26bdb18..f09d864 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -20,6 +20,7 @@
 import android.net.wifi.hotspot2.pps.HomeSp;
 import android.net.wifi.hotspot2.pps.Policy;
 import android.net.wifi.hotspot2.pps.UpdateParameter;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -30,6 +31,7 @@
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 
@@ -324,6 +326,50 @@
     }
 
     /**
+     * The map of OSU service provider names whose each element is presented in different
+     * languages for the service provider, which is used for finding a matching
+     * PasspointConfiguration with a given service provider name.
+     */
+    private Map<String, String> mServiceFriendlyNames = null;
+
+    /**
+     * @hide
+     */
+    public void setServiceFriendlyNames(Map<String, String> serviceFriendlyNames) {
+        mServiceFriendlyNames = serviceFriendlyNames;
+    }
+
+    /**
+     * @hide
+     */
+    public Map<String, String> getServiceFriendlyNames() {
+        return mServiceFriendlyNames;
+    }
+
+    /**
+     * Return the friendly Name for current language from the list of friendly names of OSU
+     * provider.
+     * The string matching the default locale will be returned if it is found, otherwise the
+     * first string in the list will be returned.  A null will be returned if the list is empty.
+     *
+     * @return String matching the default locale, null otherwise
+     * @hide
+     */
+    public String getServiceFriendlyName() {
+        if (mServiceFriendlyNames == null || mServiceFriendlyNames.isEmpty()) return null;
+        String lang = Locale.getDefault().getLanguage();
+        String friendlyName = mServiceFriendlyNames.get(lang);
+        if (friendlyName != null) {
+            return friendlyName;
+        }
+        friendlyName = mServiceFriendlyNames.get("en");
+        if (friendlyName != null) {
+            return friendlyName;
+        }
+        return mServiceFriendlyNames.get(mServiceFriendlyNames.keySet().stream().findFirst().get());
+    }
+
+    /**
      * Constructor for creating PasspointConfiguration with default values.
      */
     public PasspointConfiguration() {}
@@ -362,6 +408,7 @@
         mUsageLimitStartTimeInMillis = source.mUsageLimitStartTimeInMillis;
         mUsageLimitTimeLimitInMinutes = source.mUsageLimitTimeLimitInMinutes;
         mUsageLimitUsageTimePeriodInMinutes = source.mUsageLimitUsageTimePeriodInMinutes;
+        mServiceFriendlyNames = source.mServiceFriendlyNames;
     }
 
     @Override
@@ -385,6 +432,10 @@
         dest.writeLong(mUsageLimitStartTimeInMillis);
         dest.writeLong(mUsageLimitDataLimit);
         dest.writeLong(mUsageLimitTimeLimitInMinutes);
+        Bundle bundle = new Bundle();
+        bundle.putSerializable("serviceFriendlyNames",
+                (HashMap<String, String>) mServiceFriendlyNames);
+        dest.writeBundle(bundle);
     }
 
     @Override
@@ -398,10 +449,10 @@
         PasspointConfiguration that = (PasspointConfiguration) thatObject;
         return (mHomeSp == null ? that.mHomeSp == null : mHomeSp.equals(that.mHomeSp))
                 && (mCredential == null ? that.mCredential == null
-                        : mCredential.equals(that.mCredential))
+                : mCredential.equals(that.mCredential))
                 && (mPolicy == null ? that.mPolicy == null : mPolicy.equals(that.mPolicy))
                 && (mSubscriptionUpdate == null ? that.mSubscriptionUpdate == null
-                        : mSubscriptionUpdate.equals(that.mSubscriptionUpdate))
+                : mSubscriptionUpdate.equals(that.mSubscriptionUpdate))
                 && isTrustRootCertListEquals(mTrustRootCertList, that.mTrustRootCertList)
                 && mUpdateIdentifier == that.mUpdateIdentifier
                 && mCredentialPriority == that.mCredentialPriority
@@ -411,7 +462,9 @@
                 && mUsageLimitUsageTimePeriodInMinutes == that.mUsageLimitUsageTimePeriodInMinutes
                 && mUsageLimitStartTimeInMillis == that.mUsageLimitStartTimeInMillis
                 && mUsageLimitDataLimit == that.mUsageLimitDataLimit
-                && mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes;
+                && mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes
+                && (mServiceFriendlyNames == null ? that.mServiceFriendlyNames == null
+                : mServiceFriendlyNames.equals(that.mServiceFriendlyNames));
     }
 
     @Override
@@ -419,7 +472,8 @@
         return Objects.hash(mHomeSp, mCredential, mPolicy, mSubscriptionUpdate, mTrustRootCertList,
                 mUpdateIdentifier, mCredentialPriority, mSubscriptionCreationTimeInMillis,
                 mSubscriptionExpirationTimeInMillis, mUsageLimitUsageTimePeriodInMinutes,
-                mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes);
+                mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes,
+                mServiceFriendlyNames);
     }
 
     @Override
@@ -463,6 +517,9 @@
             builder.append("TrustRootCertServers: ").append(mTrustRootCertList.keySet())
                     .append("\n");
         }
+        if (mServiceFriendlyNames != null) {
+            builder.append("ServiceFriendlyNames: ").append(mServiceFriendlyNames);
+        }
         return builder.toString();
     }
 
@@ -562,6 +619,10 @@
                 config.setUsageLimitStartTimeInMillis(in.readLong());
                 config.setUsageLimitDataLimit(in.readLong());
                 config.setUsageLimitTimeLimitInMinutes(in.readLong());
+                Bundle bundle = in.readBundle();
+                Map<String, String> friendlyNamesMap = (HashMap) bundle.getSerializable(
+                        "serviceFriendlyNames");
+                config.setServiceFriendlyNames(friendlyNamesMap);
                 return config;
             }
 
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
index 01feb1e..72e57a1 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
@@ -17,15 +17,15 @@
 package android.net.wifi.p2p;
 
 import android.annotation.UnsupportedAppUsage;
-import android.os.Parcelable;
 import android.os.Parcel;
+import android.os.Parcelable;
 
 import java.util.ArrayList;
-import java.util.List;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.regex.Pattern;
+import java.util.List;
 import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * A class representing a Wi-Fi P2p group. A p2p group consists of a single group
@@ -67,6 +67,9 @@
     /** The network id in the wpa_supplicant */
     private int mNetId;
 
+    /** The frequency used by this group */
+    private int mFrequency;
+
     /** P2P group started string pattern */
     private static final Pattern groupStartedPattern = Pattern.compile(
         "ssid=\"(.+)\" " +
@@ -116,8 +119,9 @@
             }
 
             mNetworkName = match.group(1);
-            //freq and psk are unused right now
-            //int freq = Integer.parseInt(match.group(2));
+            // It throws NumberFormatException if the string cannot be parsed as an integer.
+            mFrequency = Integer.parseInt(match.group(2));
+            // psk is unused right now
             //String psk = match.group(3);
             mPassphrase = match.group(4);
             mOwner = new WifiP2pDevice(match.group(5));
@@ -269,6 +273,16 @@
         this.mNetId = netId;
     }
 
+    /** Get the operating frequency of the p2p group */
+    public int getFrequency() {
+        return mFrequency;
+    }
+
+    /** @hide */
+    public void setFrequency(int freq) {
+        this.mFrequency = freq;
+    }
+
     public String toString() {
         StringBuffer sbuf = new StringBuffer();
         sbuf.append("network: ").append(mNetworkName);
@@ -279,6 +293,7 @@
         }
         sbuf.append("\n interface: ").append(mInterface);
         sbuf.append("\n networkId: ").append(mNetId);
+        sbuf.append("\n frequency: ").append(mFrequency);
         return sbuf.toString();
     }
 
@@ -297,6 +312,7 @@
             mPassphrase = source.getPassphrase();
             mInterface = source.getInterface();
             mNetId = source.getNetworkId();
+            mFrequency = source.getFrequency();
         }
     }
 
@@ -312,6 +328,7 @@
         dest.writeString(mPassphrase);
         dest.writeString(mInterface);
         dest.writeInt(mNetId);
+        dest.writeInt(mFrequency);
     }
 
     /** Implement the Parcelable interface */
@@ -329,6 +346,7 @@
                 group.setPassphrase(in.readString());
                 group.setInterface(in.readString());
                 group.setNetworkId(in.readInt());
+                group.setFrequency(in.readInt());
                 return group;
             }
 
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index e0442f2..1bed914 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -311,12 +311,19 @@
             "android.net.wifi.p2p.EXTRA_HANDOVER_MESSAGE";
 
     /**
-     * The lookup key for a calling package returned by the WifiP2pService.
+     * The lookup key for a calling package name from WifiP2pManager
      * @hide
      */
     public static final String CALLING_PACKAGE =
             "android.net.wifi.p2p.CALLING_PACKAGE";
 
+    /**
+     * The lookup key for a calling package binder from WifiP2pManager
+     * @hide
+     */
+    public static final String CALLING_BINDER =
+            "android.net.wifi.p2p.CALLING_BINDER";
+
     IWifiP2pManager mService;
 
     private static final int BASE = Protocol.BASE_WIFI_P2P_MANAGER;
@@ -536,6 +543,9 @@
     /** @hide */
     public static final int RESPONSE_NETWORK_INFO                   = BASE + 95;
 
+    /** @hide */
+    public static final int UPDATE_CHANNEL_INFO                     = BASE + 96;
+
     /**
      * Create a new WifiP2pManager instance. Applications use
      * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
@@ -1098,6 +1108,11 @@
         Channel c = new Channel(srcContext, srcLooper, listener, binder, this);
         if (c.mAsyncChannel.connectSync(srcContext, c.mHandler, messenger)
                 == AsyncChannel.STATUS_SUCCESSFUL) {
+            Bundle bundle = new Bundle();
+            bundle.putString(CALLING_PACKAGE, c.mContext.getOpPackageName());
+            bundle.putBinder(CALLING_BINDER, binder);
+            c.mAsyncChannel.sendMessage(UPDATE_CHANNEL_INFO, 0,
+                    c.putListener(null), bundle);
             return c;
         } else {
             c.close();
@@ -1124,6 +1139,7 @@
      * @param c is the channel created at {@link #initialize}
      * @param listener for callbacks on success or failure. Can be null.
      */
+    @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
     public void discoverPeers(Channel c, ActionListener listener) {
         checkChannel(c);
         c.mAsyncChannel.sendMessage(DISCOVER_PEERS, 0, c.putListener(listener));
@@ -1167,6 +1183,7 @@
      * @param config options as described in {@link WifiP2pConfig} class
      * @param listener for callbacks on success or failure. Can be null.
      */
+    @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
     public void connect(Channel c, WifiP2pConfig config, ActionListener listener) {
         checkChannel(c);
         checkP2pConfig(config);
@@ -1208,6 +1225,7 @@
      * @param c is the channel created at {@link #initialize}
      * @param listener for callbacks on success or failure. Can be null.
      */
+    @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
     public void createGroup(Channel c, ActionListener listener) {
         checkChannel(c);
         c.mAsyncChannel.sendMessage(CREATE_GROUP, WifiP2pGroup.PERSISTENT_NET_ID,
@@ -1238,6 +1256,7 @@
      * @param config the configuration of a p2p group.
      * @param listener for callbacks on success or failure. Can be null.
      */
+    @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
     public void createGroup(@NonNull Channel c,
             @Nullable WifiP2pConfig config,
             @Nullable ActionListener listener) {
@@ -1262,7 +1281,16 @@
         c.mAsyncChannel.sendMessage(REMOVE_GROUP, 0, c.putListener(listener));
     }
 
-    /** @hide */
+    /**
+     * Force p2p to enter or exit listen state
+     *
+     * @param c is the channel created at {@link #initialize(Context, Looper, ChannelListener)}
+     * @param enable enables or disables listening
+     * @param listener for callbacks on success or failure. Can be null.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public void listen(Channel c, boolean enable, ActionListener listener) {
         checkChannel(c);
         c.mAsyncChannel.sendMessage(enable ? START_LISTEN : STOP_LISTEN,
@@ -1316,6 +1344,7 @@
      * @param servInfo is a local service information.
      * @param listener for callbacks on success or failure. Can be null.
      */
+    @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
     public void addLocalService(Channel c, WifiP2pServiceInfo servInfo, ActionListener listener) {
         checkChannel(c);
         checkServiceInfo(servInfo);
@@ -1425,6 +1454,7 @@
      * @param c is the channel created at {@link #initialize}
      * @param listener for callbacks on success or failure. Can be null.
      */
+    @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
     public void discoverServices(Channel c, ActionListener listener) {
         checkChannel(c);
         c.mAsyncChannel.sendMessage(DISCOVER_SERVICES, 0, c.putListener(listener));
@@ -1500,12 +1530,10 @@
      * @param c is the channel created at {@link #initialize}
      * @param listener for callback when peer list is available. Can be null.
      */
+    @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
     public void requestPeers(Channel c, PeerListListener listener) {
         checkChannel(c);
-        Bundle callingPackage = new Bundle();
-        callingPackage.putString(CALLING_PACKAGE, c.mContext.getOpPackageName());
-        c.mAsyncChannel.sendMessage(REQUEST_PEERS, 0, c.putListener(listener),
-                callingPackage);
+        c.mAsyncChannel.sendMessage(REQUEST_PEERS, 0, c.putListener(listener));
     }
 
     /**
@@ -1525,6 +1553,7 @@
      * @param c is the channel created at {@link #initialize}
      * @param listener for callback when group info is available. Can be null.
      */
+    @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
     public void requestGroupInfo(Channel c, GroupInfoListener listener) {
         checkChannel(c);
         c.mAsyncChannel.sendMessage(REQUEST_GROUP_INFO, 0, c.putListener(listener));
@@ -1697,10 +1726,7 @@
     @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public void factoryReset(@NonNull Channel c, @Nullable ActionListener listener) {
         checkChannel(c);
-        Bundle callingPackage = new Bundle();
-        callingPackage.putString(CALLING_PACKAGE, c.mContext.getOpPackageName());
-        c.mAsyncChannel.sendMessage(FACTORY_RESET, 0, c.putListener(listener),
-                callingPackage);
+        c.mAsyncChannel.sendMessage(FACTORY_RESET, 0, c.putListener(listener));
     }
 
     /**
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java b/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
index ef1bff4..f9ef08f 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
@@ -17,6 +17,7 @@
 package android.net.wifi.p2p;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcelable;
 import android.os.Parcel;
 
@@ -54,7 +55,7 @@
 
     private int mMaxThroughput;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public WifiP2pWfdInfo() {
     }
 
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
index c9e9867..e32c8e8 100644
--- a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
@@ -18,6 +18,7 @@
 
 import android.annotation.UnsupportedAppUsage;
 import android.net.nsd.DnsSdTxtRecord;
+import android.os.Build;
 import android.text.TextUtils;
 
 import java.util.ArrayList;
@@ -174,7 +175,7 @@
      * @param version version number
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     static String createRequest(String dnsName, int dnsType, int version) {
         StringBuffer sb = new StringBuffer();
 
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.java
index 3563198..e9ee7bb 100644
--- a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.java
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.java
@@ -17,6 +17,7 @@
 package android.net.wifi.p2p.nsd;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -84,7 +85,7 @@
      * {"upnp", "10", "uuid:6859dede-8574-59ab-9322-123456789012::urn:schemas-upnp
      * -org:service:ContentDirectory:2"}
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private List<String> mQueryList;
 
     /**
@@ -93,7 +94,7 @@
      * @param queryList query string for wpa_supplicant
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     protected WifiP2pServiceInfo(List<String> queryList) {
         if (queryList == null) {
             throw new IllegalArgumentException("query list cannot be null");
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java
index 2e7f448..f1f2262 100644
--- a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java
@@ -18,6 +18,7 @@
 
 import android.annotation.UnsupportedAppUsage;
 import android.net.wifi.p2p.WifiP2pManager;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -73,7 +74,7 @@
      * @param query The part of service specific query.
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     protected WifiP2pServiceRequest(int protocolType, String query) {
         validateQuery(query);
 
diff --git a/wifi/java/com/android/server/wifi/AbstractWifiService.java b/wifi/java/com/android/server/wifi/AbstractWifiService.java
index 36f66aa..3d328a1 100644
--- a/wifi/java/com/android/server/wifi/AbstractWifiService.java
+++ b/wifi/java/com/android/server/wifi/AbstractWifiService.java
@@ -21,6 +21,7 @@
 import android.content.pm.ParceledListSlice;
 import android.net.DhcpInfo;
 import android.net.Network;
+import android.net.wifi.IDppCallback;
 import android.net.wifi.INetworkRequestMatchCallback;
 import android.net.wifi.ISoftApCallback;
 import android.net.wifi.ITrafficStateCallback;
@@ -35,10 +36,12 @@
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.os.IBinder;
 import android.os.Messenger;
+import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.WorkSource;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * Abstract class implementing IWifiManager with stub methods throwing runtime exceptions.
@@ -105,7 +108,8 @@
     }
 
     @Override
-    public List<WifiConfiguration> getAllMatchingWifiConfigs(List<ScanResult> scanResults) {
+    public Map<String, List<ScanResult>> getAllMatchingFqdnsForScanResults(
+            List<ScanResult> scanResults) {
         throw new UnsupportedOperationException();
     }
 
@@ -122,7 +126,14 @@
     }
 
     @Override
-    public List<OsuProvider> getMatchingOsuProviders(List<ScanResult> scanResults) {
+    public Map<OsuProvider, List<ScanResult>> getMatchingOsuProviders(
+            List<ScanResult> scanResults) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Map<OsuProvider, PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(
+            List<OsuProvider> osuProviders) {
         throw new UnsupportedOperationException();
     }
 
@@ -148,6 +159,11 @@
     }
 
     @Override
+    public List<WifiConfiguration> getWifiConfigsForPasspointProfiles(List<String> fqdnList) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public void queryPasspointIcon(long bssid, String fileName) {
         throw new UnsupportedOperationException();
     }
@@ -457,4 +473,26 @@
     public String[] getFactoryMacAddresses() {
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public void setDeviceMobilityState(int state) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void startDppAsConfiguratorInitiator(IBinder binder, String enrolleeUri,
+            int selectedNetworkId, int netRole, IDppCallback callback) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void startDppAsEnrolleeInitiator(IBinder binder, String configuratorUri,
+            IDppCallback callback) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void stopDppSession() throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/wifi/tests/Android.mk b/wifi/tests/Android.mk
index 73341ac..401b652 100644
--- a/wifi/tests/Android.mk
+++ b/wifi/tests/Android.mk
@@ -45,7 +45,7 @@
 LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := $(jacoco_exclude)
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
+    androidx.test.rules \
     core-test-rules \
     guava \
     mockito-target-minus-junit4 \
diff --git a/wifi/tests/AndroidManifest.xml b/wifi/tests/AndroidManifest.xml
index 4eaca2b..b6c38bc 100644
--- a/wifi/tests/AndroidManifest.xml
+++ b/wifi/tests/AndroidManifest.xml
@@ -30,7 +30,7 @@
         </activity>
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="android.net.wifi.test"
         android:label="Frameworks Wifi API Tests">
     </instrumentation>
diff --git a/wifi/tests/AndroidTest.xml b/wifi/tests/AndroidTest.xml
index 45c7a17..cae19e4 100644
--- a/wifi/tests/AndroidTest.xml
+++ b/wifi/tests/AndroidTest.xml
@@ -22,7 +22,7 @@
     <option name="test-tag" value="FrameworksWifiApiTests" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.net.wifi.test" />
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
         <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/wifi/tests/README.md b/wifi/tests/README.md
index b418abd..b0594f2 100644
--- a/wifi/tests/README.md
+++ b/wifi/tests/README.md
@@ -37,7 +37,7 @@
 If you manually build and push the test APK to the device you can run tests using
 
 ```
-adb shell am instrument -w 'android.net.wifi.test/android.support.test.runner.AndroidJUnitRunner'
+adb shell am instrument -w 'android.net.wifi.test/androidx.test.runner.AndroidJUnitRunner'
 ```
 
 ## Adding Tests
diff --git a/wifi/tests/runtests.sh b/wifi/tests/runtests.sh
index 4e52b8f..2caf8a5 100755
--- a/wifi/tests/runtests.sh
+++ b/wifi/tests/runtests.sh
@@ -22,4 +22,4 @@
 adb install -r -g "$OUT/data/app/FrameworksWifiApiTests/FrameworksWifiApiTests.apk"
 
 adb shell am instrument --no-hidden-api-checks -w "$@" \
-  'android.net.wifi.test/android.support.test.runner.AndroidJUnitRunner'
+  'android.net.wifi.test/androidx.test.runner.AndroidJUnitRunner'
diff --git a/wifi/tests/src/android/net/wifi/ParcelUtilTest.java b/wifi/tests/src/android/net/wifi/ParcelUtilTest.java
index 0c3bf3b..917b1df 100644
--- a/wifi/tests/src/android/net/wifi/ParcelUtilTest.java
+++ b/wifi/tests/src/android/net/wifi/ParcelUtilTest.java
@@ -22,7 +22,8 @@
 import static org.junit.Assert.assertNull;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/ScanResultTest.java b/wifi/tests/src/android/net/wifi/ScanResultTest.java
index 458c43d..54ec325 100644
--- a/wifi/tests/src/android/net/wifi/ScanResultTest.java
+++ b/wifi/tests/src/android/net/wifi/ScanResultTest.java
@@ -22,15 +22,14 @@
 import static org.mockito.Mockito.validateMockitoUsage;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.net.wifi.WifiScanner.ScanSettings;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.MockitoAnnotations;
 
-
 /**
  * Unit tests for {@link android.net.wifi.WifiScanner}.
  */
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 8d97307..c744f18 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -26,7 +26,8 @@
 import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
index e569efe..6ec64ff 100644
--- a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
@@ -19,8 +19,8 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
@@ -28,7 +28,8 @@
 import android.net.wifi.WifiEnterpriseConfig.Phase2;
 import android.os.Parcel;
 import android.security.Credentials;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -36,7 +37,6 @@
 import java.security.PrivateKey;
 import java.security.cert.X509Certificate;
 
-
 /**
  * Unit tests for {@link android.net.wifi.WifiEnterpriseConfig}.
  */
diff --git a/wifi/tests/src/android/net/wifi/WifiInfoTest.java b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
index f9fb062..677bf37 100644
--- a/wifi/tests/src/android/net/wifi/WifiInfoTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
@@ -21,7 +21,8 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 
@@ -46,6 +47,7 @@
         writeWifiInfo.txBad = TEST_TX_BAD;
         writeWifiInfo.rxSuccess = TEST_RX_SUCCESS;
         writeWifiInfo.setTrusted(true);
+        writeWifiInfo.setOsuAp(true);
 
         Parcel parcel = Parcel.obtain();
         writeWifiInfo.writeToParcel(parcel, 0);
@@ -59,5 +61,6 @@
         assertEquals(TEST_TX_BAD, readWifiInfo.txBad);
         assertEquals(TEST_RX_SUCCESS, readWifiInfo.rxSuccess);
         assertTrue(readWifiInfo.isTrusted());
+        assertTrue(readWifiInfo.isOsuAp());
     }
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 1001b10..4fbef5a 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -66,7 +66,8 @@
 import android.os.Message;
 import android.os.Messenger;
 import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -76,7 +77,9 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Unit tests for {@link android.net.wifi.WifiManager}.
@@ -1273,14 +1276,20 @@
     }
 
     /**
-     * Check the call to getAllMatchingWifiConfigs calls getAllMatchingWifiConfigs of WifiService
-     * with the provided a list of ScanResult.
+     * Check the call to getAllMatchingWifiConfigs calls getAllMatchingFqdnsForScanResults and
+     * getWifiConfigsForPasspointProfiles of WifiService in order.
      */
     @Test
     public void testGetAllMatchingWifiConfigs() throws Exception {
+        Map<String, List<ScanResult>> fqdns = new HashMap<>();
+        fqdns.put("www.test.com", new ArrayList<>());
+        when(mWifiService.getAllMatchingFqdnsForScanResults(any(List.class))).thenReturn(fqdns);
+        InOrder inOrder = inOrder(mWifiService);
+
         mWifiManager.getAllMatchingWifiConfigs(new ArrayList<>());
 
-        verify(mWifiService).getAllMatchingWifiConfigs(any(List.class));
+        inOrder.verify(mWifiService).getAllMatchingFqdnsForScanResults(any(List.class));
+        inOrder.verify(mWifiService).getWifiConfigsForPasspointProfiles(any(List.class));
     }
 
     /**
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
index f8ab8a2..2258e4d 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
@@ -25,9 +25,10 @@
 import android.net.NetworkRequest;
 import android.os.Parcel;
 import android.os.PatternMatcher;
-import android.support.test.filters.SmallTest;
 import android.util.Pair;
 
+import androidx.test.filters.SmallTest;
+
 import org.junit.Test;
 
 /**
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
index 2505499..83627ad 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
@@ -29,7 +29,8 @@
 import android.net.NetworkSpecifier;
 import android.os.PatternMatcher;
 import android.os.Process;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
index 997282b..fdd11a3 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
@@ -29,8 +29,9 @@
 import android.net.wifi.WifiNetworkScoreCache.CacheListener;
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.google.common.collect.ImmutableList;
 
@@ -44,7 +45,6 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-
 /** Unit tests for {@link WifiNetworkScoreCache}. */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
index 856f0c7..2a8df8d 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
@@ -26,9 +26,10 @@
 import android.net.MatchAllNetworkSpecifier;
 import android.os.Parcel;
 import android.os.PatternMatcher;
-import android.support.test.filters.SmallTest;
 import android.util.Pair;
 
+import androidx.test.filters.SmallTest;
+
 import org.junit.Test;
 
 /**
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index 5cc8217..31f501f 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -19,7 +19,8 @@
 import static org.junit.Assert.*;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 
diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
index cf1ed8f..76bfff0 100644
--- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
@@ -32,7 +32,8 @@
 import android.os.Handler;
 import android.os.Parcel;
 import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import com.android.internal.util.test.BidirectionalAsyncChannelServer;
 
@@ -44,7 +45,6 @@
 
 import java.util.Arrays;
 
-
 /**
  * Unit tests for {@link android.net.wifi.WifiScanner}.
  */
diff --git a/wifi/tests/src/android/net/wifi/WifiSsidTest.java b/wifi/tests/src/android/net/wifi/WifiSsidTest.java
index b58f2c7..10a37c0 100644
--- a/wifi/tests/src/android/net/wifi/WifiSsidTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiSsidTest.java
@@ -19,7 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 
diff --git a/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java b/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java
index 6ecd931..83affed 100644
--- a/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java
@@ -18,7 +18,7 @@
 
 import static org.hamcrest.core.IsEqual.equalTo;
 
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
index 657e5a7..4189e40 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
@@ -21,7 +21,8 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 272f727..ed38c76 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -31,13 +31,15 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.net.MacAddress;
 import android.net.wifi.RttManager;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import libcore.util.HexEncoding;
 
@@ -50,6 +52,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.net.Inet6Address;
+import java.net.UnknownHostException;
 import java.util.List;
 
 /**
@@ -105,7 +109,7 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
-        mockApplicationInfo.targetSdkVersion = Build.VERSION_CODES.P;
+        mockApplicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
         when(mockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(
                 mockApplicationInfo);
         when(mockContext.getOpPackageName()).thenReturn("XXX");
@@ -915,6 +919,8 @@
         final ConfigRequest configRequest = new ConfigRequest.Builder().build();
         final PublishConfig publishConfig = new PublishConfig.Builder().build();
 
+        mockApplicationInfo.targetSdkVersion = Build.VERSION_CODES.P;
+
         ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
                 WifiAwareSession.class);
         ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
@@ -948,6 +954,9 @@
         WifiAwareNetworkSpecifier ns =
                 (WifiAwareNetworkSpecifier) publishSession.getValue().createNetworkSpecifierOpen(
                         peerHandle);
+        WifiAwareNetworkSpecifier nsb = (WifiAwareNetworkSpecifier) new WifiAwareManager
+                .NetworkSpecifierBuilder().setDiscoverySession(publishSession.getValue())
+                .setPeerHandle(peerHandle).build();
 
         // validate format
         collector.checkThat("role", role, equalTo(ns.role));
@@ -955,9 +964,18 @@
         collector.checkThat("session_id", sessionId, equalTo(ns.sessionId));
         collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId));
 
+        collector.checkThat("role", role, equalTo(nsb.role));
+        collector.checkThat("client_id", clientId, equalTo(nsb.clientId));
+        collector.checkThat("session_id", sessionId, equalTo(nsb.sessionId));
+        collector.checkThat("peer_id", peerHandle.peerId, equalTo(nsb.peerId));
+
         // (4) request an encrypted (PMK) network specifier from the session
         ns = (WifiAwareNetworkSpecifier) publishSession.getValue().createNetworkSpecifierPmk(
                 peerHandle, pmk);
+        nsb =
+                (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
+                        .setDiscoverySession(
+                        publishSession.getValue()).setPeerHandle(peerHandle).setPmk(pmk).build();
 
         // validate format
         collector.checkThat("role", role, equalTo(ns.role));
@@ -966,9 +984,18 @@
         collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId));
         collector.checkThat("pmk", pmk , equalTo(ns.pmk));
 
+        collector.checkThat("role", role, equalTo(nsb.role));
+        collector.checkThat("client_id", clientId, equalTo(nsb.clientId));
+        collector.checkThat("session_id", sessionId, equalTo(nsb.sessionId));
+        collector.checkThat("peer_id", peerHandle.peerId, equalTo(nsb.peerId));
+        collector.checkThat("pmk", pmk , equalTo(nsb.pmk));
+
         // (5) request an encrypted (Passphrase) network specifier from the session
         ns = (WifiAwareNetworkSpecifier) publishSession.getValue().createNetworkSpecifierPassphrase(
                 peerHandle, passphrase);
+        nsb = (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
+                .setDiscoverySession(publishSession.getValue()).setPeerHandle(peerHandle)
+                .setPskPassphrase(passphrase).build();
 
         // validate format
         collector.checkThat("role", role, equalTo(ns.role));
@@ -977,6 +1004,12 @@
         collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId));
         collector.checkThat("passphrase", passphrase, equalTo(ns.passphrase));
 
+        collector.checkThat("role", role, equalTo(nsb.role));
+        collector.checkThat("client_id", clientId, equalTo(nsb.clientId));
+        collector.checkThat("session_id", sessionId, equalTo(nsb.sessionId));
+        collector.checkThat("peer_id", peerHandle.peerId, equalTo(nsb.peerId));
+        collector.checkThat("passphrase", passphrase, equalTo(nsb.passphrase));
+
         verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
                 mockPublishSession, mockRttListener);
     }
@@ -1048,7 +1081,7 @@
      */
     @Test(expected = IllegalArgumentException.class)
     public void testNetworkSpecifierWithClientNullPmk() throws Exception {
-        executeNetworkSpecifierWithClient(new PeerHandle(123412), true, null, null);
+        executeNetworkSpecifierWithClient(new PeerHandle(123412), true, null, null, false);
     }
 
     /**
@@ -1056,7 +1089,7 @@
      */
     @Test(expected = IllegalArgumentException.class)
     public void testNetworkSpecifierWithClientIncorrectLengthPmk() throws Exception {
-        executeNetworkSpecifierWithClient(new PeerHandle(123412), true, PMK_INVALID, null);
+        executeNetworkSpecifierWithClient(new PeerHandle(123412), true, PMK_INVALID, null, false);
     }
 
     /**
@@ -1064,7 +1097,7 @@
      */
     @Test(expected = IllegalArgumentException.class)
     public void testNetworkSpecifierWithClientNullPassphrase() throws Exception {
-        executeNetworkSpecifierWithClient(new PeerHandle(123412), false, null, null);
+        executeNetworkSpecifierWithClient(new PeerHandle(123412), false, null, null, false);
     }
 
     /**
@@ -1073,7 +1106,7 @@
     @Test(expected = IllegalArgumentException.class)
     public void testNetworkSpecifierWithClientTooShortPassphrase() throws Exception {
         executeNetworkSpecifierWithClient(new PeerHandle(123412), false, null,
-                PASSPHRASE_TOO_SHORT);
+                PASSPHRASE_TOO_SHORT, false);
     }
 
     /**
@@ -1081,7 +1114,8 @@
      */
     @Test(expected = IllegalArgumentException.class)
     public void testNetworkSpecifierWithClientTooLongPassphrase() throws Exception {
-        executeNetworkSpecifierWithClient(new PeerHandle(123412), false, null, PASSPHRASE_TOO_LONG);
+        executeNetworkSpecifierWithClient(new PeerHandle(123412), false, null, PASSPHRASE_TOO_LONG,
+                false);
     }
 
     /**
@@ -1089,7 +1123,8 @@
      */
     @Test(expected = IllegalArgumentException.class)
     public void testNetworkSpecifierWithClientNullPeer() throws Exception {
-        executeNetworkSpecifierWithClient(null, false, null, PASSPHRASE_VALID);
+        mockApplicationInfo.targetSdkVersion = Build.VERSION_CODES.P;
+        executeNetworkSpecifierWithClient(null, false, null, PASSPHRASE_VALID, false);
     }
 
     /**
@@ -1098,11 +1133,75 @@
     @Test
     public void testNetworkSpecifierWithClientNullPeerLegacyApi() throws Exception {
         mockApplicationInfo.targetSdkVersion = Build.VERSION_CODES.O;
-        executeNetworkSpecifierWithClient(null, false, null, PASSPHRASE_VALID);
+        executeNetworkSpecifierWithClient(null, false, null, PASSPHRASE_VALID, false);
+    }
+
+    /**
+     * Validate that a null PMK triggers an exception.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testNetworkSpecifierWithClientNullPmkBuilder() throws Exception {
+        executeNetworkSpecifierWithClient(new PeerHandle(123412), true, null, null, true);
+    }
+
+    /**
+     * Validate that a non-32-bytes PMK triggers an exception.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testNetworkSpecifierWithClientIncorrectLengthPmkBuilder() throws Exception {
+        executeNetworkSpecifierWithClient(new PeerHandle(123412), true, PMK_INVALID, null, true);
+    }
+
+    /**
+     * Validate that a null Passphrase triggers an exception.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testNetworkSpecifierWithClientNullPassphraseBuilder() throws Exception {
+        executeNetworkSpecifierWithClient(new PeerHandle(123412), false, null, null, true);
+    }
+
+    /**
+     * Validate that a too short Passphrase triggers an exception.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testNetworkSpecifierWithClientTooShortPassphraseBuilder() throws Exception {
+        executeNetworkSpecifierWithClient(new PeerHandle(123412), false, null,
+                PASSPHRASE_TOO_SHORT, true);
+    }
+
+    /**
+     * Validate that a too long Passphrase triggers an exception.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testNetworkSpecifierWithClientTooLongPassphraseBuilder() throws Exception {
+        executeNetworkSpecifierWithClient(new PeerHandle(123412), false, null, PASSPHRASE_TOO_LONG,
+                true);
+    }
+
+    /**
+     * Validate that a null PeerHandle triggers an exception.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testNetworkSpecifierWithClientNullPeerBuilder() throws Exception {
+        executeNetworkSpecifierWithClient(null, false, null, PASSPHRASE_VALID, true);
+    }
+
+    /**
+     * Validate that a null PeerHandle does not trigger an exception for legacy API.
+     */
+    @Test
+    public void testNetworkSpecifierWithClientNullPeerLegacyApiBuilder() throws Exception {
+        mockApplicationInfo.targetSdkVersion = Build.VERSION_CODES.O;
+        executeNetworkSpecifierWithClient(null, false, null, PASSPHRASE_VALID, false);
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testNetworkSpecifierDeprecatedOnNewApi() throws Exception {
+        executeNetworkSpecifierWithClient(null, false, null, PASSPHRASE_VALID, false);
     }
 
     private void executeNetworkSpecifierWithClient(PeerHandle peerHandle, boolean doPmk, byte[] pmk,
-            String passphrase) throws Exception {
+            String passphrase, boolean useBuilder) throws Exception {
         final int clientId = 4565;
         final int sessionId = 123;
         final ConfigRequest configRequest = new ConfigRequest.Builder().build();
@@ -1139,9 +1238,20 @@
 
         // (3) create network specifier
         if (doPmk) {
-            publishSession.getValue().createNetworkSpecifierPmk(peerHandle, pmk);
+            if (useBuilder) {
+                new WifiAwareManager.NetworkSpecifierBuilder().setDiscoverySession(
+                        publishSession.getValue()).setPeerHandle(peerHandle).setPmk(pmk).build();
+            } else {
+                publishSession.getValue().createNetworkSpecifierPmk(peerHandle, pmk);
+            }
         } else {
-            publishSession.getValue().createNetworkSpecifierPassphrase(peerHandle, passphrase);
+            if (useBuilder) {
+                new WifiAwareManager.NetworkSpecifierBuilder().setDiscoverySession(
+                        publishSession.getValue()).setPeerHandle(peerHandle).setPskPassphrase(
+                        passphrase).build();
+            } else {
+                publishSession.getValue().createNetworkSpecifierPassphrase(peerHandle, passphrase);
+            }
         }
     }
 
@@ -1245,4 +1355,51 @@
             sessionCaptor.getValue().createNetworkSpecifierPassphrase(role, someMac, passphrase);
         }
     }
+
+    // WifiAwareNetworkInfo tests
+
+    @Test
+    public void testWifiAwareNetworkCapabilitiesParcel() throws UnknownHostException {
+        final Inet6Address inet6 = MacAddress.fromString(
+                "11:22:33:44:55:66").getLinkLocalIpv6FromEui48Mac();
+        // note: dummy scope = 5
+        final Inet6Address inet6Scoped = Inet6Address.getByAddress(null, inet6.getAddress(), 5);
+
+        assertEquals(inet6Scoped.toString(), "/fe80::1322:33ff:fe44:5566%5");
+        WifiAwareNetworkInfo cap = new WifiAwareNetworkInfo(inet6Scoped);
+
+        Parcel parcelW = Parcel.obtain();
+        cap.writeToParcel(parcelW, 0);
+        byte[] bytes = parcelW.marshall();
+        parcelW.recycle();
+
+        Parcel parcelR = Parcel.obtain();
+        parcelR.unmarshall(bytes, 0, bytes.length);
+        parcelR.setDataPosition(0);
+        WifiAwareNetworkInfo rereadCap =
+                WifiAwareNetworkInfo.CREATOR.createFromParcel(parcelR);
+
+        assertEquals(cap.getPeerIpv6Addr().toString(), "/fe80::1322:33ff:fe44:5566%5");
+        assertEquals(cap.hashCode(), rereadCap.hashCode());
+    }
+
+    // PeerHandle tests
+
+    @Test
+    public void testPeerHandleParcel() {
+        final PeerHandle peerHandle = new PeerHandle(5);
+
+        Parcel parcelW = Parcel.obtain();
+        peerHandle.writeToParcel(parcelW, 0);
+        byte[] bytes = parcelW.marshall();
+        parcelW.recycle();
+
+        Parcel parcelR = Parcel.obtain();
+        parcelR.unmarshall(bytes, 0, bytes.length);
+        parcelR.setDataPosition(0);
+        PeerHandle rereadPeerHandle = PeerHandle.CREATOR.createFromParcel(parcelR);
+
+        assertEquals(peerHandle, rereadPeerHandle);
+        assertEquals(peerHandle.hashCode(), rereadPeerHandle.hashCode());
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java
index f32fe59..d9a1d9af 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java
@@ -22,7 +22,10 @@
 import android.net.wifi.FakeKeys;
 import android.net.wifi.hotspot2.pps.Credential;
 import android.net.wifi.hotspot2.pps.HomeSp;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
 
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -30,8 +33,6 @@
 import java.io.InputStreamReader;
 import java.util.Arrays;
 
-import org.junit.Test;
-
 /**
  * Unit tests for {@link android.net.wifi.hotspot2.ConfigParser}.
  */
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java b/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java
index d3f91f0..c7e009e 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java
@@ -23,14 +23,16 @@
 import android.net.Uri;
 import android.net.wifi.WifiSsid;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 
 import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Unit tests for {@link android.net.wifi.hotspot2.OsuProvider}.
@@ -40,6 +42,15 @@
     private static final WifiSsid TEST_SSID =
             WifiSsid.createFromByteArray("TEST SSID".getBytes(StandardCharsets.UTF_8));
     private static final String TEST_FRIENDLY_NAME = "Friendly Name";
+    private static final Map<String, String> TEST_FRIENDLY_NAMES =
+            new HashMap<String, String>() {
+                {
+                    put("en", TEST_FRIENDLY_NAME);
+                    put("kr", TEST_FRIENDLY_NAME + 2);
+                    put("jp", TEST_FRIENDLY_NAME + 3);
+                }
+            };
+
     private static final String TEST_SERVICE_DESCRIPTION = "Dummy Service";
     private static final Uri TEST_SERVER_URI = Uri.parse("https://test.com");
     private static final String TEST_NAI = "test.access.com";
@@ -59,7 +70,9 @@
 
         parcel.setDataPosition(0);    // Rewind data position back to the beginning for read.
         OsuProvider readInfo = OsuProvider.CREATOR.createFromParcel(parcel);
+
         assertEquals(writeInfo, readInfo);
+        assertEquals(writeInfo.hashCode(), readInfo.hashCode());
     }
 
     /**
@@ -79,8 +92,8 @@
      */
     @Test
     public void verifyParcelWithFullProviderInfo() throws Exception {
-        verifyParcel(new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAME, TEST_SERVICE_DESCRIPTION,
-                TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST, TEST_ICON));
+        verifyParcel(new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAMES,
+                TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST, TEST_ICON));
     }
 
     /**
@@ -100,8 +113,8 @@
      */
     @Test
     public void verifyCopyConstructorWithValidSource() throws Exception {
-        OsuProvider source = new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAME, TEST_SERVICE_DESCRIPTION,
-                TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST, TEST_ICON);
+        OsuProvider source = new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAMES,
+                TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST, TEST_ICON);
         assertEquals(source, new OsuProvider(source));
     }
 
@@ -112,10 +125,12 @@
      */
     @Test
     public void verifyGetters() throws Exception {
-        OsuProvider provider = new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAME,
+        OsuProvider provider = new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAMES,
                 TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST, TEST_ICON);
+
         assertTrue(TEST_SSID.equals(provider.getOsuSsid()));
         assertTrue(TEST_FRIENDLY_NAME.equals(provider.getFriendlyName()));
+        assertTrue(TEST_FRIENDLY_NAMES.equals(provider.getFriendlyNameList()));
         assertTrue(TEST_SERVICE_DESCRIPTION.equals(provider.getServiceDescription()));
         assertTrue(TEST_SERVER_URI.equals(provider.getServerUri()));
         assertTrue(TEST_NAI.equals(provider.getNetworkAccessIdentifier()));
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
index 775ce21..fc03e7e 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -25,9 +25,10 @@
 import android.net.wifi.hotspot2.pps.Policy;
 import android.net.wifi.hotspot2.pps.UpdateParameter;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
 import android.util.Base64;
 
+import androidx.test.filters.SmallTest;
+
 import org.junit.Test;
 
 import java.nio.charset.StandardCharsets;
@@ -166,6 +167,10 @@
         config.setUsageLimitStartTimeInMillis(124214213);
         config.setUsageLimitDataLimit(14121);
         config.setUsageLimitTimeLimitInMinutes(78912);
+        Map<String, String> friendlyNames = new HashMap<>();
+        friendlyNames.put("en", "ServiceName1");
+        friendlyNames.put("kr", "ServiceName2");
+        config.setServiceFriendlyNames(friendlyNames);
         return config;
     }
 
@@ -206,6 +211,18 @@
     }
 
     /**
+     * Verify parcel read/write for a configuration that doesn't contain a list of service names.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void verifyParcelWithoutServiceNames() throws Exception {
+        PasspointConfiguration config = createConfig();
+        config.setServiceFriendlyNames(null);
+        verifyParcel(config);
+    }
+
+    /**
      * Verify parcel read/write for a configuration that doesn't contain HomeSP.
      *
      * @throws Exception
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java
index 707b64f..66c595f 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java
@@ -19,14 +19,13 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import android.net.wifi.hotspot2.omadm.PpsMoParser;
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.net.wifi.hotspot2.pps.Credential;
 import android.net.wifi.hotspot2.pps.HomeSp;
 import android.net.wifi.hotspot2.pps.Policy;
 import android.net.wifi.hotspot2.pps.UpdateParameter;
-import android.support.test.filters.SmallTest;
-import android.text.TextUtils;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java
index ef478c7..85d0a90 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java
@@ -20,7 +20,8 @@
 
 import android.net.wifi.hotspot2.omadm.XMLNode;
 import android.net.wifi.hotspot2.omadm.XMLParser;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
index c07db6c..a9d4b8f 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
@@ -22,7 +22,8 @@
 import android.net.wifi.EAPConstants;
 import android.net.wifi.FakeKeys;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java
index c7993e3..93d471a 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java
@@ -20,7 +20,8 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java
index 171d6ff..980b199 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java
@@ -20,15 +20,15 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
 import android.util.Base64;
 
+import androidx.test.filters.SmallTest;
+
 import org.junit.Test;
 
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java
index 2a7526b..0b8cd3d 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java
@@ -20,17 +20,14 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
 import android.util.Base64;
 
+import androidx.test.filters.SmallTest;
+
 import org.junit.Test;
 
 import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
 
 /**
  * Unit tests for {@link android.net.wifi.hotspot2.pps.UpdateParameter}.
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
index 80f00a4..f61e6b7 100644
--- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
@@ -19,7 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java
index 2132b41..9e8dca4 100644
--- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java
@@ -21,7 +21,8 @@
 
 import android.content.Context;
 import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import libcore.junit.util.ResourceLeakageDetector;
 
diff --git a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
index 8997ae9..afc7dff 100644
--- a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
@@ -31,7 +31,8 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;